diff options
author | Theodore Ts'o <tytso@mit.edu> | 2018-06-13 18:41:45 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2018-06-13 18:41:45 -0400 |
commit | c7c99af6bd0a34a3552b31cd622b311a73d03d22 (patch) | |
tree | 56be32004f7dea7442af6d7679cf3fc8411f5f4c /debugfs | |
parent | 1c17667391392bf5b219ab86c3ecf42deda2560e (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.in | 19 | ||||
-rw-r--r-- | debugfs/debugfs.c | 8 | ||||
-rw-r--r-- | debugfs/debugfs.h | 2 | ||||
-rw-r--r-- | debugfs/xattrs.c | 141 | ||||
-rw-r--r-- | debugfs/zap.c | 14 |
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); } |