* [PATCH] erofs-utils: fsck: support extracting subtrees
@ 2026-02-26 0:59 Inseob Kim
2026-02-26 1:50 ` Gao Xiang
0 siblings, 1 reply; 6+ messages in thread
From: Inseob Kim @ 2026-02-26 0:59 UTC (permalink / raw)
To: linux-erofs; +Cc: Inseob Kim
Add --nid and --path options to fsck.erofs to allow users to check
or extract specific sub-directories or files instead of the entire
filesystem.
This is useful for targeted data recovery or verifying specific
image components without the overhead of a full traversal.
Signed-off-by: Inseob Kim <inseob@google.com>
---
fsck/main.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 40 insertions(+), 10 deletions(-)
diff --git a/fsck/main.c b/fsck/main.c
index ab697be..a7d9f46 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -39,6 +39,8 @@ struct erofsfsck_cfg {
bool preserve_owner;
bool preserve_perms;
bool dump_xattrs;
+ erofs_nid_t nid;
+ const char *inode_path;
bool nosbcrc;
};
static struct erofsfsck_cfg fsckcfg;
@@ -59,6 +61,8 @@ static struct option long_options[] = {
{"offset", required_argument, 0, 12},
{"xattrs", no_argument, 0, 13},
{"no-xattrs", no_argument, 0, 14},
+ {"nid", required_argument, 0, 15},
+ {"path", required_argument, 0, 16},
{"no-sbcrc", no_argument, 0, 512},
{0, 0, 0, 0},
};
@@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
" --extract[=X] check if all files are well encoded, optionally\n"
" extract to X\n"
" --offset=# skip # bytes at the beginning of IMAGE\n"
+ " --nid=# check or extract from the target inode of nid #\n"
+ " --path=X check or extract from the target inode of path X\n"
" --no-sbcrc bypass the superblock checksum verification\n"
" --[no-]xattrs whether to dump extended attributes (default off)\n"
"\n"
@@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
case 14:
fsckcfg.dump_xattrs = false;
break;
+ case 15:
+ fsckcfg.nid = (erofs_nid_t)atoll(optarg);
+ break;
+ case 16:
+ fsckcfg.inode_path = optarg;
+ break;
case 512:
fsckcfg.nosbcrc = true;
break;
@@ -981,7 +993,8 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
if (S_ISDIR(inode.i_mode)) {
struct erofs_dir_context ctx = {
- .flags = EROFS_READDIR_VALID_PNID,
+ .flags = (pnid == nid && nid != g_sbi.root_nid) ?
+ 0 : EROFS_READDIR_VALID_PNID,
.pnid = pnid,
.dir = &inode,
.cb = erofsfsck_dirent_iter,
@@ -1033,6 +1046,8 @@ int main(int argc, char *argv[])
fsckcfg.preserve_owner = fsckcfg.superuser;
fsckcfg.preserve_perms = fsckcfg.superuser;
fsckcfg.dump_xattrs = false;
+ fsckcfg.nid = 0;
+ fsckcfg.inode_path = NULL;
err = erofsfsck_parse_options_cfg(argc, argv);
if (err) {
@@ -1068,22 +1083,37 @@ int main(int argc, char *argv[])
if (fsckcfg.extract_path)
erofsfsck_hardlink_init();
- if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
- err = erofs_packedfile_init(&g_sbi, false);
+ if (fsckcfg.inode_path) {
+ struct erofs_inode inode = { .sbi = &g_sbi };
+
+ err = erofs_ilookup(fsckcfg.inode_path, &inode);
if (err) {
- erofs_err("failed to initialize packedfile: %s",
- erofs_strerror(err));
+ erofs_err("failed to lookup %s", fsckcfg.inode_path);
goto exit_hardlink;
}
+ fsckcfg.nid = inode.nid;
+ } else if (!fsckcfg.nid) {
+ fsckcfg.nid = g_sbi.root_nid;
+ }
- err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
- if (err) {
- erofs_err("failed to verify packed file");
- goto exit_packedinode;
+ if (!fsckcfg.inode_path && fsckcfg.nid == g_sbi.root_nid) {
+ if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
+ err = erofs_packedfile_init(&g_sbi, false);
+ if (err) {
+ erofs_err("failed to initialize packedfile: %s",
+ erofs_strerror(err));
+ goto exit_hardlink;
+ }
+
+ err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
+ if (err) {
+ erofs_err("failed to verify packed file");
+ goto exit_packedinode;
+ }
}
}
- err = erofsfsck_check_inode(g_sbi.root_nid, g_sbi.root_nid);
+ err = erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);
if (fsckcfg.corrupted) {
if (!fsckcfg.extract_path)
erofs_err("Found some filesystem corruption");
--
2.53.0.414.gf7e9f6c205-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH] erofs-utils: fsck: support extracting subtrees
2026-02-26 0:59 [PATCH] erofs-utils: fsck: support extracting subtrees Inseob Kim
@ 2026-02-26 1:50 ` Gao Xiang
2026-02-26 2:18 ` Inseob Kim
0 siblings, 1 reply; 6+ messages in thread
From: Gao Xiang @ 2026-02-26 1:50 UTC (permalink / raw)
To: Inseob Kim, linux-erofs
Hi Inseob,
On 2026/2/26 08:59, Inseob Kim wrote:
> Add --nid and --path options to fsck.erofs to allow users to check
> or extract specific sub-directories or files instead of the entire
> filesystem.
>
> This is useful for targeted data recovery or verifying specific
> image components without the overhead of a full traversal.
Thanks for the patch!
>
> Signed-off-by: Inseob Kim <inseob@google.com>
> ---
> fsck/main.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
> 1 file changed, 40 insertions(+), 10 deletions(-)
>
> diff --git a/fsck/main.c b/fsck/main.c
> index ab697be..a7d9f46 100644
> --- a/fsck/main.c
> +++ b/fsck/main.c
> @@ -39,6 +39,8 @@ struct erofsfsck_cfg {
> bool preserve_owner;
> bool preserve_perms;
> bool dump_xattrs;
> + erofs_nid_t nid;
> + const char *inode_path;
> bool nosbcrc;
> };
> static struct erofsfsck_cfg fsckcfg;
> @@ -59,6 +61,8 @@ static struct option long_options[] = {
> {"offset", required_argument, 0, 12},
> {"xattrs", no_argument, 0, 13},
> {"no-xattrs", no_argument, 0, 14},
> + {"nid", required_argument, 0, 15},
> + {"path", required_argument, 0, 16},
> {"no-sbcrc", no_argument, 0, 512},
> {0, 0, 0, 0},
> };
> @@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
> " --extract[=X] check if all files are well encoded, optionally\n"
> " extract to X\n"
> " --offset=# skip # bytes at the beginning of IMAGE\n"
> + " --nid=# check or extract from the target inode of nid #\n"
> + " --path=X check or extract from the target inode of path X\n"
> " --no-sbcrc bypass the superblock checksum verification\n"
> " --[no-]xattrs whether to dump extended attributes (default off)\n"
> "\n"
> @@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
> case 14:
> fsckcfg.dump_xattrs = false;
> break;
> + case 15:
> + fsckcfg.nid = (erofs_nid_t)atoll(optarg);
> + break;
> + case 16:
> + fsckcfg.inode_path = optarg;
> + break;
> case 512:
> fsckcfg.nosbcrc = true;
> break;
> @@ -981,7 +993,8 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
>
> if (S_ISDIR(inode.i_mode)) {
> struct erofs_dir_context ctx = {
> - .flags = EROFS_READDIR_VALID_PNID,
> + .flags = (pnid == nid && nid != g_sbi.root_nid) ?
Does it relax the validatation check?
and does (nid == pnid && nid == fsckcfg.nid) work?
> + 0 : EROFS_READDIR_VALID_PNID,
> .pnid = pnid,
> .dir = &inode,
> .cb = erofsfsck_dirent_iter,
> @@ -1033,6 +1046,8 @@ int main(int argc, char *argv[])
> fsckcfg.preserve_owner = fsckcfg.superuser;
> fsckcfg.preserve_perms = fsckcfg.superuser;
> fsckcfg.dump_xattrs = false;
> + fsckcfg.nid = 0;
> + fsckcfg.inode_path = NULL;
>
> err = erofsfsck_parse_options_cfg(argc, argv);
> if (err) {
> @@ -1068,22 +1083,37 @@ int main(int argc, char *argv[])
> if (fsckcfg.extract_path)
> erofsfsck_hardlink_init();
>
> - if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
> - err = erofs_packedfile_init(&g_sbi, false);
> + if (fsckcfg.inode_path) {
> + struct erofs_inode inode = { .sbi = &g_sbi };
> +
> + err = erofs_ilookup(fsckcfg.inode_path, &inode);
> if (err) {
> - erofs_err("failed to initialize packedfile: %s",
> - erofs_strerror(err));
> + erofs_err("failed to lookup %s", fsckcfg.inode_path);
> goto exit_hardlink;
> }
It would be better to check if it's a directory.
Thanks,
Gao Xiang
> + fsckcfg.nid = inode.nid;
> + } else if (!fsckcfg.nid) {
> + fsckcfg.nid = g_sbi.root_nid;
> + }
>
> - err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
> - if (err) {
> - erofs_err("failed to verify packed file");
> - goto exit_packedinode;
> + if (!fsckcfg.inode_path && fsckcfg.nid == g_sbi.root_nid) {
> + if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
> + err = erofs_packedfile_init(&g_sbi, false);
> + if (err) {
> + erofs_err("failed to initialize packedfile: %s",
> + erofs_strerror(err));
> + goto exit_hardlink;
> + }
> +
> + err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
> + if (err) {
> + erofs_err("failed to verify packed file");
> + goto exit_packedinode;
> + }
> }
> }
>
> - err = erofsfsck_check_inode(g_sbi.root_nid, g_sbi.root_nid);
> + err = erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);
> if (fsckcfg.corrupted) {
> if (!fsckcfg.extract_path)
> erofs_err("Found some filesystem corruption");
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] erofs-utils: fsck: support extracting subtrees
2026-02-26 1:50 ` Gao Xiang
@ 2026-02-26 2:18 ` Inseob Kim
2026-02-26 2:37 ` Gao Xiang
0 siblings, 1 reply; 6+ messages in thread
From: Inseob Kim @ 2026-02-26 2:18 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs
On Thu, Feb 26, 2026 at 10:50 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>
> Hi Inseob,
>
> On 2026/2/26 08:59, Inseob Kim wrote:
> > Add --nid and --path options to fsck.erofs to allow users to check
> > or extract specific sub-directories or files instead of the entire
> > filesystem.
> >
> > This is useful for targeted data recovery or verifying specific
> > image components without the overhead of a full traversal.
>
> Thanks for the patch!
Thank *you* for quick response!
>
> >
> > Signed-off-by: Inseob Kim <inseob@google.com>
> > ---
> > fsck/main.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
> > 1 file changed, 40 insertions(+), 10 deletions(-)
> >
> > diff --git a/fsck/main.c b/fsck/main.c
> > index ab697be..a7d9f46 100644
> > --- a/fsck/main.c
> > +++ b/fsck/main.c
> > @@ -39,6 +39,8 @@ struct erofsfsck_cfg {
> > bool preserve_owner;
> > bool preserve_perms;
> > bool dump_xattrs;
> > + erofs_nid_t nid;
> > + const char *inode_path;
> > bool nosbcrc;
> > };
> > static struct erofsfsck_cfg fsckcfg;
> > @@ -59,6 +61,8 @@ static struct option long_options[] = {
> > {"offset", required_argument, 0, 12},
> > {"xattrs", no_argument, 0, 13},
> > {"no-xattrs", no_argument, 0, 14},
> > + {"nid", required_argument, 0, 15},
> > + {"path", required_argument, 0, 16},
> > {"no-sbcrc", no_argument, 0, 512},
> > {0, 0, 0, 0},
> > };
> > @@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
> > " --extract[=X] check if all files are well encoded, optionally\n"
> > " extract to X\n"
> > " --offset=# skip # bytes at the beginning of IMAGE\n"
> > + " --nid=# check or extract from the target inode of nid #\n"
> > + " --path=X check or extract from the target inode of path X\n"
> > " --no-sbcrc bypass the superblock checksum verification\n"
> > " --[no-]xattrs whether to dump extended attributes (default off)\n"
> > "\n"
> > @@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
> > case 14:
> > fsckcfg.dump_xattrs = false;
> > break;
> > + case 15:
> > + fsckcfg.nid = (erofs_nid_t)atoll(optarg);
> > + break;
> > + case 16:
> > + fsckcfg.inode_path = optarg;
> > + break;
> > case 512:
> > fsckcfg.nosbcrc = true;
> > break;
> > @@ -981,7 +993,8 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
> >
> > if (S_ISDIR(inode.i_mode)) {
> > struct erofs_dir_context ctx = {
> > - .flags = EROFS_READDIR_VALID_PNID,
> > + .flags = (pnid == nid && nid != g_sbi.root_nid) ?
>
> Does it relax the validatation check?
>
> and does (nid == pnid && nid == fsckcfg.nid) work?
It shouldn't relax the existing validation check.
`erofsfsck_check_inode` is called with `err =
erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);`.
* If a given path is not the root, `nid`'s parent `..` will differ
from `pnid`, causing failure. This condition only relaxes the starting
directory.
* In the case of the root, `nid`'s parent `..` should indeed be
itself. So we can still validate.
If you have any better suggestions, I'll follow them.
>
> > + 0 : EROFS_READDIR_VALID_PNID,
> > .pnid = pnid,
> > .dir = &inode,
> > .cb = erofsfsck_dirent_iter,
> > @@ -1033,6 +1046,8 @@ int main(int argc, char *argv[])
> > fsckcfg.preserve_owner = fsckcfg.superuser;
> > fsckcfg.preserve_perms = fsckcfg.superuser;
> > fsckcfg.dump_xattrs = false;
> > + fsckcfg.nid = 0;
> > + fsckcfg.inode_path = NULL;
> >
> > err = erofsfsck_parse_options_cfg(argc, argv);
> > if (err) {
> > @@ -1068,22 +1083,37 @@ int main(int argc, char *argv[])
> > if (fsckcfg.extract_path)
> > erofsfsck_hardlink_init();
> >
> > - if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
> > - err = erofs_packedfile_init(&g_sbi, false);
> > + if (fsckcfg.inode_path) {
> > + struct erofs_inode inode = { .sbi = &g_sbi };
> > +
> > + err = erofs_ilookup(fsckcfg.inode_path, &inode);
> > if (err) {
> > - erofs_err("failed to initialize packedfile: %s",
> > - erofs_strerror(err));
> > + erofs_err("failed to lookup %s", fsckcfg.inode_path);
> > goto exit_hardlink;
> > }
>
> It would be better to check if it's a directory.
My intention was that we support both directories and files. Or should
I create a separate flag like `--cat` in dump.erofs?
>
> Thanks,
> Gao Xiang
>
> > + fsckcfg.nid = inode.nid;
> > + } else if (!fsckcfg.nid) {
> > + fsckcfg.nid = g_sbi.root_nid;
> > + }
> >
> > - err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
> > - if (err) {
> > - erofs_err("failed to verify packed file");
> > - goto exit_packedinode;
> > + if (!fsckcfg.inode_path && fsckcfg.nid == g_sbi.root_nid) {
> > + if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
> > + err = erofs_packedfile_init(&g_sbi, false);
> > + if (err) {
> > + erofs_err("failed to initialize packedfile: %s",
> > + erofs_strerror(err));
> > + goto exit_hardlink;
> > + }
> > +
> > + err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
> > + if (err) {
> > + erofs_err("failed to verify packed file");
> > + goto exit_packedinode;
> > + }
> > }
> > }
> >
> > - err = erofsfsck_check_inode(g_sbi.root_nid, g_sbi.root_nid);
> > + err = erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);
> > if (fsckcfg.corrupted) {
> > if (!fsckcfg.extract_path)
> > erofs_err("Found some filesystem corruption");
>
--
Inseob Kim | Software Engineer | inseob@google.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] erofs-utils: fsck: support extracting subtrees
2026-02-26 2:18 ` Inseob Kim
@ 2026-02-26 2:37 ` Gao Xiang
2026-02-26 3:49 ` Inseob Kim
0 siblings, 1 reply; 6+ messages in thread
From: Gao Xiang @ 2026-02-26 2:37 UTC (permalink / raw)
To: Inseob Kim; +Cc: linux-erofs
On 2026/2/26 10:18, Inseob Kim wrote:
> On Thu, Feb 26, 2026 at 10:50 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>>
>> Hi Inseob,
>>
>> On 2026/2/26 08:59, Inseob Kim wrote:
>>> Add --nid and --path options to fsck.erofs to allow users to check
>>> or extract specific sub-directories or files instead of the entire
>>> filesystem.
>>>
>>> This is useful for targeted data recovery or verifying specific
>>> image components without the overhead of a full traversal.
>>
>> Thanks for the patch!
>
> Thank *you* for quick response!
>
>>
>>>
>>> Signed-off-by: Inseob Kim <inseob@google.com>
>>> ---
>>> fsck/main.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
>>> 1 file changed, 40 insertions(+), 10 deletions(-)
>>>
>>> diff --git a/fsck/main.c b/fsck/main.c
>>> index ab697be..a7d9f46 100644
>>> --- a/fsck/main.c
>>> +++ b/fsck/main.c
>>> @@ -39,6 +39,8 @@ struct erofsfsck_cfg {
>>> bool preserve_owner;
>>> bool preserve_perms;
>>> bool dump_xattrs;
>>> + erofs_nid_t nid;
>>> + const char *inode_path;
>>> bool nosbcrc;
>>> };
>>> static struct erofsfsck_cfg fsckcfg;
>>> @@ -59,6 +61,8 @@ static struct option long_options[] = {
>>> {"offset", required_argument, 0, 12},
>>> {"xattrs", no_argument, 0, 13},
>>> {"no-xattrs", no_argument, 0, 14},
>>> + {"nid", required_argument, 0, 15},
>>> + {"path", required_argument, 0, 16},
>>> {"no-sbcrc", no_argument, 0, 512},
>>> {0, 0, 0, 0},
>>> };
>>> @@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
>>> " --extract[=X] check if all files are well encoded, optionally\n"
>>> " extract to X\n"
>>> " --offset=# skip # bytes at the beginning of IMAGE\n"
>>> + " --nid=# check or extract from the target inode of nid #\n"
>>> + " --path=X check or extract from the target inode of path X\n"
>>> " --no-sbcrc bypass the superblock checksum verification\n"
>>> " --[no-]xattrs whether to dump extended attributes (default off)\n"
>>> "\n"
>>> @@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
>>> case 14:
>>> fsckcfg.dump_xattrs = false;
>>> break;
>>> + case 15:
>>> + fsckcfg.nid = (erofs_nid_t)atoll(optarg);
>>> + break;
>>> + case 16:
>>> + fsckcfg.inode_path = optarg;
>>> + break;
>>> case 512:
>>> fsckcfg.nosbcrc = true;
>>> break;
>>> @@ -981,7 +993,8 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
>>>
>>> if (S_ISDIR(inode.i_mode)) {
>>> struct erofs_dir_context ctx = {
>>> - .flags = EROFS_READDIR_VALID_PNID,
>>> + .flags = (pnid == nid && nid != g_sbi.root_nid) ?
>>
>> Does it relax the validatation check?
>>
>> and does (nid == pnid && nid == fsckcfg.nid) work?
>
> It shouldn't relax the existing validation check.
> `erofsfsck_check_inode` is called with `err =
> erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);`.
>
> * If a given path is not the root, `nid`'s parent `..` will differ
> from `pnid`, causing failure. This condition only relaxes the starting
> directory.
I just have a wild thought, if there is a directory which have
foo/
| - .. -> pointing to `foo` itself
- a/ -> pointing to `foo` directory too
will (pnid == nid && nid != g_sbi.root_nid) relaxs
the check for a/ ?
I'm not sure but you could double check.
> * In the case of the root, `nid`'s parent `..` should indeed be
> itself. So we can still validate.
>
> If you have any better suggestions, I'll follow them.
>
>>
>>> + 0 : EROFS_READDIR_VALID_PNID,
>>> .pnid = pnid,
>>> .dir = &inode,
>>> .cb = erofsfsck_dirent_iter,
>>> @@ -1033,6 +1046,8 @@ int main(int argc, char *argv[])
>>> fsckcfg.preserve_owner = fsckcfg.superuser;
>>> fsckcfg.preserve_perms = fsckcfg.superuser;
>>> fsckcfg.dump_xattrs = false;
>>> + fsckcfg.nid = 0;
>>> + fsckcfg.inode_path = NULL;
>>>
>>> err = erofsfsck_parse_options_cfg(argc, argv);
>>> if (err) {
>>> @@ -1068,22 +1083,37 @@ int main(int argc, char *argv[])
>>> if (fsckcfg.extract_path)
>>> erofsfsck_hardlink_init();
>>>
>>> - if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
>>> - err = erofs_packedfile_init(&g_sbi, false);
>>> + if (fsckcfg.inode_path) {
>>> + struct erofs_inode inode = { .sbi = &g_sbi };
>>> +
>>> + err = erofs_ilookup(fsckcfg.inode_path, &inode);
>>> if (err) {
>>> - erofs_err("failed to initialize packedfile: %s",
>>> - erofs_strerror(err));
>>> + erofs_err("failed to lookup %s", fsckcfg.inode_path);
>>> goto exit_hardlink;
>>> }
>>
>> It would be better to check if it's a directory.
>
> My intention was that we support both directories and files. Or should
> I create a separate flag like `--cat` in dump.erofs?
Ok, make sense.
Thanks,
Gao Xiang
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] erofs-utils: fsck: support extracting subtrees
2026-02-26 2:37 ` Gao Xiang
@ 2026-02-26 3:49 ` Inseob Kim
0 siblings, 0 replies; 6+ messages in thread
From: Inseob Kim @ 2026-02-26 3:49 UTC (permalink / raw)
To: Gao Xiang; +Cc: linux-erofs
On Thu, Feb 26, 2026 at 11:37 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
>
>
>
> On 2026/2/26 10:18, Inseob Kim wrote:
> > On Thu, Feb 26, 2026 at 10:50 AM Gao Xiang <hsiangkao@linux.alibaba.com> wrote:
> >>
> >> Hi Inseob,
> >>
> >> On 2026/2/26 08:59, Inseob Kim wrote:
> >>> Add --nid and --path options to fsck.erofs to allow users to check
> >>> or extract specific sub-directories or files instead of the entire
> >>> filesystem.
> >>>
> >>> This is useful for targeted data recovery or verifying specific
> >>> image components without the overhead of a full traversal.
> >>
> >> Thanks for the patch!
> >
> > Thank *you* for quick response!
> >
> >>
> >>>
> >>> Signed-off-by: Inseob Kim <inseob@google.com>
> >>> ---
> >>> fsck/main.c | 50 ++++++++++++++++++++++++++++++++++++++++----------
> >>> 1 file changed, 40 insertions(+), 10 deletions(-)
> >>>
> >>> diff --git a/fsck/main.c b/fsck/main.c
> >>> index ab697be..a7d9f46 100644
> >>> --- a/fsck/main.c
> >>> +++ b/fsck/main.c
> >>> @@ -39,6 +39,8 @@ struct erofsfsck_cfg {
> >>> bool preserve_owner;
> >>> bool preserve_perms;
> >>> bool dump_xattrs;
> >>> + erofs_nid_t nid;
> >>> + const char *inode_path;
> >>> bool nosbcrc;
> >>> };
> >>> static struct erofsfsck_cfg fsckcfg;
> >>> @@ -59,6 +61,8 @@ static struct option long_options[] = {
> >>> {"offset", required_argument, 0, 12},
> >>> {"xattrs", no_argument, 0, 13},
> >>> {"no-xattrs", no_argument, 0, 14},
> >>> + {"nid", required_argument, 0, 15},
> >>> + {"path", required_argument, 0, 16},
> >>> {"no-sbcrc", no_argument, 0, 512},
> >>> {0, 0, 0, 0},
> >>> };
> >>> @@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
> >>> " --extract[=X] check if all files are well encoded, optionally\n"
> >>> " extract to X\n"
> >>> " --offset=# skip # bytes at the beginning of IMAGE\n"
> >>> + " --nid=# check or extract from the target inode of nid #\n"
> >>> + " --path=X check or extract from the target inode of path X\n"
> >>> " --no-sbcrc bypass the superblock checksum verification\n"
> >>> " --[no-]xattrs whether to dump extended attributes (default off)\n"
> >>> "\n"
> >>> @@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
> >>> case 14:
> >>> fsckcfg.dump_xattrs = false;
> >>> break;
> >>> + case 15:
> >>> + fsckcfg.nid = (erofs_nid_t)atoll(optarg);
> >>> + break;
> >>> + case 16:
> >>> + fsckcfg.inode_path = optarg;
> >>> + break;
> >>> case 512:
> >>> fsckcfg.nosbcrc = true;
> >>> break;
> >>> @@ -981,7 +993,8 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
> >>>
> >>> if (S_ISDIR(inode.i_mode)) {
> >>> struct erofs_dir_context ctx = {
> >>> - .flags = EROFS_READDIR_VALID_PNID,
> >>> + .flags = (pnid == nid && nid != g_sbi.root_nid) ?
> >>
> >> Does it relax the validatation check?
> >>
> >> and does (nid == pnid && nid == fsckcfg.nid) work?
> >
> > It shouldn't relax the existing validation check.
> > `erofsfsck_check_inode` is called with `err =
> > erofsfsck_check_inode(fsckcfg.nid, fsckcfg.nid);`.
> >
> > * If a given path is not the root, `nid`'s parent `..` will differ
> > from `pnid`, causing failure. This condition only relaxes the starting
> > directory.
>
> I just have a wild thought, if there is a directory which have
>
> foo/
> | - .. -> pointing to `foo` itself
> - a/ -> pointing to `foo` directory too
>
> will (pnid == nid && nid != g_sbi.root_nid) relaxs
> the check for a/ ?
>
> I'm not sure but you could double check.
Thank you for pointing that out. I'll try retrieving the parent before
calling `erofsfsck_check_inode` and then pass it correctly.
>
> > * In the case of the root, `nid`'s parent `..` should indeed be
> > itself. So we can still validate.
> >
> > If you have any better suggestions, I'll follow them.
> >
> >>
> >>> + 0 : EROFS_READDIR_VALID_PNID,
> >>> .pnid = pnid,
> >>> .dir = &inode,
> >>> .cb = erofsfsck_dirent_iter,
> >>> @@ -1033,6 +1046,8 @@ int main(int argc, char *argv[])
> >>> fsckcfg.preserve_owner = fsckcfg.superuser;
> >>> fsckcfg.preserve_perms = fsckcfg.superuser;
> >>> fsckcfg.dump_xattrs = false;
> >>> + fsckcfg.nid = 0;
> >>> + fsckcfg.inode_path = NULL;
> >>>
> >>> err = erofsfsck_parse_options_cfg(argc, argv);
> >>> if (err) {
> >>> @@ -1068,22 +1083,37 @@ int main(int argc, char *argv[])
> >>> if (fsckcfg.extract_path)
> >>> erofsfsck_hardlink_init();
> >>>
> >>> - if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
> >>> - err = erofs_packedfile_init(&g_sbi, false);
> >>> + if (fsckcfg.inode_path) {
> >>> + struct erofs_inode inode = { .sbi = &g_sbi };
> >>> +
> >>> + err = erofs_ilookup(fsckcfg.inode_path, &inode);
> >>> if (err) {
> >>> - erofs_err("failed to initialize packedfile: %s",
> >>> - erofs_strerror(err));
> >>> + erofs_err("failed to lookup %s", fsckcfg.inode_path);
> >>> goto exit_hardlink;
> >>> }
> >>
> >> It would be better to check if it's a directory.
> >
> > My intention was that we support both directories and files. Or should
> > I create a separate flag like `--cat` in dump.erofs?
>
> Ok, make sense.
>
> Thanks,
> Gao Xiang
--
Inseob Kim | Software Engineer | inseob@google.com
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH] erofs-utils: fsck: support extracting subtrees
@ 2026-02-26 3:58 Inseob Kim
0 siblings, 0 replies; 6+ messages in thread
From: Inseob Kim @ 2026-02-26 3:58 UTC (permalink / raw)
To: linux-erofs; +Cc: Inseob Kim
Add --nid and --path options to fsck.erofs to allow users to check
or extract specific sub-directories or files instead of the entire
filesystem.
This is useful for targeted data recovery or verifying specific
image components without the overhead of a full traversal.
Signed-off-by: Inseob Kim <inseob@google.com>
---
v2: retrieve pnid correctly rather than pnid == nid hack
---
fsck/main.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 73 insertions(+), 9 deletions(-)
diff --git a/fsck/main.c b/fsck/main.c
index ab697be..16cc627 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -39,6 +39,8 @@ struct erofsfsck_cfg {
bool preserve_owner;
bool preserve_perms;
bool dump_xattrs;
+ erofs_nid_t nid;
+ const char *inode_path;
bool nosbcrc;
};
static struct erofsfsck_cfg fsckcfg;
@@ -59,6 +61,8 @@ static struct option long_options[] = {
{"offset", required_argument, 0, 12},
{"xattrs", no_argument, 0, 13},
{"no-xattrs", no_argument, 0, 14},
+ {"nid", required_argument, 0, 15},
+ {"path", required_argument, 0, 16},
{"no-sbcrc", no_argument, 0, 512},
{0, 0, 0, 0},
};
@@ -110,6 +114,8 @@ static void usage(int argc, char **argv)
" --extract[=X] check if all files are well encoded, optionally\n"
" extract to X\n"
" --offset=# skip # bytes at the beginning of IMAGE\n"
+ " --nid=# check or extract from the target inode of nid #\n"
+ " --path=X check or extract from the target inode of path X\n"
" --no-sbcrc bypass the superblock checksum verification\n"
" --[no-]xattrs whether to dump extended attributes (default off)\n"
"\n"
@@ -245,6 +251,12 @@ static int erofsfsck_parse_options_cfg(int argc, char **argv)
case 14:
fsckcfg.dump_xattrs = false;
break;
+ case 15:
+ fsckcfg.nid = (erofs_nid_t)atoll(optarg);
+ break;
+ case 16:
+ fsckcfg.inode_path = optarg;
+ break;
case 512:
fsckcfg.nosbcrc = true;
break;
@@ -862,6 +874,22 @@ again:
return ret;
}
+struct erofsfsck_get_parent_ctx {
+ struct erofs_dir_context ctx;
+ erofs_nid_t pnid;
+};
+
+static int erofsfsck_get_parent_cb(struct erofs_dir_context *ctx)
+{
+ struct erofsfsck_get_parent_ctx *pctx = (void *)ctx;
+
+ if (ctx->dot_dotdot && ctx->de_namelen == 2) {
+ pctx->pnid = ctx->de_nid;
+ return 1;
+ }
+ return 0;
+}
+
static int erofsfsck_dirent_iter(struct erofs_dir_context *ctx)
{
int ret;
@@ -1033,6 +1061,8 @@ int main(int argc, char *argv[])
fsckcfg.preserve_owner = fsckcfg.superuser;
fsckcfg.preserve_perms = fsckcfg.superuser;
fsckcfg.dump_xattrs = false;
+ fsckcfg.nid = 0;
+ fsckcfg.inode_path = NULL;
err = erofsfsck_parse_options_cfg(argc, argv);
if (err) {
@@ -1068,22 +1098,56 @@ int main(int argc, char *argv[])
if (fsckcfg.extract_path)
erofsfsck_hardlink_init();
- if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
- err = erofs_packedfile_init(&g_sbi, false);
+ if (fsckcfg.inode_path) {
+ struct erofs_inode inode = { .sbi = &g_sbi };
+
+ err = erofs_ilookup(fsckcfg.inode_path, &inode);
if (err) {
- erofs_err("failed to initialize packedfile: %s",
- erofs_strerror(err));
+ erofs_err("failed to lookup %s", fsckcfg.inode_path);
goto exit_hardlink;
}
+ fsckcfg.nid = inode.nid;
+ } else if (!fsckcfg.nid) {
+ fsckcfg.nid = g_sbi.root_nid;
+ }
- err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
- if (err) {
- erofs_err("failed to verify packed file");
- goto exit_packedinode;
+ if (!fsckcfg.inode_path && fsckcfg.nid == g_sbi.root_nid) {
+ if (erofs_sb_has_fragments(&g_sbi) && g_sbi.packed_nid > 0) {
+ err = erofs_packedfile_init(&g_sbi, false);
+ if (err) {
+ erofs_err("failed to initialize packedfile: %s",
+ erofs_strerror(err));
+ goto exit_hardlink;
+ }
+
+ err = erofsfsck_check_inode(g_sbi.packed_nid, g_sbi.packed_nid);
+ if (err) {
+ erofs_err("failed to verify packed file");
+ goto exit_packedinode;
+ }
+ }
+ }
+
+ {
+ erofs_nid_t pnid = fsckcfg.nid;
+
+ if (fsckcfg.nid != g_sbi.root_nid) {
+ struct erofs_inode inode = { .sbi = &g_sbi, .nid = fsckcfg.nid };
+
+ if (!erofs_read_inode_from_disk(&inode) &&
+ S_ISDIR(inode.i_mode)) {
+ struct erofsfsck_get_parent_ctx ctx = {
+ .ctx.dir = &inode,
+ .ctx.cb = erofsfsck_get_parent_cb,
+ };
+
+ if (erofs_iterate_dir(&ctx.ctx, false) == 1)
+ pnid = ctx.pnid;
+ }
}
+ err = erofsfsck_check_inode(pnid, fsckcfg.nid);
}
- err = erofsfsck_check_inode(g_sbi.root_nid, g_sbi.root_nid);
if (fsckcfg.corrupted) {
if (!fsckcfg.extract_path)
erofs_err("Found some filesystem corruption");
--
2.53.0.414.gf7e9f6c205-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-02-26 3:59 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-26 0:59 [PATCH] erofs-utils: fsck: support extracting subtrees Inseob Kim
2026-02-26 1:50 ` Gao Xiang
2026-02-26 2:18 ` Inseob Kim
2026-02-26 2:37 ` Gao Xiang
2026-02-26 3:49 ` Inseob Kim
-- strict thread matches above, loose matches on Subject: below --
2026-02-26 3:58 Inseob Kim
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox