Search code examples
python-3.xlinuxinodeext4ext2

Is there any fast way to examine ext2/3/4 free inodes (on an unmounted disk) ? and/or why do they not all have the same "bad type" status


Using debugfs to check free inodes (list got from dumpe2fs), I observed that most of them have a bad type status, except for a few ones which are regular. I'd like to check them all to see how many and which of them have a different status.

Therefore I made a little python/pexpect program which starts debugfs and interactively sends stat <inode> requests for all those free inodes to find which or them have other that bad type status.

However, it appears that my program is going to need about 2 years to get all the requested information. What faster way could I use to get those inodes content ?

Optionally, I would be glad to get information about why free inodes can have a regular status


Solution

  • Since I didn't get any simple solution, I ended up patching debugfs by adding a new function do_rstat based on do_stat in debugfs/debugfs.c. Kind of like this :

    void do_rstat(int argc, char *argv[])
    {
            ext3_ino_t      ifrom, ito, inode;
            struct ext3_inode * inode_buf;
            errcode_t r;
    
            if (argc<4) {
                    fprintf(stderr, "do_rstat: needs an inodes range (3 arguments).\n");
                    return;
            }
    
            if (check_fs_open(argv[1]))
                    return;
    
            if (string_to_inode_range(argv[2], argv[2], &ifrom, &ito) & 0x2) {
                    fprintf(stderr, "do_rstat: invalid inodes range.\n");
                    return;
            }
    
            inode_buf = (struct ext3_inode *)
                            malloc(EXT3_INODE_SIZE(current_fs->super));
            if (!inode_buf) {
                    fprintf(stderr, "do_rstat: can't allocate buffer\n");
                    return;
            }
    
            for (inode = ifrom; inode<ito; inode++) {
                    r = debugfs_read_inode_full(inode, inode_buf, argv[1],
                                                    EXT3_INODE_SIZE(current_fs->super));
                    if (r) {
                            FILE    *out;
                            out = open_pager();
                            fprintf(out, "%13ud ERROR %ld\n\n", inode, r);
                            close_pager(out);
                    } else
                            dump_inode(inode, inode_buf);
            }
    
            free(inode_buf);
            return;
    }
    

    (However, I won't detail it here because it's quite specific to my needs, but I also replaced the dump_inode(inode, inode_buf); call to get the output on one line with fixed width fields in order to remove the need for pattern matching in the calling program). In order to do that, I inspired myself from the dump_inode and dump_inode_internal functions.

    and I also needed to add two functions in debugfs/util.c

    /*
     * This routine is used whenever a command needs to turn an <ino> string
     * into an inode.
     */
    ext2_ino_t string_to_inode_number(char *str)
    {
            ext2_ino_t      ino;
            int             len = strlen(str);
            char            *end;
    
            if ((len<2) || (str[0] != '<') || (str[len-1] != '>')) return 0;
            ino=strtoul(str+1, &end, 0);
            if (*end!='>') return -1;
            return ino;
    }
                   
    /*
     * This routine is used whenever a command needs to turn 2 <ino> strings
     * into an inodes range ( [ino1, ino2[ ).
     */
    int string_to_inode_range(char *str1, char *str2, ext2_ino_t *ino1, ext2_ino_t *ino2)
    {
            int invalid = 0;
            ext2_ino_t inox;
    
            *ino1 = string_to_inode_number(str1);
            *ino2 = string_to_inode_number(str2);
            if (*ino2 == -1) {
                    *ino2 = current_fs->super->s_inodes_count+1;
                    invalid |= 0x1;
            }
            if (*ino1 >= *ino2) {
                    inox = *ino1;
                    *ino1 = *ino2;
                    *ino2 = inox;
                    invalid |= 0x2;
            }
            if (*ino1 <= 0) {
                    *ino1 = 1;
                    invalid |= 0x4;
            }
            if (*ino2 > current_fs->super->s_inodes_count+1) {
                    *ino2 = current_fs->super->s_inodes_count+1;
                    invalid |= 0x8;
            }
            return invalid;
    }
    

    I needed to add their prototypes in debugfs/debugfs.h

    extern ext2_ino_t string_to_inode_number(char *str);
    extern int string_to_inode_range(char *str1, char *str2, ext2_ino_t *ino1, ext2_ino_t *ino2);
    

    and i also added an entry for the new commant in debug_commands.ct:

    request do_rstat, "Show inodes information ",
            show_inodes_info, rstat;
    

    and when I call it, i need to make sure the output is piped (like in | cat), in order to deactivate the annoying pager.

    eg:

    > debugfs -cD -R 'rstat <100> <200>' /dev/sdXXX | cat
    

    (Don't forget that /dev/sdXXX must not be mounted)