Whamcloud - gitweb
debugfs: add support to display details of extended attribute structures
authorTheodore Ts'o <tytso@mit.edu>
Wed, 13 Jun 2018 22:41:45 +0000 (18:41 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Wed, 13 Jun 2018 22:41:45 +0000 (18:41 -0400)
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>
debugfs/debugfs.8.in
debugfs/debugfs.c
debugfs/debugfs.h
debugfs/xattrs.c
debugfs/zap.c

index 358f802..393c000 100644 (file)
@@ -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)
index 3780d39..cbcfa24 100644 (file)
@@ -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);
 }
index 4f25850..3322d05 100644 (file)
@@ -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);
index 390165c..6c32712 100644 (file)
@@ -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);
+}
index 047e785..a849b90 100644 (file)
@@ -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);
 }