summaryrefslogtreecommitdiff
path: root/debugfs
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2018-06-13 18:41:45 -0400
committerTheodore Ts'o <tytso@mit.edu>2018-06-13 18:41:45 -0400
commitc7c99af6bd0a34a3552b31cd622b311a73d03d22 (patch)
tree56be32004f7dea7442af6d7679cf3fc8411f5f4c /debugfs
parent1c17667391392bf5b219ab86c3ecf42deda2560e (diff)
debugfs: add support to display details of extended attribute structures
Teach the inode_dump and block_dump commands the -x option, which tries to interpret the data structures as xattr data strucgtures. Signed-off-by: Theodore Ts'o <tytso@mit.edu>
Diffstat (limited to 'debugfs')
-rw-r--r--debugfs/debugfs.8.in19
-rw-r--r--debugfs/debugfs.c8
-rw-r--r--debugfs/debugfs.h2
-rw-r--r--debugfs/xattrs.c141
-rw-r--r--debugfs/zap.c14
5 files changed, 173 insertions, 11 deletions
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 358f802b..393c0009 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -190,16 +190,21 @@ Print or set the physical block number corresponding to the logical block number
in the inode
.IR filespec .
If the
-.I -a
+.I \-a
flag is specified, try to allocate a block if necessary.
.TP
-.BI block_dump " [-f filespec] block_num"
+.BI block_dump " '[ -x ] [-f filespec] block_num"
Dump the filesystem block given by
.I block_num
in hex and ASCII format to the console. If the
-.I -f
+.I \-f
option is specified, the block number is relative to the start of the given
.BR filespec .
+If the
+.I \-x
+option is specified, the block is interpreted as an extended attribute
+block and printed to show the structure of extended attribute data
+structures.
.TP
.BI cat " filespec"
Dump the contents of the inode
@@ -424,7 +429,7 @@ showing its tree structure.
Print a listing of the inodes which use the one or more blocks specified
on the command line.
.TP
-.BI inode_dump " [-b]|[-e] filespec"
+.BI inode_dump " [-b]|[-e]|[-x] filespec"
Print the contents of the inode data structure in hex and ASCII format.
The
.I \-b
@@ -433,7 +438,11 @@ option causes the command to only dump the contents of the
array. The
.I \-e
option causes the command to only dump the contents of the extra inode
-space, which is used to store in-line extended attributes.
+space, which is used to store in-line extended attributes. The
+.I \-x
+option causes the command to dump the extra inode space interpreted and
+extended attributes. This is useful to debug corrupted inodes
+containing extended attributes.
.TP
.BI imap " filespec"
Print the location of the inode data structure (in the inode table)
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 3780d394..cbcfa24c 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -2105,7 +2105,7 @@ void do_idump(int argc, char *argv[])
int c, mode = 0;
reset_getopt();
- while ((c = getopt (argc, argv, "be")) != EOF) {
+ while ((c = getopt (argc, argv, "bex")) != EOF) {
if (mode || c == '?') {
print_usage:
com_err(argv[0], 0,
@@ -2145,6 +2145,7 @@ void do_idump(int argc, char *argv[])
offset = ((char *) (&inode->i_block)) - ((char *) buf);
size = sizeof(inode->i_block);
break;
+ case 'x':
case 'e':
if (size <= EXT2_GOOD_OLD_INODE_SIZE) {
no_extra_space:
@@ -2157,7 +2158,10 @@ void do_idump(int argc, char *argv[])
size -= offset;
break;
}
- do_byte_hexdump(stdout, buf + offset, size);
+ if (mode == 'x')
+ raw_inode_xattr_dump(stdout, buf + offset, size);
+ else
+ do_byte_hexdump(stdout, buf + offset, size);
err:
ext2fs_free_mem(&buf);
}
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 4f25850d..3322d05a 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -198,6 +198,8 @@ void do_get_xattr(int argc, char **argv);
void do_set_xattr(int argc, char **argv);
void do_rm_xattr(int argc, char **argv);
void do_list_xattr(int argc, char **argv);
+void raw_inode_xattr_dump(FILE *f, unsigned char *buf, unsigned int len);
+void block_xattr_dump(FILE *f, unsigned char *buf, unsigned int len);
/* zap.c */
extern void do_zap_block(int argc, char **argv);
diff --git a/debugfs/xattrs.c b/debugfs/xattrs.c
index 390165c2..6c32712a 100644
--- a/debugfs/xattrs.c
+++ b/debugfs/xattrs.c
@@ -358,3 +358,144 @@ out:
if (err)
com_err(argv[0], err, "while removing extended attribute");
}
+
+/*
+ * Return non-zero if the string has a minimal number of non-printable
+ * characters.
+ */
+static int is_mostly_printable(const char *cp, int len)
+{
+ int np = 0;
+
+ if (len < 0)
+ len = strlen(cp);
+
+ while (len--) {
+ if (!isprint(*cp++)) {
+ np++;
+ if (np > 3)
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void safe_print(FILE *f, const char *cp, int len)
+{
+ unsigned char ch;
+
+ if (len < 0)
+ len = strlen(cp);
+
+ while (len--) {
+ ch = *cp++;
+ if (ch > 128) {
+ fputs("M-", f);
+ ch -= 128;
+ }
+ if ((ch < 32) || (ch == 0x7f)) {
+ fputc('^', f);
+ ch ^= 0x40; /* ^@, ^A, ^B; ^? for DEL */
+ }
+ fputc(ch, f);
+ }
+}
+
+static void dump_xattr_raw_entries(FILE *f, unsigned char *buf,
+ unsigned int start, unsigned int len,
+ unsigned value_start)
+{
+ struct ext2_ext_attr_entry ent;
+ char *name;
+ unsigned int off = start;
+ unsigned int vstart;
+
+ while (off < len) {
+ if ((*(__u16 *) (buf + off)) == 0) {
+ fprintf(f, "last entry found at offset %u (%04o)\n",
+ off, off);
+ break;
+ }
+ if ((off + sizeof(struct ext2_ext_attr_entry)) >= len) {
+ overrun:
+ fprintf(f, "xattr buffer overrun at %u (len = %u)\n",
+ off, len);
+ break;
+ }
+#if WORDS_BIGENDIAN
+ ext2fs_swap_ext_attr_entry(&ent,
+ (struct ext2_ext_attr_entry *) (buf + off));
+#else
+ ent = *((struct ext2_ext_attr_entry *) (buf + off));
+#endif
+ fprintf(f, "offset = %d (%04o), name_len = %u, "
+ "name_index = %u\n",
+ off, off, ent.e_name_len, ent.e_name_index);
+ vstart = value_start + ent.e_value_offs;
+ fprintf(f, "value_offset = %d (%04o), value_inum = %u, "
+ "value_size = %u\n", ent.e_value_offs,
+ vstart, ent.e_value_inum, ent.e_value_size);
+ off += sizeof(struct ext2_ext_attr_entry);
+ fprintf(f, "name = ");
+ if ((off + ent.e_name_len) >= len)
+ fprintf(f, "<runs off end>");
+ else
+ safe_print(f, (char *)(buf + off), ent.e_name_len);
+ fputc('\n', f);
+ if (ent.e_value_size == 0)
+ goto skip_value;
+ fprintf(f, "value = ");
+ if (ent.e_value_inum)
+ fprintf(f, "<ino %u>", ent.e_value_inum);
+ else if (ent.e_value_offs >= len ||
+ (vstart + ent.e_value_size) > len)
+ fprintf(f, "<runs off end>");
+ if (is_mostly_printable((char *)(buf + vstart),
+ ent.e_value_size))
+ safe_print(f, (char *)(buf + vstart),
+ ent.e_value_size);
+ else {
+ fprintf(f, "<hexdump>\n");
+ do_byte_hexdump(f, (char *)(buf + vstart),
+ ent.e_value_size);
+ }
+ fputc('\n', f);
+ skip_value:
+ fputc('\n', f);
+ off += (ent.e_name_len + 3) & ~3;
+ }
+}
+
+void raw_inode_xattr_dump(FILE *f, unsigned char *buf, unsigned int len)
+{
+ __u32 magic = ext2fs_le32_to_cpu(*((__le32 *) buf));
+
+ fprintf(f, "magic = %08x, length = %u, value_start =4 \n\n",
+ magic, len);
+ if (magic == EXT2_EXT_ATTR_MAGIC)
+ dump_xattr_raw_entries(f, buf, 4, len, 4);
+}
+
+void block_xattr_dump(FILE *f, unsigned char *buf, unsigned int len)
+{
+ struct ext2_ext_attr_header header;
+
+#ifdef WORDS_BIGENDIAN
+ ext2fs_swap_ext_attr_header(&header,
+ (struct ext2_ext_attr_header *) buf);
+#else
+ header = *((struct ext2_ext_attr_header *) buf);
+#endif
+ fprintf(f, "magic = %08x, length = %u\n", header.h_magic, len);
+ if (header.h_magic != EXT2_EXT_ATTR_MAGIC)
+ return;
+ fprintf(f, "refcount = %u, blocks = %u\n", header.h_refcount,
+ header.h_blocks);
+ fprintf(f, "hash = %08x, checksum = %08x\n", header.h_hash,
+ header.h_checksum);
+ fprintf(f, "reserved: %08x %08x %08x\n\n", header.h_reserved[0],
+ header.h_reserved[1], header.h_reserved[2]);
+
+ dump_xattr_raw_entries(f, buf,
+ sizeof(struct ext2_ext_attr_header), len, 0);
+}
diff --git a/debugfs/zap.c b/debugfs/zap.c
index 047e7856..a849b90b 100644
--- a/debugfs/zap.c
+++ b/debugfs/zap.c
@@ -174,18 +174,21 @@ void do_block_dump(int argc, char *argv[])
errcode_t errcode;
blk64_t block;
char *file = NULL;
+ int xattr_dump = 0;
int c, err;
if (check_fs_open(argv[0]))
return;
reset_getopt();
- while ((c = getopt (argc, argv, "f:")) != EOF) {
+ while ((c = getopt (argc, argv, "f:x")) != EOF) {
switch (c) {
case 'f':
file = optarg;
break;
-
+ case 'x':
+ xattr_dump = 1;
+ break;
default:
goto print_usage;
}
@@ -193,7 +196,7 @@ void do_block_dump(int argc, char *argv[])
if (argc != optind + 1) {
print_usage:
- com_err(0, 0, "Usage: block_dump [-f inode] block_num");
+ com_err(0, 0, "Usage: block_dump [-x] [-f inode] block_num");
return;
}
@@ -227,7 +230,10 @@ void do_block_dump(int argc, char *argv[])
goto errout;
}
- do_byte_hexdump(stdout, buf, current_fs->blocksize);
+ if (xattr_dump)
+ block_xattr_dump(stdout, buf, current_fs->blocksize);
+ else
+ do_byte_hexdump(stdout, buf, current_fs->blocksize);
errout:
free(buf);
}