forked from mirrors/linux
		
	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 <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Brian Foster <bfoster@redhat.com>
This commit is contained in:
		
							parent
							
								
									f2f7b9ff62
								
							
						
					
					
						commit
						7317a03df7
					
				
					 4 changed files with 77 additions and 43 deletions
				
			
		| 
						 | 
					@ -1275,24 +1275,23 @@ xfs_ioctl_setattr_prepare_dax(
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static struct xfs_trans *
 | 
					static struct xfs_trans *
 | 
				
			||||||
xfs_ioctl_setattr_get_trans(
 | 
					xfs_ioctl_setattr_get_trans(
 | 
				
			||||||
	struct xfs_inode	*ip)
 | 
						struct xfs_inode	*ip,
 | 
				
			||||||
 | 
						struct xfs_dquot	*pdqp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct xfs_mount	*mp = ip->i_mount;
 | 
						struct xfs_mount	*mp = ip->i_mount;
 | 
				
			||||||
	struct xfs_trans	*tp;
 | 
						struct xfs_trans	*tp;
 | 
				
			||||||
	int			error = -EROFS;
 | 
						int			error = -EROFS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (mp->m_flags & XFS_MOUNT_RDONLY)
 | 
						if (mp->m_flags & XFS_MOUNT_RDONLY)
 | 
				
			||||||
		goto out_unlock;
 | 
							goto out_error;
 | 
				
			||||||
	error = -EIO;
 | 
						error = -EIO;
 | 
				
			||||||
	if (XFS_FORCED_SHUTDOWN(mp))
 | 
						if (XFS_FORCED_SHUTDOWN(mp))
 | 
				
			||||||
		goto out_unlock;
 | 
							goto out_error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
 | 
						error = xfs_trans_alloc_ichange(ip, NULL, NULL, pdqp,
 | 
				
			||||||
 | 
								capable(CAP_FOWNER), &tp);
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		goto out_unlock;
 | 
							goto out_error;
 | 
				
			||||||
 | 
					 | 
				
			||||||
	xfs_ilock(ip, XFS_ILOCK_EXCL);
 | 
					 | 
				
			||||||
	xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * CAP_FOWNER overrides the following restrictions:
 | 
						 * CAP_FOWNER overrides the following restrictions:
 | 
				
			||||||
| 
						 | 
					@ -1312,7 +1311,7 @@ xfs_ioctl_setattr_get_trans(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_cancel:
 | 
					out_cancel:
 | 
				
			||||||
	xfs_trans_cancel(tp);
 | 
						xfs_trans_cancel(tp);
 | 
				
			||||||
out_unlock:
 | 
					out_error:
 | 
				
			||||||
	return ERR_PTR(error);
 | 
						return ERR_PTR(error);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1462,20 +1461,12 @@ xfs_ioctl_setattr(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xfs_ioctl_setattr_prepare_dax(ip, fa);
 | 
						xfs_ioctl_setattr_prepare_dax(ip, fa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tp = xfs_ioctl_setattr_get_trans(ip);
 | 
						tp = xfs_ioctl_setattr_get_trans(ip, pdqp);
 | 
				
			||||||
	if (IS_ERR(tp)) {
 | 
						if (IS_ERR(tp)) {
 | 
				
			||||||
		code = PTR_ERR(tp);
 | 
							code = PTR_ERR(tp);
 | 
				
			||||||
		goto error_free_dquots;
 | 
							goto error_free_dquots;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (XFS_IS_QUOTA_RUNNING(mp) && XFS_IS_PQUOTA_ON(mp) &&
 | 
					 | 
				
			||||||
	    ip->i_d.di_projid != fa->fsx_projid) {
 | 
					 | 
				
			||||||
		code = xfs_qm_vop_chown_reserve(tp, ip, NULL, NULL, pdqp,
 | 
					 | 
				
			||||||
				capable(CAP_FOWNER) ?  XFS_QMOPT_FORCE_RES : 0);
 | 
					 | 
				
			||||||
		if (code)	/* out of quota */
 | 
					 | 
				
			||||||
			goto error_trans_cancel;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	xfs_fill_fsxattr(ip, false, &old_fa);
 | 
						xfs_fill_fsxattr(ip, false, &old_fa);
 | 
				
			||||||
	code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
 | 
						code = vfs_ioc_fssetxattr_check(VFS_I(ip), &old_fa, fa);
 | 
				
			||||||
	if (code)
 | 
						if (code)
 | 
				
			||||||
| 
						 | 
					@ -1608,7 +1599,7 @@ xfs_ioc_setxflags(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xfs_ioctl_setattr_prepare_dax(ip, &fa);
 | 
						xfs_ioctl_setattr_prepare_dax(ip, &fa);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tp = xfs_ioctl_setattr_get_trans(ip);
 | 
						tp = xfs_ioctl_setattr_get_trans(ip, NULL);
 | 
				
			||||||
	if (IS_ERR(tp)) {
 | 
						if (IS_ERR(tp)) {
 | 
				
			||||||
		error = PTR_ERR(tp);
 | 
							error = PTR_ERR(tp);
 | 
				
			||||||
		goto out_drop_write;
 | 
							goto out_drop_write;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -700,13 +700,11 @@ xfs_setattr_nonsize(
 | 
				
			||||||
			return error;
 | 
								return error;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
 | 
						error = xfs_trans_alloc_ichange(ip, udqp, gdqp, NULL,
 | 
				
			||||||
 | 
								capable(CAP_FOWNER), &tp);
 | 
				
			||||||
	if (error)
 | 
						if (error)
 | 
				
			||||||
		goto out_dqrele;
 | 
							goto out_dqrele;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xfs_ilock(ip, XFS_ILOCK_EXCL);
 | 
					 | 
				
			||||||
	xfs_trans_ijoin(tp, ip, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Change file ownership.  Must be the owner or privileged.
 | 
						 * Change file ownership.  Must be the owner or privileged.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -722,21 +720,6 @@ xfs_setattr_nonsize(
 | 
				
			||||||
		gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
 | 
							gid = (mask & ATTR_GID) ? iattr->ia_gid : igid;
 | 
				
			||||||
		uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
 | 
							uid = (mask & ATTR_UID) ? iattr->ia_uid : iuid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/*
 | 
					 | 
				
			||||||
		 * Do a quota reservation only if uid/gid is actually
 | 
					 | 
				
			||||||
		 * going to change.
 | 
					 | 
				
			||||||
		 */
 | 
					 | 
				
			||||||
		if (XFS_IS_QUOTA_RUNNING(mp) &&
 | 
					 | 
				
			||||||
		    ((XFS_IS_UQUOTA_ON(mp) && !uid_eq(iuid, uid)) ||
 | 
					 | 
				
			||||||
		     (XFS_IS_GQUOTA_ON(mp) && !gid_eq(igid, gid)))) {
 | 
					 | 
				
			||||||
			ASSERT(tp);
 | 
					 | 
				
			||||||
			error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp,
 | 
					 | 
				
			||||||
						NULL, capable(CAP_FOWNER) ?
 | 
					 | 
				
			||||||
						XFS_QMOPT_FORCE_RES : 0);
 | 
					 | 
				
			||||||
			if (error)	/* out of quota */
 | 
					 | 
				
			||||||
				goto out_cancel;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		/*
 | 
							/*
 | 
				
			||||||
		 * CAP_FSETID overrides the following restrictions:
 | 
							 * CAP_FSETID overrides the following restrictions:
 | 
				
			||||||
		 *
 | 
							 *
 | 
				
			||||||
| 
						 | 
					@ -786,8 +769,6 @@ xfs_setattr_nonsize(
 | 
				
			||||||
		xfs_trans_set_sync(tp);
 | 
							xfs_trans_set_sync(tp);
 | 
				
			||||||
	error = xfs_trans_commit(tp);
 | 
						error = xfs_trans_commit(tp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Release any dquot(s) the inode had kept before chown.
 | 
						 * Release any dquot(s) the inode had kept before chown.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					@ -814,9 +795,6 @@ xfs_setattr_nonsize(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
out_cancel:
 | 
					 | 
				
			||||||
	xfs_trans_cancel(tp);
 | 
					 | 
				
			||||||
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
 | 
					 | 
				
			||||||
out_dqrele:
 | 
					out_dqrele:
 | 
				
			||||||
	xfs_qm_dqrele(udqp);
 | 
						xfs_qm_dqrele(udqp);
 | 
				
			||||||
	xfs_qm_dqrele(gdqp);
 | 
						xfs_qm_dqrele(gdqp);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1107,3 +1107,65 @@ xfs_trans_alloc_icreate(
 | 
				
			||||||
	*tpp = tp;
 | 
						*tpp = tp;
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Allocate an transaction, lock and join the inode to it, and reserve quota
 | 
				
			||||||
 | 
					 * in preparation for inode attribute changes that include uid, gid, or prid
 | 
				
			||||||
 | 
					 * changes.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The caller must ensure that the on-disk dquots attached to this inode have
 | 
				
			||||||
 | 
					 * already been allocated and initialized.  The ILOCK will be dropped when the
 | 
				
			||||||
 | 
					 * transaction is committed or cancelled.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					int
 | 
				
			||||||
 | 
					xfs_trans_alloc_ichange(
 | 
				
			||||||
 | 
						struct xfs_inode	*ip,
 | 
				
			||||||
 | 
						struct xfs_dquot	*udqp,
 | 
				
			||||||
 | 
						struct xfs_dquot	*gdqp,
 | 
				
			||||||
 | 
						struct xfs_dquot	*pdqp,
 | 
				
			||||||
 | 
						bool			force,
 | 
				
			||||||
 | 
						struct xfs_trans	**tpp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct xfs_trans	*tp;
 | 
				
			||||||
 | 
						struct xfs_mount	*mp = ip->i_mount;
 | 
				
			||||||
 | 
						int			error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error = xfs_trans_alloc(mp, &M_RES(mp)->tr_ichange, 0, 0, 0, &tp);
 | 
				
			||||||
 | 
						if (error)
 | 
				
			||||||
 | 
							return error;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						xfs_ilock(ip, XFS_ILOCK_EXCL);
 | 
				
			||||||
 | 
						xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						error = xfs_qm_dqattach_locked(ip, false);
 | 
				
			||||||
 | 
						if (error) {
 | 
				
			||||||
 | 
							/* Caller should have allocated the dquots! */
 | 
				
			||||||
 | 
							ASSERT(error != -ENOENT);
 | 
				
			||||||
 | 
							goto out_cancel;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * For each quota type, skip quota reservations if the inode's dquots
 | 
				
			||||||
 | 
						 * now match the ones that came from the caller, or the caller didn't
 | 
				
			||||||
 | 
						 * pass one in.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (udqp == ip->i_udquot)
 | 
				
			||||||
 | 
							udqp = NULL;
 | 
				
			||||||
 | 
						if (gdqp == ip->i_gdquot)
 | 
				
			||||||
 | 
							gdqp = NULL;
 | 
				
			||||||
 | 
						if (pdqp == ip->i_pdquot)
 | 
				
			||||||
 | 
							pdqp = NULL;
 | 
				
			||||||
 | 
						if (udqp || gdqp || pdqp) {
 | 
				
			||||||
 | 
							error = xfs_qm_vop_chown_reserve(tp, ip, udqp, gdqp, pdqp,
 | 
				
			||||||
 | 
									force ? XFS_QMOPT_FORCE_RES : 0);
 | 
				
			||||||
 | 
							if (error)
 | 
				
			||||||
 | 
								goto out_cancel;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*tpp = tp;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_cancel:
 | 
				
			||||||
 | 
						xfs_trans_cancel(tp);
 | 
				
			||||||
 | 
						return error;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -277,5 +277,8 @@ int xfs_trans_alloc_icreate(struct xfs_mount *mp, struct xfs_trans_res *resv,
 | 
				
			||||||
		struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
 | 
							struct xfs_dquot *udqp, struct xfs_dquot *gdqp,
 | 
				
			||||||
		struct xfs_dquot *pdqp, unsigned int dblocks,
 | 
							struct xfs_dquot *pdqp, unsigned int dblocks,
 | 
				
			||||||
		struct xfs_trans **tpp);
 | 
							struct xfs_trans **tpp);
 | 
				
			||||||
 | 
					int xfs_trans_alloc_ichange(struct xfs_inode *ip, struct xfs_dquot *udqp,
 | 
				
			||||||
 | 
							struct xfs_dquot *gdqp, struct xfs_dquot *pdqp, bool force,
 | 
				
			||||||
 | 
							struct xfs_trans **tpp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif	/* __XFS_TRANS_H__ */
 | 
					#endif	/* __XFS_TRANS_H__ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue