From: Baokun Li In quota_inode_init_new(), we attempt to read and truncate an existing quota inode before proceeding with its initialization. This read operation verifies the inode's checksum. This works fine for USRQUOTA and GRPQUOTA inodes because write_reserved_inodes() is always called during ext4 image creation to set appropriate checksums for these reserved inodes. However, the PRJQUOTA inode is not reserved, and its corresponding inode table block may not have been zeroed, potentially containing stale data. Consequently, reading this inode can fail due to a checksum mismatch. This can be reproduced by running the following sequence: dd if=/dev/random of=$DISK bs=1M count=128 mkfs.ext4 -F -q -b 1024 $DISK 5G tune2fs -O quota,project $DISK Which results in the following error output: tune2fs 1.47.3 (8-Jul-2025) [ERROR] quotaio.c:279:quota_inode_init_new: ex2fs_read_inode failed [ERROR] quotaio.c:341:quota_file_create: init_new_quota_inode failed tune2fs: Inode checksum does not match inode while writing quota file (2) While running `kvm-xfstests -c ext4/1k -C 1 generic/383`, the test itself does not fail, but checksum verification failures are reported even without fault injection, which led to discovering this issue. To fix this, we stop attempting to read the quota inode that is about to be initialized inside quota_inode_init_new(). Instead, the logic to attempt truncation of an existing quota inode is moved to be handled inside quota_file_create(). Fixes: 080e09b4 ("Add project quota support") Signed-off-by: Baokun Li --- lib/support/quotaio.c | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c index f5f2c7f7..827df85b 100644 --- a/lib/support/quotaio.c +++ b/lib/support/quotaio.c @@ -274,18 +274,6 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino) errcode_t err = 0; time_t now; - err = ext2fs_read_inode(fs, ino, &inode); - if (err) { - log_err("ex2fs_read_inode failed"); - return err; - } - - if (EXT2_I_SIZE(&inode)) { - err = quota_inode_truncate(fs, ino); - if (err) - return err; - } - memset(&inode, 0, sizeof(struct ext2_inode)); ext2fs_iblk_set(fs, &inode, 0); now = fs->now ? fs->now : time(0); @@ -319,6 +307,10 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, if (fmt == -1) fmt = QFMT_VFS_V1; + err = ext2fs_read_bitmaps(fs); + if (err) + goto out_err; + h->qh_qf.fs = fs; qf_inum = quota_type2inum(qtype, fs->super); if (qf_inum == 0 && qtype == PRJQUOTA) { @@ -330,15 +322,19 @@ errcode_t quota_file_create(struct quota_handle *h, ext2_filsys fs, ext2fs_mark_ib_dirty(fs); } else if (qf_inum == 0) { return EXT2_ET_BAD_INODE_NUM; + } else { + err = quota_inode_truncate(fs, qf_inum); + if (err) { + log_err("quota_inode_truncate failed, ino=%u, type=%d", + qf_inum, qtype); + return err; + } } - err = ext2fs_read_bitmaps(fs); - if (err) - goto out_err; - err = quota_inode_init_new(fs, qf_inum); if (err) { - log_err("init_new_quota_inode failed"); + log_err("init_new_quota_inode failed, ino=%u, type=%d", + qf_inum, qtype); goto out_err; } h->qh_qf.ino = qf_inum; -- 2.46.1