Whamcloud - gitweb
e2fsck: Fix inode nlink accounting that could cause PROGRAMMING BUG errors
authorTheodore Ts'o <tytso@mit.edu>
Sat, 27 Nov 2010 00:09:43 +0000 (19:09 -0500)
committerTheodore Ts'o <tytso@mit.edu>
Sat, 30 Apr 2011 16:27:14 +0000 (12:27 -0400)
This fixes two possible causes for the error message:

WARNING: PROGRAMMING BUG IN E2FSCK!
        OR SOME BONEHEAD (YOU) IS CHECKING A MOUNTED (LIVE) FILESYSTEM.
inode_link_info[X] is Y, inode.i_links_count is Z.  They should be the same!

One cause which can trigger this message is when an inode has an
illegal link count > 65500 --- for example, 65535.  This was the case
in the Debian Bug report #555456.

Another cause which could trigger this message is if an ext4 directory
previously had more than 65000 subdirectories (thus causing
i_link_count to be set to 1), but then some of the subdirectories were
deleted, such that i_link_count should now be the actual number of
subdirectories.

Addresses-Debian-Bug: #555456

Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
e2fsck/pass4.c

index d9706ce..515cb84 100644 (file)
@@ -121,6 +121,8 @@ void e2fsck_pass4(e2fsck_t ctx)
 
        /* Protect loop from wrap-around if s_inodes_count maxed */
        for (i=1; i <= fs->super->s_inodes_count && i > 0; i++) {
+               int isdir = ext2fs_test_inode_bitmap(ctx->inode_dir_map, i);
+
                if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
                        goto errout;
                if ((i % fs->super->s_inodes_per_group) == 0) {
@@ -153,14 +155,14 @@ void e2fsck_pass4(e2fsck_t ctx)
                        ext2fs_icount_fetch(ctx->inode_count, i,
                                            &link_counted);
                }
-               if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, i) &&
-                   (link_counted > EXT2_LINK_MAX))
+               if (isdir && (link_counted > EXT2_LINK_MAX))
                        link_counted = 1;
                if (link_counted != link_count) {
                        e2fsck_read_inode(ctx, i, inode, "pass4");
                        pctx.ino = i;
                        pctx.inode = inode;
-                       if (link_count != inode->i_links_count) {
+                       if ((link_count != inode->i_links_count) && !isdir &&
+                           (inode->i_links_count <= EXT2_LINK_MAX)) {
                                pctx.num = link_count;
                                fix_problem(ctx,
                                            PR_4_INCONSISTENT_COUNT, &pctx);
@@ -168,10 +170,10 @@ void e2fsck_pass4(e2fsck_t ctx)
                        pctx.num = link_counted;
                        /* i_link_count was previously exceeded, but no longer
                         * is, fix this but don't consider it an error */
-                       if ((LINUX_S_ISDIR(inode->i_mode) && link_counted > 1 &&
+                       if ((isdir && link_counted > 1 &&
                             (inode->i_flags & EXT2_INDEX_FL) &&
                             link_count == 1 && !(ctx->options & E2F_OPT_NO)) ||
-                            (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx))) {
+                           fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) {
                                inode->i_links_count = link_counted;
                                e2fsck_write_inode(ctx, i, inode, "pass4");
                        }