lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20220926130708.15476-1-lhenriques@suse.de>
Date:   Mon, 26 Sep 2022 14:07:08 +0100
From:   Luís Henriques <lhenriques@...e.de>
To:     linux-ext4@...r.kernel.org
Cc:     Theodore Ts'o <tytso@....edu>,
        Luís Henriques <lhenriques@...e.de>
Subject: [RFC PATCH] debugfs: add commands to dump and change extent header fields

This commit adds two new commands to debugfs:

* dump_extent_header - dump an extent header
* set_extent_header_field - modifying an extent header field

These commands take a block number as an argument, and will take the extent
header from the beginning of this block.  Basic validation of the header
data is done, but it is possible to override this validation when modifying
the header.

Signed-off-by: Luís Henriques <lhenriques@...e.de>
---
 debugfs/debug_cmds.ct |  6 +++
 debugfs/debugfs.8.in  | 24 +++++++++++
 debugfs/debugfs.c     | 50 +++++++++++++++++++++++
 debugfs/debugfs.h     |  2 +
 debugfs/set_fields.c  | 94 +++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 176 insertions(+)

diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct
index 1ff6c9dcd6b5..4ed613627258 100644
--- a/debugfs/debug_cmds.ct
+++ b/debugfs/debug_cmds.ct
@@ -49,6 +49,12 @@ request do_stat, "Show inode information ",
 request do_dump_extents, "Dump extents information ",
 	dump_extents, extents, ex;
 
+request do_dump_extent_header, "Dump extent header information",
+	dump_extent_header, deh;
+
+request do_set_extent_header_field, "Sets an extent header field",
+	set_extent_header_field, seh;
+
 request do_blocks, "Dump blocks used by an inode ",
 	blocks;
 
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index a3227a80ab24..f50fd6d9e987 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -298,6 +298,13 @@ not stored in file system data structures.   Hence, the values displayed
 may not necessarily by accurate and does not indicate a problem or
 corruption in the file system.)
 .TP
+.BI dump_extent_header " block"
+Dump the contents of the extent header at the beginning of block
+.IR block .
+A warning will be emitted if the block doesn't start with a valid extent
+header (for example, if the extent header doesn't start with the '0xf30a'
+magic value).
+.TP
 .B dump_unused
 Dump unused blocks which contain non-null bytes.
 .TP
@@ -739,6 +746,23 @@ can be displayed by using the command:
 Also available as
 .BR ssv .
 .TP
+.BI set_extent_header_field " -l | block field value"
+Change the value of
+.IR field
+to
+.IR value
+in the extent header at the beginning of
+.IR block.
+If
+.IR block
+doesn't start with a valid extent header, a warning will be emitted and field
+isn't changed.  To force the change, the
+.IR -f
+flag can be used.
+The fields that may be changed with this command can be listed by using the
+.IR -l
+flag.
+.TP
 .B show_debugfs_params
 Display
 .B debugfs
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 78b93eda7b61..f9fb0dc9b347 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -1079,6 +1079,56 @@ void do_dump_extents(int argc, char **argv, int sci_idx EXT2FS_ATTR((unused)),
 	return;
 }
 
+void do_dump_extent_header(int argc, char **argv,
+			   int sci_idx EXT2FS_ATTR((unused)),
+			   void *infop EXT2FS_ATTR((unused)))
+{
+	struct ext3_extent_header *eh;
+	errcode_t	errcode;
+	blk64_t		block;
+	unsigned char	*buf;
+	int		err;
+
+	if (check_fs_open(argv[0]))
+		return;
+
+	if (common_args_process(argc, argv, 2, 2, argv[0], "<block>",
+				CHECK_FS_BITMAPS)) {
+		return;
+	}
+	err = strtoblk(argv[0], argv[1], "physical block", &block);
+	if (err)
+		return;
+
+	buf = malloc(current_fs->blocksize);
+	if (!buf) {
+		com_err(argv[0], 0, "Couldn't allocate block buffer");
+		return;
+	}
+
+	errcode = io_channel_read_blk64(current_fs->io, block, 1, buf);
+	if (errcode) {
+		com_err(argv[0], errcode, "while reading block %llu\n",
+			(unsigned long long) block);
+		goto errout;
+	}
+
+	eh = (struct ext3_extent_header *)buf;
+	if (ext2fs_extent_header_verify(eh, current_fs->blocksize))
+		fprintf(stdout, "Warning: block %llu doesn't seem to contain a "
+			"valid extent header\n", block);
+	fprintf(stdout,
+		"header: magic=%x entries=%u max=%u depth=%u generation=%u\n",
+		ext2fs_le16_to_cpu(eh->eh_magic),
+		ext2fs_le16_to_cpu(eh->eh_entries),
+		ext2fs_le16_to_cpu(eh->eh_max),
+		ext2fs_le16_to_cpu(eh->eh_depth),
+		ext2fs_le32_to_cpu(eh->eh_generation));
+
+errout:
+	free(buf);
+}
+
 static int print_blocks_proc(ext2_filsys fs EXT2FS_ATTR((unused)),
 			     blk64_t *blocknr,
 			     e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 39bc0247f527..f1797c660f55 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -144,6 +144,8 @@ extern void do_find_free_block(int argc, char **argv, int sci_idx, void *infop);
 extern void do_find_free_inode(int argc, char **argv, int sci_idx, void *infop);
 extern void do_stat(int argc, char **argv, int sci_idx, void *infop);
 extern void do_dump_extents(int argc, char **argv, int sci_idx, void *infop);
+extern void do_dump_extent_header(int argc, char **argv, int sci_idx, void *infop);
+extern void do_set_extent_header_field(int argc, char **argv, int sci_idx, void *infop);
 extern void do_blocks(int argc, char *argv[], int sci_idx, void *infop);
 
 extern void do_chroot(int argc, char **argv, int sci_idx, void *infop);
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index f916deab8cea..3f74331ae85c 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -47,6 +47,7 @@ static struct ext2_inode_large set_inode;
 static struct ext2_group_desc set_gd;
 static struct ext4_group_desc set_gd4;
 static struct mmp_struct set_mmp;
+static struct ext3_extent_header set_eh;
 static dgrp_t set_bg;
 static ext2_ino_t set_ino;
 static int array_idx;
@@ -297,6 +298,16 @@ static struct field_set_info mmp_fields[] = {
 	{ "checksum", &set_mmp.mmp_checksum, NULL, 4, parse_uint },
 	{ 0, 0, 0, 0 }
 };
+
+static struct field_set_info extent_header_fields[] = {
+	{"magic", &set_eh.eh_magic, NULL, 2, parse_uint },
+	{"entries", &set_eh.eh_entries, NULL, 2, parse_uint },
+	{"max", &set_eh.eh_max, NULL, 2, parse_uint },
+	{"depth", &set_eh.eh_depth, NULL, 2, parse_uint },
+	{"generation", &set_eh.eh_generation, NULL, 4, parse_uint },
+	{0, 0, 0, 0 }
+};
+
 #if __GNUC_PREREQ (4, 6)
 #pragma GCC diagnostic pop
 #endif
@@ -1022,3 +1033,86 @@ void do_set_mmp_value(int argc EXT2FS_ATTR((unused)),
 }
 #endif
 
+void do_set_extent_header_field(int argc, char **argv,
+				int sci_idx EXT2FS_ATTR((unused)),
+				void *infop EXT2FS_ATTR((unused)))
+{
+	struct ext3_extent_header *eh;
+	struct field_set_info	*ss;
+	const char		*usage = "-l | <block> <field> <value>";
+	errcode_t		errcode;
+	blk64_t			block;
+	unsigned char		*buf;
+	int			c, err, force = 0;
+
+	if (check_fs_open(argv[0]) || check_fs_read_write(argv[0]) ||
+	    check_fs_bitmaps(argv[0]))
+		return;
+
+	reset_getopt();
+	while ((c = getopt(argc, argv, "lf")) != EOF) {
+		switch (c) {
+		case 'l':
+			print_possible_fields(extent_header_fields);
+			return;
+			break;
+		case 'f':
+			force = 1;
+			break;
+		default:
+			com_err(argv[0], 0, usage);
+			return;
+		}
+	}
+	if (argc != optind + 3) {
+		com_err(argv[0], 0, usage);
+		return;
+	}
+
+	err = strtoblk(argv[0], argv[optind++], "physical block", &block);
+	if (err)
+		return;
+
+	ss = find_field(extent_header_fields, argv[optind]);
+	if (ss == 0) {
+		com_err(argv[0], 0, "invalid field specifier: %s",
+			argv[optind]);
+		return;
+	}
+
+	buf = malloc(current_fs->blocksize);
+	if (!buf) {
+		com_err(argv[0], 0, "Couldn't allocate block buffer");
+		return;
+	}
+
+	errcode = io_channel_read_blk64(current_fs->io, block, 1, buf);
+	if (errcode) {
+		com_err(argv[0], errcode, "while reading block %llu\n",
+			(unsigned long long) block);
+		goto errout;
+	}
+
+	eh = (struct ext3_extent_header *)buf;
+	if (ext2fs_extent_header_verify(eh, current_fs->blocksize)) {
+		fprintf(stdout, "Warning: block %llu doesn't seem to contain a "
+			"valid extent header.  ", block);
+		if (!force) {
+			fprintf(stdout, "Use '-f' to force\n");
+			goto errout;
+		} else
+			fprintf(stdout, "Forcing write.\n");
+	}
+	memcpy(&set_eh, eh, sizeof(set_eh));
+	if (ss->func(ss, argv[optind], argv[optind + 1]) == 0) {
+		memcpy(eh, &set_eh, sizeof(set_eh));
+		errcode = io_channel_write_blk(current_fs->io, block, 1, buf);
+		if (errcode) {
+			com_err(argv[0], errcode, "while writing block %llu\n",
+				(unsigned long long) block);
+		}
+	}
+
+errout:
+	free(buf);
+}

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ