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 <[email protected]>
Signed-off-by: Kalpak Shah <[email protected]>
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.
On Mar 21, 2007 19:42 +0530, Kalpak Shah wrote:
> 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
> @@ -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)
Ted, can you give a bit of clarification for these different masks.
For EXT2_LIB_SOFTSUPP_RO_COMPAT, it says "These features are only allowed
if EXT2_FLAG_SOFTSUPP_FEATURES is passed to ext2fs_openfs()". If the patch
from Kalpak adds DIR_NLINK to EXT2_LIB_FEATURE_RO_COMPAT_SUPP, does that
mean we should remove it from EXT2_FLAG_SOFTSUPP_FEATURES?
Also, I notice in ext2fs_open2() that there is a bit of code:
#ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT
if (flags & EXT2_FLAG_SOFTSUPP_FEATURES)
features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT;
#endif
But what does "!EXT2_LIB_SOFTSUPP_RO_COMPAT" really mean? Should this be
~EXT2_LIB_SOFTSUPP_RO_COMPAT? Should it be instead:
features &= ~(EXT2_LIB_SOFTSUPP_RO_COMPAT|EXT2_LIB_FEATURE_RO_COMPAT_SUPP)
so that we don't mask out all of the features that are NOT in
SOFTSUPP_RO_COMPAT? Otherwise we've just broken e2fsprogs feature compat
support badly I think. A similar hunk exists for EXT2_LIB_SOFTSUPP_INCOMPAT.
Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.