summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongjiang Zhu <zhudongjiang@fnnas.com>2026-06-09 11:23:18 +0800
committerDavid Sterba <dsterba@suse.com>2026-08-07 19:16:27 +0200
commit63a01c7578a655e560868fd47889b992418359bf (patch)
treeb3083b7a92c75e83356a505410d4d939d0934f2c
parent4ac48dc0cbd65fc91c45de17c3afadab85f4bb79 (diff)
downloadlinux-63a01c7578a655e560868fd47889b992418359bf.tar.gz
linux-63a01c7578a655e560868fd47889b992418359bf.zip
btrfs: start qgroup ioctl transactions on the quota root
The qgroup ioctls update the quota tree, but they currently start their transactions using the root of the inode passed to the ioctl. This makes the transaction reservation depend on the path used for the ioctl instead of the tree being modified. Start qgroup ioctl transactions on the quota root instead. Take a reference to fs_info->quota_root under qgroup_ioctl_lock before starting the transaction, because quota disable can clear and put fs_info->quota_root after the early quota-enabled check. Keep the reference until the transaction handle is ended. Suggested-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Dongjiang Zhu <zhudongjiang@fnnas.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--fs/btrfs/ioctl.c50
1 files changed, 43 insertions, 7 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index baa645e98812..6607e505dd68 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -3613,7 +3613,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
{
struct inode *inode = file_inode(file);
struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
- struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_root *quota_root;
struct btrfs_ioctl_qgroup_assign_args AUTO_KFREE(sa);
struct btrfs_qgroup_list AUTO_KFREE(prealloc);
struct btrfs_trans_handle *trans;
@@ -3644,10 +3644,20 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
}
}
+ mutex_lock(&fs_info->qgroup_ioctl_lock);
+ quota_root = btrfs_grab_root(fs_info->quota_root);
+ mutex_unlock(&fs_info->qgroup_ioctl_lock);
+
+ if (!quota_root) {
+ ret = -ENOTCONN;
+ goto drop_write;
+ }
+
/* 2 BTRFS_QGROUP_RELATION_KEY items. */
- trans = btrfs_start_transaction(root, 2);
+ trans = btrfs_start_transaction(quota_root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ btrfs_put_root(quota_root);
goto drop_write;
}
@@ -3671,6 +3681,7 @@ static long btrfs_ioctl_qgroup_assign(struct file *file, void __user *arg)
"qgroup status update failed after %s relation, marked as inconsistent",
sa->assign ? "adding" : "deleting");
err = btrfs_end_transaction(trans);
+ btrfs_put_root(quota_root);
if (err && !ret)
ret = err;
@@ -3682,7 +3693,8 @@ drop_write:
static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
{
struct inode *inode = file_inode(file);
- struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_fs_info *fs_info = inode_to_fs_info(inode);
+ struct btrfs_root *quota_root;
struct btrfs_ioctl_qgroup_create_args AUTO_KFREE(sa);
struct btrfs_trans_handle *trans;
int ret;
@@ -3691,7 +3703,7 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (!btrfs_qgroup_enabled(root->fs_info))
+ if (!btrfs_qgroup_enabled(fs_info))
return -ENOTCONN;
ret = mnt_want_write_file(file);
@@ -3714,13 +3726,23 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
goto drop_write;
}
+ mutex_lock(&fs_info->qgroup_ioctl_lock);
+ quota_root = btrfs_grab_root(fs_info->quota_root);
+ mutex_unlock(&fs_info->qgroup_ioctl_lock);
+
+ if (!quota_root) {
+ ret = -ENOTCONN;
+ goto drop_write;
+ }
+
/*
* 1 BTRFS_QGROUP_INFO_KEY item.
* 1 BTRFS_QGROUP_LIMIT_KEY item.
*/
- trans = btrfs_start_transaction(root, 2);
+ trans = btrfs_start_transaction(quota_root, 2);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ btrfs_put_root(quota_root);
goto drop_write;
}
@@ -3731,6 +3753,7 @@ static long btrfs_ioctl_qgroup_create(struct file *file, void __user *arg)
}
err = btrfs_end_transaction(trans);
+ btrfs_put_root(quota_root);
if (err && !ret)
ret = err;
@@ -3743,6 +3766,8 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
{
struct inode *inode = file_inode(file);
struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_root *quota_root;
+ struct btrfs_fs_info *fs_info = root->fs_info;
struct btrfs_ioctl_qgroup_limit_args AUTO_KFREE(sa);
struct btrfs_trans_handle *trans;
int ret;
@@ -3752,7 +3777,7 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- if (!btrfs_qgroup_enabled(root->fs_info))
+ if (!btrfs_qgroup_enabled(fs_info))
return -ENOTCONN;
ret = mnt_want_write_file(file);
@@ -3765,10 +3790,20 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
goto drop_write;
}
+ mutex_lock(&fs_info->qgroup_ioctl_lock);
+ quota_root = btrfs_grab_root(fs_info->quota_root);
+ mutex_unlock(&fs_info->qgroup_ioctl_lock);
+
+ if (!quota_root) {
+ ret = -ENOTCONN;
+ goto drop_write;
+ }
+
/* 1 BTRFS_QGROUP_LIMIT_KEY item. */
- trans = btrfs_start_transaction(root, 1);
+ trans = btrfs_start_transaction(quota_root, 1);
if (IS_ERR(trans)) {
ret = PTR_ERR(trans);
+ btrfs_put_root(quota_root);
goto drop_write;
}
@@ -3781,6 +3816,7 @@ static long btrfs_ioctl_qgroup_limit(struct file *file, void __user *arg)
ret = btrfs_limit_qgroup(trans, qgroupid, &sa->lim);
err = btrfs_end_transaction(trans);
+ btrfs_put_root(quota_root);
if (err && !ret)
ret = err;