Blame SOURCES/0007-for-next-xfsrestore-fix-rootdir-due-to-xfsdump-bulkstat-misus.patch

2fc998
From d7cba7410710cd3ec2c2d9fafd4d93437097f473 Mon Sep 17 00:00:00 2001
2fc998
From: Gao Xiang <hsiangkao@redhat.com>
2fc998
Date: Wed, 28 Sep 2022 15:10:52 -0400
2fc998
Subject: [PATCH] xfsrestore: fix rootdir due to xfsdump bulkstat misuse
2fc998
2fc998
If rootino is wrong and misspecified to a subdir inode #,
2fc998
the following assertion could be triggered:
2fc998
  assert(ino != persp->p_rootino || hardh == persp->p_rooth);
2fc998
2fc998
This patch adds a '-x' option (another awkward thing is that
2fc998
the codebase doesn't support long options) to address
2fc998
problamatic images by searching for the real dir, the reason
2fc998
that I don't enable it by default is that I'm not very confident
2fc998
with the xfsrestore codebase and xfsdump bulkstat issue will
2fc998
also be fixed immediately as well, so this function might be
2fc998
optional and only useful for pre-exist corrupted dumps.
2fc998
2fc998
In details, my understanding of the original logic is
2fc998
 1) xfsrestore will create a rootdir node_t (p_rooth);
2fc998
 2) it will build the tree hierarchy from inomap and adopt
2fc998
    the parent if needed (so inodes whose parent ino hasn't
2fc998
    detected will be in the orphan dir, p_orphh);
2fc998
 3) during this period, if ino == rootino and
2fc998
    hardh != persp->p_rooth (IOWs, another node_t with
2fc998
    the same ino # is created), that'd be definitely wrong.
2fc998
2fc998
So the proposal fix is that
2fc998
 - considering the xfsdump root ino # is a subdir inode, it'll
2fc998
   trigger ino == rootino && hardh != persp->p_rooth condition;
2fc998
 - so we log this node_t as persp->p_rooth rather than the
2fc998
   initial rootdir node_t created in 1);
2fc998
 - we also know that this node is actually a subdir, and after
2fc998
   the whole inomap is scanned (IOWs, the tree is built),
2fc998
   the real root dir will have the orphan dir parent p_orphh;
2fc998
 - therefore, we walk up by the parent until some node_t has
2fc998
   the p_orphh, so that's it.
2fc998
2fc998
Cc: Donald Douwsma <ddouwsma@redhat.com>
2fc998
Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
2fc998
Signed-off-by: Hironori Shiina <shiina.hironori@fujitsu.com>
2fc998
Reviwed-by: Eric Sandeen <sandeen@redhat.com>
2fc998
Signed-off-by: Carlos Maiolino <cem@kernel.org>
2fc998
Signed-off-by: Pavel Reichl <preichl@redhat.com>
2fc998
---
2fc998
 common/main.c         |  1 +
2fc998
 man/man8/xfsrestore.8 | 14 +++++++++
2fc998
 restore/content.c     |  7 +++++
2fc998
 restore/getopt.h      |  4 +--
2fc998
 restore/tree.c        | 72 ++++++++++++++++++++++++++++++++++++++++---
2fc998
 restore/tree.h        |  2 ++
2fc998
 6 files changed, 94 insertions(+), 6 deletions(-)
2fc998
2fc998
diff --git a/common/main.c b/common/main.c
2fc998
index 1db07d4..6141ffb 100644
2fc998
--- a/common/main.c
2fc998
+++ b/common/main.c
2fc998
@@ -988,6 +988,7 @@ usage(void)
2fc998
 	ULO(_("(contents only)"),			GETOPT_TOC );
2fc998
 	ULO(_("<verbosity {silent, verbose, trace}>"),	GETOPT_VERBOSITY );
2fc998
 	ULO(_("(use small tree window)"),		GETOPT_SMALLWINDOW );
2fc998
+	ULO(_("(try to fix rootdir due to xfsdump issue)"),GETOPT_FIXROOTDIR);
2fc998
 	ULO(_("(don't restore extended file attributes)"),GETOPT_NOEXTATTR );
2fc998
 	ULO(_("(restore root dir owner/permissions)"),	GETOPT_ROOTPERM );
2fc998
 	ULO(_("(restore DMAPI event settings)"),	GETOPT_SETDM );
2fc998
diff --git a/man/man8/xfsrestore.8 b/man/man8/xfsrestore.8
2fc998
index 60e4309..df7dde0 100644
2fc998
--- a/man/man8/xfsrestore.8
2fc998
+++ b/man/man8/xfsrestore.8
2fc998
@@ -240,6 +240,20 @@ but does not create or modify any files or directories.
2fc998
 It may be desirable to set the verbosity level to \f3silent\f1
2fc998
 when using this option.
2fc998
 .TP 5
2fc998
+.B \-x
2fc998
+This option may be useful to fix an issue which the files are restored
2fc998
+to orphanage directory because of xfsdump (v3.1.7 - v3.1.9) problem.
2fc998
+A normal dump cannot be restored with this option. This option works
2fc998
+only for a corrupted dump.
2fc998
+If a dump is created by problematic xfsdump (v3.1.7 - v3.1.9), you
2fc998
+should see the contents of the dump with \f3\-t\f1 option before
2fc998
+restoring. Then, if a file is placed to the orphanage directory, you need to
2fc998
+use this \f3\-x\f1 option to restore the dump. Otherwise, you can restore
2fc998
+the dump without this option.
2fc998
+
2fc998
+In the cumulative mode, this option is required only for a base (level 0)
2fc998
+dump. You no longer need this option for level 1+ dumps.
2fc998
+.TP 5
2fc998
 \f3\-v\f1 \f2verbosity\f1
2fc998
 .\" set inter-paragraph distance to 0
2fc998
 .PD 0
2fc998
diff --git a/restore/content.c b/restore/content.c
2fc998
index 8bb5fa4..488ae20 100644
2fc998
--- a/restore/content.c
2fc998
+++ b/restore/content.c
2fc998
@@ -861,6 +861,7 @@ static int quotafilecheck(char *type, char *dstdir, char *quotafile);
2fc998
 
2fc998
 bool_t content_media_change_needed;
2fc998
 bool_t restore_rootdir_permissions;
2fc998
+bool_t need_fixrootdir;
2fc998
 char *media_change_alert_program = NULL;
2fc998
 size_t perssz;
2fc998
 
2fc998
@@ -958,6 +959,7 @@ content_init(int argc, char *argv[], size64_t vmsz)
2fc998
 	stsz = 0;
2fc998
 	interpr = BOOL_FALSE;
2fc998
 	restore_rootdir_permissions = BOOL_FALSE;
2fc998
+	need_fixrootdir = BOOL_FALSE;
2fc998
 	optind = 1;
2fc998
 	opterr = 0;
2fc998
 	while ( ( c = getopt( argc, argv, GETOPT_CMDSTRING )) != EOF ) {
2fc998
@@ -1186,6 +1188,9 @@ content_init(int argc, char *argv[], size64_t vmsz)
2fc998
 		case GETOPT_FMT2COMPAT:
2fc998
 			tranp->t_truncategenpr = BOOL_TRUE;
2fc998
 			break;
2fc998
+		case GETOPT_FIXROOTDIR:
2fc998
+			need_fixrootdir = BOOL_TRUE;
2fc998
+			break;
2fc998
 		}
2fc998
 	}
2fc998
 
2fc998
@@ -3129,6 +3134,8 @@ applydirdump(drive_t *drivep,
2fc998
 			return rv;
2fc998
 		}
2fc998
 
2fc998
+		if (need_fixrootdir)
2fc998
+			tree_fixroot();
2fc998
 		persp->s.dirdonepr = BOOL_TRUE;
2fc998
 	}
2fc998
 
2fc998
diff --git a/restore/getopt.h b/restore/getopt.h
2fc998
index b5bc004..b0c0c7d 100644
2fc998
--- a/restore/getopt.h
2fc998
+++ b/restore/getopt.h
2fc998
@@ -26,7 +26,7 @@
2fc998
  * purpose is to contain that command string.
2fc998
  */
2fc998
 
2fc998
-#define GETOPT_CMDSTRING	"a:b:c:def:himn:op:qrs:tv:wABCDEFG:H:I:JKL:M:NO:PQRS:TUVWX:Y:"
2fc998
+#define GETOPT_CMDSTRING	"a:b:c:def:himn:op:qrs:tv:wxABCDEFG:H:I:JKL:M:NO:PQRS:TUVWX:Y:"
2fc998
 
2fc998
 #define GETOPT_WORKSPACE	'a'	/* workspace dir (content.c) */
2fc998
 #define GETOPT_BLOCKSIZE        'b'     /* blocksize for rmt */
2fc998
@@ -51,7 +51,7 @@
2fc998
 /*				'u' */
2fc998
 #define	GETOPT_VERBOSITY	'v'	/* verbosity level (0 to 4 ) */
2fc998
 #define	GETOPT_SMALLWINDOW	'w'	/* use a small window for dir entries */
2fc998
-/*				'x' */
2fc998
+#define GETOPT_FIXROOTDIR	'x'	/* try to fix rootdir due to bulkstat misuse */
2fc998
 /*				'y' */
2fc998
 /*				'z' */
2fc998
 #define	GETOPT_NOEXTATTR	'A'	/* do not restore ext. file attr. */
2fc998
diff --git a/restore/tree.c b/restore/tree.c
2fc998
index 5429b74..bfa07fe 100644
2fc998
--- a/restore/tree.c
2fc998
+++ b/restore/tree.c
2fc998
@@ -15,7 +15,6 @@
2fc998
  * along with this program; if not, write the Free Software Foundation,
2fc998
  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
2fc998
  */
2fc998
-
2fc998
 #include <stdio.h>
2fc998
 #include <unistd.h>
2fc998
 #include <stdlib.h>
2fc998
@@ -262,6 +261,7 @@ extern void usage(void);
2fc998
 extern size_t pgsz;
2fc998
 extern size_t pgmask;
2fc998
 extern bool_t restore_rootdir_permissions;
2fc998
+extern bool_t need_fixrootdir;
2fc998
 
2fc998
 /* forward declarations of locally defined static functions ******************/
2fc998
 
2fc998
@@ -328,10 +328,47 @@ static tran_t *tranp = 0;
2fc998
 static char *persname = PERS_NAME;
2fc998
 static char *orphname = ORPH_NAME;
2fc998
 static xfs_ino_t orphino = ORPH_INO;
2fc998
+static nh_t orig_rooth = NH_NULL;
2fc998
 
2fc998
 
2fc998
 /* definition of locally defined global functions ****************************/
2fc998
 
2fc998
+void
2fc998
+tree_fixroot(void)
2fc998
+{
2fc998
+	nh_t		rooth = persp->p_rooth;
2fc998
+	xfs_ino_t 	rootino;
2fc998
+
2fc998
+	while (1) {
2fc998
+		nh_t	parh;
2fc998
+		node_t *rootp = Node_map(rooth);
2fc998
+
2fc998
+		rootino = rootp->n_ino;
2fc998
+		parh = rootp->n_parh;
2fc998
+		Node_unmap(rooth, &rootp);
2fc998
+
2fc998
+		if (parh == rooth ||
2fc998
+		/*
2fc998
+		 * since all new node (including non-parent)
2fc998
+		 * would be adopted into orphh
2fc998
+		 */
2fc998
+		    parh == persp->p_orphh ||
2fc998
+		    parh == NH_NULL)
2fc998
+			break;
2fc998
+		rooth = parh;
2fc998
+	}
2fc998
+
2fc998
+	if (rooth != persp->p_rooth) {
2fc998
+		persp->p_rooth = rooth;
2fc998
+		persp->p_rootino = rootino;
2fc998
+		disown(rooth);
2fc998
+		adopt(persp->p_rooth, persp->p_orphh, NH_NULL);
2fc998
+
2fc998
+		mlog(MLOG_NORMAL, _("fix root # to %llu (bind mount?)\n"),
2fc998
+		     rootino);
2fc998
+	}
2fc998
+}
2fc998
+
2fc998
 /* ARGSUSED */
2fc998
 bool_t
2fc998
 tree_init( char *hkdir,
2fc998
@@ -746,7 +783,8 @@ tree_begindir(filehdr_t *fhdrp, dah_t *dahp)
2fc998
 	/* lookup head of hardlink list
2fc998
 	 */
2fc998
 	hardh = link_hardh( ino, gen );
2fc998
-	assert( ino != persp->p_rootino || hardh == persp->p_rooth );
2fc998
+	if (need_fixrootdir == BOOL_FALSE)
2fc998
+		assert(ino != persp->p_rootino || hardh == persp->p_rooth);
2fc998
 
2fc998
 	/* already present
2fc998
 	 */
2fc998
@@ -815,7 +853,6 @@ tree_begindir(filehdr_t *fhdrp, dah_t *dahp)
2fc998
 		adopt( persp->p_orphh, hardh, NRH_NULL );
2fc998
 		*dahp = dah;
2fc998
 	}
2fc998
-
2fc998
 	return hardh;
2fc998
 }
2fc998
 
2fc998
@@ -960,6 +997,7 @@ tree_addent(nh_t parh, xfs_ino_t ino, gen_t gen, char *name, size_t namelen)
2fc998
 				}
2fc998
 			} else {
2fc998
 				assert( hardp->n_nrh != NRH_NULL );
2fc998
+
2fc998
 				namebuflen
2fc998
 				=
2fc998
 				namreg_get( hardp->n_nrh,
2fc998
@@ -1110,6 +1148,13 @@ tree_addent(nh_t parh, xfs_ino_t ino, gen_t gen, char *name, size_t namelen)
2fc998
 		      ino,
2fc998
 		      gen );
2fc998
 	}
2fc998
+	/* found the fake rootino from subdir, need fix p_rooth. */
2fc998
+	if (need_fixrootdir == BOOL_TRUE &&
2fc998
+	    persp->p_rootino == ino && hardh != persp->p_rooth) {
2fc998
+		mlog(MLOG_NORMAL,
2fc998
+		     _("found fake rootino #%llu, will fix.\n"), ino);
2fc998
+		persp->p_rooth = hardh;
2fc998
+	}
2fc998
 	return RV_OK;
2fc998
 }
2fc998
 
2fc998
@@ -3808,7 +3853,26 @@ selsubtree_recurse_down(nh_t nh, bool_t sensepr)
2fc998
 static nh_t
2fc998
 link_hardh( xfs_ino_t ino, gen_t gen )
2fc998
 {
2fc998
-	return hash_find( ino, gen );
2fc998
+	nh_t tmp = hash_find(ino, gen);
2fc998
+
2fc998
+	/*
2fc998
+	 * XXX (another workaround): the simply way is that don't reuse node_t
2fc998
+	 * with gen = 0 created in tree_init(). Otherwise, it could cause
2fc998
+	 * xfsrestore: tree.c:1003: tree_addent: Assertion
2fc998
+	 * `hardp->n_nrh != NRH_NULL' failed.
2fc998
+	 * and that node_t is a dir node but the fake rootino could be a non-dir
2fc998
+	 * plus reusing it could cause potential loop in tree hierarchy.
2fc998
+	 */
2fc998
+	if (need_fixrootdir == BOOL_TRUE &&
2fc998
+	    ino == persp->p_rootino && gen == 0 &&
2fc998
+	    orig_rooth == NH_NULL) {
2fc998
+		mlog(MLOG_NORMAL,
2fc998
+_("link out fake rootino %llu with gen=0 created in tree_init()\n"), ino);
2fc998
+		link_out(tmp);
2fc998
+		orig_rooth = tmp;
2fc998
+		return NH_NULL;
2fc998
+	}
2fc998
+	return tmp;
2fc998
 }
2fc998
 
2fc998
 /* returns following node in hard link list
2fc998
diff --git a/restore/tree.h b/restore/tree.h
2fc998
index bf66e3d..f5bd4ff 100644
2fc998
--- a/restore/tree.h
2fc998
+++ b/restore/tree.h
2fc998
@@ -18,6 +18,8 @@
2fc998
 #ifndef TREE_H
2fc998
 #define TREE_H
2fc998
 
2fc998
+void tree_fixroot(void);
2fc998
+
2fc998
 /* tree_init - creates a new tree abstraction.
2fc998
  */
2fc998
 extern bool_t tree_init( char *hkdir,
2fc998
-- 
2fc998
2.41.0
2fc998