]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blobdiff - repair/phase6.c
xfs_repair: detect and fix padding fields that changed with nrext64
[thirdparty/xfsprogs-dev.git] / repair / phase6.c
index 6bddfefa3cb286f0f3ab075cbf0debf30765f075..c04b2e0999c0a181f93b6b63a6a7dcb5453b6fef 100644 (file)
@@ -237,6 +237,21 @@ dir_hash_add(
        return !dup;
 }
 
+/* Mark an existing directory hashtable entry as junk. */
+static void
+dir_hash_junkit(
+       struct dir_hash_tab     *hashtab,
+       xfs_dir2_dataptr_t      addr)
+{
+       struct dir_hash_ent     *p;
+
+       p = radix_tree_lookup(&hashtab->byaddr, addr);
+       assert(p != NULL);
+
+       p->junkit = 1;
+       p->namebuf[0] = '/';
+}
+
 static int
 dir_hash_check(
        struct dir_hash_tab     *hashtab,
@@ -413,11 +428,10 @@ bmap_next_offset(
                return EIO;
        }
 
-       if (!(ip->i_df.if_flags & XFS_IFEXTENTS)) {
-               error = -libxfs_iread_extents(NULL, ip, XFS_DATA_FORK);
-               if (error)
-                       return error;
-       }
+        /* Read extent map. */
+       error = -libxfs_iread_extents(NULL, ip, XFS_DATA_FORK);
+       if (error)
+               return error;
 
        bno = *bnop + 1;
        if (!libxfs_iext_lookup_extent(ip, &ip->i_df, bno, &icur, &got))
@@ -437,6 +451,22 @@ res_failed(
                do_error(_("xfs_trans_reserve returned %d\n"), err);
 }
 
+static inline void
+reset_inode_fields(struct xfs_inode *ip)
+{
+       ip->i_projid = 0;
+       ip->i_disk_size = 0;
+       ip->i_nblocks = 0;
+       ip->i_extsize = 0;
+       ip->i_cowextsize = 0;
+       ip->i_flushiter = 0;
+       ip->i_forkoff = 0;
+       ip->i_diflags = 0;
+       ip->i_diflags2 = 0;
+       ip->i_crtime.tv_sec = 0;
+       ip->i_crtime.tv_nsec = 0;
+}
+
 static void
 mk_rbmino(xfs_mount_t *mp)
 {
@@ -465,7 +495,7 @@ mk_rbmino(xfs_mount_t *mp)
                        error);
        }
 
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = S_IFREG;
        ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
@@ -475,9 +505,9 @@ mk_rbmino(xfs_mount_t *mp)
        set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (xfs_sb_version_has_v3inode(&mp->m_sb)) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -485,11 +515,10 @@ mk_rbmino(xfs_mount_t *mp)
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
-       ip->i_d.di_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
+       ip->i_disk_size = mp->m_sb.sb_rbmblocks * mp->m_sb.sb_blocksize;
 
        /*
         * commit changes
@@ -706,7 +735,7 @@ mk_rsumino(xfs_mount_t *mp)
                        error);
        }
 
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = S_IFREG;
        ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
@@ -716,9 +745,9 @@ mk_rsumino(xfs_mount_t *mp)
        set_nlink(VFS_I(ip), 1);        /* account for sb ptr */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (xfs_sb_version_has_v3inode(&mp->m_sb)) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -726,11 +755,10 @@ mk_rsumino(xfs_mount_t *mp)
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
-       ip->i_d.di_size = mp->m_rsumsize;
+       ip->i_disk_size = mp->m_rsumsize;
 
        /*
         * commit changes
@@ -806,7 +834,7 @@ mk_root_dir(xfs_mount_t *mp)
        /*
         * take care of the core -- initialization from xfs_ialloc()
         */
-       memset(&ip->i_d, 0, sizeof(ip->i_d));
+       reset_inode_fields(ip);
 
        VFS_I(ip)->i_mode = mode|S_IFDIR;
        ip->i_df.if_format = XFS_DINODE_FMT_EXTENTS;
@@ -816,9 +844,9 @@ mk_root_dir(xfs_mount_t *mp)
        set_nlink(VFS_I(ip), 2);        /* account for . and .. */
 
        times = XFS_ICHGTIME_CHG | XFS_ICHGTIME_MOD;
-       if (xfs_sb_version_has_v3inode(&mp->m_sb)) {
+       if (xfs_has_v3inodes(mp)) {
                VFS_I(ip)->i_version = 1;
-               ip->i_d.di_flags2 = 0;
+               ip->i_diflags2 = 0;
                times |= XFS_ICHGTIME_CREATE;
        }
        libxfs_trans_ichgtime(tp, ip, times);
@@ -828,7 +856,6 @@ mk_root_dir(xfs_mount_t *mp)
        /*
         * now the ifork
         */
-       ip->i_df.if_flags = XFS_IFEXTENTS;
        ip->i_df.if_bytes = 0;
        ip->i_df.if_u1.if_root = NULL;
 
@@ -1040,9 +1067,7 @@ mv_orphanage(
                        err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_rename,
                                                  nres, 0, 0, &tp);
                        if (err)
-                               do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                                       err);
+                               res_failed(err);
 
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1051,8 +1076,7 @@ mv_orphanage(
                                                ino, nres);
                        if (err)
                                do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                                       ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
 
                        if (irec)
                                add_inode_ref(irec, ino_offset);
@@ -1064,8 +1088,7 @@ mv_orphanage(
                                        orphanage_ino, nres);
                        if (err)
                                do_error(
-       _("creation of .. entry failed (%d), filesystem may be out of space\n"),
-                                       err);
+       _("creation of .. entry failed (%d)\n"), err);
 
                        inc_nlink(VFS_I(ino_p));
                        libxfs_trans_log_inode(tp, ino_p, XFS_ILOG_CORE);
@@ -1077,9 +1100,7 @@ mv_orphanage(
                        err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_rename,
                                                  nres, 0, 0, &tp);
                        if (err)
-                               do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                                       err);
+                               res_failed(err);
 
                        libxfs_trans_ijoin(tp, orphanage_ip, 0);
                        libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1089,8 +1110,7 @@ mv_orphanage(
                                                ino, nres);
                        if (err)
                                do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                                       ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
 
                        if (irec)
                                add_inode_ref(irec, ino_offset);
@@ -1108,8 +1128,7 @@ mv_orphanage(
                                                nres);
                                if (err)
                                        do_error(
-       _("name replace op failed (%d), filesystem may be out of space\n"),
-                                               err);
+       _("name replace op failed (%d)\n"), err);
                        }
 
                        err = -libxfs_trans_commit(tp);
@@ -1129,9 +1148,7 @@ mv_orphanage(
                err = -libxfs_trans_alloc(mp, &M_RES(mp)->tr_remove,
                                          nres, 0, 0, &tp);
                if (err)
-                       do_error(
-       _("space reservation failed (%d), filesystem may be out of space\n"),
-                               err);
+                       res_failed(err);
 
                libxfs_trans_ijoin(tp, orphanage_ip, 0);
                libxfs_trans_ijoin(tp, ino_p, 0);
@@ -1140,8 +1157,7 @@ mv_orphanage(
                                                nres);
                if (err)
                        do_error(
-       _("name create failed in %s (%d), filesystem may be out of space\n"),
-                               ORPHANAGE, err);
+       _("name create failed in %s (%d)\n"), ORPHANAGE, err);
                ASSERT(err == 0);
 
                set_nlink(VFS_I(ino_p), 1);
@@ -1324,8 +1340,7 @@ longform_dir2_rebuild(
                                                nres);
                if (error) {
                        do_warn(
-_("name create failed in ino %" PRIu64 " (%d), filesystem may be out of space\n"),
-                               ino, error);
+_("name create failed in ino %" PRIu64 " (%d)\n"), ino, error);
                        goto out_bmap_cancel;
                }
 
@@ -1385,6 +1400,48 @@ dir2_kill_block(
 _("directory shrink failed (%d)\n"), error);
 }
 
+static inline void
+check_longform_ftype(
+       struct xfs_mount        *mp,
+       struct xfs_inode        *ip,
+       xfs_dir2_data_entry_t   *dep,
+       ino_tree_node_t         *irec,
+       int                     ino_offset,
+       struct dir_hash_tab     *hashtab,
+       xfs_dir2_dataptr_t      addr,
+       struct xfs_da_args      *da,
+       struct xfs_buf          *bp)
+{
+       xfs_ino_t               inum = be64_to_cpu(dep->inumber);
+       uint8_t                 dir_ftype;
+       uint8_t                 ino_ftype;
+
+       if (!xfs_has_ftype(mp))
+               return;
+
+       dir_ftype = libxfs_dir2_data_get_ftype(mp, dep);
+       ino_ftype = get_inode_ftype(irec, ino_offset);
+
+       if (dir_ftype == ino_ftype)
+               return;
+
+       if (no_modify) {
+               do_warn(
+_("would fix ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
+                       dir_ftype, ino_ftype,
+                       ip->i_ino, inum);
+               return;
+       }
+
+       do_warn(
+_("fixing ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
+               dir_ftype, ino_ftype,
+               ip->i_ino, inum);
+       libxfs_dir2_data_put_ftype(mp, dep, ino_ftype);
+       libxfs_dir2_data_log_entry(da, bp, dep);
+       dir_hash_update_ftype(hashtab, addr, ino_ftype);
+}
+
 /*
  * process a data block, also checks for .. entry
  * and corrects it to match what we think .. should be
@@ -1447,13 +1504,13 @@ longform_dir2_entry_check_data(
                endptr = (char *)blp;
                if (endptr > (char *)btp)
                        endptr = (char *)btp;
-               if (xfs_sb_version_hascrc(&mp->m_sb))
+               if (xfs_has_crc(mp))
                        wantmagic = XFS_DIR3_BLOCK_MAGIC;
                else
                        wantmagic = XFS_DIR2_BLOCK_MAGIC;
        } else {
                endptr = (char *)d + mp->m_dir_geo->blksize;
-               if (xfs_sb_version_hascrc(&mp->m_sb))
+               if (xfs_has_crc(mp))
                        wantmagic = XFS_DIR3_DATA_MAGIC;
                else
                        wantmagic = XFS_DIR2_DATA_MAGIC;
@@ -1717,10 +1774,16 @@ longform_dir2_entry_check_data(
                                if (entry_junked(
        _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is not in the the first block"), fname,
                                                inum, ip->i_ino)) {
+                                       dir_hash_junkit(hashtab, addr);
                                        dep->name[0] = '/';
                                        libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
+
+                       if (!nbad)
+                               check_longform_ftype(mp, ip, dep, irec,
+                                               ino_offset, hashtab, addr, &da,
+                                               bp);
                        continue;
                }
                ASSERT(no_modify || libxfs_verify_dir_ino(mp, inum));
@@ -1744,10 +1807,16 @@ longform_dir2_entry_check_data(
                                if (entry_junked(
        _("entry \"%s\" in dir %" PRIu64 " is not the first entry"),
                                                fname, inum, ip->i_ino)) {
+                                       dir_hash_junkit(hashtab, addr);
                                        dep->name[0] = '/';
                                        libxfs_dir2_data_log_entry(&da, bp, dep);
                                }
                        }
+
+                       if (!nbad)
+                               check_longform_ftype(mp, ip, dep, irec,
+                                               ino_offset, hashtab, addr, &da,
+                                               bp);
                        *need_dot = 0;
                        continue;
                }
@@ -1758,31 +1827,8 @@ longform_dir2_entry_check_data(
                        continue;
 
                /* validate ftype field if supported */
-               if (xfs_sb_version_hasftype(&mp->m_sb)) {
-                       uint8_t dir_ftype;
-                       uint8_t ino_ftype;
-
-                       dir_ftype = libxfs_dir2_data_get_ftype(mp, dep);
-                       ino_ftype = get_inode_ftype(irec, ino_offset);
-
-                       if (dir_ftype != ino_ftype) {
-                               if (no_modify) {
-                                       do_warn(
-       _("would fix ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
-                                               dir_ftype, ino_ftype,
-                                               ip->i_ino, inum);
-                               } else {
-                                       do_warn(
-       _("fixing ftype mismatch (%d/%d) in directory/child inode %" PRIu64 "/%" PRIu64 "\n"),
-                                               dir_ftype, ino_ftype,
-                                               ip->i_ino, inum);
-                                       libxfs_dir2_data_put_ftype(mp, dep, ino_ftype);
-                                       libxfs_dir2_data_log_entry(&da, bp, dep);
-                                       dir_hash_update_ftype(hashtab, addr,
-                                                             ino_ftype);
-                               }
-                       }
-               }
+               check_longform_ftype(mp, ip, dep, irec, ino_offset, hashtab,
+                               addr, &da, bp);
 
                /*
                 * check easy case first, regular inode, just bump
@@ -1832,6 +1878,7 @@ _("entry \"%s\" in dir inode %" PRIu64 " inconsistent with .. value (%" PRIu64 "
                                orphanage_ino = 0;
                        nbad++;
                        if (!no_modify)  {
+                               dir_hash_junkit(hashtab, addr);
                                dep->name[0] = '/';
                                libxfs_dir2_data_log_entry(&da, bp, dep);
                                if (verbose)
@@ -1875,21 +1922,21 @@ __check_dir3_header(
        if (be64_to_cpu(owner) != ino) {
                do_warn(
 _("expected owner inode %" PRIu64 ", got %llu, directory block %" PRIu64 "\n"),
-                       ino, (unsigned long long)be64_to_cpu(owner), bp->b_bn);
+                       ino, (unsigned long long)be64_to_cpu(owner), xfs_buf_daddr(bp));
                return 1;
        }
        /* verify block number */
-       if (be64_to_cpu(blkno) != bp->b_bn) {
+       if (be64_to_cpu(blkno) != xfs_buf_daddr(bp)) {
                do_warn(
 _("expected block %" PRIu64 ", got %llu, directory inode %" PRIu64 "\n"),
-                       bp->b_bn, (unsigned long long)be64_to_cpu(blkno), ino);
+                       xfs_buf_daddr(bp), (unsigned long long)be64_to_cpu(blkno), ino);
                return 1;
        }
        /* verify uuid */
        if (platform_uuid_compare(uuid, &mp->m_sb.sb_meta_uuid) != 0) {
                do_warn(
 _("wrong FS UUID, directory inode %" PRIu64 " block %" PRIu64 "\n"),
-                       ino, bp->b_bn);
+                       ino, xfs_buf_daddr(bp));
                return 1;
        }
 
@@ -2207,14 +2254,14 @@ longform_dir2_entry_check(
        struct xfs_da_args      args;
 
        *need_dot = 1;
-       freetab = malloc(FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
+       freetab = malloc(FREETAB_SIZE(ip->i_disk_size / mp->m_dir_geo->blksize));
        if (!freetab) {
                do_error(_("malloc failed in %s (%" PRId64 " bytes)\n"),
                        __func__,
-                       FREETAB_SIZE(ip->i_d.di_size / mp->m_dir_geo->blksize));
+                       FREETAB_SIZE(ip->i_disk_size / mp->m_dir_geo->blksize));
                exit(1);
        }
-       freetab->naents = ip->i_d.di_size / mp->m_dir_geo->blksize;
+       freetab->naents = ip->i_disk_size / mp->m_dir_geo->blksize;
        freetab->nents = 0;
        for (i = 0; i < freetab->naents; i++) {
                freetab->ents[i].v = NULLDATAOFF;
@@ -2427,7 +2474,7 @@ shortform_dir2_entry_check(
        bytes_deleted = 0;
 
        max_size = ifp->if_bytes;
-       ASSERT(ip->i_d.di_size <= ifp->if_bytes);
+       ASSERT(ip->i_disk_size <= ifp->if_bytes);
 
        /*
         * if just rebuild a directory due to a "..", update and return
@@ -2491,7 +2538,7 @@ shortform_dir2_entry_check(
                        bad_sfnamelen = 1;
 
                        if (i == sfp->count - 1)  {
-                               namelen = ip->i_d.di_size -
+                               namelen = ip->i_disk_size -
                                        ((intptr_t) &sfep->name[0] -
                                         (intptr_t) sfp);
                        } else  {
@@ -2503,11 +2550,11 @@ shortform_dir2_entry_check(
                        }
                } else if (no_modify && (intptr_t) sfep - (intptr_t) sfp +
                                + libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen)
-                               > ip->i_d.di_size)  {
+                               > ip->i_disk_size)  {
                        bad_sfnamelen = 1;
 
                        if (i == sfp->count - 1)  {
-                               namelen = ip->i_d.di_size -
+                               namelen = ip->i_disk_size -
                                        ((intptr_t) &sfep->name[0] -
                                         (intptr_t) sfp);
                        } else  {
@@ -2654,7 +2701,7 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
                }
 
                /* validate ftype field if supported */
-               if (xfs_sb_version_hasftype(&mp->m_sb)) {
+               if (xfs_has_ftype(mp)) {
                        uint8_t dir_ftype;
                        uint8_t ino_ftype;
 
@@ -2725,17 +2772,17 @@ _("entry \"%s\" (ino %" PRIu64 ") in dir %" PRIu64 " is a duplicate name"),
        if (*ino_dirty && bytes_deleted > 0)  {
                ASSERT(!no_modify);
                libxfs_idata_realloc(ip, -bytes_deleted, XFS_DATA_FORK);
-               ip->i_d.di_size -= bytes_deleted;
+               ip->i_disk_size -= bytes_deleted;
        }
 
-       if (ip->i_d.di_size != ip->i_df.if_bytes)  {
+       if (ip->i_disk_size != ip->i_df.if_bytes)  {
                ASSERT(ip->i_df.if_bytes == (xfs_fsize_t)
                                ((intptr_t) next_sfep - (intptr_t) sfp));
-               ip->i_d.di_size = (xfs_fsize_t)
+               ip->i_disk_size = (xfs_fsize_t)
                                ((intptr_t) next_sfep - (intptr_t) sfp);
                do_warn(
        _("setting size to %" PRId64 " bytes to reflect junked entries\n"),
-                       ip->i_d.di_size);
+                       ip->i_disk_size);
                *ino_dirty = 1;
        }
 }
@@ -2809,7 +2856,7 @@ process_dir_inode(
 
        add_inode_refchecked(irec, ino_offset);
 
-       hashtab = dir_hash_init(ip->i_d.di_size);
+       hashtab = dir_hash_init(ip->i_disk_size);
 
        /*
         * look for bogus entries