xfs_qm_syscalls.c 24.1 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1
/*
2
3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
Linus Torvalds's avatar
Linus Torvalds committed
4
 *
5
6
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
Linus Torvalds's avatar
Linus Torvalds committed
7
8
 * published by the Free Software Foundation.
 *
9
10
11
12
 * 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.
Linus Torvalds's avatar
Linus Torvalds committed
13
 *
14
15
16
 * 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
Linus Torvalds's avatar
Linus Torvalds committed
17
 */
18
19
20

#include <linux/capability.h>

Linus Torvalds's avatar
Linus Torvalds committed
21
22
#include "xfs.h"
#include "xfs_fs.h"
23
#include "xfs_bit.h"
Linus Torvalds's avatar
Linus Torvalds committed
24
25
26
#include "xfs_log.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
27
#include "xfs_ag.h"
Linus Torvalds's avatar
Linus Torvalds committed
28
29
30
31
32
#include "xfs_alloc.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
33
#include "xfs_inode_item.h"
34
#include "xfs_itable.h"
Linus Torvalds's avatar
Linus Torvalds committed
35
36
37
38
39
40
41
#include "xfs_bmap.h"
#include "xfs_rtalloc.h"
#include "xfs_error.h"
#include "xfs_attr.h"
#include "xfs_buf_item.h"
#include "xfs_utils.h"
#include "xfs_qm.h"
Christoph Hellwig's avatar
Christoph Hellwig committed
42
#include "xfs_trace.h"
Linus Torvalds's avatar
Linus Torvalds committed
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

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_export_flags(uint);
STATIC uint	xfs_qm_export_qtype_flags(uint);

/*
 * 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.
 */
58
int
Linus Torvalds's avatar
Linus Torvalds committed
59
60
xfs_qm_scall_quotaoff(
	xfs_mount_t		*mp,
61
	uint			flags)
Linus Torvalds's avatar
Linus Torvalds committed
62
{
63
	struct xfs_quotainfo	*q = mp->m_quotainfo;
Linus Torvalds's avatar
Linus Torvalds committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
	uint			dqtype;
	int			error;
	uint			inactivate_flags;
	xfs_qoff_logitem_t	*qoffstart;

	/*
	 * 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.
	 */
86
87
	ASSERT(q);
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
88
89
90
91
92
93
94

	/*
	 * If we're just turning off quota enforcement, change mp and go.
	 */
	if ((flags & XFS_ALL_QUOTA_ACCT) == 0) {
		mp->m_qflags &= ~(flags);

Eric Sandeen's avatar
Eric Sandeen committed
95
		spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
96
		mp->m_sb.sb_qflags = mp->m_qflags;
Eric Sandeen's avatar
Eric Sandeen committed
97
		spin_unlock(&mp->m_sb_lock);
98
		mutex_unlock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

		/* 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;
119
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
Linus Torvalds's avatar
Linus Torvalds committed
120
		inactivate_flags |= XFS_GQUOTA_ACTIVE;
121
122
123
124
	} else if (flags & XFS_PQUOTA_ACCT) {
		dqtype |= XFS_QMOPT_PQUOTA;
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
		inactivate_flags |= XFS_PQUOTA_ACTIVE;
Linus Torvalds's avatar
Linus Torvalds committed
125
126
127
128
129
130
	}

	/*
	 * Nothing to do?  Don't complain. This happens when we're just
	 * turning off quota enforcement.
	 */
131
132
	if ((mp->m_qflags & flags) == 0)
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
133
134
135

	/*
	 * Write the LI_QUOTAOFF log record, and do SB changes atomically,
136
137
	 * and synchronously. If we fail to write, we should abort the
	 * operation as it cannot be recovered safely if we crash.
Linus Torvalds's avatar
Linus Torvalds committed
138
	 */
139
140
	error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
	if (error)
141
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

	/*
	 * 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
Lucas De Marchi's avatar
Lucas De Marchi committed
171
	 * don't want to take a mrlock every time we depend on quotas being on.
Linus Torvalds's avatar
Linus Torvalds committed
172
	 */
173
	mp->m_qflags &= ~flags;
Linus Torvalds's avatar
Linus Torvalds committed
174
175
176

	/*
	 * Go through all the dquots of this file system and purge them,
177
	 * according to what was turned off.
Linus Torvalds's avatar
Linus Torvalds committed
178
	 */
179
	xfs_qm_dqpurge_all(mp, dqtype);
Linus Torvalds's avatar
Linus Torvalds committed
180
181
182
183
184
185
186
187
188
189
190
191

	/*
	 * 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...
	 */
192
193
194
195
	error = xfs_qm_log_quotaoff_end(mp, qoffstart, flags);
	if (error) {
		/* We're screwed now. Shutdown is the only option. */
		xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
196
		goto out_unlock;
197
	}
Linus Torvalds's avatar
Linus Torvalds committed
198
199
200
201

	/*
	 * If quotas is completely disabled, close shop.
	 */
202
203
	if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||
	    ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
204
		mutex_unlock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
205
206
207
208
209
		xfs_qm_destroy_quotainfo(mp);
		return (0);
	}

	/*
210
	 * Release our quotainode references if we don't need them anymore.
Linus Torvalds's avatar
Linus Torvalds committed
211
	 */
212
213
214
	if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) {
		IRELE(q->qi_uquotaip);
		q->qi_uquotaip = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
215
	}
216
217
218
	if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) {
		IRELE(q->qi_gquotaip);
		q->qi_gquotaip = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
219
220
	}

221
222
223
out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
224
225
}

226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
STATIC int
xfs_qm_scall_trunc_qfile(
	struct xfs_mount	*mp,
	xfs_ino_t		ino)
{
	struct xfs_inode	*ip;
	struct xfs_trans	*tp;
	int			error;

	if (ino == NULLFSINO)
		return 0;

	error = xfs_iget(mp, NULL, ino, 0, 0, &ip);
	if (error)
		return error;

	xfs_ilock(ip, XFS_IOLOCK_EXCL);

	tp = xfs_trans_alloc(mp, XFS_TRANS_TRUNCATE_FILE);
	error = xfs_trans_reserve(tp, 0, XFS_ITRUNCATE_LOG_RES(mp), 0,
				  XFS_TRANS_PERM_LOG_RES,
				  XFS_ITRUNCATE_LOG_COUNT);
	if (error) {
		xfs_trans_cancel(tp, 0);
		xfs_iunlock(ip, XFS_IOLOCK_EXCL);
		goto out_put;
	}

	xfs_ilock(ip, XFS_ILOCK_EXCL);
255
	xfs_trans_ijoin(tp, ip, 0);
256

257
258
259
260
	ip->i_d.di_size = 0;
	xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);

	error = xfs_itruncate_extents(&tp, ip, XFS_DATA_FORK, 0);
261
262
263
264
265
266
	if (error) {
		xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
				     XFS_TRANS_ABORT);
		goto out_unlock;
	}

267
268
	ASSERT(ip->i_d.di_nextents == 0);

269
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
270
271
272
273
274
275
276
277
278
	error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES);

out_unlock:
	xfs_iunlock(ip, XFS_ILOCK_EXCL | XFS_IOLOCK_EXCL);
out_put:
	IRELE(ip);
	return error;
}

279
int
Linus Torvalds's avatar
Linus Torvalds committed
280
281
282
283
xfs_qm_scall_trunc_qfiles(
	xfs_mount_t	*mp,
	uint		flags)
{
284
	int		error = 0, error2 = 0;
Linus Torvalds's avatar
Linus Torvalds committed
285

286
	if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) {
287
288
		xfs_debug(mp, "%s: flags=%x m_qflags=%x\n",
			__func__, flags, mp->m_qflags);
Linus Torvalds's avatar
Linus Torvalds committed
289
290
291
		return XFS_ERROR(EINVAL);
	}

292
293
294
295
	if (flags & XFS_DQ_USER)
		error = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_uquotino);
	if (flags & (XFS_DQ_GROUP|XFS_DQ_PROJ))
		error2 = xfs_qm_scall_trunc_qfile(mp, mp->m_sb.sb_gquotino);
Linus Torvalds's avatar
Linus Torvalds committed
296

297
	return error ? error : error2;
Linus Torvalds's avatar
Linus Torvalds committed
298
299
300
301
302
303
304
}

/*
 * Switch on (a given) quota enforcement for a filesystem.  This takes
 * effect immediately.
 * (Switching on quota accounting must be done at mount time.)
 */
305
int
Linus Torvalds's avatar
Linus Torvalds committed
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
xfs_qm_scall_quotaon(
	xfs_mount_t	*mp,
	uint		flags)
{
	int		error;
	uint		qf;
	__int64_t	sbflags;

	flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD);
	/*
	 * Switching on quota accounting must be done at mount time.
	 */
	flags &= ~(XFS_ALL_QUOTA_ACCT);

	sbflags = 0;

	if (flags == 0) {
323
324
		xfs_debug(mp, "%s: zero flags, m_qflags=%x\n",
			__func__, mp->m_qflags);
Linus Torvalds's avatar
Linus Torvalds committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
		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))
	    ||
340
341
	    ((flags & XFS_PQUOTA_ACCT) == 0 &&
	    (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 &&
342
	    (flags & XFS_GQUOTA_ACCT) == 0 &&
Linus Torvalds's avatar
Linus Torvalds committed
343
	    (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&
344
	    (flags & XFS_OQUOTA_ENFD))) {
345
346
347
		xfs_debug(mp,
			"%s: Can't enforce without acct, flags=%x sbflags=%x\n",
			__func__, flags, mp->m_sb.sb_qflags);
Linus Torvalds's avatar
Linus Torvalds committed
348
349
350
		return XFS_ERROR(EINVAL);
	}
	/*
Lucas De Marchi's avatar
Lucas De Marchi committed
351
	 * If everything's up to-date incore, then don't waste time.
Linus Torvalds's avatar
Linus Torvalds committed
352
353
354
355
356
357
358
359
	 */
	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.
	 */
Eric Sandeen's avatar
Eric Sandeen committed
360
	spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
361
362
	qf = mp->m_sb.sb_qflags;
	mp->m_sb.sb_qflags = qf | flags;
Eric Sandeen's avatar
Eric Sandeen committed
363
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378

	/*
	 * 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.
	 */
	if  (((mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) !=
	     (mp->m_qflags & XFS_UQUOTA_ACCT)) ||
379
380
381
382
	     ((mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) !=
	     (mp->m_qflags & XFS_PQUOTA_ACCT)) ||
	     ((mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) !=
	     (mp->m_qflags & XFS_GQUOTA_ACCT)) ||
Linus Torvalds's avatar
Linus Torvalds committed
383
384
385
386
387
388
389
390
391
	    (flags & XFS_ALL_QUOTA_ENFD) == 0)
		return (0);

	if (! XFS_IS_QUOTA_RUNNING(mp))
		return XFS_ERROR(ESRCH);

	/*
	 * Switch on quota enforcement in core.
	 */
392
	mutex_lock(&mp->m_quotainfo->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
393
	mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD);
394
	mutex_unlock(&mp->m_quotainfo->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
395
396
397
398
399
400
401
402

	return (0);
}


/*
 * Return quota status information, such as uquota-off, enforcements, etc.
 */
403
int
Linus Torvalds's avatar
Linus Torvalds committed
404
xfs_qm_scall_getqstat(
405
406
	struct xfs_mount	*mp,
	struct fs_quota_stat	*out)
Linus Torvalds's avatar
Linus Torvalds committed
407
{
408
409
410
	struct xfs_quotainfo	*q = mp->m_quotainfo;
	struct xfs_inode	*uip, *gip;
	boolean_t		tempuqip, tempgqip;
Linus Torvalds's avatar
Linus Torvalds committed
411
412
413
414
415
416

	uip = gip = NULL;
	tempuqip = tempgqip = B_FALSE;
	memset(out, 0, sizeof(fs_quota_stat_t));

	out->qs_version = FS_QSTAT_VERSION;
417
	if (!xfs_sb_version_hasquota(&mp->m_sb)) {
Linus Torvalds's avatar
Linus Torvalds committed
418
419
420
421
422
423
424
425
426
427
428
		out->qs_uquota.qfs_ino = NULLFSINO;
		out->qs_gquota.qfs_ino = NULLFSINO;
		return (0);
	}
	out->qs_flags = (__uint16_t) xfs_qm_export_flags(mp->m_qflags &
							(XFS_ALL_QUOTA_ACCT|
							 XFS_ALL_QUOTA_ENFD));
	out->qs_pad = 0;
	out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino;
	out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino;

429
430
431
	if (q) {
		uip = q->qi_uquotaip;
		gip = q->qi_gquotaip;
Linus Torvalds's avatar
Linus Torvalds committed
432
433
434
	}
	if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) {
		if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
435
					0, 0, &uip) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
436
437
438
439
			tempuqip = B_TRUE;
	}
	if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) {
		if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
440
					0, 0, &gip) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
441
442
443
444
445
446
			tempgqip = B_TRUE;
	}
	if (uip) {
		out->qs_uquota.qfs_nblks = uip->i_d.di_nblocks;
		out->qs_uquota.qfs_nextents = uip->i_d.di_nextents;
		if (tempuqip)
447
			IRELE(uip);
Linus Torvalds's avatar
Linus Torvalds committed
448
449
450
451
452
	}
	if (gip) {
		out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks;
		out->qs_gquota.qfs_nextents = gip->i_d.di_nextents;
		if (tempgqip)
453
			IRELE(gip);
Linus Torvalds's avatar
Linus Torvalds committed
454
	}
455
456
457
458
459
460
461
	if (q) {
		out->qs_incoredqs = q->qi_dquots;
		out->qs_btimelimit = q->qi_btimelimit;
		out->qs_itimelimit = q->qi_itimelimit;
		out->qs_rtbtimelimit = q->qi_rtbtimelimit;
		out->qs_bwarnlimit = q->qi_bwarnlimit;
		out->qs_iwarnlimit = q->qi_iwarnlimit;
Linus Torvalds's avatar
Linus Torvalds committed
462
	}
463
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
464
465
}

Christoph Hellwig's avatar
Christoph Hellwig committed
466
467
468
#define XFS_DQ_MASK \
	(FS_DQ_LIMIT_MASK | FS_DQ_TIMER_MASK | FS_DQ_WARNS_MASK)

Linus Torvalds's avatar
Linus Torvalds committed
469
470
471
/*
 * Adjust quota limits, and start/stop timers accordingly.
 */
472
int
Linus Torvalds's avatar
Linus Torvalds committed
473
474
475
476
477
478
xfs_qm_scall_setqlim(
	xfs_mount_t		*mp,
	xfs_dqid_t		id,
	uint			type,
	fs_disk_quota_t		*newlim)
{
479
	struct xfs_quotainfo	*q = mp->m_quotainfo;
Linus Torvalds's avatar
Linus Torvalds committed
480
481
482
483
484
485
	xfs_disk_dquot_t	*ddq;
	xfs_dquot_t		*dqp;
	xfs_trans_t		*tp;
	int			error;
	xfs_qcnt_t		hard, soft;

Christoph Hellwig's avatar
Christoph Hellwig committed
486
487
488
489
	if (newlim->d_fieldmask & ~XFS_DQ_MASK)
		return EINVAL;
	if ((newlim->d_fieldmask & XFS_DQ_MASK) == 0)
		return 0;
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494
495
496
497
498
499
500
501
502
503

	tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM);
	if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_disk_dquot_t) + 128,
				      0, 0, XFS_DEFAULT_LOG_COUNT))) {
		xfs_trans_cancel(tp, 0);
		return (error);
	}

	/*
	 * We don't want to race with a quotaoff so take the quotaoff lock.
	 * (We don't hold an inode lock, so there's nothing else to stop
	 * a quotaoff from happening). (XXXThis doesn't currently happen
	 * because we take the vfslock before calling xfs_qm_sysent).
	 */
504
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
505
506
507
508
509
510
511
512

	/*
	 * Get the dquot (locked), and join it to the transaction.
	 * Allocate the dquot if this doesn't exist.
	 */
	if ((error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp))) {
		xfs_trans_cancel(tp, XFS_TRANS_ABORT);
		ASSERT(error != ENOENT);
513
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
514
515
516
517
518
519
520
521
522
	}
	xfs_trans_dqjoin(tp, dqp);
	ddq = &dqp->q_core;

	/*
	 * Make sure that hardlimits are >= soft limits before changing.
	 */
	hard = (newlim->d_fieldmask & FS_DQ_BHARD) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_hardlimit) :
523
			be64_to_cpu(ddq->d_blk_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
524
525
	soft = (newlim->d_fieldmask & FS_DQ_BSOFT) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_softlimit) :
526
			be64_to_cpu(ddq->d_blk_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
527
	if (hard == 0 || hard >= soft) {
528
529
		ddq->d_blk_hardlimit = cpu_to_be64(hard);
		ddq->d_blk_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
530
		if (id == 0) {
531
532
			q->qi_bhardlimit = hard;
			q->qi_bsoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
533
534
		}
	} else {
535
		xfs_debug(mp, "blkhard %Ld < blksoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
536
537
538
	}
	hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_hardlimit) :
539
			be64_to_cpu(ddq->d_rtb_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
540
541
	soft = (newlim->d_fieldmask & FS_DQ_RTBSOFT) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_softlimit) :
542
			be64_to_cpu(ddq->d_rtb_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
543
	if (hard == 0 || hard >= soft) {
544
545
		ddq->d_rtb_hardlimit = cpu_to_be64(hard);
		ddq->d_rtb_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
546
		if (id == 0) {
547
548
			q->qi_rtbhardlimit = hard;
			q->qi_rtbsoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
549
550
		}
	} else {
551
		xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
552
553
554
555
	}

	hard = (newlim->d_fieldmask & FS_DQ_IHARD) ?
		(xfs_qcnt_t) newlim->d_ino_hardlimit :
556
			be64_to_cpu(ddq->d_ino_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
557
558
	soft = (newlim->d_fieldmask & FS_DQ_ISOFT) ?
		(xfs_qcnt_t) newlim->d_ino_softlimit :
559
			be64_to_cpu(ddq->d_ino_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
560
	if (hard == 0 || hard >= soft) {
561
562
		ddq->d_ino_hardlimit = cpu_to_be64(hard);
		ddq->d_ino_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
563
		if (id == 0) {
564
565
			q->qi_ihardlimit = hard;
			q->qi_isoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
566
567
		}
	} else {
568
		xfs_debug(mp, "ihard %Ld < isoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
569
570
	}

571
572
573
574
	/*
	 * Update warnings counter(s) if requested
	 */
	if (newlim->d_fieldmask & FS_DQ_BWARNS)
575
		ddq->d_bwarns = cpu_to_be16(newlim->d_bwarns);
576
	if (newlim->d_fieldmask & FS_DQ_IWARNS)
577
		ddq->d_iwarns = cpu_to_be16(newlim->d_iwarns);
578
	if (newlim->d_fieldmask & FS_DQ_RTBWARNS)
579
		ddq->d_rtbwarns = cpu_to_be16(newlim->d_rtbwarns);
580

Linus Torvalds's avatar
Linus Torvalds committed
581
582
583
584
585
	if (id == 0) {
		/*
		 * Timelimits for the super user set the relative time
		 * the other users can be over quota for this file system.
		 * If it is zero a default is used.  Ditto for the default
586
587
		 * soft and hard limit values (already done, above), and
		 * for warnings.
Linus Torvalds's avatar
Linus Torvalds committed
588
589
		 */
		if (newlim->d_fieldmask & FS_DQ_BTIMER) {
590
			q->qi_btimelimit = newlim->d_btimer;
591
			ddq->d_btimer = cpu_to_be32(newlim->d_btimer);
Linus Torvalds's avatar
Linus Torvalds committed
592
593
		}
		if (newlim->d_fieldmask & FS_DQ_ITIMER) {
594
			q->qi_itimelimit = newlim->d_itimer;
595
			ddq->d_itimer = cpu_to_be32(newlim->d_itimer);
Linus Torvalds's avatar
Linus Torvalds committed
596
597
		}
		if (newlim->d_fieldmask & FS_DQ_RTBTIMER) {
598
			q->qi_rtbtimelimit = newlim->d_rtbtimer;
599
			ddq->d_rtbtimer = cpu_to_be32(newlim->d_rtbtimer);
Linus Torvalds's avatar
Linus Torvalds committed
600
		}
601
		if (newlim->d_fieldmask & FS_DQ_BWARNS)
602
			q->qi_bwarnlimit = newlim->d_bwarns;
603
		if (newlim->d_fieldmask & FS_DQ_IWARNS)
604
			q->qi_iwarnlimit = newlim->d_iwarns;
605
		if (newlim->d_fieldmask & FS_DQ_RTBWARNS)
606
			q->qi_rtbwarnlimit = newlim->d_rtbwarns;
607
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
608
609
610
611
612
613
614
615
616
617
618
619
		/*
		 * If the user is now over quota, start the timelimit.
		 * The user will not be 'warned'.
		 * Note that we keep the timers ticking, whether enforcement
		 * is on or off. We don't really want to bother with iterating
		 * over all ondisk dquots and turning the timers on/off.
		 */
		xfs_qm_adjust_dqtimers(mp, ddq);
	}
	dqp->dq_flags |= XFS_DQ_DIRTY;
	xfs_trans_log_dquot(tp, dqp);

620
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
621
622
	xfs_qm_dqrele(dqp);

623
624
 out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
625
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
626
627
628
629
630
631
632
633
}

STATIC int
xfs_qm_log_quotaoff_end(
	xfs_mount_t		*mp,
	xfs_qoff_logitem_t	*startqoff,
	uint			flags)
{
634
	xfs_trans_t		*tp;
Linus Torvalds's avatar
Linus Torvalds committed
635
	int			error;
636
	xfs_qoff_logitem_t	*qoffi;
Linus Torvalds's avatar
Linus Torvalds committed
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655

	tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF_END);

	if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_qoff_logitem_t) * 2,
				      0, 0, XFS_DEFAULT_LOG_COUNT))) {
		xfs_trans_cancel(tp, 0);
		return (error);
	}

	qoffi = xfs_trans_get_qoff_item(tp, startqoff,
					flags & XFS_ALL_QUOTA_ACCT);
	xfs_trans_log_quotaoff_item(tp, qoffi);

	/*
	 * We have to make sure that the transaction is secure on disk before we
	 * return and actually stop quota accounting. So, make it synchronous.
	 * We don't care about quotoff's performance.
	 */
	xfs_trans_set_sync(tp);
656
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
	return (error);
}


STATIC int
xfs_qm_log_quotaoff(
	xfs_mount_t	       *mp,
	xfs_qoff_logitem_t     **qoffstartp,
	uint		       flags)
{
	xfs_trans_t	       *tp;
	int			error;
	xfs_qoff_logitem_t     *qoffi=NULL;
	uint			oldsbqflag=0;

	tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF);
	if ((error = xfs_trans_reserve(tp, 0,
				      sizeof(xfs_qoff_logitem_t) * 2 +
				      mp->m_sb.sb_sectsize + 128,
				      0,
				      0,
				      XFS_DEFAULT_LOG_COUNT))) {
		goto error0;
	}

	qoffi = xfs_trans_get_qoff_item(tp, NULL, flags & XFS_ALL_QUOTA_ACCT);
	xfs_trans_log_quotaoff_item(tp, qoffi);

Eric Sandeen's avatar
Eric Sandeen committed
685
	spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
686
687
	oldsbqflag = mp->m_sb.sb_qflags;
	mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL;
Eric Sandeen's avatar
Eric Sandeen committed
688
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
689
690
691
692
693
694
695
696
697

	xfs_mod_sb(tp, XFS_SB_QFLAGS);

	/*
	 * We have to make sure that the transaction is secure on disk before we
	 * return and actually stop quota accounting. So, make it synchronous.
	 * We don't care about quotoff's performance.
	 */
	xfs_trans_set_sync(tp);
698
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
699
700
701
702
703
704
705
706

error0:
	if (error) {
		xfs_trans_cancel(tp, 0);
		/*
		 * No one else is modifying sb_qflags, so this is OK.
		 * We still hold the quotaofflock.
		 */
Eric Sandeen's avatar
Eric Sandeen committed
707
		spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
708
		mp->m_sb.sb_qflags = oldsbqflag;
Eric Sandeen's avatar
Eric Sandeen committed
709
		spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
710
711
712
713
714
715
	}
	*qoffstartp = qoffi;
	return (error);
}


716
717
718
719
720
int
xfs_qm_scall_getquota(
	struct xfs_mount	*mp,
	xfs_dqid_t		id,
	uint			type,
Linus Torvalds's avatar
Linus Torvalds committed
721
722
	struct fs_disk_quota	*dst)
{
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
	struct xfs_dquot	*dqp;
	int			error;

	/*
	 * Try to get the dquot. We don't want it allocated on disk, so
	 * we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't
	 * exist, we'll get ENOENT back.
	 */
	error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp);
	if (error)
		return error;

	/*
	 * If everything's NULL, this dquot doesn't quite exist as far as
	 * our utility programs are concerned.
	 */
	if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) {
		error = XFS_ERROR(ENOENT);
		goto out_put;
	}

Linus Torvalds's avatar
Linus Torvalds committed
744
	memset(dst, 0, sizeof(*dst));
745
746
747
	dst->d_version = FS_DQUOT_VERSION;
	dst->d_flags = xfs_qm_export_qtype_flags(dqp->q_core.d_flags);
	dst->d_id = be32_to_cpu(dqp->q_core.d_id);
748
	dst->d_blk_hardlimit =
749
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
750
	dst->d_blk_softlimit =
751
752
753
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit));
	dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit);
	dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit);
754
755
	dst->d_bcount = XFS_FSB_TO_BB(mp, dqp->q_res_bcount);
	dst->d_icount = dqp->q_res_icount;
756
757
758
759
	dst->d_btimer = be32_to_cpu(dqp->q_core.d_btimer);
	dst->d_itimer = be32_to_cpu(dqp->q_core.d_itimer);
	dst->d_iwarns = be16_to_cpu(dqp->q_core.d_iwarns);
	dst->d_bwarns = be16_to_cpu(dqp->q_core.d_bwarns);
760
	dst->d_rtb_hardlimit =
761
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit));
762
	dst->d_rtb_softlimit =
763
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit));
764
	dst->d_rtbcount = XFS_FSB_TO_BB(mp, dqp->q_res_rtbcount);
765
766
	dst->d_rtbtimer = be32_to_cpu(dqp->q_core.d_rtbtimer);
	dst->d_rtbwarns = be16_to_cpu(dqp->q_core.d_rtbwarns);
Linus Torvalds's avatar
Linus Torvalds committed
767
768
769

	/*
	 * Internally, we don't reset all the timers when quota enforcement
770
	 * gets turned off. No need to confuse the user level code,
Linus Torvalds's avatar
Linus Torvalds committed
771
772
	 * so return zeroes in that case.
	 */
773
	if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) ||
774
	    (!XFS_IS_OQUOTA_ENFORCED(mp) &&
775
			(dqp->q_core.d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) {
Linus Torvalds's avatar
Linus Torvalds committed
776
777
778
779
780
781
		dst->d_btimer = 0;
		dst->d_itimer = 0;
		dst->d_rtbtimer = 0;
	}

#ifdef DEBUG
782
	if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) ||
783
	     (XFS_IS_OQUOTA_ENFORCED(mp) &&
784
			(dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) &&
785
	    dst->d_id != 0) {
786
		if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) &&
Linus Torvalds's avatar
Linus Torvalds committed
787
788
789
		    (dst->d_blk_softlimit > 0)) {
			ASSERT(dst->d_btimer != 0);
		}
790
		if (((int) dst->d_icount > (int) dst->d_ino_softlimit) &&
Linus Torvalds's avatar
Linus Torvalds committed
791
792
793
794
795
		    (dst->d_ino_softlimit > 0)) {
			ASSERT(dst->d_itimer != 0);
		}
	}
#endif
796
797
798
out_put:
	xfs_qm_dqput(dqp);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
799
800
801
802
803
804
805
}

STATIC uint
xfs_qm_export_qtype_flags(
	uint flags)
{
	/*
806
	 * Can't be more than one, or none.
Linus Torvalds's avatar
Linus Torvalds committed
807
	 */
808
809
810
811
812
813
814
	ASSERT((flags & (FS_PROJ_QUOTA | FS_USER_QUOTA)) !=
		(FS_PROJ_QUOTA | FS_USER_QUOTA));
	ASSERT((flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)) !=
		(FS_PROJ_QUOTA | FS_GROUP_QUOTA));
	ASSERT((flags & (FS_USER_QUOTA | FS_GROUP_QUOTA)) !=
		(FS_USER_QUOTA | FS_GROUP_QUOTA));
	ASSERT((flags & (FS_PROJ_QUOTA|FS_USER_QUOTA|FS_GROUP_QUOTA)) != 0);
Linus Torvalds's avatar
Linus Torvalds committed
815
816

	return (flags & XFS_DQ_USER) ?
817
818
		FS_USER_QUOTA : (flags & XFS_DQ_PROJ) ?
			FS_PROJ_QUOTA : FS_GROUP_QUOTA;
Linus Torvalds's avatar
Linus Torvalds committed
819
820
821
822
823
824
825
826
827
828
}

STATIC uint
xfs_qm_export_flags(
	uint flags)
{
	uint uflags;

	uflags = 0;
	if (flags & XFS_UQUOTA_ACCT)
829
		uflags |= FS_QUOTA_UDQ_ACCT;
830
	if (flags & XFS_PQUOTA_ACCT)
831
		uflags |= FS_QUOTA_PDQ_ACCT;
Linus Torvalds's avatar
Linus Torvalds committed
832
	if (flags & XFS_GQUOTA_ACCT)
833
		uflags |= FS_QUOTA_GDQ_ACCT;
Linus Torvalds's avatar
Linus Torvalds committed
834
	if (flags & XFS_UQUOTA_ENFD)
835
		uflags |= FS_QUOTA_UDQ_ENFD;
836
837
	if (flags & (XFS_OQUOTA_ENFD)) {
		uflags |= (flags & XFS_GQUOTA_ACCT) ?
838
			FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD;
839
	}
Linus Torvalds's avatar
Linus Torvalds committed
840
841
842
843
	return (uflags);
}


844
845
846
847
848
STATIC int
xfs_dqrele_inode(
	struct xfs_inode	*ip,
	struct xfs_perag	*pag,
	int			flags)
Linus Torvalds's avatar
Linus Torvalds committed
849
{
850
	/* skip quota inodes */
851
852
	if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
	    ip == ip->i_mount->m_quotainfo->qi_gquotaip) {
853
854
855
856
		ASSERT(ip->i_udquot == NULL);
		ASSERT(ip->i_gdquot == NULL);
		return 0;
	}
857

858
859
860
861
862
863
864
865
866
	xfs_ilock(ip, XFS_ILOCK_EXCL);
	if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) {
		xfs_qm_dqrele(ip->i_udquot);
		ip->i_udquot = NULL;
	}
	if (flags & (XFS_PQUOTA_ACCT|XFS_GQUOTA_ACCT) && ip->i_gdquot) {
		xfs_qm_dqrele(ip->i_gdquot);
		ip->i_gdquot = NULL;
	}
Christoph Hellwig's avatar
Christoph Hellwig committed
867
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
868
	return 0;
869
870
}

871

872
873
/*
 * Go thru all the inodes in the file system, releasing their dquots.
874
 *
875
 * Note that the mount structure gets modified to indicate that quotas are off
876
 * AFTER this, in the case of quotaoff.
877
878
879
880
881
882
883
 */
void
xfs_qm_dqrele_all_inodes(
	struct xfs_mount *mp,
	uint		 flags)
{
	ASSERT(mp->m_quotainfo);
884
	xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags);
Linus Torvalds's avatar
Linus Torvalds committed
885
}