Skip to contentSkip to conversation

quota-related e2fsck fixes and tests

PATCH Superseded Luis Henriques (SUSE) 2 participants 10 messages Mar 28, 2024 – Apr 3, 2024

v1 of 3 Superseded Latest is v3 →

lore
Share & tools
lore
#0 lore raw

cover quota-related e2fsck fixes and tests

Hi!

I'm sending two fixes to e2fsck that are related with quota handling. The fixes are the first two patches, the other two are test cases for these fixes.

The first patch is actually re-send, which Andreas Dilger had already kindly reviewed and suggested to add a test for it. One (important!) thing I forgot to mention and to include in that initial email was that there _is_ already a test case for it: it's in the fstests suite, ext4/014. That's how I found the issue initially. Thus, my test case for e2fsck is nothing but a filesystem generated with a simplified version of that test.

As for the second issue, it was also found by an fstest, ext4/019, and the test I'm sending is also based on it.

Cheers,

Signature · 27 lines
Luis

Luis Henriques (SUSE) (4):
  e2fsck: update quota accounting after directory optimization
  e2fsck: update quota when deallocating a bad inode
  tests: new test to check quota after directory optimization
  tests: new test to check quota after a bad inode deallocation

 e2fsck/pass2.c                          |  33 +++++++++++++------
 e2fsck/rehash.c                         |  27 ++++++++++++----
 tests/f_quota_deallocate_inode/expect.1 |  18 +++++++++++
 tests/f_quota_deallocate_inode/expect.2 |   7 +++++
 tests/f_quota_deallocate_inode/image.gz | Bin 0 -> 11594 bytes
 tests/f_quota_deallocate_inode/name     |   1 +
 tests/f_quota_shrinkdir/expect.1        |  40 ++++++++++++++++++++++++
 tests/f_quota_shrinkdir/expect.2        |   7 +++++
 tests/f_quota_shrinkdir/image.gz        | Bin 0 -> 11453 bytes
 tests/f_quota_shrinkdir/name            |   1 +
 10 files changed, 118 insertions(+), 16 deletions(-)
 create mode 100644 tests/f_quota_deallocate_inode/expect.1
 create mode 100644 tests/f_quota_deallocate_inode/expect.2
 create mode 100644 tests/f_quota_deallocate_inode/image.gz
 create mode 100644 tests/f_quota_deallocate_inode/name
 create mode 100644 tests/f_quota_shrinkdir/expect.1
 create mode 100644 tests/f_quota_shrinkdir/expect.2
 create mode 100644 tests/f_quota_shrinkdir/image.gz
 create mode 100644 tests/f_quota_shrinkdir/name
#1 lore raw

PATCH 1/4 e2fsck: update quota accounting after directory optimization

In "Pass 3A: Optimizing directories", a directory may have it's size reduced. If that happens and quota is enabled in the filesystem, the quota information will be incorrect because it doesn't take the rehash into account. This issue was detected by running fstest ext4/014.

This patch simply updates the quota data accordingly, after the directory is written and it's size has been updated.

---
 e2fsck/rehash.c | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)
e2fsck/rehash.c +21 -6
index c1da7d52724e..4847d172e5fe 100644
@@ -987,14 +987,18 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
 {
 	ext2_filsys 		fs = ctx->fs;
 	errcode_t		retval;
-	struct ext2_inode 	inode;
+	struct ext2_inode_large	inode;
 	char			*dir_buf = 0;
 	struct fill_dir_struct	fd = { NULL, NULL, 0, 0, 0, NULL,
 				       0, 0, 0, 0, 0, 0 };
 	struct out_dir		outdir = { 0, 0, 0, 0 };
-	struct name_cmp_ctx name_cmp_ctx = {0, NULL};
+	struct name_cmp_ctx	name_cmp_ctx = {0, NULL};
+	__u64			osize;
 
-	e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+	e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+			       sizeof(inode), "rehash_dir");
+
+	osize = EXT2_I_SIZE(&inode);
 
 	if (ext2fs_has_feature_inline_data(fs->super) &&
 	   (inode.i_flags & EXT4_INLINE_DATA_FL))
@@ -1013,7 +1017,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
 	fd.ino = ino;
 	fd.ctx = ctx;
 	fd.buf = dir_buf;
-	fd.inode = &inode;
+	fd.inode = EXT2_INODE(&inode);
 	fd.dir = ino;
 	if (!ext2fs_has_feature_dir_index(fs->super) ||
 	    (inode.i_size / fs->blocksize) < 2)
@@ -1092,14 +1096,25 @@ resort:
 			goto errout;
 	}
 
-	retval = write_directory(ctx, fs, &outdir, ino, &inode, fd.compress);
+	retval = write_directory(ctx, fs, &outdir, ino, EXT2_INODE(&inode),
+				 fd.compress);
 	if (retval)
 		goto errout;
 
+	if ((osize > EXT2_I_SIZE(&inode)) &&
+	    (ino != quota_type2inum(PRJQUOTA, fs->super)) &&
+	    (ino != fs->super->s_orphan_file_inum) &&
+	    (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+	    !(inode.i_flags & EXT4_EA_INODE_FL)) {
+		quota_data_sub(ctx->qctx, &inode,
+			       ino, osize - EXT2_I_SIZE(&inode));
+	}
+
 	if (ctx->options & E2F_OPT_CONVERT_BMAP)
 		retval = e2fsck_rebuild_extents_later(ctx, ino);
 	else
-		retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx);
+		retval = e2fsck_check_rebuild_extents(ctx, ino,
+						      EXT2_INODE(&inode), pctx);
 errout:
 	ext2fs_free_mem(&dir_buf);
 	ext2fs_free_mem(&fd.harray);
Reviewed-by on v1 1/4
#2 lore raw

Re: [PATCH 1/4] e2fsck: update quota accounting after directory optimization

Quoted 11 lines · Luis Henriques (SUSE)

In "Pass 3A: Optimizing directories", a directory may have it's size reduced. If that happens and quota is enabled in the filesystem, the quota information will be incorrect because it doesn't take the rehash into account. This issue was detected by running fstest ext4/014.

This patch simply updates the quota data accordingly, after the directory is written and it's size has been updated.

Quoted 65 lines

---
e2fsck/rehash.c | 27 +++++++++++++++++++++------

1 file changed, 21 insertions(+), 6 deletions(-)
e2fsck/rehash.c +21 -6
index c1da7d52724e..4847d172e5fe 100644
@@ -987,14 +987,18 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
 {
 	ext2_filsys 		fs = ctx->fs;
 	errcode_t		retval;
-	struct ext2_inode 	inode;
+	struct ext2_inode_large	inode;
 	char			*dir_buf = 0;
 	struct fill_dir_struct	fd = { NULL, NULL, 0, 0, 0, NULL,
 				       0, 0, 0, 0, 0, 0 };
 	struct out_dir		outdir = { 0, 0, 0, 0 };
-	struct name_cmp_ctx name_cmp_ctx = {0, NULL};
+	struct name_cmp_ctx	name_cmp_ctx = {0, NULL};
+	__u64			osize;
 
-	e2fsck_read_inode(ctx, ino, &inode, "rehash_dir");
+	e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+			       sizeof(inode), "rehash_dir");
+
+	osize = EXT2_I_SIZE(&inode);
 
 	if (ext2fs_has_feature_inline_data(fs->super) &&
 	   (inode.i_flags & EXT4_INLINE_DATA_FL))
@@ -1013,7 +1017,7 @@ errcode_t e2fsck_rehash_dir(e2fsck_t ctx, ext2_ino_t ino,
 	fd.ino = ino;
 	fd.ctx = ctx;
 	fd.buf = dir_buf;
-	fd.inode = &inode;
+	fd.inode = EXT2_INODE(&inode);
 	fd.dir = ino;
 	if (!ext2fs_has_feature_dir_index(fs->super) ||
 	    (inode.i_size / fs->blocksize) < 2)
@@ -1092,14 +1096,25 @@ resort:
 			goto errout;
 	}
 
-	retval = write_directory(ctx, fs, &outdir, ino, &inode, fd.compress);
+	retval = write_directory(ctx, fs, &outdir, ino, EXT2_INODE(&inode),
+				 fd.compress);
 	if (retval)
 		goto errout;
 
+	if ((osize > EXT2_I_SIZE(&inode)) &&
+	    (ino != quota_type2inum(PRJQUOTA, fs->super)) &&
+	    (ino != fs->super->s_orphan_file_inum) &&
+	    (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+	    !(inode.i_flags & EXT4_EA_INODE_FL)) {
+		quota_data_sub(ctx->qctx, &inode,
+			       ino, osize - EXT2_I_SIZE(&inode));
+	}
+
 	if (ctx->options & E2F_OPT_CONVERT_BMAP)
 		retval = e2fsck_rebuild_extents_later(ctx, ino);
 	else
-		retval = e2fsck_check_rebuild_extents(ctx, ino, &inode, pctx);
+		retval = e2fsck_check_rebuild_extents(ctx, ino,
+						      EXT2_INODE(&inode), pctx);

errout:

Quoted 2 lines
	ext2fs_free_mem(&dir_buf);
	ext2fs_free_mem(&fd.harray);

Cheers, Andreas

#3 lore raw

PATCH 2/4 e2fsck: update quota when deallocating a bad inode

In reply to Luis Henriques (SUSE)

If a bad inode is found it will be deallocated. However, if the filesystem has quota enabled, the quota information isn't being updated accordingly. This issue was detected by running fstest ext4/019.

This patch fixes the issue by decreasing the inode count from the quota and, if blocks are also being released, also subtract them as well.

---
 e2fsck/pass2.c | 33 +++++++++++++++++++++++----------
 1 file changed, 23 insertions(+), 10 deletions(-)
e2fsck/pass2.c +23 -10
index b91628567a7f..e16b488af643 100644
@@ -1859,12 +1859,13 @@ static int deallocate_inode_block(ext2_filsys fs,
 static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 {
 	ext2_filsys fs = ctx->fs;
-	struct ext2_inode	inode;
+	struct ext2_inode_large	inode;
 	struct problem_context	pctx;
 	__u32			count;
 	struct del_block	del_block;
 
-	e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+	e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+			       sizeof(inode), "deallocate_inode");
 	clear_problem_context(&pctx);
 	pctx.ino = ino;
 
@@ -1874,29 +1875,29 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 	e2fsck_read_bitmaps(ctx);
 	ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
 
-	if (ext2fs_file_acl_block(fs, &inode) &&
+	if (ext2fs_file_acl_block(fs, EXT2_INODE(&inode)) &&
 	    ext2fs_has_feature_xattr(fs->super)) {
 		pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
-				ext2fs_file_acl_block(fs, &inode),
+				ext2fs_file_acl_block(fs, EXT2_INODE(&inode)),
 				block_buf, -1, &count, ino);
 		if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
 			pctx.errcode = 0;
 			count = 1;
 		}
 		if (pctx.errcode) {
-			pctx.blk = ext2fs_file_acl_block(fs, &inode);
+			pctx.blk = ext2fs_file_acl_block(fs, EXT2_INODE(&inode));
 			fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
 			ctx->flags |= E2F_FLAG_ABORT;
 			return;
 		}
 		if (count == 0) {
 			ext2fs_block_alloc_stats2(fs,
-				  ext2fs_file_acl_block(fs, &inode), -1);
+				  ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), -1);
 		}
-		ext2fs_file_acl_block_set(fs, &inode, 0);
+		ext2fs_file_acl_block_set(fs, EXT2_INODE(&inode), 0);
 	}
 
-	if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+	if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode)))
 		goto clear_inode;
 
 	/* Inline data inodes don't have blocks to iterate */
@@ -1921,10 +1922,22 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 		ctx->flags |= E2F_FLAG_ABORT;
 		return;
 	}
+
+	if ((ino != quota_type2inum(PRJQUOTA, fs->super)) &&
+	    (ino != fs->super->s_orphan_file_inum) &&
+	    (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+	    !(inode.i_flags & EXT4_EA_INODE_FL)) {
+		if (del_block.num > 0)
+			quota_data_sub(ctx->qctx, &inode, ino,
+				       del_block.num * EXT2_CLUSTER_SIZE(fs->super));
+		quota_data_inodes(ctx->qctx, (struct ext2_inode_large *)&inode,
+				  ino, -1);
+	}
+
 clear_inode:
 	/* Inode may have changed by block_iterate, so reread it */
-	e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
-	e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode");
+	e2fsck_read_inode(ctx, ino, EXT2_INODE(&inode), "deallocate_inode");
+	e2fsck_clear_inode(ctx, ino, EXT2_INODE(&inode), 0, "deallocate_inode");
 }
 
 /*
Reviewed-by on v1 2/4
#4 lore raw

Re: [PATCH 2/4] e2fsck: update quota when deallocating a bad inode

Quoted 7 lines · Luis Henriques (SUSE)

If a bad inode is found it will be deallocated. However, if the filesystem has quota enabled, the quota information isn't being updated accordingly. This issue was detected by running fstest ext4/019.

This patch fixes the issue by decreasing the inode count from the quota and, if blocks are also being released, also subtract them as well.

I was wondering about the safety of this patch, since "bad inode" usually means corrupted inode with random garbage, and continuing to process it is as likely to introduce problems as it is to fix them.

The only caller of deallocate_inode() is e2fsck_process_bad_inode() when the file type is bad or not matching the inode content. This appears it would mostly affect only the inode quota, though in some rare cases it might affect the block quota (xattr or long symlink).

Looking at the history of deallocate_inode(), it appears risky that it processes blocks from a corrupt inode at all. The saving grace is that it should only "undo" the blocks marked in-use by pass1 so that a second e2fsck run is not needed to fix up the bitmaps and counters. It looks like the same is true with the quota accounting, that deallocate_inode() is only updating the in-memory inode/block counters for the UID/GID/PRJID but not messing with any on-disk data until on-disk quotas are updated in pass5.

It wouldn't be terrible to update the comment before deallocate_inode() to explain this more clearly, since "This function deallocates an inode" doesn't really give the reader much more insight than the function name "deallocate_inode()". Maybe something more useful like:

/*
* This function reverts various counters and bitmaps incremented in
* pass1 for the inode, blocks, and quotas before it was decided the
* inode was corrupt and needed to be cleared. This avoids the need
* to run e2fsck a second time (or have it restart itself) to repair
* these counters.
*
* It does not modify any on-disk state, so even if the inode is bad
* it _should_ reset in-memory state to before the inode was first
* processed.
*/

... would be helpful to readers in the future. In either case, you can add my:

Cheers, Andreas

Quoted 86 lines

---
e2fsck/pass2.c | 33 +++++++++++++++++++++++----------

1 file changed, 23 insertions(+), 10 deletions(-)
e2fsck/pass2.c +21 -8
index b91628567a7f..e16b488af643 100644
@@ -1859,12 +1859,13 @@ static int deallocate_inode_block(ext2_filsys fs,
 static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 {
 	ext2_filsys fs = ctx->fs;
-	struct ext2_inode	inode;
+	struct ext2_inode_large	inode;
 	struct problem_context	pctx;
 	__u32			count;
 	struct del_block	del_block;
 
-	e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
+	e2fsck_read_inode_full(ctx, ino, EXT2_INODE(&inode),
+			       sizeof(inode), "deallocate_inode");
 	clear_problem_context(&pctx);
 	pctx.ino = ino;
 
@@ -1874,29 +1875,29 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 	e2fsck_read_bitmaps(ctx);
 	ext2fs_inode_alloc_stats2(fs, ino, -1, LINUX_S_ISDIR(inode.i_mode));
 
-	if (ext2fs_file_acl_block(fs, &inode) &&
+	if (ext2fs_file_acl_block(fs, EXT2_INODE(&inode)) &&
 	    ext2fs_has_feature_xattr(fs->super)) {
 		pctx.errcode = ext2fs_adjust_ea_refcount3(fs,
-				ext2fs_file_acl_block(fs, &inode),
+				ext2fs_file_acl_block(fs, EXT2_INODE(&inode)),
 				block_buf, -1, &count, ino);
 		if (pctx.errcode == EXT2_ET_BAD_EA_BLOCK_NUM) {
 			pctx.errcode = 0;
 			count = 1;
 		}
 		if (pctx.errcode) {
-			pctx.blk = ext2fs_file_acl_block(fs, &inode);
+			pctx.blk = ext2fs_file_acl_block(fs, EXT2_INODE(&inode));
 			fix_problem(ctx, PR_2_ADJ_EA_REFCOUNT, &pctx);
 			ctx->flags |= E2F_FLAG_ABORT;
 			return;
 		}
 		if (count == 0) {
 			ext2fs_block_alloc_stats2(fs,
-				  ext2fs_file_acl_block(fs, &inode), -1);
+				  ext2fs_file_acl_block(fs, EXT2_INODE(&inode)), -1);
 		}
-		ext2fs_file_acl_block_set(fs, &inode, 0);
+		ext2fs_file_acl_block_set(fs, EXT2_INODE(&inode), 0);
 	}
 
-	if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
+	if (!ext2fs_inode_has_valid_blocks2(fs, EXT2_INODE(&inode)))
 		goto clear_inode;
 
 	/* Inline data inodes don't have blocks to iterate */
@@ -1921,10 +1922,22 @@ static void deallocate_inode(e2fsck_t ctx, ext2_ino_t ino, char* block_buf)
 		ctx->flags |= E2F_FLAG_ABORT;
 		return;
 	}
+
+	if ((ino != quota_type2inum(PRJQUOTA, fs->super)) &&
+	    (ino != fs->super->s_orphan_file_inum) &&
+	    (ino == EXT2_ROOT_INO || ino >= EXT2_FIRST_INODE(ctx->fs->super)) &&
+	    !(inode.i_flags & EXT4_EA_INODE_FL)) {
+		if (del_block.num > 0)
+			quota_data_sub(ctx->qctx, &inode, ino,
+				       del_block.num * EXT2_CLUSTER_SIZE(fs->super));
+		quota_data_inodes(ctx->qctx, (struct ext2_inode_large *)&inode,
+				  ino, -1);
+	}
+

clear_inode:

	/* Inode may have changed by block_iterate, so reread it */
e2fsck/pass2.c +2 -2
-	e2fsck_read_inode(ctx, ino, &inode, "deallocate_inode");
-	e2fsck_clear_inode(ctx, ino, &inode, 0, "deallocate_inode");
+	e2fsck_read_inode(ctx, ino, EXT2_INODE(&inode), "deallocate_inode");
+	e2fsck_clear_inode(ctx, ino, EXT2_INODE(&inode), 0, "deallocate_inode");

}

Quoted 1 line

/*

Cheers, Andreas

#5 lore raw

PATCH 3/4 tests: new test to check quota after directory optimization

In reply to Luis Henriques (SUSE)

This new test validates e2fsck by verifying that quota data is updated after a directory optimization is performed. It mimics fstest ext4/014 by including a filesystem image where a file is created inside a new directory on the filesystem root and then root block 0 is wiped:

  # debugfs -w -R 'zap -f / 0' f_testnew/image
---
 tests/f_quota_shrinkdir/expect.1 |  40 +++++++++++++++++++++++++++++++
 tests/f_quota_shrinkdir/expect.2 |   7 ++++++
 tests/f_quota_shrinkdir/image.gz | Bin 0 -> 11453 bytes
 tests/f_quota_shrinkdir/name     |   1 +
 4 files changed, 48 insertions(+)
 create mode 100644 tests/f_quota_shrinkdir/expect.1
 create mode 100644 tests/f_quota_shrinkdir/expect.2
 create mode 100644 tests/f_quota_shrinkdir/image.gz
 create mode 100644 tests/f_quota_shrinkdir/name
tests/f_quota_shrinkdir/expect.1 new file +40 -0
new file mode 100644
index 000000000000..812fe44b887d
@@ -0,0 +1,40 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Directory inode 2, block #0, offset 0: directory corrupted
+Salvage? yes
+
+Missing '.' in directory inode 2.
+Fix? yes
+
+Missing '..' in directory inode 2.
+Fix? yes
+
+Pass 3: Checking directory connectivity
+'..' in / (2) is <The NULL inode> (0), should be / (2).
+Fix? yes
+
+Unconnected directory inode 11 (was in /)
+Connect to /lost+found? yes
+
+/lost+found not found.  Create? yes
+
+Unconnected directory inode 12 (was in /)
+Connect to /lost+found? yes
+
+Pass 3A: Optimizing directories
+Pass 4: Checking reference counts
+Inode 11 ref count is 3, should be 2.  Fix? yes
+
+Inode 12 ref count is 3, should be 2.  Fix? yes
+
+Pass 5: Checking group summary information
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (4096, 5) != expected (14336, 4)
+Update quota info for quota type 0? yes
+
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (4096, 5) != expected (14336, 4)
+Update quota info for quota type 1? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 14/256 files (14.3% non-contiguous), 1146/8192 blocks
+Exit status is 1
tests/f_quota_shrinkdir/expect.2 new file +7 -0
new file mode 100644
index 000000000000..814f84a54fd6
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 14/256 files (14.3% non-contiguous), 1146/8192 blocks
+Exit status is 0
tests/f_quota_shrinkdir/image.gz new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..753774f6a11f8a60fdbdf2ce205e64a36d753893
GIT binary patch
tests/f_quota_shrinkdir/name new file +1 -0
new file mode 100644
index 000000000000..8772ae5c814b
@@ -0,0 +1 @@
+update quota on directory optimization
#6 lore raw

Re: [PATCH 3/4] tests: new test to check quota after directory optimization

Quoted 7 lines · Luis Henriques (SUSE)

This new test validates e2fsck by verifying that quota data is updated after a directory optimization is performed. It mimics fstest ext4/014 by including a filesystem image where a file is created inside a new directory on the filesystem root and then root block 0 is wiped:

 # debugfs -w -R 'zap -f / 0' f_testnew/image

I appreciate the test case, and I hate to be difficult, but IMHO this test case is not ideal. It is *still* reporting quota inconsistency at the end, so it is difficult to see whether the patch is actually improving anything or not?

This is because the image is testing a number of different things at once (repairing the root inode, superblock, etc). IMHO, it would be better to have this test be specific to the directory shrink issue (e.g. a large directory is created, many files are deleted from it, then optimized), and ideally have a non-root user, group, and project involved so that it is verifying that all of the quotas are updated.

Cheers, Andreas

Quoted 148 lines

---

tests/f_quota_shrinkdir/expect.1 |  40 +++++++++++++++++++++++++++++++
tests/f_quota_shrinkdir/expect.2 |   7 ++++++
tests/f_quota_shrinkdir/image.gz | Bin 0 -> 11453 bytes
tests/f_quota_shrinkdir/name     |   1 +
4 files changed, 48 insertions(+)

create mode 100644 tests/f_quota_shrinkdir/expect.1 create mode 100644 tests/f_quota_shrinkdir/expect.2 create mode 100644 tests/f_quota_shrinkdir/image.gz create mode 100644 tests/f_quota_shrinkdir/name

tests/f_quota_shrinkdir/expect.1 new file +40 -0
new file mode 100644
index 000000000000..812fe44b887d
@@ -0,0 +1,40 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Directory inode 2, block #0, offset 0: directory corrupted
+Salvage? yes
+
+Missing '.' in directory inode 2.
+Fix? yes
+
+Missing '..' in directory inode 2.
+Fix? yes
+
+Pass 3: Checking directory connectivity
+'..' in / (2) is <The NULL inode> (0), should be / (2).
+Fix? yes
+
+Unconnected directory inode 11 (was in /)
+Connect to /lost+found? yes
+
+/lost+found not found.  Create? yes
+
+Unconnected directory inode 12 (was in /)
+Connect to /lost+found? yes
+
+Pass 3A: Optimizing directories
+Pass 4: Checking reference counts
+Inode 11 ref count is 3, should be 2.  Fix? yes
+
+Inode 12 ref count is 3, should be 2.  Fix? yes
+
+Pass 5: Checking group summary information
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (4096, 5) != expected (14336, 4)
+Update quota info for quota type 0? yes
+
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (4096, 5) != expected (14336, 4)
+Update quota info for quota type 1? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 14/256 files (14.3% non-contiguous), 1146/8192 blocks
+Exit status is 1
tests/f_quota_shrinkdir/expect.2 new file +7 -0
new file mode 100644
index 000000000000..814f84a54fd6
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 14/256 files (14.3% non-contiguous), 1146/8192 blocks
+Exit status is 0
tests/f_quota_shrinkdir/image.gz new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..753774f6a11f8a60fdbdf2ce205e64a36d753893
GIT binary patch
tests/f_quota_shrinkdir/name new file +0 -0
new file mode 100644
index 000000000000..8772ae5c814b

tests/f_quota_shrinkdir/name line 1

Quoted 1 line
tests/f_quota_shrinkdir/name new file +1 -0
@@ -0,0 +1 @@
+update quota on directory optimization

Cheers, Andreas

#7 lore raw

Re: [PATCH 3/4] tests: new test to check quota after directory optimization

Quoted 7 lines · Andreas Dilger
Quoted 6 lines · Luis Henriques (SUSE)

This new test validates e2fsck by verifying that quota data is updated after a directory optimization is performed. It mimics fstest ext4/014 by including a filesystem image where a file is created inside a new directory on the filesystem root and then root block 0 is wiped:

Quoted 6 lines
Quoted 1 line
 # debugfs -w -R 'zap -f / 0' f_testnew/image

I appreciate the test case, and I hate to be difficult, but IMHO this test case is not ideal. It is *still* reporting quota inconsistency at the end, so it is difficult to see whether the patch is actually improving anything or not?

Maybe I misunderstood how the tests really work. Here's what I understood:

e2fsck is run twice. During the first run the filesystem is recovered. And that's the output of expect.1 -- it reports the quota inconsistency because quota data needs to be fixed. And it is fixed in that first run, where e2fsck returns '1' ("File system errors corrected"). The second time e2fsck is run (expect.2) it will do nothing, and '0' is returned because the filesystem hasn't been modified.

Without the first patch in this series the second time e2fsck is executed it will still fail and report inconsistencies because the first time the fix wasn't correct. (And after this second time the filesystem should actually be corrected, a third run of e2fsck should return '0'.)

Quoted 6 lines

This is because the image is testing a number of different things at once (repairing the root inode, superblock, etc). IMHO, it would be better to have this test be specific to the directory shrink issue (e.g. a large directory is created, many files are deleted from it, then optimized), and ideally have a non-root user, group, and project involved so that it is verifying that all of the quotas are updated.

Right, that makes sense. However, I'm failing to narrow the test to that specific case. I've tried to create a bunch of files in a directory and used the debugfs 'kill_file' to remove files from that directory. However, in that case e2fsck isn't reporting quota inconsistencies as I would expect. Which may hint at yet more quota-related bugs. But I'm still looking.

Cheers,

Signature · 1 line
Luis
#8 lore raw

Re: [PATCH 3/4] tests: new test to check quota after directory optimization

Quoted 38 lines · Luis Henriques
Quoted 7 lines · Andreas Dilger
Quoted 6 lines · Luis Henriques (SUSE)

This new test validates e2fsck by verifying that quota data is updated after a directory optimization is performed. It mimics fstest ext4/014 by including a filesystem image where a file is created inside a new directory on the filesystem root and then root block 0 is wiped:

Quoted 6 lines
Quoted 1 line
 # debugfs -w -R 'zap -f / 0' f_testnew/image

I appreciate the test case, and I hate to be difficult, but IMHO this test case is not ideal. It is *still* reporting quota inconsistency at the end, so it is difficult to see whether the patch is actually improving anything or not?

Maybe I misunderstood how the tests really work. Here's what I understood:

e2fsck is run twice. During the first run the filesystem is recovered. And that's the output of expect.1 -- it reports the quota inconsistency because quota data needs to be fixed. And it is fixed in that first run, where e2fsck returns '1' ("File system errors corrected"). The second time e2fsck is run (expect.2) it will do nothing, and '0' is returned because the filesystem hasn't been modified.

Without the first patch in this series the second time e2fsck is executed it will still fail and report inconsistencies because the first time the fix wasn't correct. (And after this second time the filesystem should actually be corrected, a third run of e2fsck should return '0'.)

Quoted 6 lines

This is because the image is testing a number of different things at once (repairing the root inode, superblock, etc). IMHO, it would be better to have this test be specific to the directory shrink issue (e.g. a large directory is created, many files are deleted from it, then optimized), and ideally have a non-root user, group, and project involved so that it is verifying that all of the quotas are updated.

Quoted 6 lines

Right, that makes sense. However, I'm failing to narrow the test to that specific case. I've tried to create a bunch of files in a directory and used the debugfs 'kill_file' to remove files from that directory. However, in that case e2fsck isn't reporting quota inconsistencies as I would expect. Which may hint at yet more quota-related bugs. But I'm still looking.

OK, I _may_ have found a simple way to generate an image to test my patch. Here's what I came up with:

make testnew
tune2fs -O quota f_testnew/image

debugfs -w -R "ln lost+found foo" f_testnew/image debugfs -w -R "unlink lost+found" f_testnew/image

echo "update quota on directory optimization" > f_testnew/name make testend
mv f_testnew f_quota_shrinkdir

This will trigger a directory optimization after the recreation of the lost+found directory. Do you think this would be good enough?

Cheers,

Signature · 1 line
Luis
#9 lore raw

PATCH 4/4 tests: new test to check quota after a bad inode deallocation

In reply to Luis Henriques (SUSE)

This new test validates e2fsck by verifying that quota is updated after a bad inode is deallocated. It mimics fstest ext4/019 by including a filesystem image where a symbolic link was created to an existing file, using a long symlink name. This symbolic link was then wiped with:

  # debugfs -w -R 'zap -f /testlink 0' f_testnew/image
---
 tests/f_quota_deallocate_inode/expect.1 |  18 ++++++++++++++++++
 tests/f_quota_deallocate_inode/expect.2 |   7 +++++++
 tests/f_quota_deallocate_inode/image.gz | Bin 0 -> 11594 bytes
 tests/f_quota_deallocate_inode/name     |   1 +
 4 files changed, 26 insertions(+)
 create mode 100644 tests/f_quota_deallocate_inode/expect.1
 create mode 100644 tests/f_quota_deallocate_inode/expect.2
 create mode 100644 tests/f_quota_deallocate_inode/image.gz
 create mode 100644 tests/f_quota_deallocate_inode/name
tests/f_quota_deallocate_inode/expect.1 new file +18 -0
new file mode 100644
index 000000000000..2b2f128dbb57
@@ -0,0 +1,18 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Symlink /testlink (inode #14) is invalid.
+Clear? yes
+
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (15360, 4) != expected (16384, 5)
+Update quota info for quota type 0? yes
+
+[QUOTA WARNING] Usage inconsistent for ID 0:actual (15360, 4) != expected (16384, 5)
+Update quota info for quota type 1? yes
+
+
+test_filesys: ***** FILE SYSTEM WAS MODIFIED *****
+test_filesys: 13/256 files (15.4% non-contiguous), 1157/8192 blocks
+Exit status is 1
tests/f_quota_deallocate_inode/expect.2 new file +7 -0
new file mode 100644
index 000000000000..802317949959
@@ -0,0 +1,7 @@
+Pass 1: Checking inodes, blocks, and sizes
+Pass 2: Checking directory structure
+Pass 3: Checking directory connectivity
+Pass 4: Checking reference counts
+Pass 5: Checking group summary information
+test_filesys: 13/256 files (15.4% non-contiguous), 1157/8192 blocks
+Exit status is 0
tests/f_quota_deallocate_inode/image.gz new file binary +0 -0
new file mode 100644
index 0000000000000000000000000000000000000000..798a72c3a0da0323fc55c5e034f778e967394c81
GIT binary patch
tests/f_quota_deallocate_inode/name new file +1 -0
new file mode 100644
index 000000000000..396887c16f84
@@ -0,0 +1 @@
+update quota when deallocating bad inode