1/*
2 * Copyright (c) 2000-2006 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18#include "xfs.h"
19#include "xfs_fs.h"
20#include "xfs_bit.h"
21#include "xfs_log.h"
22#include "xfs_inum.h"
23#include "xfs_clnt.h"
24#include "xfs_trans.h"
25#include "xfs_sb.h"
26#include "xfs_ag.h"
27#include "xfs_dir2.h"
28#include "xfs_alloc.h"
29#include "xfs_dmapi.h"
30#include "xfs_quota.h"
31#include "xfs_mount.h"
32#include "xfs_bmap_btree.h"
33#include "xfs_alloc_btree.h"
34#include "xfs_ialloc_btree.h"
35#include "xfs_dir2_sf.h"
36#include "xfs_attr_sf.h"
37#include "xfs_dinode.h"
38#include "xfs_inode.h"
39#include "xfs_ialloc.h"
40#include "xfs_itable.h"
41#include "xfs_btree.h"
42#include "xfs_bmap.h"
43#include "xfs_rtalloc.h"
44#include "xfs_error.h"
45#include "xfs_rw.h"
46#include "xfs_acl.h"
47#include "xfs_attr.h"
48#include "xfs_buf_item.h"
49#include "xfs_qm.h"
50
51#define MNTOPT_QUOTA	"quota"		/* disk quotas (user) */
52#define MNTOPT_NOQUOTA	"noquota"	/* no quotas */
53#define MNTOPT_USRQUOTA	"usrquota"	/* user quota enabled */
54#define MNTOPT_GRPQUOTA	"grpquota"	/* group quota enabled */
55#define MNTOPT_PRJQUOTA	"prjquota"	/* project quota enabled */
56#define MNTOPT_UQUOTA	"uquota"	/* user quota (IRIX variant) */
57#define MNTOPT_GQUOTA	"gquota"	/* group quota (IRIX variant) */
58#define MNTOPT_PQUOTA	"pquota"	/* project quota (IRIX variant) */
59#define MNTOPT_UQUOTANOENF "uqnoenforce"/* user quota limit enforcement */
60#define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */
61#define MNTOPT_PQUOTANOENF "pqnoenforce"/* project quota limit enforcement */
62#define MNTOPT_QUOTANOENF  "qnoenforce"	/* same as uqnoenforce */
63
64STATIC int
65xfs_qm_parseargs(
66	struct bhv_desc		*bhv,
67	char			*options,
68	struct xfs_mount_args	*args,
69	int			update)
70{
71	size_t			length;
72	char			*local_options = options;
73	char			*this_char;
74	int			error;
75	int			referenced = update;
76
77	while ((this_char = strsep(&local_options, ",")) != NULL) {
78		length = strlen(this_char);
79		if (local_options)
80			length++;
81
82		if (!strcmp(this_char, MNTOPT_NOQUOTA)) {
83			args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA);
84			args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA);
85			referenced = update;
86		} else if (!strcmp(this_char, MNTOPT_QUOTA) ||
87			   !strcmp(this_char, MNTOPT_UQUOTA) ||
88			   !strcmp(this_char, MNTOPT_USRQUOTA)) {
89			args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF;
90			referenced = 1;
91		} else if (!strcmp(this_char, MNTOPT_QUOTANOENF) ||
92			   !strcmp(this_char, MNTOPT_UQUOTANOENF)) {
93			args->flags |= XFSMNT_UQUOTA;
94			args->flags &= ~XFSMNT_UQUOTAENF;
95			referenced = 1;
96		} else if (!strcmp(this_char, MNTOPT_PQUOTA) ||
97			   !strcmp(this_char, MNTOPT_PRJQUOTA)) {
98			args->flags |= XFSMNT_PQUOTA | XFSMNT_PQUOTAENF;
99			referenced = 1;
100		} else if (!strcmp(this_char, MNTOPT_PQUOTANOENF)) {
101			args->flags |= XFSMNT_PQUOTA;
102			args->flags &= ~XFSMNT_PQUOTAENF;
103			referenced = 1;
104		} else if (!strcmp(this_char, MNTOPT_GQUOTA) ||
105			   !strcmp(this_char, MNTOPT_GRPQUOTA)) {
106			args->flags |= XFSMNT_GQUOTA | XFSMNT_GQUOTAENF;
107			referenced = 1;
108		} else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) {
109			args->flags |= XFSMNT_GQUOTA;
110			args->flags &= ~XFSMNT_GQUOTAENF;
111			referenced = 1;
112		} else {
113			if (local_options)
114				*(local_options-1) = ',';
115			continue;
116		}
117
118		while (length--)
119			*this_char++ = ',';
120	}
121
122	if ((args->flags & XFSMNT_GQUOTA) && (args->flags & XFSMNT_PQUOTA)) {
123		cmn_err(CE_WARN,
124			"XFS: cannot mount with both project and group quota");
125		return XFS_ERROR(EINVAL);
126	}
127
128	error = bhv_next_vfs_parseargs(BHV_NEXT(bhv), options, args, update);
129	if (!error && !referenced)
130		bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM);
131	return error;
132}
133
134STATIC int
135xfs_qm_showargs(
136	struct bhv_desc		*bhv,
137	struct seq_file		*m)
138{
139	struct bhv_vfs		*vfsp = bhvtovfs(bhv);
140	struct xfs_mount	*mp = XFS_VFSTOM(vfsp);
141
142	if (mp->m_qflags & XFS_UQUOTA_ACCT) {
143		(mp->m_qflags & XFS_UQUOTA_ENFD) ?
144			seq_puts(m, "," MNTOPT_USRQUOTA) :
145			seq_puts(m, "," MNTOPT_UQUOTANOENF);
146	}
147
148	if (mp->m_qflags & XFS_PQUOTA_ACCT) {
149		(mp->m_qflags & XFS_OQUOTA_ENFD) ?
150			seq_puts(m, "," MNTOPT_PRJQUOTA) :
151			seq_puts(m, "," MNTOPT_PQUOTANOENF);
152	}
153
154	if (mp->m_qflags & XFS_GQUOTA_ACCT) {
155		(mp->m_qflags & XFS_OQUOTA_ENFD) ?
156			seq_puts(m, "," MNTOPT_GRPQUOTA) :
157			seq_puts(m, "," MNTOPT_GQUOTANOENF);
158	}
159
160	if (!(mp->m_qflags & XFS_ALL_QUOTA_ACCT))
161		seq_puts(m, "," MNTOPT_NOQUOTA);
162
163	return bhv_next_vfs_showargs(BHV_NEXT(bhv), m);
164}
165
166STATIC int
167xfs_qm_mount(
168	struct bhv_desc		*bhv,
169	struct xfs_mount_args	*args,
170	struct cred		*cr)
171{
172	struct bhv_vfs		*vfsp = bhvtovfs(bhv);
173	struct xfs_mount	*mp = XFS_VFSTOM(vfsp);
174
175	if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA | XFSMNT_PQUOTA))
176		xfs_qm_mount_quotainit(mp, args->flags);
177	return bhv_next_vfs_mount(BHV_NEXT(bhv), args, cr);
178}
179
180/*
181 * Directory tree accounting is implemented using project quotas, where
182 * the project identifier is inherited from parent directories.
183 * A statvfs (df, etc.) of a directory that is using project quota should
184 * return a statvfs of the project, not the entire filesystem.
185 * This makes such trees appear as if they are filesystems in themselves.
186 */
187STATIC int
188xfs_qm_statvfs(
189	struct bhv_desc		*bhv,
190	bhv_statvfs_t		*statp,
191	struct bhv_vnode	*vnode)
192{
193	xfs_mount_t		*mp;
194	xfs_inode_t		*ip;
195	xfs_dquot_t		*dqp;
196	xfs_disk_dquot_t	*dp;
197	__uint64_t		limit;
198	int			error;
199
200	error = bhv_next_vfs_statvfs(BHV_NEXT(bhv), statp, vnode);
201	if (error || !vnode)
202		return error;
203
204	mp = xfs_vfstom(bhvtovfs(bhv));
205	ip = xfs_vtoi(vnode);
206
207	if (!(ip->i_d.di_flags & XFS_DIFLAG_PROJINHERIT))
208		return 0;
209	if (!(mp->m_qflags & XFS_PQUOTA_ACCT))
210		return 0;
211	if (!(mp->m_qflags & XFS_OQUOTA_ENFD))
212		return 0;
213
214	if (xfs_qm_dqget(mp, NULL, ip->i_d.di_projid, XFS_DQ_PROJ, 0, &dqp))
215		return 0;
216	dp = &dqp->q_core;
217
218	limit = dp->d_blk_softlimit ?
219		be64_to_cpu(dp->d_blk_softlimit) :
220		be64_to_cpu(dp->d_blk_hardlimit);
221	if (limit && statp->f_blocks > limit) {
222		statp->f_blocks = limit;
223		statp->f_bfree =
224			(statp->f_blocks > be64_to_cpu(dp->d_bcount)) ?
225			 (statp->f_blocks - be64_to_cpu(dp->d_bcount)) : 0;
226	}
227
228	limit = dp->d_ino_softlimit ?
229		be64_to_cpu(dp->d_ino_softlimit) :
230		be64_to_cpu(dp->d_ino_hardlimit);
231	if (limit && statp->f_files > limit) {
232		statp->f_files = limit;
233		statp->f_ffree =
234			(statp->f_files > be64_to_cpu(dp->d_icount)) ?
235			 (statp->f_ffree - be64_to_cpu(dp->d_icount)) : 0;
236	}
237
238	xfs_qm_dqput(dqp);
239	return 0;
240}
241
242STATIC int
243xfs_qm_syncall(
244	struct bhv_desc		*bhv,
245	int			flags,
246	cred_t			*credp)
247{
248	struct bhv_vfs		*vfsp = bhvtovfs(bhv);
249	struct xfs_mount	*mp = XFS_VFSTOM(vfsp);
250	int			error;
251
252	/*
253	 * Get the Quota Manager to flush the dquots.
254	 */
255	if (XFS_IS_QUOTA_ON(mp)) {
256		if ((error = xfs_qm_sync(mp, flags))) {
257			/*
258			 * If we got an IO error, we will be shutting down.
259			 * So, there's nothing more for us to do here.
260			 */
261			ASSERT(error != EIO || XFS_FORCED_SHUTDOWN(mp));
262			if (XFS_FORCED_SHUTDOWN(mp)) {
263				return XFS_ERROR(error);
264			}
265		}
266	}
267	return bhv_next_vfs_sync(BHV_NEXT(bhv), flags, credp);
268}
269
270STATIC int
271xfs_qm_newmount(
272	xfs_mount_t	*mp,
273	uint		*needquotamount,
274	uint		*quotaflags)
275{
276	uint		quotaondisk;
277	uint		uquotaondisk = 0, gquotaondisk = 0, pquotaondisk = 0;
278
279	*quotaflags = 0;
280	*needquotamount = B_FALSE;
281
282	quotaondisk = XFS_SB_VERSION_HASQUOTA(&mp->m_sb) &&
283				(mp->m_sb.sb_qflags & XFS_ALL_QUOTA_ACCT);
284
285	if (quotaondisk) {
286		uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT;
287		pquotaondisk = mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT;
288		gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT;
289	}
290
291	/*
292	 * If the device itself is read-only, we can't allow
293	 * the user to change the state of quota on the mount -
294	 * this would generate a transaction on the ro device,
295	 * which would lead to an I/O error and shutdown
296	 */
297
298	if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) ||
299	    (!uquotaondisk &&  XFS_IS_UQUOTA_ON(mp)) ||
300	     (pquotaondisk && !XFS_IS_PQUOTA_ON(mp)) ||
301	    (!pquotaondisk &&  XFS_IS_PQUOTA_ON(mp)) ||
302	     (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) ||
303	    (!gquotaondisk &&  XFS_IS_OQUOTA_ON(mp)))  &&
304	    xfs_dev_is_read_only(mp, "changing quota state")) {
305		cmn_err(CE_WARN,
306			"XFS: please mount with%s%s%s%s.",
307			(!quotaondisk ? "out quota" : ""),
308			(uquotaondisk ? " usrquota" : ""),
309			(pquotaondisk ? " prjquota" : ""),
310			(gquotaondisk ? " grpquota" : ""));
311		return XFS_ERROR(EPERM);
312	}
313
314	if (XFS_IS_QUOTA_ON(mp) || quotaondisk) {
315		/*
316		 * Call mount_quotas at this point only if we won't have to do
317		 * a quotacheck.
318		 */
319		if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) {
320			/*
321			 * If an error occured, qm_mount_quotas code
322			 * has already disabled quotas. So, just finish
323			 * mounting, and get on with the boring life
324			 * without disk quotas.
325			 */
326			xfs_qm_mount_quotas(mp, 0);
327		} else {
328			/*
329			 * Clear the quota flags, but remember them. This
330			 * is so that the quota code doesn't get invoked
331			 * before we're ready. This can happen when an
332			 * inode goes inactive and wants to free blocks,
333			 * or via xfs_log_mount_finish.
334			 */
335			*needquotamount = B_TRUE;
336			*quotaflags = mp->m_qflags;
337			mp->m_qflags = 0;
338		}
339	}
340
341	return 0;
342}
343
344STATIC int
345xfs_qm_endmount(
346	xfs_mount_t	*mp,
347	uint		needquotamount,
348	uint		quotaflags,
349	int		mfsi_flags)
350{
351	if (needquotamount) {
352		ASSERT(mp->m_qflags == 0);
353		mp->m_qflags = quotaflags;
354		xfs_qm_mount_quotas(mp, mfsi_flags);
355	}
356
357#if defined(DEBUG) && defined(XFS_LOUD_RECOVERY)
358	if (! (XFS_IS_QUOTA_ON(mp)))
359		xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas not turned on");
360	else
361		xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas turned on");
362#endif
363
364#ifdef QUOTADEBUG
365	if (XFS_IS_QUOTA_ON(mp) && xfs_qm_internalqcheck(mp))
366		cmn_err(CE_WARN, "XFS: mount internalqcheck failed");
367#endif
368
369	return 0;
370}
371
372STATIC void
373xfs_qm_dqrele_null(
374	xfs_dquot_t	*dq)
375{
376	/*
377	 * Called from XFS, where we always check first for a NULL dquot.
378	 */
379	if (!dq)
380		return;
381	xfs_qm_dqrele(dq);
382}
383
384
385static struct xfs_qmops xfs_qmcore_xfs = {
386	.xfs_qminit		= xfs_qm_newmount,
387	.xfs_qmdone		= xfs_qm_unmount_quotadestroy,
388	.xfs_qmmount		= xfs_qm_endmount,
389	.xfs_qmunmount		= xfs_qm_unmount_quotas,
390	.xfs_dqrele		= xfs_qm_dqrele_null,
391	.xfs_dqattach		= xfs_qm_dqattach,
392	.xfs_dqdetach		= xfs_qm_dqdetach,
393	.xfs_dqpurgeall		= xfs_qm_dqpurge_all,
394	.xfs_dqvopalloc		= xfs_qm_vop_dqalloc,
395	.xfs_dqvopcreate	= xfs_qm_vop_dqattach_and_dqmod_newinode,
396	.xfs_dqvoprename	= xfs_qm_vop_rename_dqattach,
397	.xfs_dqvopchown		= xfs_qm_vop_chown,
398	.xfs_dqvopchownresv	= xfs_qm_vop_chown_reserve,
399	.xfs_dqtrxops		= &xfs_trans_dquot_ops,
400};
401
402struct bhv_module_vfsops xfs_qmops = { {
403	BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM),
404	.vfs_parseargs		= xfs_qm_parseargs,
405	.vfs_showargs		= xfs_qm_showargs,
406	.vfs_mount		= xfs_qm_mount,
407	.vfs_statvfs		= xfs_qm_statvfs,
408	.vfs_sync		= xfs_qm_syncall,
409	.vfs_quotactl		= xfs_qm_quotactl, },
410};
411
412
413void __init
414xfs_qm_init(void)
415{
416	static char	message[] __initdata =
417		KERN_INFO "SGI XFS Quota Management subsystem\n";
418
419	printk(message);
420	mutex_init(&xfs_Gqm_lock);
421	vfs_bhv_set_custom(&xfs_qmops, &xfs_qmcore_xfs);
422	xfs_qm_init_procfs();
423}
424
425void __exit
426xfs_qm_exit(void)
427{
428	vfs_bhv_clr_custom(&xfs_qmops);
429	xfs_qm_cleanup_procfs();
430	if (qm_dqzone)
431		kmem_zone_destroy(qm_dqzone);
432	if (qm_dqtrxzone)
433		kmem_zone_destroy(qm_dqtrxzone);
434}
435