Blob Blame History Raw
From 5862f45c314aaf97ce098add06c42b5d592dc984 Mon Sep 17 00:00:00 2001
From: Theodore Ts'o <tytso@mit.edu>
Date: Thu, 13 Dec 2018 00:53:16 -0500
Subject: [PATCH 2/4] debugfs: fix set_inode_field so it can set the checksum
 field

Previously, setting the inode field was a no-op, since the library
function ext2fs_write_inode_full() would override whatever value was
set by debugfs.  Use the new ext2fs_write_inode2() interface so we can
in fact set the checksum to a potentially wrong value.  Also, ignore
the inode checksum failures if we are setting the checksum, and if the
checksum value is "calc", set the inode checksum to the correct value.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
---
 debugfs/debugfs.c    | 12 +++----
 debugfs/debugfs.h    |  8 ++---
 debugfs/set_fields.c | 84 +++++++++++++++++++++++++++++++++++++++-----
 debugfs/util.c       | 17 +++++----
 4 files changed, 94 insertions(+), 27 deletions(-)

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index faae12da..06a93270 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -985,8 +985,8 @@ void do_stat(int argc, char *argv[])
 		return;
 	}
 
-	if (debugfs_read_inode_full(inode, inode_buf, argv[0],
-					EXT2_INODE_SIZE(current_fs->super))) {
+	if (debugfs_read_inode2(inode, inode_buf, argv[0],
+				EXT2_INODE_SIZE(current_fs->super), 0)) {
 		free(inode_buf);
 		return;
 	}
@@ -1608,12 +1608,12 @@ void do_copy_inode(int argc, char *argv[])
 	if (!dest_ino)
 		return;
 
-	if (debugfs_read_inode_full(src_ino, (struct ext2_inode *) buf,
-				    argv[0], sizeof(buf)))
+	if (debugfs_read_inode2(src_ino, (struct ext2_inode *) buf,
+				argv[0], sizeof(buf), 0))
 		return;
 
-	if (debugfs_write_inode_full(dest_ino, (struct ext2_inode *) buf,
-				     argv[0], sizeof(buf)))
+	if (debugfs_write_inode2(dest_ino, (struct ext2_inode *) buf,
+				 argv[0], sizeof(buf), 0))
 		return;
 }
 
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 93f036de..97fdde7e 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -54,12 +54,12 @@ extern int common_block_args_process(int argc, char *argv[],
 				     blk64_t *block, blk64_t *count);
 extern int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
 			      const char *cmd);
-extern int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode,
-				   const char *cmd, int bufsize);
+extern int debugfs_read_inode2(ext2_ino_t ino, struct ext2_inode * inode,
+			       const char *cmd, int bufsize, int flags);
 extern int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
 			       const char *cmd);
-extern int debugfs_write_inode_full(ext2_ino_t ino, struct ext2_inode * inode,
-				    const char *cmd, int bufsize);
+extern int debugfs_write_inode2(ext2_ino_t ino, struct ext2_inode * inode,
+				const char *cmd, int bufsize, int flags);
 extern int debugfs_write_new_inode(ext2_ino_t ino, struct ext2_inode * inode,
 				   const char *cmd);
 extern int ext2_file_type(unsigned int mode);
diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index 3cdf617c..4f033249 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -53,6 +53,7 @@ static int array_idx;
 
 #define FLAG_ARRAY	0x0001
 #define FLAG_ALIAS	0x0002	/* Data intersects with other field */
+#define FLAG_CSUM	0x0004
 
 struct field_set_info {
 	const char	*name;
@@ -72,6 +73,8 @@ static errcode_t parse_hashalg(struct field_set_info *info, char *field, char *a
 static errcode_t parse_time(struct field_set_info *info, char *field, char *arg);
 static errcode_t parse_bmap(struct field_set_info *info, char *field, char *arg);
 static errcode_t parse_gd_csum(struct field_set_info *info, char *field, char *arg);
+static errcode_t parse_inode_csum(struct field_set_info *info, char *field,
+				  char *arg);
 static errcode_t parse_mmp_clear(struct field_set_info *info, char *field,
 				 char *arg);
 
@@ -218,7 +221,7 @@ static struct field_set_info inode_fields[] = {
 	{ "frag", &set_inode.osd2.hurd2.h_i_frag, NULL, 1, parse_uint, FLAG_ALIAS },
 	{ "fsize", &set_inode.osd2.hurd2.h_i_fsize, NULL, 1, parse_uint },
 	{ "checksum", &set_inode.osd2.linux2.l_i_checksum_lo, 
-		&set_inode.i_checksum_hi, 2, parse_uint },
+		&set_inode.i_checksum_hi, 2, parse_inode_csum, FLAG_CSUM },
 	{ "author", &set_inode.osd2.hurd2.h_i_author, NULL,
 		4, parse_uint, FLAG_ALIAS },
 	{ "extra_isize", &set_inode.i_extra_isize, NULL,
@@ -665,6 +668,68 @@ static errcode_t parse_gd_csum(struct field_set_info *info, char *field,
 	return parse_uint(info, field, arg);
 }
 
+static errcode_t parse_inode_csum(struct field_set_info *info, char *field,
+				  char *arg)
+{
+	errcode_t	retval = 0;
+	__u32		crc;
+	int		is_large_inode = 0;
+	struct ext2_inode_large *tmp_inode;
+
+	if (strcmp(arg, "calc") == 0) {
+		size_t sz = EXT2_INODE_SIZE(current_fs->super);
+		struct ext2_inode_large *tmp_inode = NULL;
+
+		retval = ext2fs_get_mem(sz, &tmp_inode);
+		if (retval)
+			goto out;
+
+		retval = ext2fs_read_inode_full(current_fs, set_ino,
+				     (struct ext2_inode *) tmp_inode,
+				     sz);
+		if (retval)
+			goto out;
+
+#ifdef WORDS_BIGENDIAN
+		ext2fs_swap_inode_full(current_fs, tmp_inode,
+				       tmp_inode, 1, sz);
+#endif
+
+		if (sz > EXT2_GOOD_OLD_INODE_SIZE)
+			is_large_inode = 1;
+
+		retval = ext2fs_inode_csum_set(current_fs, set_ino,
+					       tmp_inode);
+		if (retval)
+			goto out;
+#ifdef WORDS_BIGENDIAN
+		crc = set_inode.i_checksum_lo =
+			ext2fs_swab16(tmp_inode->i_checksum_lo);
+
+#else
+		crc = set_inode.i_checksum_lo = tmp_inode->i_checksum_lo;
+#endif
+		if (is_large_inode &&
+		    set_inode.i_extra_isize >=
+				(offsetof(struct ext2_inode_large,
+					  i_checksum_hi) -
+				 EXT2_GOOD_OLD_INODE_SIZE)) {
+#ifdef WORDS_BIGENDIAN
+			set_inode.i_checksum_lo =
+				ext2fs_swab16(tmp_inode->i_checksum_lo);
+#else
+			set_inode.i_checksum_hi = tmp_inode->i_checksum_hi;
+#endif
+			crc |= ((__u32)set_inode.i_checksum_hi) << 16;
+		}
+		printf("Checksum set to 0x%08x\n", crc);
+	out:
+		ext2fs_free_mem(&tmp_inode);
+		return retval;
+	}
+	return parse_uint(info, field, arg);
+}
+
 static void print_possible_fields(struct field_set_info *fields)
 {
 	struct field_set_info *ss;
@@ -775,16 +840,19 @@ void do_set_inode(int argc, char *argv[])
 	if (!set_ino)
 		return;
 
-	if (debugfs_read_inode_full(set_ino,
-			(struct ext2_inode *) &set_inode, argv[1],
-				    sizeof(set_inode)))
+	if (debugfs_read_inode2(set_ino,
+				(struct ext2_inode *) &set_inode, argv[1],
+				sizeof(set_inode),
+				(ss->flags & FLAG_CSUM) ?
+				READ_INODE_NOCSUM : 0))
 		return;
 
 	if (ss->func(ss, argv[2], argv[3]) == 0) {
-		if (debugfs_write_inode_full(set_ino, 
-			     (struct ext2_inode *) &set_inode,
-			     argv[1], sizeof(set_inode)))
-			return;
+		debugfs_write_inode2(set_ino,
+				     (struct ext2_inode *) &set_inode,
+				     argv[1], sizeof(set_inode),
+				     (ss->flags & FLAG_CSUM) ?
+				     WRITE_INODE_NOCSUM : 0);
 	}
 }
 
diff --git a/debugfs/util.c b/debugfs/util.c
index 452de749..759bb392 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -420,12 +420,12 @@ int common_block_args_process(int argc, char *argv[],
 	return 0;
 }
 
-int debugfs_read_inode_full(ext2_ino_t ino, struct ext2_inode * inode,
-			const char *cmd, int bufsize)
+int debugfs_read_inode2(ext2_ino_t ino, struct ext2_inode * inode,
+			const char *cmd, int bufsize, int flags)
 {
 	int retval;
 
-	retval = ext2fs_read_inode_full(current_fs, ino, inode, bufsize);
+	retval = ext2fs_read_inode2(current_fs, ino, inode, bufsize, flags);
 	if (retval) {
 		com_err(cmd, retval, "while reading inode %u", ino);
 		return 1;
@@ -446,15 +446,14 @@ int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
 	return 0;
 }
 
-int debugfs_write_inode_full(ext2_ino_t ino,
-			     struct ext2_inode *inode,
-			     const char *cmd,
-			     int bufsize)
+int debugfs_write_inode2(ext2_ino_t ino,
+			 struct ext2_inode *inode,
+			 const char *cmd,
+			 int bufsize, int flags)
 {
 	int retval;
 
-	retval = ext2fs_write_inode_full(current_fs, ino,
-					 inode, bufsize);
+	retval = ext2fs_write_inode2(current_fs, ino, inode, bufsize, flags);
 	if (retval) {
 		com_err(cmd, retval, "while writing inode %u", ino);
 		return 1;
-- 
2.20.1