2007-07-24 11:04:19

by Girish Shilamkar

[permalink] [raw]
Subject: [Patch 6/13] Allow more than 32000 subdirectories

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.1/e2fsck/pass2.c
===================================================================
--- e2fsprogs-1.40.1.orig/e2fsck/pass2.c
+++ e2fsprogs-1.40.1/e2fsck/pass2.c
@@ -717,7 +717,7 @@ static int check_dir_block(ext2_filsys f
blk_t block_nr = db->blk;
ext2_ino_t ino = db->ino;
ext2_ino_t subdir_parent;
- __u16 links;
+ __u32 links;
struct check_dir_struct *cd;
char *buf;
e2fsck_t ctx;
@@ -1024,9 +1024,11 @@ 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) ?
+ EXT2_LINK_MAX : (__u32)~0U);
if (links > 1)
ctx->fs_links_count++;
ctx->fs_total_count++;
Index: e2fsprogs-1.40.1/e2fsck/pass4.c
===================================================================
--- e2fsprogs-1.40.1.orig/e2fsck/pass4.c
+++ e2fsprogs-1.40.1/e2fsck/pass4.c
@@ -99,7 +99,8 @@ void e2fsck_pass4(e2fsck_t ctx)
struct resource_track rtrack;
#endif
struct problem_context pctx;
- __u16 link_count, link_counted;
+ __u16 link_count;
+ __u32 link_counted;
char *buf = 0;
int group, maxgroup;

@@ -145,7 +146,7 @@ void e2fsck_pass4(e2fsck_t ctx)
ext2fs_test_inode_bitmap(ctx->inode_bb_map, i)))
continue;
ext2fs_icount_fetch(ctx->inode_link_info, i, &link_count);
- ext2fs_icount_fetch(ctx->inode_count, i, &link_counted);
+ ext2fs_icount_fetch32(ctx->inode_count, i, &link_counted);
if (link_counted == 0) {
if (!buf)
buf = e2fsck_allocate_memory(ctx,
@@ -156,10 +157,12 @@ void e2fsck_pass4(e2fsck_t ctx)
continue;
ext2fs_icount_fetch(ctx->inode_link_info, i,
&link_count);
- ext2fs_icount_fetch(ctx->inode_count, i,
- &link_counted);
+ ext2fs_icount_fetch32(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_LINK_MAX)) {
e2fsck_read_inode(ctx, i, inode, "pass4");
pctx.ino = i;
pctx.inode = inode;
@@ -169,7 +172,12 @@ 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");
}
Index: e2fsprogs-1.40.1/lib/ext2fs/ext2_fs.h
===================================================================
--- e2fsprogs-1.40.1.orig/lib/ext2fs/ext2_fs.h
+++ e2fsprogs-1.40.1/lib/ext2fs/ext2_fs.h
@@ -646,6 +646,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.1/lib/ext2fs/ext2fs.h
===================================================================
--- e2fsprogs-1.40.1.orig/lib/ext2fs/ext2fs.h
+++ e2fsprogs-1.40.1/lib/ext2fs/ext2fs.h
@@ -462,7 +462,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
@@ -471,7 +472,6 @@ typedef struct ext2_icount *ext2_icount_
#define EXT2_LIB_SOFTSUPP_INCOMPAT (EXT3_FEATURE_INCOMPAT_EXTENTS)
#define EXT2_LIB_SOFTSUPP_RO_COMPAT (EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
EXT4_FEATURE_RO_COMPAT_GDT_CSUM|\
- EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\
EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)

/*
@@ -795,12 +795,20 @@ extern errcode_t ext2fs_create_icount2(e
extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags,
unsigned int size,
ext2_icount_t *ret);
+extern errcode_t ext2fs_icount_fetch32(ext2_icount_t icount, ext2_ino_t ino,
+ __u32 *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, __u32 overflow);
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);
extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
__u16 *ret);
+extern errcode_t ext2fs_icount_store32(ext2_icount_t icount, ext2_ino_t ino,
+ __u32 count);
extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
__u16 count);
extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount);
Index: e2fsprogs-1.40.1/lib/ext2fs/icount.c
===================================================================
--- e2fsprogs-1.40.1.orig/lib/ext2fs/icount.c
+++ e2fsprogs-1.40.1/lib/ext2fs/icount.c
@@ -43,7 +43,7 @@

struct ext2_icount_el {
ext2_ino_t ino;
- __u16 count;
+ __u32 count;
};

struct ext2_icount {
@@ -397,16 +397,16 @@ static struct ext2_icount_el *get_icount
}

static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino,
- __u16 count)
+ __u32 count)
{
- struct ext2_icount_el *el;
+ struct ext2_icount_el *el;
TDB_DATA key, data;

if (icount->tdb) {
key.dptr = (unsigned char *) &ino;
key.dsize = sizeof(ext2_ino_t);
data.dptr = (unsigned char *) &count;
- data.dsize = sizeof(__u16);
+ data.dsize = sizeof(__u32);
if (count) {
if (tdb_store(icount->tdb, key, data, TDB_REPLACE))
return tdb_error(icount->tdb) +
@@ -428,9 +428,9 @@ static errcode_t set_inode_count(ext2_ic
}

static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino,
- __u16 *count)
+ __u32 *count)
{
- struct ext2_icount_el *el;
+ struct ext2_icount_el *el;
TDB_DATA key, data;

if (icount->tdb) {
@@ -443,7 +443,7 @@ static errcode_t get_inode_count(ext2_ic
return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS;
}

- *count = *((__u16 *) data.dptr);
+ *count = *((__u32 *) data.dptr);
free(data.dptr);
return 0;
}
@@ -480,7 +480,7 @@ errcode_t ext2fs_icount_validate(ext2_ic
return ret;
}

-errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
+errcode_t ext2fs_icount_fetch32(ext2_icount_t icount, ext2_ino_t ino, __u32 *ret)
{
EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);

@@ -500,10 +500,21 @@ 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_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret)
{
- __u16 curr_value;
+ __u32 ret32 = ret ? *ret : 0;
+ errcode_t err;
+
+ err = ext2fs_icount_fetch32(icount, ino, &ret32);
+ *ret = (__u16)ret32;
+
+ return err;
+}
+
+errcode_t ext2fs_icount_inc32(ext2_icount_t icount, ext2_ino_t ino,
+ __u32 *ret, __u32 overflow)
+{
+ __u32 curr_value;

EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT);

@@ -528,6 +539,8 @@ errcode_t ext2fs_icount_increment(ext2_i
if (ext2fs_test_inode_bitmap(icount->multiple, ino)) {
get_inode_count(icount, ino, &curr_value);
curr_value++;
+ if (curr_value >= overflow)
+ curr_value = overflow + 10;
if (set_inode_count(icount, ino, curr_value))
return EXT2_ET_NO_MEMORY;
} else {
@@ -547,6 +560,8 @@ errcode_t ext2fs_icount_increment(ext2_i
*/
get_inode_count(icount, ino, &curr_value);
curr_value++;
+ if (curr_value >= overflow)
+ curr_value = overflow + 10;
if (set_inode_count(icount, ino, curr_value))
return EXT2_ET_NO_MEMORY;
}
@@ -557,10 +572,23 @@ errcode_t ext2fs_icount_increment(ext2_i
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)
{
- __u16 curr_value;
+ __u32 ret32 = ret ? *ret : 0;
+ errcode_t err;
+
+ err = ext2fs_icount_inc32(icount, ino, &ret32, (__u16)~0U);
+ if (ret)
+ *ret = ret32;
+
+ return err;
+}
+
+errcode_t ext2fs_icount_dec32(ext2_icount_t icount, ext2_ino_t ino,
+ __u32 *ret)
+{
+ __u32 curr_value;

if (!ino || (ino > icount->num_inodes))
return EXT2_ET_INVALID_ARGUMENT;
@@ -600,8 +628,21 @@ errcode_t ext2fs_icount_decrement(ext2_i
return 0;
}

-errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
- __u16 count)
+errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 *ret)
+{
+ __u32 ret32 = ret ? *ret : 0;
+ errcode_t err;
+
+ err = ext2fs_icount_dec32(icount, ino, &ret32);
+ if (ret)
+ *ret = ret32;
+
+ return err;
+}
+
+errcode_t ext2fs_icount_store32(ext2_icount_t icount, ext2_ino_t ino,
+ __u32 count)
{
if (!ino || (ino > icount->num_inodes))
return EXT2_ET_INVALID_ARGUMENT;
@@ -635,6 +676,12 @@ errcode_t ext2fs_icount_store(ext2_icoun
return 0;
}

+errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino,
+ __u16 count)
+{
+ return ext2fs_icount_store32(icount, ino, count);
+}
+
ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount)
{
if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT)
Index: e2fsprogs-1.40.1/e2fsck/pass3.c
===================================================================
--- e2fsprogs-1.40.1.orig/e2fsck/pass3.c
+++ e2fsprogs-1.40.1/e2fsck/pass3.c
@@ -581,19 +581,22 @@ errcode_t e2fsck_adjust_inode_count(e2fs
#endif

if (adj == 1) {
- ext2fs_icount_increment(ctx->inode_count, ino, 0);
+ ext2fs_icount_inc32(ctx->inode_count, ino, 0,
+ ext2fs_test_inode_bitmap(ctx->inode_dir_map,
+ ino) ?
+ EXT2_LINK_MAX : ~0U);
if (inode.i_links_count == (__u16) ~0)
return 0;
ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
inode.i_links_count++;
} else if (adj == -1) {
- ext2fs_icount_decrement(ctx->inode_count, ino, 0);
+ ext2fs_icount_dec32(ctx->inode_count, ino, 0);
if (inode.i_links_count == 0)
return 0;
ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
inode.i_links_count--;
}
-
+
retval = ext2fs_write_inode(fs, ino, &inode);
if (retval)
return retval;


2007-08-04 13:55:57

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [Patch 6/13] Allow more than 32000 subdirectories

On Tue, Jul 24, 2007 at 04:34:56PM +0530, Girish Shilamkar wrote:
> 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.

Has this actually been implemented in the kernel? I don't think I've
seen a patch which implements the a large number file links. The
65000 subdir patch does *not* define EXT2_NLINK_MAXED, nor does this
patch. And this patch seems to use EXT2_LINK_MAX + 10, not +100.

- Ted

2007-08-04 16:47:26

by Andreas Dilger

[permalink] [raw]
Subject: Re: [Patch 6/13] Allow more than 32000 subdirectories

On Aug 04, 2007 09:51 -0400, Theodore Tso wrote:
> On Tue, Jul 24, 2007 at 04:34:56PM +0530, Girish Shilamkar wrote:
> > 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.
>
> Has this actually been implemented in the kernel? I don't think I've
> seen a patch which implements the a large number file links. The
> 65000 subdir patch does *not* define EXT2_NLINK_MAXED, nor does this
> patch. And this patch seems to use EXT2_LINK_MAX + 10, not +100.

I haven't tested hard-linked files myself, but they should remain with
a limit of EXT2_LINK_MAX. It is only for directories that the link
count can be exceeded. We have definitely tested with > 65000 subdirs.

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.