From d2ccc2155217bfdd7e19db4480d981a8f3fad46f Mon Sep 17 00:00:00 2001 From: Jitendra Patidar Date: Thu, 15 Aug 2024 06:29:19 +0530 Subject: [PATCH] Fix projid accounting for xattr objects zpool upgraded with 'feature@project_quota' needs re-layout of SA's to fix the SA_ZPL_PROJID at SA_PROJID_OFFSET (128). Its necessary for the correct accounting of object usage against its projid. Old object (created before upgrade) when gets a projid assigned, its SA gets re-layout via sa_add_projid(). If object has xattr dir, SA of xattr dir also gets re-layout. But SA re-layout of xattr objects inside a xattr dir is not done. Fix zfs_setattr_dir() to re-layout SA's on xattr objects, when setting projid on old xattr object (created before upgrade). Reviewed-by: Tony Hutter Reviewed-by: Brian Behlendorf Signed-off-by: Jitendra Patidar Closes #16355 Closes #16356 --- module/os/linux/zfs/zfs_vnops_os.c | 28 +++++++++++++------ .../upgrade/upgrade_projectquota_001_pos.ksh | 19 +++++++++++-- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c index 8061169c32..9803c7fecb 100644 --- a/module/os/linux/zfs/zfs_vnops_os.c +++ b/module/os/linux/zfs/zfs_vnops_os.c @@ -1789,24 +1789,36 @@ zfs_setattr_dir(znode_t *dzp) &gid, sizeof (gid)); } - if (zp->z_projid != dzp->z_projid) { + + uint64_t projid = dzp->z_projid; + if (zp->z_projid != projid) { if (!(zp->z_pflags & ZFS_PROJID)) { - zp->z_pflags |= ZFS_PROJID; - SA_ADD_BULK_ATTR(bulk, count, - SA_ZPL_FLAGS(zfsvfs), NULL, &zp->z_pflags, - sizeof (zp->z_pflags)); + err = sa_add_projid(zp->z_sa_hdl, tx, projid); + if (unlikely(err == EEXIST)) { + err = 0; + } else if (err != 0) { + goto sa_add_projid_err; + } else { + projid = ZFS_INVALID_PROJID; + } } - zp->z_projid = dzp->z_projid; - SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PROJID(zfsvfs), - NULL, &zp->z_projid, sizeof (zp->z_projid)); + if (projid != ZFS_INVALID_PROJID) { + zp->z_projid = projid; + SA_ADD_BULK_ATTR(bulk, count, + SA_ZPL_PROJID(zfsvfs), NULL, &zp->z_projid, + sizeof (zp->z_projid)); + } } +sa_add_projid_err: mutex_exit(&dzp->z_lock); if (likely(count > 0)) { err = sa_bulk_update(zp->z_sa_hdl, bulk, count, tx); dmu_tx_commit(tx); + } else if (projid == ZFS_INVALID_PROJID) { + dmu_tx_commit(tx); } else { dmu_tx_abort(tx); } diff --git a/tests/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh b/tests/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh index 2ad37e06a5..2c365e37af 100755 --- a/tests/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh +++ b/tests/zfs-tests/tests/functional/upgrade/upgrade_projectquota_001_pos.ksh @@ -63,6 +63,7 @@ log_must mkfiles $TESTDIR/fs2/tf $((RANDOM % 100 + 1)) log_must zfs create $TESTPOOL/fs3 log_must mkdir $TESTDIR/fs3/dir log_must mkfiles $TESTDIR/fs3/tf $((RANDOM % 100 + 1)) +log_must set_xattr_stdin passwd $TESTDIR/fs3/dir < /etc/passwd # Make sure project quota is disabled zfs projectspace -o used $TESTPOOL | grep -q "USED" && @@ -109,9 +110,23 @@ log_must chattr -p 100 $TESTDIR/fs3/dir log_must sleep 5 # upgrade done in the background so let's wait for a while zfs projectspace -o used $TESTPOOL/fs3 | grep -q "USED" || log_fail "project quota should be enabled for $TESTPOOL/fs3" +dirino=$(stat -c '%i' $TESTDIR/fs3/dir) +log_must zdb -ddddd $TESTPOOL/fs3 $dirino +xattrdirino=$(zdb -ddddd $TESTPOOL/fs3 $dirino |grep -w "xattr" |awk '{print $2}') +echo "xattrdirino: $xattrdirino" +expectedcnt=1 +echo "expectedcnt: $expectedcnt" +if [ "$xattrdirino" != "" ]; then + expectedcnt=$(($expectedcnt + 1)) + echo "expectedcnt: $expectedcnt" + log_must zdb -ddddd $TESTPOOL/fs3 $xattrdirino + xattrinocnt=$(zdb -ddddd $TESTPOOL/fs3 $xattrdirino |grep -w "(type:" |wc -l) + echo "xattrinocnt: $xattrinocnt" + expectedcnt=$(($expectedcnt + $xattrinocnt)) + echo "expectedcnt: $expectedcnt" +fi cnt=$(get_prop projectobjused@100 $TESTPOOL/fs3) -# if 'xattr=on', then 'cnt = 2' -[[ $cnt -ne 1 ]] && [[ $cnt -ne 2 ]] && +[[ $cnt -ne $expectedcnt ]] && log_fail "projectquota accounting failed $cnt" # All in all, after having been through this, the dataset for testpool