]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commitdiff
xfs_repair: implement custom ifork verifiers
authorDarrick J. Wong <darrick.wong@oracle.com>
Tue, 27 Mar 2018 02:27:28 +0000 (21:27 -0500)
committerEric Sandeen <sandeen@redhat.com>
Tue, 27 Mar 2018 02:27:28 +0000 (21:27 -0500)
There are a few cases where an early stage of xfs_repair will write an
invalid inode fork buffer to signal to a later stage that it needs to
correct the value.  This happens in phase 4 when we detect an inline
format directory with an invalid .. pointer.  To avoid triggering the
ifork verifiers on this, inject a custom verifier for phase 6 that lets
this pass for now.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
libxfs/libxfs_api_defs.h
repair/phase6.c

index 5d56340c06b9954fd41831160aa60b38c526efdf..78daca05bd725ef9dd37338f5753341b3370eae6 100644 (file)
 #define xfs_rmap_lookup_le_range       libxfs_rmap_lookup_le_range
 #define xfs_refc_block                 libxfs_refc_block
 #define xfs_rmap_compare               libxfs_rmap_compare
+#define xfs_dir_get_ops                        libxfs_dir_get_ops
+#define xfs_default_ifork_ops          libxfs_default_ifork_ops
 
 #endif /* __LIBXFS_API_DEFS_H__ */
index aff83bc6e80f380d8d9c378faa8ce53e866c1987..ed005e8cc69969093c71a8a8dd3d82a828781ce2 100644 (file)
@@ -38,6 +38,61 @@ static struct xfs_name               xfs_name_dot = {(unsigned char *)".",
                                                1,
                                                XFS_DIR3_FT_DIR};
 
+/*
+ * When we're checking directory inodes, we're allowed to set a directory's
+ * dotdot entry to zero to signal that the parent needs to be reconnected
+ * during phase 6.  If we're handling a shortform directory the ifork
+ * verifiers will fail, so temporarily patch out this canary so that we can
+ * verify the rest of the fork and move on to fixing the dir.
+ */
+static xfs_failaddr_t
+phase6_verify_dir(
+       struct xfs_inode                *ip)
+{
+       struct xfs_mount                *mp = ip->i_mount;
+       const struct xfs_dir_ops        *dops;
+       struct xfs_ifork                *ifp;
+       struct xfs_dir2_sf_hdr          *sfp;
+       xfs_failaddr_t                  fa;
+       xfs_ino_t                       old_parent;
+       bool                            parent_bypass = false;
+       int                             size;
+
+       dops = libxfs_dir_get_ops(mp, NULL);
+
+       ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+       sfp = (struct xfs_dir2_sf_hdr *)ifp->if_u1.if_data;
+       size = ifp->if_bytes;
+
+       /*
+        * If this is a shortform directory, phase4 may have set the parent
+        * inode to zero to indicate that it must be fixed.  Temporarily
+        * set a valid parent so that the directory verifier will pass.
+        */
+       if (size > offsetof(struct xfs_dir2_sf_hdr, parent) &&
+           size >= xfs_dir2_sf_hdr_size(sfp->i8count)) {
+               old_parent = dops->sf_get_parent_ino(sfp);
+               if (old_parent == 0) {
+                       dops->sf_put_parent_ino(sfp, mp->m_sb.sb_rootino);
+                       parent_bypass = true;
+               }
+       }
+
+       fa = libxfs_default_ifork_ops.verify_dir(ip);
+
+       /* Put it back. */
+       if (parent_bypass)
+               dops->sf_put_parent_ino(sfp, old_parent);
+
+       return fa;
+}
+
+static struct xfs_ifork_ops phase6_ifork_ops = {
+       .verify_attr    = xfs_attr_shortform_verify,
+       .verify_dir     = phase6_verify_dir,
+       .verify_symlink = xfs_symlink_shortform_verify,
+};
+
 /*
  * Data structures used to keep track of directories where the ".."
  * entries are updated. These must be rebuilt after the initial pass
@@ -2833,7 +2888,7 @@ process_dir_inode(
 
        ASSERT(!is_inode_refchecked(irec, ino_offset) || dotdot_update);
 
-       error = -libxfs_iget(mp, NULL, ino, 0, &ip, NULL);
+       error = -libxfs_iget(mp, NULL, ino, 0, &ip, &phase6_ifork_ops);
        if (error) {
                if (!no_modify)
                        do_error(