From: Hironori Shiina <shiina.hironori@gmail.com>
To: Gao Xiang <hsiangkao@redhat.com>, linux-xfs@vger.kernel.org
Cc: Eric Sandeen <sandeen@sandeen.net>, Donald Douwsma <ddouwsma@redhat.com>
Subject: Re: [RFC PATCH v2] xfsrestore: fix rootdir due to xfsdump bulkstat misuse
Date: Thu, 30 Jun 2022 15:19:07 -0400 [thread overview]
Message-ID: <e61ae295-a331-d36a-cae1-646022dc2a6e@gmail.com> (raw)
In-Reply-To: <aed54ce4-e1bf-39f7-cf91-a67e29f59d52@gmail.com>
On 6/10/22 19:05, Hironori Shiina wrote:
>
>
> On 11/16/20 03:07, Gao Xiang wrote:
>> If rootino is wrong and misspecified to a subdir inode #,
>> the following assertion could be triggered:
>> assert(ino != persp->p_rootino || hardh == persp->p_rooth);
>>
>> This patch adds a '-x' option (another awkward thing is that
>> the codebase doesn't support long options) to address
>> problamatic images by searching for the real dir, the reason
>> that I don't enable it by default is that I'm not very confident
>> with the xfsrestore codebase and xfsdump bulkstat issue will
>> also be fixed immediately as well, so this function might be
>> optional and only useful for pre-exist corrupted dumps.
>
> I agree that this function is optional for another reason. This fix
> cannot be the default behavior because of a workaround for a case where
> a fake root's gen is zero. This workaround prevents a correct dump
> without a fake root from being restored.
>
>>
>> In details, my understanding of the original logic is
>> 1) xfsrestore will create a rootdir node_t (p_rooth);
>> 2) it will build the tree hierarchy from inomap and adopt
>> the parent if needed (so inodes whose parent ino hasn't
>> detected will be in the orphan dir, p_orphh);
>> 3) during this period, if ino == rootino and
>> hardh != persp->p_rooth (IOWs, another node_t with
>> the same ino # is created), that'd be definitely wrong.
>>
>> So the proposal fix is that
>> - considering the xfsdump root ino # is a subdir inode, it'll
>> trigger ino == rootino && hardh != persp->p_rooth condition;
>> - so we log this node_t as persp->p_rooth rather than the
>> initial rootdir node_t created in 1);
>> - we also know that this node is actually a subdir, and after
>> the whole inomap is scanned (IOWs, the tree is built),
>> the real root dir will have the orphan dir parent p_orphh;
>> - therefore, we walk up by the parent until some node_t has
>> the p_orphh, so that's it.
>>
>> Cc: Donald Douwsma <ddouwsma@redhat.com>
>> Signed-off-by: Gao Xiang <hsiangkao@redhat.com>
>> ---
>> changes since RFC v1:
>> - fix non-dir fake rootino cases since tree_begindir()
>> won't be triggered for these non-dir, and we could do
>> that in tree_addent() instead (fault injection verified);
>>
>> - fix fake rootino case with gen = 0 as well, for more
>> details, see the inlined comment in link_hardh()
>> (fault injection verified as well).
>>
>> Anyway, all of this behaves like a workaround and I have
>> no idea how to deal it more gracefully.
>>
>> restore/content.c | 7 +++++
>> restore/getopt.h | 4 +--
>> restore/tree.c | 70 ++++++++++++++++++++++++++++++++++++++++++++---
>> restore/tree.h | 2 ++
>> 4 files changed, 77 insertions(+), 6 deletions(-)
>>
>> diff --git a/restore/content.c b/restore/content.c
>> index 6b22965..e807a83 100644
>> --- a/restore/content.c
>> +++ b/restore/content.c
>> @@ -865,6 +865,7 @@ static int quotafilecheck(char *type, char *dstdir, char *quotafile);
>>
>> bool_t content_media_change_needed;
>> bool_t restore_rootdir_permissions;
>> +bool_t need_fixrootdir;
>> char *media_change_alert_program = NULL;
>> size_t perssz;
>>
>> @@ -964,6 +965,7 @@ content_init(int argc, char *argv[], size64_t vmsz)
>> stsz = 0;
>> interpr = BOOL_FALSE;
>> restore_rootdir_permissions = BOOL_FALSE;
>> + need_fixrootdir = BOOL_FALSE;
>> optind = 1;
>> opterr = 0;
>> while ((c = getopt(argc, argv, GETOPT_CMDSTRING)) != EOF) {
>> @@ -1189,6 +1191,9 @@ content_init(int argc, char *argv[], size64_t vmsz)
>> case GETOPT_FMT2COMPAT:
>> tranp->t_truncategenpr = BOOL_TRUE;
>> break;
>> + case GETOPT_FIXROOTDIR:
>> + need_fixrootdir = BOOL_TRUE;
>> + break;
>> }
>> }
>>
>> @@ -3140,6 +3145,8 @@ applydirdump(drive_t *drivep,
>> return rv;
>> }
>>
>> + if (need_fixrootdir)
>> + tree_fixroot();
>> persp->s.dirdonepr = BOOL_TRUE;
>> }
>>
>> diff --git a/restore/getopt.h b/restore/getopt.h
>> index b5bc004..b0c0c7d 100644
>> --- a/restore/getopt.h
>> +++ b/restore/getopt.h
>> @@ -26,7 +26,7 @@
>> * purpose is to contain that command string.
>> */
>>
>> -#define GETOPT_CMDSTRING "a:b:c:def:himn:op:qrs:tv:wABCDEFG:H:I:JKL:M:NO:PQRS:TUVWX:Y:"
>> +#define GETOPT_CMDSTRING "a:b:c:def:himn:op:qrs:tv:wxABCDEFG:H:I:JKL:M:NO:PQRS:TUVWX:Y:"
>>
>> #define GETOPT_WORKSPACE 'a' /* workspace dir (content.c) */
>> #define GETOPT_BLOCKSIZE 'b' /* blocksize for rmt */
>> @@ -51,7 +51,7 @@
>> /* 'u' */
>> #define GETOPT_VERBOSITY 'v' /* verbosity level (0 to 4) */
>> #define GETOPT_SMALLWINDOW 'w' /* use a small window for dir entries */
>> -/* 'x' */
>> +#define GETOPT_FIXROOTDIR 'x' /* try to fix rootdir due to bulkstat misuse */
>> /* 'y' */
>> /* 'z' */
>> #define GETOPT_NOEXTATTR 'A' /* do not restore ext. file attr. */
>> diff --git a/restore/tree.c b/restore/tree.c
>> index 0670318..918fa01 100644
>> --- a/restore/tree.c
>> +++ b/restore/tree.c
>> @@ -15,7 +15,6 @@
>> * along with this program; if not, write the Free Software Foundation,
>> * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
>> */
>> -
>> #include <stdio.h>
>> #include <unistd.h>
>> #include <stdlib.h>
>> @@ -265,6 +264,7 @@ extern void usage(void);
>> extern size_t pgsz;
>> extern size_t pgmask;
>> extern bool_t restore_rootdir_permissions;
>> +extern bool_t need_fixrootdir;
>>
>> /* forward declarations of locally defined static functions ******************/
>>
>> @@ -331,10 +331,45 @@ static tran_t *tranp = 0;
>> static char *persname = PERS_NAME;
>> static char *orphname = ORPH_NAME;
>> static xfs_ino_t orphino = ORPH_INO;
>> +static nh_t orig_rooth = NH_NULL;
>>
>>
>> /* definition of locally defined global functions ****************************/
>>
>> +void
>> +tree_fixroot(void)
>> +{
>> + nh_t rooth = persp->p_rooth;
>> + xfs_ino_t rootino;
>> +
>> + while (1) {
>> + nh_t parh;
>> + node_t *rootp = Node_map(rooth);
>> +
>> + rootino = rootp->n_ino;
>> + parh = rootp->n_parh;
>> + Node_unmap(rooth, &rootp);
>> +
>> + if (parh == rooth ||
>> + /*
>> + * since all new node (including non-parent)
>> + * would be adopted into orphh
>> + */
>> + parh == persp->p_orphh ||
>> + parh == NH_NULL)
>> + break;
>> + rooth = parh;
>> + }
>> +
>> + if (rooth != persp->p_rooth) {
>> + persp->p_rooth = rooth;
>> + persp->p_rootino = rootino;
>> +
>
> As far as I see intialization for a root in tree_init(), isn't it
> necessary to adopt the orphanage node(persp->p_orphh) to the new root?
>
These two steps are necessary here:
+ disown(rooth);
+ adopt(persp->p_rooth, persp->p_orphh, NH_NULL);
Otherwise, we hit an assertion error when restroing a renamed file in
the cumulative mode. We need to re-adopt the orphanage dir to the fixed
root for creating a correct path of a node put to orphanage here:
https://git.kernel.org/pub/scm/fs/xfs/xfsdump-dev.git/tree/restore/tree.c#n1498
>> + mlog(MLOG_NORMAL, _("fix root # to %llu (bind mount?)\n"),
>> + rootino);
>> + }
>> +}
>> +
>> /* ARGSUSED */
>> bool_t
>> tree_init(char *hkdir,
>> @@ -754,7 +789,8 @@ tree_begindir(filehdr_t *fhdrp, dah_t *dahp)
>> /* lookup head of hardlink list
>> */
>> hardh = link_hardh(ino, gen);
>> - assert(ino != persp->p_rootino || hardh == persp->p_rooth);
>> + if (need_fixrootdir == BOOL_FALSE)
>> + assert(ino != persp->p_rootino || hardh == persp->p_rooth);
>>
>> /* already present
>> */
>> @@ -823,7 +859,6 @@ tree_begindir(filehdr_t *fhdrp, dah_t *dahp)
>> adopt(persp->p_orphh, hardh, NRH_NULL);
>> *dahp = dah;
>> }
>> -
>> return hardh;
>> }
>>
>> @@ -968,6 +1003,7 @@ tree_addent(nh_t parh, xfs_ino_t ino, gen_t gen, char *name, size_t namelen)
>> }
>> } else {
>> assert(hardp->n_nrh != NRH_NULL);
>> +
>> namebuflen
>> =
>> namreg_get(hardp->n_nrh,
>> @@ -1118,6 +1154,13 @@ tree_addent(nh_t parh, xfs_ino_t ino, gen_t gen, char *name, size_t namelen)
>> ino,
>> gen);
>> }
>> + /* found the fake rootino from subdir, need fix p_rooth. */
>> + if (need_fixrootdir == BOOL_TRUE &&
>> + persp->p_rootino == ino && hardh != persp->p_rooth) {
>> + mlog(MLOG_NORMAL,
>> + _("found fake rootino #%llu, will fix.\n"), ino);
>> + persp->p_rooth = hardh;
>> + }
>> return RV_OK;
>> }
>>
>> @@ -3841,7 +3884,26 @@ selsubtree_recurse_down(nh_t nh, bool_t sensepr)
>> static nh_t
>> link_hardh(xfs_ino_t ino, gen_t gen)
>> {
>> - return hash_find(ino, gen);
>> + nh_t tmp = hash_find(ino, gen);
>> +
>> + /*
>> + * XXX (another workaround): the simply way is that don't reuse node_t
>> + * with gen = 0 created in tree_init(). Otherwise, it could cause
>> + * xfsrestore: tree.c:1003: tree_addent: Assertion
>> + * `hardp->n_nrh != NRH_NULL' failed.
>> + * and that node_t is a dir node but the fake rootino could be a non-dir
>> + * plus reusing it could cause potential loop in tree hierarchy.
>> + */
>> + if (need_fixrootdir == BOOL_TRUE &&
>> + ino == persp->p_rootino && gen == 0 &&
>> + orig_rooth == NH_NULL) {
>
> As I mentioned above, this workaround makes this correction optional.
> This workaround assumes the initially created root is fake. If a dump is
> created correctly without a fake root, this function returns NH_NULL for
> the correct root.
>
>
Due to this part, '-x' flag does not work for the correct dump. We need
to provide procudure for a user who may have a wrong dump with man or
the help message like this:
---------
A user who has a dump created by xfsdump with this bug should see a
table of contents of the dump file before restoring:
xfsrestore -t -f <dumpfile>
If a similar message to the following one is displayed, '-x' is required
to restore the dump:
NOTE: ino 128 salvaging file, placing in orphanage/1024.0/FILE_056
Otherwise, '-x' is not required.
---------
I tried the cumulative mode locally with this fix by combining xfs/064,
xfs/065 and xfs/545 in xfstests. Then, the issue regarding a renamed
file, which is mentioned above, was found. Based on the results of the
tests, the following information also should be provied to users:
---------
In the cumulative mode, a user needs to check with '-t' and use '-x'
flag only for the level 0 dump. Once the root is fixed in restoring the
level 0 dump, '-x' flag is no longer required for level 1+ dumps.
---------
Thanks,
Hironori
> Thanks,
> Hironori
>
>> + mlog(MLOG_NORMAL,
>> +_("link out fake rootino %llu with gen=0 created in tree_init()\n"), ino);
>> + link_out(tmp);
>> + orig_rooth = tmp;
>> + return NH_NULL;
>> + }
>> + return tmp;
>> }
>>
>> /* returns following node in hard link list
>> diff --git a/restore/tree.h b/restore/tree.h
>> index 4f9ffe8..5d0c346 100644
>> --- a/restore/tree.h
>> +++ b/restore/tree.h
>> @@ -18,6 +18,8 @@
>> #ifndef TREE_H
>> #define TREE_H
>>
>> +void tree_fixroot(void);
>> +
>> /* tree_init - creates a new tree abstraction.
>> */
>> extern bool_t tree_init(char *hkdir,
next prev parent reply other threads:[~2022-06-30 19:19 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-11-13 12:51 [RFC PATCH] xfsrestore: fix rootdir due to xfsdump bulkstat misuse Gao Xiang
2020-11-13 14:10 ` Eric Sandeen
2020-11-13 14:15 ` Gao Xiang
2020-11-16 8:23 ` Gao Xiang
2020-11-16 8:07 ` [RFC PATCH v2] " Gao Xiang
2020-11-16 8:13 ` Gao Xiang
2021-09-27 15:00 ` Bill O'Donnell
2021-12-20 15:14 ` Masayoshi Mizuma
2022-06-10 23:05 ` Hironori Shiina
2022-06-30 19:19 ` Hironori Shiina [this message]
2022-09-28 19:10 ` [RFC PATCH V3] " Hironori Shiina
2022-09-29 18:17 ` Darrick J. Wong
2023-04-19 14:16 ` Eric Sandeen
2023-04-19 15:40 ` Darrick J. Wong
2023-04-21 9:10 ` Carlos Maiolino
2023-04-21 16:14 ` Darrick J. Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e61ae295-a331-d36a-cae1-646022dc2a6e@gmail.com \
--to=shiina.hironori@gmail.com \
--cc=ddouwsma@redhat.com \
--cc=hsiangkao@redhat.com \
--cc=linux-xfs@vger.kernel.org \
--cc=sandeen@sandeen.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.