|
|
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 |
|