⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xfs_qm_syscalls.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (c) 2000-2005 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */#include "xfs.h"#include "xfs_fs.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir.h"#include "xfs_dir2.h"#include "xfs_alloc.h"#include "xfs_dmapi.h"#include "xfs_quota.h"#include "xfs_mount.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir_sf.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_ialloc.h"#include "xfs_itable.h"#include "xfs_bmap.h"#include "xfs_btree.h"#include "xfs_rtalloc.h"#include "xfs_error.h"#include "xfs_rw.h"#include "xfs_acl.h"#include "xfs_cap.h"#include "xfs_mac.h"#include "xfs_attr.h"#include "xfs_buf_item.h"#include "xfs_utils.h"#include "xfs_qm.h"#ifdef DEBUG# define qdprintk(s, args...)	cmn_err(CE_DEBUG, s, ## args)#else# define qdprintk(s, args...)	do { } while (0)#endifSTATIC int	xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint);STATIC int	xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint,					fs_disk_quota_t *);STATIC int	xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *);STATIC int	xfs_qm_scall_setqlim(xfs_mount_t *, xfs_dqid_t, uint,					fs_disk_quota_t *);STATIC int	xfs_qm_scall_quotaon(xfs_mount_t *, uint);STATIC int	xfs_qm_scall_quotaoff(xfs_mount_t *, uint, boolean_t);STATIC int	xfs_qm_log_quotaoff(xfs_mount_t *, xfs_qoff_logitem_t **, uint);STATIC int	xfs_qm_log_quotaoff_end(xfs_mount_t *, xfs_qoff_logitem_t *,					uint);STATIC uint	xfs_qm_import_flags(uint);STATIC uint	xfs_qm_export_flags(uint);STATIC uint	xfs_qm_import_qtype_flags(uint);STATIC uint	xfs_qm_export_qtype_flags(uint);STATIC void	xfs_qm_export_dquot(xfs_mount_t *, xfs_disk_dquot_t *,					fs_disk_quota_t *);/* * The main distribution switch of all XFS quotactl system calls. */intxfs_qm_quotactl(	struct bhv_desc *bdp,	int		cmd,	int		id,	xfs_caddr_t	addr){	xfs_mount_t	*mp;	int		error;	struct vfs	*vfsp;	vfsp = bhvtovfs(bdp);	mp = XFS_VFSTOM(vfsp);	ASSERT(addr != NULL || cmd == Q_XQUOTASYNC);	/*	 * The following commands are valid even when quotaoff.	 */	switch (cmd) {	case Q_XQUOTARM:		/*		 * Truncate quota files. quota must be off.		 */		if (XFS_IS_QUOTA_ON(mp))			return XFS_ERROR(EINVAL);		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		return (xfs_qm_scall_trunc_qfiles(mp,			       xfs_qm_import_qtype_flags(*(uint *)addr)));	case Q_XGETQSTAT:		/*		 * Get quota status information.		 */		return (xfs_qm_scall_getqstat(mp, (fs_quota_stat_t *)addr));	case Q_XQUOTAON:		/*		 * QUOTAON - enabling quota enforcement.		 * Quota accounting must be turned on at mount time.		 */		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		return (xfs_qm_scall_quotaon(mp,					  xfs_qm_import_flags(*(uint *)addr)));	case Q_XQUOTAOFF:		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		break;	case Q_XQUOTASYNC:		return (xfs_sync_inodes(mp, SYNC_DELWRI, 0, NULL));	default:		break;	}	if (! XFS_IS_QUOTA_ON(mp))		return XFS_ERROR(ESRCH);	switch (cmd) {	case Q_XQUOTAOFF:		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		error = xfs_qm_scall_quotaoff(mp,					    xfs_qm_import_flags(*(uint *)addr),					    B_FALSE);		break;	case Q_XGETQUOTA:		error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_USER,					(fs_disk_quota_t *)addr);		break;	case Q_XGETGQUOTA:		error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_GROUP,					(fs_disk_quota_t *)addr);		break;	case Q_XGETPQUOTA:		error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_PROJ,					(fs_disk_quota_t *)addr);		break;	case Q_XSETQLIM:		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_USER,					     (fs_disk_quota_t *)addr);		break;	case Q_XSETGQLIM:		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_GROUP,					     (fs_disk_quota_t *)addr);		break;	case Q_XSETPQLIM:		if (vfsp->vfs_flag & VFS_RDONLY)			return XFS_ERROR(EROFS);		error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_PROJ,					     (fs_disk_quota_t *)addr);		break;	default:		error = XFS_ERROR(EINVAL);		break;	}	return (error);}/* * Turn off quota accounting and/or enforcement for all udquots and/or * gdquots. Called only at unmount time. * * This assumes that there are no dquots of this file system cached * incore, and modifies the ondisk dquot directly. Therefore, for example, * it is an error to call this twice, without purging the cache. */STATIC intxfs_qm_scall_quotaoff(	xfs_mount_t		*mp,	uint			flags,	boolean_t		force){	uint			dqtype;	unsigned long	s;	int			error;	uint			inactivate_flags;	xfs_qoff_logitem_t	*qoffstart;	int			nculprits;	if (!force && !capable(CAP_SYS_ADMIN))		return XFS_ERROR(EPERM);	/*	 * No file system can have quotas enabled on disk but not in core.	 * Note that quota utilities (like quotaoff) _expect_	 * errno == EEXIST here.	 */	if ((mp->m_qflags & flags) == 0)		return XFS_ERROR(EEXIST);	error = 0;	flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);	/*	 * We don't want to deal with two quotaoffs messing up each other,	 * so we're going to serialize it. quotaoff isn't exactly a performance	 * critical thing.	 * If quotaoff, then we must be dealing with the root filesystem.	 */	ASSERT(mp->m_quotainfo);	if (mp->m_quotainfo)		mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD);	ASSERT(mp->m_quotainfo);	/*	 * If we're just turning off quota enforcement, change mp and go.	 */	if ((flags & XFS_ALL_QUOTA_ACCT) == 0) {		mp->m_qflags &= ~(flags);		s = XFS_SB_LOCK(mp);		mp->m_sb.sb_qflags = mp->m_qflags;		XFS_SB_UNLOCK(mp, s);		mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));		/* XXX what to do if error ? Revert back to old vals incore ? */		error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS);		return (error);	}	dqtype = 0;	inactivate_flags = 0;	/*	 * If accounting is off, we must turn enforcement off, clear the	 * quota 'CHKD' certificate to make it known that we have to	 * do a quotacheck the next time this quota is turned on.	 */	if (flags & XFS_UQUOTA_ACCT) {		dqtype |= XFS_QMOPT_UQUOTA;		flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD);		inactivate_flags |= XFS_UQUOTA_ACTIVE;	}	if (flags & XFS_GQUOTA_ACCT) {		dqtype |= XFS_QMOPT_GQUOTA;		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);		inactivate_flags |= XFS_GQUOTA_ACTIVE;	} else if (flags & XFS_PQUOTA_ACCT) {		dqtype |= XFS_QMOPT_PQUOTA;		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);		inactivate_flags |= XFS_PQUOTA_ACTIVE;	}	/*	 * Nothing to do?  Don't complain. This happens when we're just	 * turning off quota enforcement.	 */	if ((mp->m_qflags & flags) == 0) {		mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));		return (0);	}	/*	 * Write the LI_QUOTAOFF log record, and do SB changes atomically,	 * and synchronously.	 */	xfs_qm_log_quotaoff(mp, &qoffstart, flags);	/*	 * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct	 * to take care of the race between dqget and quotaoff. We don't take	 * any special locks to reset these bits. All processes need to check	 * these bits *after* taking inode lock(s) to see if the particular	 * quota type is in the process of being turned off. If *ACTIVE, it is	 * guaranteed that all dquot structures and all quotainode ptrs will all	 * stay valid as long as that inode is kept locked.	 *	 * There is no turning back after this.	 */	mp->m_qflags &= ~inactivate_flags;	/*	 * Give back all the dquot reference(s) held by inodes.	 * Here we go thru every single incore inode in this file system, and	 * do a dqrele on the i_udquot/i_gdquot that it may have.	 * Essentially, as long as somebody has an inode locked, this guarantees	 * that quotas will not be turned off. This is handy because in a	 * transaction once we lock the inode(s) and check for quotaon, we can	 * depend on the quota inodes (and other things) being valid as long as	 * we keep the lock(s).	 */	xfs_qm_dqrele_all_inodes(mp, flags);	/*	 * Next we make the changes in the quota flag in the mount struct.	 * This isn't protected by a particular lock directly, because we	 * don't want to take a mrlock everytime we depend on quotas being on.	 */	mp->m_qflags &= ~(flags);	/*	 * Go through all the dquots of this file system and purge them,	 * according to what was turned off. We may not be able to get rid	 * of all dquots, because dquots can have temporary references that	 * are not attached to inodes. eg. xfs_setattr, xfs_create.	 * So, if we couldn't purge all the dquots from the filesystem,	 * we can't get rid of the incore data structures.	 */	while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF)))		delay(10 * nculprits);	/*	 * Transactions that had started before ACTIVE state bit was cleared	 * could have logged many dquots, so they'd have higher LSNs than	 * the first QUOTAOFF log record does. If we happen to crash when	 * the tail of the log has gone past the QUOTAOFF record, but	 * before the last dquot modification, those dquots __will__	 * recover, and that's not good.	 *	 * So, we have QUOTAOFF start and end logitems; the start	 * logitem won't get overwritten until the end logitem appears...	 */	xfs_qm_log_quotaoff_end(mp, qoffstart, flags);	/*	 * If quotas is completely disabled, close shop.	 */	if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||	    ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {		mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));		xfs_qm_destroy_quotainfo(mp);		return (0);	}	/*	 * Release our quotainode references, and vn_purge them,	 * if we don't need them anymore.	 */	if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) {		XFS_PURGE_INODE(XFS_QI_UQIP(mp));		XFS_QI_UQIP(mp) = NULL;	}	if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && XFS_QI_GQIP(mp)) {		XFS_PURGE_INODE(XFS_QI_GQIP(mp));		XFS_QI_GQIP(mp) = NULL;	}	mutex_unlock(&(XFS_QI_QOFFLOCK(mp)));	return (error);}STATIC intxfs_qm_scall_trunc_qfiles(	xfs_mount_t	*mp,	uint		flags){	int		error;	xfs_inode_t	*qip;	if (!capable(CAP_SYS_ADMIN))		return XFS_ERROR(EPERM);	error = 0;	if (!XFS_SB_VERSION_HASQUOTA(&mp->m_sb) || flags == 0) {		qdprintk("qtrunc flags=%x m_qflags=%x\n", flags, mp->m_qflags);		return XFS_ERROR(EINVAL);	}	if ((flags & XFS_DQ_USER) && mp->m_sb.sb_uquotino != NULLFSINO) {		error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip, 0);		if (! error) {			(void) xfs_truncate_file(mp, qip);			VN_RELE(XFS_ITOV(qip));		}	}	if ((flags & (XFS_DQ_GROUP|XFS_DQ_PROJ)) &&	    mp->m_sb.sb_gquotino != NULLFSINO) {		error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip, 0);		if (! error) {			(void) xfs_truncate_file(mp, qip);			VN_RELE(XFS_ITOV(qip));		}	}	return (error);}/* * Switch on (a given) quota enforcement for a filesystem.  This takes * effect immediately. * (Switching on quota accounting must be done at mount time.) */STATIC intxfs_qm_scall_quotaon(	xfs_mount_t	*mp,	uint		flags){	int		error;	unsigned long	s;	uint		qf;	uint		accflags;	__int64_t	sbflags;	if (!capable(CAP_SYS_ADMIN))		return XFS_ERROR(EPERM);	flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);	/*	 * Switching on quota accounting must be done at mount time.	 */	accflags = flags & XFS_ALL_QUOTA_ACCT;	flags &= ~(XFS_ALL_QUOTA_ACCT);	sbflags = 0;	if (flags == 0) {		qdprintk("quotaon: zero flags, m_qflags=%x\n", mp->m_qflags);		return XFS_ERROR(EINVAL);	}	/* No fs can turn on quotas with a delayed effect */	ASSERT((flags & XFS_ALL_QUOTA_ACCT) == 0);	/*	 * Can't enforce without accounting. We check the superblock	 * qflags here instead of m_qflags because rootfs can have	 * quota acct on ondisk without m_qflags' knowing.	 */	if (((flags & XFS_UQUOTA_ACCT) == 0 &&	    (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 &&	    (flags & XFS_UQUOTA_ENFD))	    ||	    ((flags & XFS_PQUOTA_ACCT) == 0 &&	    (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 &&	    (flags & XFS_OQUOTA_ENFD))	    ||	    ((flags & XFS_GQUOTA_ACCT) == 0 &&	    (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&	    (flags & XFS_OQUOTA_ENFD))) {		qdprintk("Can't enforce without acct, flags=%x sbflags=%x\n",			flags, mp->m_sb.sb_qflags);		return XFS_ERROR(EINVAL);	}	/*	 * If everything's upto-date incore, then don't waste time.	 */	if ((mp->m_qflags & flags) == flags)		return XFS_ERROR(EEXIST);	/*	 * Change sb_qflags on disk but not incore mp->qflags	 * if this is the root filesystem.	 */	s = XFS_SB_LOCK(mp);	qf = mp->m_sb.sb_qflags;	mp->m_sb.sb_qflags = qf | flags;	XFS_SB_UNLOCK(mp, s);	/*	 * There's nothing to change if it's the same.	 */	if ((qf & flags) == flags && sbflags == 0)		return XFS_ERROR(EEXIST);	sbflags |= XFS_SB_QFLAGS;	if ((error = xfs_qm_write_sb_changes(mp, sbflags)))		return (error);	/*	 * If we aren't trying to switch on quota enforcement, we are done.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -