xfs: include reservations in quota reporting
[linux-2.6.git] / fs / xfs / xfs_ialloc.c
index 52c9d00..dad1a31 100644 (file)
 #include "xfs_trans.h"
 #include "xfs_sb.h"
 #include "xfs_ag.h"
-#include "xfs_dir2.h"
-#include "xfs_dmapi.h"
 #include "xfs_mount.h"
 #include "xfs_bmap_btree.h"
 #include "xfs_alloc_btree.h"
 #include "xfs_ialloc_btree.h"
-#include "xfs_dir2_sf.h"
-#include "xfs_attr_sf.h"
 #include "xfs_dinode.h"
 #include "xfs_inode.h"
 #include "xfs_btree.h"
@@ -154,7 +150,7 @@ xfs_check_agi_freecount(
 /*
  * Initialise a new set of inodes.
  */
-STATIC void
+STATIC int
 xfs_ialloc_inode_init(
        struct xfs_mount        *mp,
        struct xfs_trans        *tp,
@@ -205,10 +201,9 @@ xfs_ialloc_inode_init(
                d = XFS_AGB_TO_DADDR(mp, agno, agbno + (j * blks_per_cluster));
                fbuf = xfs_trans_get_buf(tp, mp->m_ddev_targp, d,
                                         mp->m_bsize * blks_per_cluster,
-                                        XFS_BUF_LOCK);
-               ASSERT(fbuf);
-               ASSERT(!XFS_BUF_GETERROR(fbuf));
-
+                                        XBF_LOCK);
+               if (!fbuf)
+                       return ENOMEM;
                /*
                 * Initialize all inodes in this buffer and then log them.
                 *
@@ -216,7 +211,7 @@ xfs_ialloc_inode_init(
                 *      to log a whole cluster of inodes instead of all the
                 *      individual transactions causing a lot of log traffic.
                 */
-               xfs_biozero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog);
+               xfs_buf_zero(fbuf, 0, ninodes << mp->m_sb.sb_inodelog);
                for (i = 0; i < ninodes; i++) {
                        int     ioffset = i << mp->m_sb.sb_inodelog;
                        uint    isize = sizeof(struct xfs_dinode);
@@ -230,6 +225,7 @@ xfs_ialloc_inode_init(
                }
                xfs_trans_inode_alloc_buf(tp, fbuf);
        }
+       return 0;
 }
 
 /*
@@ -374,9 +370,11 @@ xfs_ialloc_ag_alloc(
         * rather than a linear progression to prevent the next generation
         * number from being easily guessable.
         */
-       xfs_ialloc_inode_init(args.mp, tp, agno, args.agbno, args.len,
-                             random32());
+       error = xfs_ialloc_inode_init(args.mp, tp, agno, args.agbno,
+                       args.len, random32());
 
+       if (error)
+               return error;
        /*
         * Convert the results.
         */
@@ -449,7 +447,7 @@ STATIC xfs_buf_t *                  /* allocation group buffer */
 xfs_ialloc_ag_select(
        xfs_trans_t     *tp,            /* transaction pointer */
        xfs_ino_t       parent,         /* parent directory inode number */
-       mode_t          mode,           /* bits set to indicate file type */
+       umode_t         mode,           /* bits set to indicate file type */
        int             okalloc)        /* ok to allocate more space */
 {
        xfs_buf_t       *agbp;          /* allocation group header buffer */
@@ -642,7 +640,7 @@ int
 xfs_dialloc(
        xfs_trans_t     *tp,            /* transaction pointer */
        xfs_ino_t       parent,         /* parent inode (directory) */
-       mode_t          mode,           /* mode bits for new inode */
+       umode_t         mode,           /* mode bits for new inode */
        int             okalloc,        /* ok to allocate more space */
        xfs_buf_t       **IO_agbp,      /* in/out ag header's buffer */
        boolean_t       *alloc_done,    /* true if we needed to replenish
@@ -687,7 +685,7 @@ xfs_dialloc(
                        return 0;
                }
                agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC);
+               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
        } else {
                /*
                 * Continue where we left off before.  In this case, we
@@ -695,7 +693,7 @@ xfs_dialloc(
                 */
                agbp = *IO_agbp;
                agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC);
+               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
                ASSERT(be32_to_cpu(agi->agi_freecount) > 0);
        }
        mp = tp->t_mountp;
@@ -779,7 +777,7 @@ nextag:
                if (error)
                        goto nextag;
                agi = XFS_BUF_TO_AGI(agbp);
-               ASSERT(be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC);
+               ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
        }
        /*
         * Here with an allocation group that has a free inode.
@@ -948,7 +946,7 @@ nextag:
         * See if the most recently allocated block has any free.
         */
 newino:
-       if (be32_to_cpu(agi->agi_newino) != NULLAGINO) {
+       if (agi->agi_newino != cpu_to_be32(NULLAGINO)) {
                error = xfs_inobt_lookup(cur, be32_to_cpu(agi->agi_newino),
                                         XFS_LOOKUP_EQ, &i);
                if (error)
@@ -1059,28 +1057,23 @@ xfs_difree(
         */
        agno = XFS_INO_TO_AGNO(mp, inode);
        if (agno >= mp->m_sb.sb_agcount)  {
-               cmn_err(CE_WARN,
-                       "xfs_difree: agno >= mp->m_sb.sb_agcount (%d >= %d) on %s.  Returning EINVAL.",
-                       agno, mp->m_sb.sb_agcount, mp->m_fsname);
+               xfs_warn(mp, "%s: agno >= mp->m_sb.sb_agcount (%d >= %d).",
+                       __func__, agno, mp->m_sb.sb_agcount);
                ASSERT(0);
                return XFS_ERROR(EINVAL);
        }
        agino = XFS_INO_TO_AGINO(mp, inode);
        if (inode != XFS_AGINO_TO_INO(mp, agno, agino))  {
-               cmn_err(CE_WARN,
-                       "xfs_difree: inode != XFS_AGINO_TO_INO() "
-                       "(%llu != %llu) on %s.  Returning EINVAL.",
-                       (unsigned long long)inode,
-                       (unsigned long long)XFS_AGINO_TO_INO(mp, agno, agino),
-                       mp->m_fsname);
+               xfs_warn(mp, "%s: inode != XFS_AGINO_TO_INO() (%llu != %llu).",
+                       __func__, (unsigned long long)inode,
+                       (unsigned long long)XFS_AGINO_TO_INO(mp, agno, agino));
                ASSERT(0);
                return XFS_ERROR(EINVAL);
        }
        agbno = XFS_AGINO_TO_AGBNO(mp, agino);
        if (agbno >= mp->m_sb.sb_agblocks)  {
-               cmn_err(CE_WARN,
-                       "xfs_difree: agbno >= mp->m_sb.sb_agblocks (%d >= %d) on %s.  Returning EINVAL.",
-                       agbno, mp->m_sb.sb_agblocks, mp->m_fsname);
+               xfs_warn(mp, "%s: agbno >= mp->m_sb.sb_agblocks (%d >= %d).",
+                       __func__, agbno, mp->m_sb.sb_agblocks);
                ASSERT(0);
                return XFS_ERROR(EINVAL);
        }
@@ -1089,13 +1082,12 @@ xfs_difree(
         */
        error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
        if (error) {
-               cmn_err(CE_WARN,
-                       "xfs_difree: xfs_ialloc_read_agi() returned an error %d on %s.  Returning error.",
-                       error, mp->m_fsname);
+               xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.",
+                       __func__, error);
                return error;
        }
        agi = XFS_BUF_TO_AGI(agbp);
-       ASSERT(be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC);
+       ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
        ASSERT(agbno < be32_to_cpu(agi->agi_length));
        /*
         * Initialize the cursor.
@@ -1110,17 +1102,15 @@ xfs_difree(
         * Look for the entry describing this inode.
         */
        if ((error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i))) {
-               cmn_err(CE_WARN,
-                       "xfs_difree: xfs_inobt_lookup returned()  an error %d on %s.  Returning error.",
-                       error, mp->m_fsname);
+               xfs_warn(mp, "%s: xfs_inobt_lookup() returned error %d.",
+                       __func__, error);
                goto error0;
        }
        XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
        error = xfs_inobt_get_rec(cur, &rec, &i);
        if (error) {
-               cmn_err(CE_WARN,
-                       "xfs_difree: xfs_inobt_get_rec()  returned an error %d on %s.  Returning error.",
-                       error, mp->m_fsname);
+               xfs_warn(mp, "%s: xfs_inobt_get_rec() returned error %d.",
+                       __func__, error);
                goto error0;
        }
        XFS_WANT_CORRUPTED_GOTO(i == 1, error0);
@@ -1161,8 +1151,8 @@ xfs_difree(
                xfs_trans_mod_sb(tp, XFS_TRANS_SB_IFREE, -(ilen - 1));
 
                if ((error = xfs_btree_delete(cur, &i))) {
-                       cmn_err(CE_WARN, "xfs_difree: xfs_btree_delete returned an error %d on %s.\n",
-                               error, mp->m_fsname);
+                       xfs_warn(mp, "%s: xfs_btree_delete returned error %d.",
+                               __func__, error);
                        goto error0;
                }
 
@@ -1174,9 +1164,8 @@ xfs_difree(
 
                error = xfs_inobt_update(cur, &rec);
                if (error) {
-                       cmn_err(CE_WARN,
-       "xfs_difree: xfs_inobt_update returned an error %d on %s.",
-                               error, mp->m_fsname);
+                       xfs_warn(mp, "%s: xfs_inobt_update returned error %d.",
+                               __func__, error);
                        goto error0;
                }
 
@@ -1203,6 +1192,66 @@ error0:
        return error;
 }
 
+STATIC int
+xfs_imap_lookup(
+       struct xfs_mount        *mp,
+       struct xfs_trans        *tp,
+       xfs_agnumber_t          agno,
+       xfs_agino_t             agino,
+       xfs_agblock_t           agbno,
+       xfs_agblock_t           *chunk_agbno,
+       xfs_agblock_t           *offset_agbno,
+       int                     flags)
+{
+       struct xfs_inobt_rec_incore rec;
+       struct xfs_btree_cur    *cur;
+       struct xfs_buf          *agbp;
+       int                     error;
+       int                     i;
+
+       error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
+       if (error) {
+               xfs_alert(mp,
+                       "%s: xfs_ialloc_read_agi() returned error %d, agno %d",
+                       __func__, error, agno);
+               return error;
+       }
+
+       /*
+        * Lookup the inode record for the given agino. If the record cannot be
+        * found, then it's an invalid inode number and we should abort. Once
+        * we have a record, we need to ensure it contains the inode number
+        * we are looking up.
+        */
+       cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
+       error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i);
+       if (!error) {
+               if (i)
+                       error = xfs_inobt_get_rec(cur, &rec, &i);
+               if (!error && i == 0)
+                       error = EINVAL;
+       }
+
+       xfs_trans_brelse(tp, agbp);
+       xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+       if (error)
+               return error;
+
+       /* check that the returned record contains the required inode */
+       if (rec.ir_startino > agino ||
+           rec.ir_startino + XFS_IALLOC_INODES(mp) <= agino)
+               return EINVAL;
+
+       /* for untrusted inodes check it is allocated first */
+       if ((flags & XFS_IGET_UNTRUSTED) &&
+           (rec.ir_free & XFS_INOBT_MASK(agino - rec.ir_startino)))
+               return EINVAL;
+
+       *chunk_agbno = XFS_AGINO_TO_AGBNO(mp, rec.ir_startino);
+       *offset_agbno = agbno - *chunk_agbno;
+       return 0;
+}
+
 /*
  * Return the location of the inode in imap, for mapping it into a buffer.
  */
@@ -1235,34 +1284,51 @@ xfs_imap(
        if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||
            ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
 #ifdef DEBUG
-               /* no diagnostics for bulkstat, ino comes from userspace */
-               if (flags & XFS_IGET_BULKSTAT)
+               /*
+                * Don't output diagnostic information for untrusted inodes
+                * as they can be invalid without implying corruption.
+                */
+               if (flags & XFS_IGET_UNTRUSTED)
                        return XFS_ERROR(EINVAL);
                if (agno >= mp->m_sb.sb_agcount) {
-                       xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_imap: agno (%d) >= "
-                                       "mp->m_sb.sb_agcount (%d)",
-                                       agno,  mp->m_sb.sb_agcount);
+                       xfs_alert(mp,
+                               "%s: agno (%d) >= mp->m_sb.sb_agcount (%d)",
+                               __func__, agno, mp->m_sb.sb_agcount);
                }
                if (agbno >= mp->m_sb.sb_agblocks) {
-                       xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_imap: agbno (0x%llx) >= "
-                                       "mp->m_sb.sb_agblocks (0x%lx)",
-                                       (unsigned long long) agbno,
-                                       (unsigned long) mp->m_sb.sb_agblocks);
+                       xfs_alert(mp,
+               "%s: agbno (0x%llx) >= mp->m_sb.sb_agblocks (0x%lx)",
+                               __func__, (unsigned long long)agbno,
+                               (unsigned long)mp->m_sb.sb_agblocks);
                }
                if (ino != XFS_AGINO_TO_INO(mp, agno, agino)) {
-                       xfs_fs_cmn_err(CE_ALERT, mp,
-                                       "xfs_imap: ino (0x%llx) != "
-                                       "XFS_AGINO_TO_INO(mp, agno, agino) "
-                                       "(0x%llx)",
-                                       ino, XFS_AGINO_TO_INO(mp, agno, agino));
+                       xfs_alert(mp,
+               "%s: ino (0x%llx) != XFS_AGINO_TO_INO() (0x%llx)",
+                               __func__, ino,
+                               XFS_AGINO_TO_INO(mp, agno, agino));
                }
                xfs_stack_trace();
 #endif /* DEBUG */
                return XFS_ERROR(EINVAL);
        }
 
+       blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;
+
+       /*
+        * For bulkstat and handle lookups, we have an untrusted inode number
+        * that we have to verify is valid. We cannot do this just by reading
+        * the inode buffer as it may have been unlinked and removed leaving
+        * inodes in stale state on disk. Hence we have to do a btree lookup
+        * in all cases where an untrusted inode number is passed.
+        */
+       if (flags & XFS_IGET_UNTRUSTED) {
+               error = xfs_imap_lookup(mp, tp, agno, agino, agbno,
+                                       &chunk_agbno, &offset_agbno, flags);
+               if (error)
+                       return error;
+               goto out_map;
+       }
+
        /*
         * If the inode cluster size is the same as the blocksize or
         * smaller we get to the buffer by simple arithmetics.
@@ -1277,24 +1343,6 @@ xfs_imap(
                return 0;
        }
 
-       blks_per_cluster = XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_blocklog;
-
-       /*
-        * If we get a block number passed from bulkstat we can use it to
-        * find the buffer easily.
-        */
-       if (imap->im_blkno) {
-               offset = XFS_INO_TO_OFFSET(mp, ino);
-               ASSERT(offset < mp->m_sb.sb_inopblock);
-
-               cluster_agbno = xfs_daddr_to_agbno(mp, imap->im_blkno);
-               offset += (agbno - cluster_agbno) * mp->m_sb.sb_inopblock;
-
-               imap->im_len = XFS_FSB_TO_BB(mp, blks_per_cluster);
-               imap->im_boffset = (ushort)(offset << mp->m_sb.sb_inodelog);
-               return 0;
-       }
-
        /*
         * If the inode chunks are aligned then use simple maths to
         * find the location. Otherwise we have to do a btree
@@ -1304,50 +1352,13 @@ xfs_imap(
                offset_agbno = agbno & mp->m_inoalign_mask;
                chunk_agbno = agbno - offset_agbno;
        } else {
-               xfs_btree_cur_t *cur;   /* inode btree cursor */
-               xfs_inobt_rec_incore_t chunk_rec;
-               xfs_buf_t       *agbp;  /* agi buffer */
-               int             i;      /* temp state */
-
-               error = xfs_ialloc_read_agi(mp, tp, agno, &agbp);
-               if (error) {
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
-                                       "xfs_ialloc_read_agi() returned "
-                                       "error %d, agno %d",
-                                       error, agno);
-                       return error;
-               }
-
-               cur = xfs_inobt_init_cursor(mp, tp, agbp, agno);
-               error = xfs_inobt_lookup(cur, agino, XFS_LOOKUP_LE, &i);
-               if (error) {
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
-                                       "xfs_inobt_lookup() failed");
-                       goto error0;
-               }
-
-               error = xfs_inobt_get_rec(cur, &chunk_rec, &i);
-               if (error) {
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
-                                       "xfs_inobt_get_rec() failed");
-                       goto error0;
-               }
-               if (i == 0) {
-#ifdef DEBUG
-                       xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
-                                       "xfs_inobt_get_rec() failed");
-#endif /* DEBUG */
-                       error = XFS_ERROR(EINVAL);
-               }
- error0:
-               xfs_trans_brelse(tp, agbp);
-               xfs_btree_del_cursor(cur, XFS_BTREE_NOERROR);
+               error = xfs_imap_lookup(mp, tp, agno, agino, agbno,
+                                       &chunk_agbno, &offset_agbno, flags);
                if (error)
                        return error;
-               chunk_agbno = XFS_AGINO_TO_AGBNO(mp, chunk_rec.ir_startino);
-               offset_agbno = agbno - chunk_agbno;
        }
 
+out_map:
        ASSERT(agbno >= chunk_agbno);
        cluster_agbno = chunk_agbno +
                ((offset_agbno / blks_per_cluster) * blks_per_cluster);
@@ -1366,10 +1377,9 @@ xfs_imap(
         */
        if ((imap->im_blkno + imap->im_len) >
            XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks)) {
-               xfs_fs_cmn_err(CE_ALERT, mp, "xfs_imap: "
-                       "(imap->im_blkno (0x%llx) + imap->im_len (0x%llx)) > "
-                       " XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks) (0x%llx)",
-                       (unsigned long long) imap->im_blkno,
+               xfs_alert(mp,
+       "%s: (im_blkno (0x%llx) + im_len (0x%llx)) > sb_dblocks (0x%llx)",
+                       __func__, (unsigned long long) imap->im_blkno,
                        (unsigned long long) imap->im_len,
                        XFS_FSB_TO_BB(mp, mp->m_sb.sb_dblocks));
                return XFS_ERROR(EINVAL);
@@ -1430,7 +1440,7 @@ xfs_ialloc_log_agi(
        xfs_agi_t               *agi;   /* allocation group header */
 
        agi = XFS_BUF_TO_AGI(bp);
-       ASSERT(be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC);
+       ASSERT(agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC));
 #endif
        /*
         * Compute byte offsets for the first and last fields.
@@ -1478,13 +1488,13 @@ xfs_read_agi(
        if (error)
                return error;
 
-       ASSERT(*bpp && !XFS_BUF_GETERROR(*bpp));
+       ASSERT(!xfs_buf_geterror(*bpp));
        agi = XFS_BUF_TO_AGI(*bpp);
 
        /*
         * Validate the magic number of the agi block.
         */
-       agi_ok = be32_to_cpu(agi->agi_magicnum) == XFS_AGI_MAGIC &&
+       agi_ok = agi->agi_magicnum == cpu_to_be32(XFS_AGI_MAGIC) &&
                XFS_AGI_GOOD_VERSION(be32_to_cpu(agi->agi_versionnum)) &&
                be32_to_cpu(agi->agi_seqno) == agno;
        if (unlikely(XFS_TEST_ERROR(!agi_ok, mp, XFS_ERRTAG_IALLOC_READ_AGI,
@@ -1495,7 +1505,7 @@ xfs_read_agi(
                return XFS_ERROR(EFSCORRUPTED);
        }
 
-       XFS_BUF_SET_VTYPE_REF(*bpp, B_FS_AGI, XFS_AGI_REF);
+       xfs_buf_set_ref(*bpp, XFS_AGI_REF);
 
        xfs_check_agi_unlinked(agi);
        return 0;