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"
43
#include "xfs_icache.h"
Linus Torvalds's avatar
Linus Torvalds committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

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.
 */
59
int
Linus Torvalds's avatar
Linus Torvalds committed
60
61
xfs_qm_scall_quotaoff(
	xfs_mount_t		*mp,
62
	uint			flags)
Linus Torvalds's avatar
Linus Torvalds committed
63
{
64
	struct xfs_quotainfo	*q = mp->m_quotainfo;
Linus Torvalds's avatar
Linus Torvalds committed
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
	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.
	 */
87
88
	ASSERT(q);
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
89
90
91
92
93
94
95

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

		/* 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;
120
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
Linus Torvalds's avatar
Linus Torvalds committed
121
		inactivate_flags |= XFS_GQUOTA_ACTIVE;
122
123
124
125
	} 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
126
127
128
129
130
131
	}

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

	/*
	 * Write the LI_QUOTAOFF log record, and do SB changes atomically,
137
138
	 * 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
139
	 */
140
141
	error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
	if (error)
142
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
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
171

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

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

	/*
	 * 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...
	 */
193
194
195
196
	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);
197
		goto out_unlock;
198
	}
Linus Torvalds's avatar
Linus Torvalds committed
199
200
201
202

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

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

222
223
224
out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
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
255
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);
256
	xfs_trans_ijoin(tp, ip, 0);
257

258
259
260
261
	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);
262
263
264
265
266
267
	if (error) {
		xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
				     XFS_TRANS_ABORT);
		goto out_unlock;
	}

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

270
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
271
272
273
274
275
276
277
278
279
	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;
}

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

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

293
294
295
296
	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
297

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

/*
 * Switch on (a given) quota enforcement for a filesystem.  This takes
 * effect immediately.
 * (Switching on quota accounting must be done at mount time.)
 */
306
int
Linus Torvalds's avatar
Linus Torvalds committed
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
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) {
324
325
		xfs_debug(mp, "%s: zero flags, m_qflags=%x\n",
			__func__, mp->m_qflags);
Linus Torvalds's avatar
Linus Torvalds committed
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
		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))
	    ||
341
342
	    ((flags & XFS_PQUOTA_ACCT) == 0 &&
	    (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 &&
343
	    (flags & XFS_GQUOTA_ACCT) == 0 &&
Linus Torvalds's avatar
Linus Torvalds committed
344
	    (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 &&
345
	    (flags & XFS_OQUOTA_ENFD))) {
346
347
348
		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
349
350
351
		return XFS_ERROR(EINVAL);
	}
	/*
Lucas De Marchi's avatar
Lucas De Marchi committed
352
	 * If everything's up to-date incore, then don't waste time.
Linus Torvalds's avatar
Linus Torvalds committed
353
354
355
356
357
358
359
360
	 */
	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
361
	spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
362
363
	qf = mp->m_sb.sb_qflags;
	mp->m_sb.sb_qflags = qf | flags;
Eric Sandeen's avatar
Eric Sandeen committed
364
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

	/*
	 * 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)) ||
380
381
382
383
	     ((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
384
385
386
387
388
389
390
391
392
	    (flags & XFS_ALL_QUOTA_ENFD) == 0)
		return (0);

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

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

	return (0);
}


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

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

	out->qs_version = FS_QSTAT_VERSION;
418
	if (!xfs_sb_version_hasquota(&mp->m_sb)) {
Linus Torvalds's avatar
Linus Torvalds committed
419
420
421
422
423
424
425
426
427
428
429
		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;

430
431
432
	if (q) {
		uip = q->qi_uquotaip;
		gip = q->qi_gquotaip;
Linus Torvalds's avatar
Linus Torvalds committed
433
434
435
	}
	if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) {
		if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino,
436
					0, 0, &uip) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
437
438
439
440
			tempuqip = B_TRUE;
	}
	if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) {
		if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino,
441
					0, 0, &gip) == 0)
Linus Torvalds's avatar
Linus Torvalds committed
442
443
444
445
446
447
			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)
448
			IRELE(uip);
Linus Torvalds's avatar
Linus Torvalds committed
449
450
451
452
453
	}
	if (gip) {
		out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks;
		out->qs_gquota.qfs_nextents = gip->i_d.di_nextents;
		if (tempgqip)
454
			IRELE(gip);
Linus Torvalds's avatar
Linus Torvalds committed
455
	}
456
457
458
459
460
461
462
	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
463
	}
464
	return 0;
Linus Torvalds's avatar
Linus Torvalds committed
465
466
}

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

Linus Torvalds's avatar
Linus Torvalds committed
470
471
472
/*
 * Adjust quota limits, and start/stop timers accordingly.
 */
473
int
Linus Torvalds's avatar
Linus Torvalds committed
474
475
476
477
478
479
xfs_qm_scall_setqlim(
	xfs_mount_t		*mp,
	xfs_dqid_t		id,
	uint			type,
	fs_disk_quota_t		*newlim)
{
480
	struct xfs_quotainfo	*q = mp->m_quotainfo;
Linus Torvalds's avatar
Linus Torvalds committed
481
482
483
484
485
486
	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
487
488
489
490
	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
491
492
493
494
495
496
497
498
499
500
501
502
503
504

	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).
	 */
505
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
506
507
508
509
510
511
512
513

	/*
	 * 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);
514
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
515
516
517
518
519
520
521
522
523
	}
	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) :
524
			be64_to_cpu(ddq->d_blk_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
525
526
	soft = (newlim->d_fieldmask & FS_DQ_BSOFT) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_softlimit) :
527
			be64_to_cpu(ddq->d_blk_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
528
	if (hard == 0 || hard >= soft) {
529
530
		ddq->d_blk_hardlimit = cpu_to_be64(hard);
		ddq->d_blk_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
531
		if (id == 0) {
532
533
			q->qi_bhardlimit = hard;
			q->qi_bsoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
534
535
		}
	} else {
536
		xfs_debug(mp, "blkhard %Ld < blksoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
537
538
539
	}
	hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_hardlimit) :
540
			be64_to_cpu(ddq->d_rtb_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
541
542
	soft = (newlim->d_fieldmask & FS_DQ_RTBSOFT) ?
		(xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_softlimit) :
543
			be64_to_cpu(ddq->d_rtb_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
544
	if (hard == 0 || hard >= soft) {
545
546
		ddq->d_rtb_hardlimit = cpu_to_be64(hard);
		ddq->d_rtb_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
547
		if (id == 0) {
548
549
			q->qi_rtbhardlimit = hard;
			q->qi_rtbsoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
550
551
		}
	} else {
552
		xfs_debug(mp, "rtbhard %Ld < rtbsoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
553
554
555
556
	}

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

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

Linus Torvalds's avatar
Linus Torvalds committed
582
583
584
585
586
	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
587
588
		 * soft and hard limit values (already done, above), and
		 * for warnings.
Linus Torvalds's avatar
Linus Torvalds committed
589
590
		 */
		if (newlim->d_fieldmask & FS_DQ_BTIMER) {
591
			q->qi_btimelimit = newlim->d_btimer;
592
			ddq->d_btimer = cpu_to_be32(newlim->d_btimer);
Linus Torvalds's avatar
Linus Torvalds committed
593
594
		}
		if (newlim->d_fieldmask & FS_DQ_ITIMER) {
595
			q->qi_itimelimit = newlim->d_itimer;
596
			ddq->d_itimer = cpu_to_be32(newlim->d_itimer);
Linus Torvalds's avatar
Linus Torvalds committed
597
598
		}
		if (newlim->d_fieldmask & FS_DQ_RTBTIMER) {
599
			q->qi_rtbtimelimit = newlim->d_rtbtimer;
600
			ddq->d_rtbtimer = cpu_to_be32(newlim->d_rtbtimer);
Linus Torvalds's avatar
Linus Torvalds committed
601
		}
602
		if (newlim->d_fieldmask & FS_DQ_BWARNS)
603
			q->qi_bwarnlimit = newlim->d_bwarns;
604
		if (newlim->d_fieldmask & FS_DQ_IWARNS)
605
			q->qi_iwarnlimit = newlim->d_iwarns;
606
		if (newlim->d_fieldmask & FS_DQ_RTBWARNS)
607
			q->qi_rtbwarnlimit = newlim->d_rtbwarns;
608
	} else {
Linus Torvalds's avatar
Linus Torvalds committed
609
610
611
612
613
614
615
616
617
618
619
620
		/*
		 * 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);

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

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

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

	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);
657
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
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
685
	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
686
	spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
687
688
	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
689
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
690
691
692
693
694
695
696
697
698

	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);
699
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
700
701
702
703
704
705
706
707

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
708
		spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
709
		mp->m_sb.sb_qflags = oldsbqflag;
Eric Sandeen's avatar
Eric Sandeen committed
710
		spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
711
712
713
714
715
716
	}
	*qoffstartp = qoffi;
	return (error);
}


717
718
719
720
721
int
xfs_qm_scall_getquota(
	struct xfs_mount	*mp,
	xfs_dqid_t		id,
	uint			type,
Linus Torvalds's avatar
Linus Torvalds committed
722
723
	struct fs_disk_quota	*dst)
{
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
	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
745
	memset(dst, 0, sizeof(*dst));
746
747
748
	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);
749
	dst->d_blk_hardlimit =
750
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
751
	dst->d_blk_softlimit =
752
753
754
		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);
755
756
	dst->d_bcount = XFS_FSB_TO_BB(mp, dqp->q_res_bcount);
	dst->d_icount = dqp->q_res_icount;
757
758
759
760
	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);
761
	dst->d_rtb_hardlimit =
762
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit));
763
	dst->d_rtb_softlimit =
764
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit));
765
	dst->d_rtbcount = XFS_FSB_TO_BB(mp, dqp->q_res_rtbcount);
766
767
	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
768
769
770

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

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

STATIC uint
xfs_qm_export_qtype_flags(
	uint flags)
{
	/*
807
	 * Can't be more than one, or none.
Linus Torvalds's avatar
Linus Torvalds committed
808
	 */
809
810
811
812
813
814
815
	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
816
817

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

STATIC uint
xfs_qm_export_flags(
	uint flags)
{
	uint uflags;

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


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

860
861
862
863
864
865
866
867
868
	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
869
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
870
	return 0;
871
872
}

873

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