Skip to content

Commit 7317a03

Browse files
author
Darrick J. Wong
committed
xfs: refactor inode ownership change transaction/inode/quota allocation idiom
For file ownership (uid, gid, prid) changes, create a new helper xfs_trans_alloc_ichange that allocates a transaction and reserves the appropriate amount of quota against that transction in preparation for a change of user, group, or project id. Replace all the open-coded idioms with a single call to this helper so that we can contain the retry loops in the next patchset. This changes the locking behavior for ichange transactions slightly. Since tr_ichange does not have a permanent reservation and cannot roll, we pass XFS_ILOCK_EXCL to ijoin so that the inode will be unlocked automatically at commit time. Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Christoph Hellwig <[email protected]> Reviewed-by: Brian Foster <[email protected]>
1 parent f2f7b9f commit 7317a03

File tree

4 files changed

+77
-43
lines changed

4 files changed

+77
-43
lines changed

fs/xfs/xfs_ioctl.c

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,24 +1275,23 @@ xfs_ioctl_setattr_prepare_dax(
12751275
*/
12761276
static struct xfs_trans *
12771277
xfs_ioctl_setattr_get_trans(
1278-
struct xfs_inode *ip)
1278+
struct xfs_inode *ip,
1279+
struct xfs_dquot *pdqp)
12791280
{
12801281
struct xfs_mount *mp = ip->i_mount;
12811282
struct xfs_trans *tp;
12821283
int error = -EROFS;
12831284

12841285
if (mp->m_flags & XFS_MOUNT_RDONLY)
1285-
goto out_unlock;
1286+
goto out_error;
12861287
error = -EIO;
12871288
if (XFS_FORCED_SHUTDOWN(mp))
1288-
goto out_unlock;
1289+
goto out_error;
12891290

1290-
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1291+
error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
1292+
capable(CAP_FOWNER), &tp);
12911293
if (error)
1292-
goto out_unlock;
1293-
1294-
xfs_ilock(ip, XFS_ILOCK_EXCL);
1295-
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
1294+
goto out_error;
12961295

12971296
/*
12981297
* CAP_FOWNER overrides the following restrictions:
@@ -1312,7 +1311,7 @@ xfs_ioctl_setattr_get_trans(
13121311

13131312
out_cancel:
13141313
xfs_trans_cancel(tp);
1315-
out_unlock:
1314+
out_error:
13161315
return ERR_PTR(error);
13171316
}
13181317

@@ -1462,20 +1461,12 @@ xfs_ioctl_setattr(
14621461

14631462
xfs_ioctl_setattr_prepare_dax(ip, fa);
14641463

1465-
tp = xfs_ioctl_setattr_get_trans(ip);
1464+
tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
14661465
if (IS_ERR(tp)) {
14671466
code = PTR_ERR(tp);
14681467
goto error_free_dquots;
14691468
}
14701469

1471-
if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
1472-
ip->i_d.di_projid != fa->fsx_projid) {
1473-
code = xfs_qm_vop_chown_reserve(tp, ip, NULL, NULL, pdqp,
1474-
capable(CAP_FOWNER) ? XFS_QMOPT_FORCE_RES : 0);
1475-
if (code) /* out of quota */
1476-
goto error_trans_cancel;
1477-
}
1478-
14791470
xfs_fill_fsxattr(ip, false, &old_fa);
14801471
code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
14811472
if (code)
@@ -1608,7 +1599,7 @@ xfs_ioc_setxflags(
16081599

16091600
xfs_ioctl_setattr_prepare_dax(ip, &fa);
16101601

1611-
tp = xfs_ioctl_setattr_get_trans(ip);
1602+
tp = xfs_ioctl_setattr_get_trans(ip, NULL);
16121603
if (IS_ERR(tp)) {
16131604
error = PTR_ERR(tp);
16141605
goto out_drop_write;

fs/xfs/xfs_iops.c

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -700,13 +700,11 @@ xfs_setattr_nonsize(
700700
return error;
701701
}
702702

703-
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
703+
error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
704+
capable(CAP_FOWNER), &tp);
704705
if (error)
705706
goto out_dqrele;
706707

707-
xfs_ilock(ip, XFS_ILOCK_EXCL);
708-
xfs_trans_ijoin(tp, ip, 0);
709-
710708
/*
711709
* Change file ownership. Must be the owner or privileged.
712710
*/
@@ -722,21 +720,6 @@ xfs_setattr_nonsize(
722720
gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
723721
uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
724722

725-
/*
726-
* Do a quota reservation only if uid/gid is actually
727-
* going to change.
728-
*/
729-
if (XFS_IS_QUOTA_RUNNING(mp) &&
730-
((XFS_IS_UQUOTA_ON(mp) && !uid_eq(iuid, uid)) ||
731-
(XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
732-
ASSERT(tp);
733-
error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
734-
NULL, capable(CAP_FOWNER) ?
735-
XFS_QMOPT_FORCE_RES : 0);
736-
if (error) /* out of quota */
737-
goto out_cancel;
738-
}
739-
740723
/*
741724
* CAP_FSETID overrides the following restrictions:
742725
*
@@ -786,8 +769,6 @@ xfs_setattr_nonsize(
786769
xfs_trans_set_sync(tp);
787770
error = xfs_trans_commit(tp);
788771

789-
xfs_iunlock(ip, XFS_ILOCK_EXCL);
790-
791772
/*
792773
* Release any dquot(s) the inode had kept before chown.
793774
*/
@@ -814,9 +795,6 @@ xfs_setattr_nonsize(
814795

815796
return 0;
816797

817-
out_cancel:
818-
xfs_trans_cancel(tp);
819-
xfs_iunlock(ip, XFS_ILOCK_EXCL);
820798
out_dqrele:
821799
xfs_qm_dqrele(udqp);
822800
xfs_qm_dqrele(gdqp);

fs/xfs/xfs_trans.c

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1107,3 +1107,65 @@ xfs_trans_alloc_icreate(
11071107
*tpp = tp;
11081108
return 0;
11091109
}
1110+
1111+
/*
1112+
* Allocate an transaction, lock and join the inode to it, and reserve quota
1113+
* in preparation for inode attribute changes that include uid, gid, or prid
1114+
* changes.
1115+
*
1116+
* The caller must ensure that the on-disk dquots attached to this inode have
1117+
* already been allocated and initialized. The ILOCK will be dropped when the
1118+
* transaction is committed or cancelled.
1119+
*/
1120+
int
1121+
xfs_trans_alloc_ichange(
1122+
struct xfs_inode *ip,
1123+
struct xfs_dquot *udqp,
1124+
struct xfs_dquot *gdqp,
1125+
struct xfs_dquot *pdqp,
1126+
bool force,
1127+
struct xfs_trans **tpp)
1128+
{
1129+
struct xfs_trans *tp;
1130+
struct xfs_mount *mp = ip->i_mount;
1131+
int error;
1132+
1133+
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
1134+
if (error)
1135+
return error;
1136+
1137+
xfs_ilock(ip, XFS_ILOCK_EXCL);
1138+
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
1139+
1140+
error = xfs_qm_dqattach_locked(ip, false);
1141+
if (error) {
1142+
/* Caller should have allocated the dquots! */
1143+
ASSERT(error != -ENOENT);
1144+
goto out_cancel;
1145+
}
1146+
1147+
/*
1148+
* For each quota type, skip quota reservations if the inode's dquots
1149+
* now match the ones that came from the caller, or the caller didn't
1150+
* pass one in.
1151+
*/
1152+
if (udqp == ip->i_udquot)
1153+
udqp = NULL;
1154+
if (gdqp == ip->i_gdquot)
1155+
gdqp = NULL;
1156+
if (pdqp == ip->i_pdquot)
1157+
pdqp = NULL;
1158+
if (udqp || gdqp || pdqp) {
1159+
error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, pdqp,
1160+
force ? XFS_QMOPT_FORCE_RES : 0);
1161+
if (error)
1162+
goto out_cancel;
1163+
}
1164+
1165+
*tpp = tp;
1166+
return 0;
1167+
1168+
out_cancel:
1169+
xfs_trans_cancel(tp);
1170+
return error;
1171+
}

fs/xfs/xfs_trans.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,5 +277,8 @@ int xfs_trans_alloc_icreate(struct xfs_mount *mp, struct xfs_trans_res *resv,
277277
struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
278278
struct xfs_dquot *pdqp, unsigned int dblocks,
279279
struct xfs_trans **tpp);
280+
int xfs_trans_alloc_ichange(struct xfs_inode *ip, struct xfs_dquot *udqp,
281+
struct xfs_dquot *gdqp, struct xfs_dquot *pdqp, bool force,
282+
struct xfs_trans **tpp);
280283

281284
#endif /* __XFS_TRANS_H__ */

0 commit comments

Comments
 (0)