162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2000-2006 Silicon Graphics, Inc.
462306a36Sopenharmony_ci * All Rights Reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include "xfs.h"
762306a36Sopenharmony_ci#include "xfs_fs.h"
862306a36Sopenharmony_ci#include "xfs_shared.h"
962306a36Sopenharmony_ci#include "xfs_format.h"
1062306a36Sopenharmony_ci#include "xfs_log_format.h"
1162306a36Sopenharmony_ci#include "xfs_trans_resv.h"
1262306a36Sopenharmony_ci#include "xfs_quota.h"
1362306a36Sopenharmony_ci#include "xfs_mount.h"
1462306a36Sopenharmony_ci#include "xfs_inode.h"
1562306a36Sopenharmony_ci#include "xfs_trans.h"
1662306a36Sopenharmony_ci#include "xfs_qm.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciSTATIC void
2062306a36Sopenharmony_cixfs_fill_statvfs_from_dquot(
2162306a36Sopenharmony_ci	struct kstatfs		*statp,
2262306a36Sopenharmony_ci	struct xfs_dquot	*dqp)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	uint64_t		limit;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	limit = dqp->q_blk.softlimit ?
2762306a36Sopenharmony_ci		dqp->q_blk.softlimit :
2862306a36Sopenharmony_ci		dqp->q_blk.hardlimit;
2962306a36Sopenharmony_ci	if (limit && statp->f_blocks > limit) {
3062306a36Sopenharmony_ci		statp->f_blocks = limit;
3162306a36Sopenharmony_ci		statp->f_bfree = statp->f_bavail =
3262306a36Sopenharmony_ci			(statp->f_blocks > dqp->q_blk.reserved) ?
3362306a36Sopenharmony_ci			 (statp->f_blocks - dqp->q_blk.reserved) : 0;
3462306a36Sopenharmony_ci	}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	limit = dqp->q_ino.softlimit ?
3762306a36Sopenharmony_ci		dqp->q_ino.softlimit :
3862306a36Sopenharmony_ci		dqp->q_ino.hardlimit;
3962306a36Sopenharmony_ci	if (limit && statp->f_files > limit) {
4062306a36Sopenharmony_ci		statp->f_files = limit;
4162306a36Sopenharmony_ci		statp->f_ffree =
4262306a36Sopenharmony_ci			(statp->f_files > dqp->q_ino.reserved) ?
4362306a36Sopenharmony_ci			 (statp->f_files - dqp->q_ino.reserved) : 0;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/*
4962306a36Sopenharmony_ci * Directory tree accounting is implemented using project quotas, where
5062306a36Sopenharmony_ci * the project identifier is inherited from parent directories.
5162306a36Sopenharmony_ci * A statvfs (df, etc.) of a directory that is using project quota should
5262306a36Sopenharmony_ci * return a statvfs of the project, not the entire filesystem.
5362306a36Sopenharmony_ci * This makes such trees appear as if they are filesystems in themselves.
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_civoid
5662306a36Sopenharmony_cixfs_qm_statvfs(
5762306a36Sopenharmony_ci	struct xfs_inode	*ip,
5862306a36Sopenharmony_ci	struct kstatfs		*statp)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct xfs_mount	*mp = ip->i_mount;
6162306a36Sopenharmony_ci	struct xfs_dquot	*dqp;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	if (!xfs_qm_dqget(mp, ip->i_projid, XFS_DQTYPE_PROJ, false, &dqp)) {
6462306a36Sopenharmony_ci		xfs_fill_statvfs_from_dquot(statp, dqp);
6562306a36Sopenharmony_ci		xfs_qm_dqput(dqp);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciint
7062306a36Sopenharmony_cixfs_qm_newmount(
7162306a36Sopenharmony_ci	xfs_mount_t	*mp,
7262306a36Sopenharmony_ci	uint		*needquotamount,
7362306a36Sopenharmony_ci	uint		*quotaflags)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	uint		quotaondisk;
7662306a36Sopenharmony_ci	uint		uquotaondisk = 0, gquotaondisk = 0, pquotaondisk = 0;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	quotaondisk = xfs_has_quota(mp) &&
7962306a36Sopenharmony_ci				(mp->m_sb.sb_qflags & XFS_ALL_QUOTA_ACCT);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (quotaondisk) {
8262306a36Sopenharmony_ci		uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT;
8362306a36Sopenharmony_ci		pquotaondisk = mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT;
8462306a36Sopenharmony_ci		gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT;
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	/*
8862306a36Sopenharmony_ci	 * If the device itself is read-only, we can't allow
8962306a36Sopenharmony_ci	 * the user to change the state of quota on the mount -
9062306a36Sopenharmony_ci	 * this would generate a transaction on the ro device,
9162306a36Sopenharmony_ci	 * which would lead to an I/O error and shutdown
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
9562306a36Sopenharmony_ci	    (!uquotaondisk &&  XFS_IS_UQUOTA_ON(mp)) ||
9662306a36Sopenharmony_ci	     (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
9762306a36Sopenharmony_ci	    (!gquotaondisk &&  XFS_IS_GQUOTA_ON(mp)) ||
9862306a36Sopenharmony_ci	     (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
9962306a36Sopenharmony_ci	    (!pquotaondisk &&  XFS_IS_PQUOTA_ON(mp)))  &&
10062306a36Sopenharmony_ci	    xfs_dev_is_read_only(mp, "changing quota state")) {
10162306a36Sopenharmony_ci		xfs_warn(mp, "please mount with%s%s%s%s.",
10262306a36Sopenharmony_ci			(!quotaondisk ? "out quota" : ""),
10362306a36Sopenharmony_ci			(uquotaondisk ? " usrquota" : ""),
10462306a36Sopenharmony_ci			(gquotaondisk ? " grpquota" : ""),
10562306a36Sopenharmony_ci			(pquotaondisk ? " prjquota" : ""));
10662306a36Sopenharmony_ci		return -EPERM;
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	if (XFS_IS_QUOTA_ON(mp) || quotaondisk) {
11062306a36Sopenharmony_ci		/*
11162306a36Sopenharmony_ci		 * Call mount_quotas at this point only if we won't have to do
11262306a36Sopenharmony_ci		 * a quotacheck.
11362306a36Sopenharmony_ci		 */
11462306a36Sopenharmony_ci		if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) {
11562306a36Sopenharmony_ci			/*
11662306a36Sopenharmony_ci			 * If an error occurred, qm_mount_quotas code
11762306a36Sopenharmony_ci			 * has already disabled quotas. So, just finish
11862306a36Sopenharmony_ci			 * mounting, and get on with the boring life
11962306a36Sopenharmony_ci			 * without disk quotas.
12062306a36Sopenharmony_ci			 */
12162306a36Sopenharmony_ci			xfs_qm_mount_quotas(mp);
12262306a36Sopenharmony_ci		} else {
12362306a36Sopenharmony_ci			/*
12462306a36Sopenharmony_ci			 * Clear the quota flags, but remember them. This
12562306a36Sopenharmony_ci			 * is so that the quota code doesn't get invoked
12662306a36Sopenharmony_ci			 * before we're ready. This can happen when an
12762306a36Sopenharmony_ci			 * inode goes inactive and wants to free blocks,
12862306a36Sopenharmony_ci			 * or via xfs_log_mount_finish.
12962306a36Sopenharmony_ci			 */
13062306a36Sopenharmony_ci			*needquotamount = true;
13162306a36Sopenharmony_ci			*quotaflags = mp->m_qflags;
13262306a36Sopenharmony_ci			mp->m_qflags = 0;
13362306a36Sopenharmony_ci		}
13462306a36Sopenharmony_ci	}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	return 0;
13762306a36Sopenharmony_ci}
138