]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
repair: reconstruct sparse inode records correctly on disk
authorBrian Foster <bfoster@redhat.com>
Thu, 30 Jul 2015 23:18:22 +0000 (09:18 +1000)
committerDave Chinner <david@fromorbit.com>
Thu, 30 Jul 2015 23:18:22 +0000 (09:18 +1000)
Phase 5 traverses all of the in-core inode records and regenerates the
inode btrees a record at a time. The record insertion code doesn't
account for sparse inodes which means the ir_holemask and ir_count
fields are not set on-disk and ir_freecount is set with an invalid value
for sparse inode records.

Update build_ino_tree() to handle sparse inode records correctly. We
must account real, allocated inodes only into the ir_freecount field.
The 64-bit in-core sparse inode bitmask must be converted to compressed
16-bit ir_holemask format. Finally, the ir_count field must set to the
total (non-sparse) inode count of the record.

If the fs does not support sparse inodes, both the ir_holemask and
ir_count field are initialized to zero to preserve backwards
compatibility. These bytes historically landed in the high order bytes
of ir_freecount and must be 0 to be interpreted correctly by older XFS
implementations without sparse inode support.

Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
repair/phase5.c

index 30f2d05fa3b16af1f7a947f5e384630c276bba08..0601810a81aaeb48bd14c46cce477cc45c215333 100644 (file)
@@ -1158,8 +1158,12 @@ build_ino_tree(xfs_mount_t *mp, xfs_agnumber_t agno,
        xfs_agino_t             count = 0;
        xfs_agino_t             freecount = 0;
        int                     inocnt;
+       uint8_t                 finocnt;
        int                     k;
        int                     level = btree_curs->num_levels;
+       int                     spmask;
+       uint64_t                sparse;
+       uint16_t                holemask;
 
        for (i = 0; i < level; i++)  {
                lptr = &btree_curs->level[i];
@@ -1243,19 +1247,46 @@ build_ino_tree(xfs_mount_t *mp, xfs_agnumber_t agno,
                                        cpu_to_be32(ino_rec->ino_startnum);
                        bt_rec[j].ir_free = cpu_to_be64(ino_rec->ir_free);
 
-                       inocnt = 0;
+                       inocnt = finocnt = 0;
                        for (k = 0; k < sizeof(xfs_inofree_t)*NBBY; k++)  {
                                ASSERT(is_inode_confirmed(ino_rec, k));
-                               inocnt += is_inode_free(ino_rec, k);
+
+                               if (is_inode_sparse(ino_rec, k))
+                                       continue;
+                               if (is_inode_free(ino_rec, k))
+                                       finocnt++;
+                               inocnt++;
                        }
 
-                       if (xfs_sb_version_hassparseinodes(&mp->m_sb))
-                               bt_rec[j].ir_u.sp.ir_freecount = inocnt;
-                       else
+                       if (!xfs_sb_version_hassparseinodes(&mp->m_sb)) {
                                bt_rec[j].ir_u.f.ir_freecount =
-                                                       cpu_to_be32(inocnt);
-                       freecount += inocnt;
-                       count += XFS_INODES_PER_CHUNK;
+                                                       cpu_to_be32(finocnt);
+                               goto nextrec;
+                       }
+
+                       /*
+                        * Convert the 64-bit in-core sparse inode state to the
+                        * 16-bit on-disk holemask.
+                        */
+                       holemask = 0;
+                       spmask = (1 << XFS_INODES_PER_HOLEMASK_BIT) - 1;
+                       sparse = ino_rec->ir_sparse;
+                       for (k = 0; k < XFS_INOBT_HOLEMASK_BITS; k++) {
+                               if (sparse & spmask) {
+                                       ASSERT((sparse & spmask) == spmask);
+                                       holemask |= (1 << k);
+                               } else
+                                       ASSERT((sparse & spmask) == 0);
+                               sparse >>= XFS_INODES_PER_HOLEMASK_BIT;
+                       }
+
+                       bt_rec[j].ir_u.sp.ir_freecount = finocnt;
+                       bt_rec[j].ir_u.sp.ir_count = inocnt;
+                       bt_rec[j].ir_u.sp.ir_holemask = cpu_to_be16(holemask);
+
+nextrec:
+                       freecount += finocnt;
+                       count += inocnt;
 
                        if (finobt)
                                ino_rec = next_free_ino_rec(ino_rec);