Hi, This patch includes the changes required to e2fsck to understand the nlink count changes made in the kernel. In pass2, while counting the links for a directory, if the link count exceeds 65000, its permanently set to EXT2_NLINK_MAXED (EXT2_LINK_MAX + 100). In pass4, when the counted and actual nlink counts are compared, e2fsck does not flag an error if counted links = EXT2_NLINK_MAXED and existing link count is 1. It also handles the case when a directory had more than 65000 subdirs and they were later deleted. The nlink count of such a directory remains 1. In pass4 if counted links are 2 and if existing nlink count = 1, e2fsck corrects the nlink count without displaying any errors. Signed-off-by: Andreas Dilger <adilger@xxxxxxxxxxxxx> Signed-off-by: Kalpak Shah <kalpak@xxxxxxxxxxxxx> Index: e2fsprogs-1.40/e2fsck/pass4.c =================================================================== --- e2fsprogs-1.40.orig/e2fsck/pass4.c +++ e2fsprogs-1.40/e2fsck/pass4.c @@ -145,7 +145,9 @@ void e2fsck_pass4(e2fsck_t ctx) ext2fs_icount_fetch(ctx->inode_count, i, &link_counted); } - if (link_counted != link_count) { + if (link_counted != link_count && + !(ext2fs_test_inode_bitmap(ctx->inode_dir_map, i) && + link_count == 1 && link_counted == EXT2_NLINK_MAXED)) { e2fsck_read_inode(ctx, i, &inode, "pass4"); pctx.ino = i; pctx.inode = &inode; @@ -155,12 +157,37 @@ void e2fsck_pass4(e2fsck_t ctx) PR_4_INCONSISTENT_COUNT, &pctx); } pctx.num = link_counted; - if (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx)) { + /* 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 && + (inode.i_flags & EXT2_INDEX_FL) && + link_count == 1 && !(ctx->options & E2F_OPT_NO)) || + (fix_problem(ctx, PR_4_BAD_REF_COUNT, &pctx))) { inode.i_links_count = link_counted; e2fsck_write_inode(ctx, i, &inode, "pass4"); } } + if (link_counted == EXT2_NLINK_MAXED) + ctx->fs_many_subdirs++; } + if (ctx->fs_many_subdirs) { + if (!(fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_DIR_NLINK) && + fix_problem(ctx, PR_4_FEATURE_DIR_NLINK, &pctx)) { + fs->super->s_feature_ro_compat |= + EXT4_FEATURE_RO_COMPAT_DIR_NLINK; + ext2fs_mark_super_dirty(fs); + } + } else if (!ctx->fs_many_subdirs && + (fs->super->s_feature_ro_compat & + EXT4_FEATURE_RO_COMPAT_DIR_NLINK)) { + if (fs->flags & EXT2_FLAG_RW) { + fs->super->s_feature_ro_compat &= + ~EXT4_FEATURE_RO_COMPAT_DIR_NLINK; + ext2fs_mark_super_dirty(fs); + } + } + ext2fs_free_icount(ctx->inode_link_info); ctx->inode_link_info = 0; ext2fs_free_icount(ctx->inode_count); ctx->inode_count = 0; ext2fs_free_inode_bitmap(ctx->inode_bb_map); Index: e2fsprogs-1.40/lib/ext2fs/ext2_fs.h =================================================================== --- e2fsprogs-1.40.orig/lib/ext2fs/ext2_fs.h +++ e2fsprogs-1.40/lib/ext2fs/ext2_fs.h @@ -635,6 +635,7 @@ struct ext2_super_block { #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) /* Index: e2fsprogs-1.40/lib/ext2fs/ext2fs.h =================================================================== --- e2fsprogs-1.40.orig/lib/ext2fs/ext2fs.h +++ e2fsprogs-1.40/lib/ext2fs/ext2fs.h @@ -403,6 +403,9 @@ typedef struct ext2_struct_inode_scan *e typedef struct ext2_icount *ext2_icount_t; +/* To handle the case when a directory has nlink = 1, but is empty. */ +#define EXT2_NLINK_MAXED EXT2_LINK_MAX + 100 + /* * Flags for ext2fs_bmap */ @@ -460,7 +463,8 @@ typedef struct ext2_icount *ext2_icount_ EXT3_FEATURE_INCOMPAT_RECOVER) #endif #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ - EXT2_FEATURE_RO_COMPAT_LARGE_FILE) + EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK) /* * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed @@ -791,8 +795,12 @@ extern errcode_t ext2fs_create_icount(ex ext2_icount_t *ret); extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); +extern errcode_t ext2fs_icount_inc32(ext2_icount_t icount, ext2_ino_t ino, + __u32 *ret, int is_dir); extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); +extern errcode_t ext2fs_icount_dec32(ext2_icount_t icount, ext2_ino_t ino, + __u32 *ret, int is_dir); extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, Index: e2fsprogs-1.40/e2fsck/pass2.c =================================================================== --- e2fsprogs-1.40.orig/e2fsck/pass2.c +++ e2fsprogs-1.40/e2fsck/pass2.c @@ -710,7 +710,7 @@ static int check_dir_block(ext2_filsys f int dot_state; blk_t block_nr = db->blk; ext2_ino_t ino = db->ino; - __u16 links; + __u32 links; struct check_dir_struct *cd; char *buf; e2fsck_t ctx; @@ -1014,9 +1014,10 @@ static int check_dir_block(ext2_filsys f dups_found++; } else dict_alloc_insert(&de_dict, dirent, dirent); - - ext2fs_icount_increment(ctx->inode_count, dirent->inode, - &links); + + ext2fs_icount_inc32(ctx->inode_count, dirent->inode, &links, + ext2fs_test_inode_bitmap(ctx->inode_dir_map, + dirent->inode)); if (links > 1) ctx->fs_links_count++; ctx->fs_total_count++; Index: e2fsprogs-1.40/lib/ext2fs/icount.c =================================================================== --- e2fsprogs-1.40.orig/lib/ext2fs/icount.c +++ e2fsprogs-1.40/lib/ext2fs/icount.c @@ -321,8 +321,8 @@ errcode_t ext2fs_icount_fetch(ext2_icoun return 0; } -errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, - __u16 *ret) +errcode_t ext2fs_icount_inc32(ext2_icount_t icount, ext2_ino_t ino, + __u32 *ret, int is_dir) { struct ext2_icount_el *el; @@ -381,14 +381,29 @@ errcode_t ext2fs_icount_increment(ext2_i } if (icount->multiple) ext2fs_mark_inode_bitmap(icount->multiple, ino); + if (el->count >= EXT2_LINK_MAX && is_dir) + el->count = EXT2_NLINK_MAXED; if (ret) *ret = el->count; return 0; } -errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, +errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { + __u32 links; + errcode_t err; + + err = ext2fs_icount_inc32(icount, ino, &links, 0); + if (ret) + *ret = links; + + return err; +} + +errcode_t ext2fs_icount_dec32(ext2_icount_t icount, ext2_ino_t ino, + __u32 *ret, int is_dir) +{ struct ext2_icount_el *el; if (!ino || (ino > icount->num_inodes)) @@ -429,6 +444,19 @@ errcode_t ext2fs_icount_decrement(ext2_i return 0; } +errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, + __u16 *ret) +{ + __u32 links; + errcode_t err; + + err = ext2fs_icount_dec32(icount, ino, &links, 0); + if (ret) + *ret = links; + + return err; +} + errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, __u16 count) { Index: e2fsprogs-1.40/e2fsck/e2fsck.c =================================================================== --- e2fsprogs-1.40.orig/e2fsck/e2fsck.c +++ e2fsprogs-1.40/e2fsck/e2fsck.c @@ -150,6 +150,7 @@ errcode_t e2fsck_reset_context(e2fsck_t ctx->fs_tind_count = 0; ctx->fs_fragmented = 0; ctx->large_files = 0; + ctx->fs_many_subdirs = 0; /* Reset the superblock to the user's requested value */ ctx->superblock = ctx->use_superblock; Index: e2fsprogs-1.40/e2fsck/e2fsck.h =================================================================== --- e2fsprogs-1.40.orig/e2fsck/e2fsck.h +++ e2fsprogs-1.40/e2fsck/e2fsck.h @@ -327,6 +327,7 @@ struct e2fsck_struct { __u32 large_files; __u32 fs_ext_attr_inodes; __u32 fs_ext_attr_blocks; + __u32 fs_many_subdirs; time_t now; Index: e2fsprogs-1.40/e2fsck/problem.c =================================================================== --- e2fsprogs-1.40.orig/e2fsck/problem.c +++ e2fsprogs-1.40/e2fsck/problem.c @@ -1366,6 +1366,12 @@ static struct e2fsck_problem problem_tab "They @s the same!\n"), PROMPT_NONE, 0 }, + /* DIR_NLINK flag not set but dirs with > 65000 subdirs found */ + { PR_4_FEATURE_DIR_NLINK, + N_("@f contains directories with > 65000 subdirs, but lacks " + "DIR_NLINK flag in @S.\n"), + PROMPT_FIX, 0 }, + /* Pass 5 errors */ /* Pass 5: Checking group summary information */ Index: e2fsprogs-1.40/e2fsck/problem.h =================================================================== --- e2fsprogs-1.40.orig/e2fsck/problem.h +++ e2fsprogs-1.40/e2fsck/problem.h @@ -821,6 +821,10 @@ struct problem_context { /* Inconsistent inode count information cached */ #define PR_4_INCONSISTENT_COUNT 0x040004 +/* Directory with > EXT2_LINK_MAX subdirs found but + * EXT4_FEATURE_RO_COMPAT_DIR_NLINK flag is reset */ +#define PR_4_FEATURE_DIR_NLINK 0x040005 + /* * Pass 5 errors */ Thanks, Kalpak Shah. - To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html