public inbox for linux-erofs@ozlabs.org
 help / color / mirror / Atom feed
* [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 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  3:58 [PATCH] erofs-utils: fsck: support extracting subtrees Inseob Kim
  -- strict thread matches above, loose matches on Subject: below --
2026-02-26  0:59 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox