xfs_qm_syscalls.c 24.9 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
#include "xfs_log.h"
25
#include "xfs_inum.h"
Linus Torvalds's avatar
Linus Torvalds committed
26
27
#include "xfs_trans.h"
#include "xfs_sb.h"
28
#include "xfs_ag.h"
Linus Torvalds's avatar
Linus Torvalds committed
29
30
31
32
33
#include "xfs_alloc.h"
#include "xfs_quota.h"
#include "xfs_mount.h"
#include "xfs_bmap_btree.h"
#include "xfs_inode.h"
34
#include "xfs_inode_item.h"
35
#include "xfs_itable.h"
Linus Torvalds's avatar
Linus Torvalds committed
36
37
38
39
40
41
42
#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
43
#include "xfs_trace.h"
Linus Torvalds's avatar
Linus Torvalds committed
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

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);
STATIC void	xfs_qm_export_dquot(xfs_mount_t *, xfs_disk_dquot_t *,
					fs_disk_quota_t *);


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

	/*
	 * 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.
	 */
91
92
	ASSERT(q);
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
93
94
95
96
97
98
99

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

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

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

	/*
	 * Write the LI_QUOTAOFF log record, and do SB changes atomically,
141
142
	 * 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
143
	 */
144
145
	error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
	if (error)
146
		goto out_unlock;
Linus Torvalds's avatar
Linus Torvalds committed
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
172
173
174
175

	/*
	 * 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
176
	 * don't want to take a mrlock every time we depend on quotas being on.
Linus Torvalds's avatar
Linus Torvalds committed
177
178
179
180
181
182
183
184
185
186
187
	 */
	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.
	 */
188
	while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype)))
Linus Torvalds's avatar
Linus Torvalds committed
189
190
191
192
193
194
195
196
197
198
199
200
201
		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...
	 */
202
203
204
205
	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);
206
		goto out_unlock;
207
	}
Linus Torvalds's avatar
Linus Torvalds committed
208
209
210
211

	/*
	 * If quotas is completely disabled, close shop.
	 */
212
213
	if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||
	    ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
214
		mutex_unlock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
215
216
217
218
219
		xfs_qm_destroy_quotainfo(mp);
		return (0);
	}

	/*
220
	 * Release our quotainode references if we don't need them anymore.
Linus Torvalds's avatar
Linus Torvalds committed
221
	 */
222
223
224
	if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) {
		IRELE(q->qi_uquotaip);
		q->qi_uquotaip = NULL;
Linus Torvalds's avatar
Linus Torvalds committed
225
	}
226
227
228
	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
229
230
	}

231
232
233
out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
234
235
}

236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
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);
265
	xfs_trans_ijoin(tp, ip, 0);
266

267
268
269
270
	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);
271
272
273
274
275
276
	if (error) {
		xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES |
				     XFS_TRANS_ABORT);
		goto out_unlock;
	}

277
278
	ASSERT(ip->i_d.di_nextents == 0);

279
	xfs_trans_ichgtime(tp, ip, XFS_ICHGTIME_MOD | XFS_ICHGTIME_CHG);
280
281
282
283
284
285
286
287
288
	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;
}

289
int
Linus Torvalds's avatar
Linus Torvalds committed
290
291
292
293
xfs_qm_scall_trunc_qfiles(
	xfs_mount_t	*mp,
	uint		flags)
{
294
	int		error = 0, error2 = 0;
Linus Torvalds's avatar
Linus Torvalds committed
295

296
	if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) {
297
298
		xfs_debug(mp, "%s: flags=%x m_qflags=%x\n",
			__func__, flags, mp->m_qflags);
Linus Torvalds's avatar
Linus Torvalds committed
299
300
301
		return XFS_ERROR(EINVAL);
	}

302
303
304
305
	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
306

307
	return error ? error : error2;
Linus Torvalds's avatar
Linus Torvalds committed
308
309
310
311
312
313
314
}

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

	/*
	 * 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)) ||
389
390
391
392
	     ((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
393
394
395
396
397
398
399
400
401
	    (flags & XFS_ALL_QUOTA_ENFD) == 0)
		return (0);

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

	/*
	 * Switch on quota enforcement in core.
	 */
402
	mutex_lock(&mp->m_quotainfo->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
403
	mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD);
404
	mutex_unlock(&mp->m_quotainfo->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
405
406
407
408
409
410
411
412

	return (0);
}


/*
 * Return quota status information, such as uquota-off, enforcements, etc.
 */
413
int
Linus Torvalds's avatar
Linus Torvalds committed
414
xfs_qm_scall_getqstat(
415
416
	struct xfs_mount	*mp,
	struct fs_quota_stat	*out)
Linus Torvalds's avatar
Linus Torvalds committed
417
{
418
419
420
	struct xfs_quotainfo	*q = mp->m_quotainfo;
	struct xfs_inode	*uip, *gip;
	boolean_t		tempuqip, tempgqip;
Linus Torvalds's avatar
Linus Torvalds committed
421
422
423
424
425
426

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

	out->qs_version = FS_QSTAT_VERSION;
427
	if (!xfs_sb_version_hasquota(&mp->m_sb)) {
Linus Torvalds's avatar
Linus Torvalds committed
428
429
430
431
432
433
434
435
436
437
438
		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;

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

Christoph Hellwig's avatar
Christoph Hellwig committed
476
477
478
#define XFS_DQ_MASK \
	(FS_DQ_LIMIT_MASK | FS_DQ_TIMER_MASK | FS_DQ_WARNS_MASK)

Linus Torvalds's avatar
Linus Torvalds committed
479
480
481
/*
 * Adjust quota limits, and start/stop timers accordingly.
 */
482
int
Linus Torvalds's avatar
Linus Torvalds committed
483
484
485
486
487
488
xfs_qm_scall_setqlim(
	xfs_mount_t		*mp,
	xfs_dqid_t		id,
	uint			type,
	fs_disk_quota_t		*newlim)
{
489
	struct xfs_quotainfo	*q = mp->m_quotainfo;
Linus Torvalds's avatar
Linus Torvalds committed
490
491
492
493
494
495
	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
496
497
498
499
	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
500
501
502
503
504
505
506
507
508
509
510
511
512
513

	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).
	 */
514
	mutex_lock(&q->qi_quotaofflock);
Linus Torvalds's avatar
Linus Torvalds committed
515
516
517
518
519
520
521
522

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

	hard = (newlim->d_fieldmask & FS_DQ_IHARD) ?
		(xfs_qcnt_t) newlim->d_ino_hardlimit :
566
			be64_to_cpu(ddq->d_ino_hardlimit);
Linus Torvalds's avatar
Linus Torvalds committed
567
568
	soft = (newlim->d_fieldmask & FS_DQ_ISOFT) ?
		(xfs_qcnt_t) newlim->d_ino_softlimit :
569
			be64_to_cpu(ddq->d_ino_softlimit);
Linus Torvalds's avatar
Linus Torvalds committed
570
	if (hard == 0 || hard >= soft) {
571
572
		ddq->d_ino_hardlimit = cpu_to_be64(hard);
		ddq->d_ino_softlimit = cpu_to_be64(soft);
Linus Torvalds's avatar
Linus Torvalds committed
573
		if (id == 0) {
574
575
			q->qi_ihardlimit = hard;
			q->qi_isoftlimit = soft;
Linus Torvalds's avatar
Linus Torvalds committed
576
577
		}
	} else {
578
		xfs_debug(mp, "ihard %Ld < isoft %Ld\n", hard, soft);
Linus Torvalds's avatar
Linus Torvalds committed
579
580
	}

581
582
583
584
	/*
	 * Update warnings counter(s) if requested
	 */
	if (newlim->d_fieldmask & FS_DQ_BWARNS)
585
		ddq->d_bwarns = cpu_to_be16(newlim->d_bwarns);
586
	if (newlim->d_fieldmask & FS_DQ_IWARNS)
587
		ddq->d_iwarns = cpu_to_be16(newlim->d_iwarns);
588
	if (newlim->d_fieldmask & FS_DQ_RTBWARNS)
589
		ddq->d_rtbwarns = cpu_to_be16(newlim->d_rtbwarns);
590

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

630
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
631
632
	xfs_qm_dqrele(dqp);

633
634
 out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
635
	return error;
Linus Torvalds's avatar
Linus Torvalds committed
636
637
}

638
int
Linus Torvalds's avatar
Linus Torvalds committed
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
xfs_qm_scall_getquota(
	xfs_mount_t	*mp,
	xfs_dqid_t	id,
	uint		type,
	fs_disk_quota_t *out)
{
	xfs_dquot_t	*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.
	 */
	if ((error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp))) {
		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)) {
		xfs_qm_dqput(dqp);
		return XFS_ERROR(ENOENT);
	}
	/*
	 * Convert the disk dquot to the exportable format
	 */
	xfs_qm_export_dquot(mp, &dqp->q_core, out);
	xfs_qm_dqput(dqp);
	return (error ? XFS_ERROR(EFAULT) : 0);
}


STATIC int
xfs_qm_log_quotaoff_end(
	xfs_mount_t		*mp,
	xfs_qoff_logitem_t	*startqoff,
	uint			flags)
{
680
	xfs_trans_t		*tp;
Linus Torvalds's avatar
Linus Torvalds committed
681
	int			error;
682
	xfs_qoff_logitem_t	*qoffi;
Linus Torvalds's avatar
Linus Torvalds committed
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701

	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);
702
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
	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
731
	spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
732
733
	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
734
	spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
735
736
737
738
739
740
741
742
743

	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);
744
	error = xfs_trans_commit(tp, 0);
Linus Torvalds's avatar
Linus Torvalds committed
745
746
747
748
749
750
751
752

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
753
		spin_lock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
754
		mp->m_sb.sb_qflags = oldsbqflag;
Eric Sandeen's avatar
Eric Sandeen committed
755
		spin_unlock(&mp->m_sb_lock);
Linus Torvalds's avatar
Linus Torvalds committed
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
	}
	*qoffstartp = qoffi;
	return (error);
}


/*
 * Translate an internal style on-disk-dquot to the exportable format.
 * The main differences are that the counters/limits are all in Basic
 * Blocks (BBs) instead of the internal FSBs, and all on-disk data has
 * to be converted to the native endianness.
 */
STATIC void
xfs_qm_export_dquot(
	xfs_mount_t		*mp,
	xfs_disk_dquot_t	*src,
	struct fs_disk_quota	*dst)
{
	memset(dst, 0, sizeof(*dst));
	dst->d_version = FS_DQUOT_VERSION;  /* different from src->d_version */
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
	dst->d_flags = xfs_qm_export_qtype_flags(src->d_flags);
	dst->d_id = be32_to_cpu(src->d_id);
	dst->d_blk_hardlimit =
		XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_blk_hardlimit));
	dst->d_blk_softlimit =
		XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_blk_softlimit));
	dst->d_ino_hardlimit = be64_to_cpu(src->d_ino_hardlimit);
	dst->d_ino_softlimit = be64_to_cpu(src->d_ino_softlimit);
	dst->d_bcount = XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_bcount));
	dst->d_icount = be64_to_cpu(src->d_icount);
	dst->d_btimer = be32_to_cpu(src->d_btimer);
	dst->d_itimer = be32_to_cpu(src->d_itimer);
	dst->d_iwarns = be16_to_cpu(src->d_iwarns);
	dst->d_bwarns = be16_to_cpu(src->d_bwarns);
	dst->d_rtb_hardlimit =
		XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_rtb_hardlimit));
	dst->d_rtb_softlimit =
		XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_rtb_softlimit));
	dst->d_rtbcount = XFS_FSB_TO_BB(mp, be64_to_cpu(src->d_rtbcount));
	dst->d_rtbtimer = be32_to_cpu(src->d_rtbtimer);
	dst->d_rtbwarns = be16_to_cpu(src->d_rtbwarns);
Linus Torvalds's avatar
Linus Torvalds committed
797
798
799

	/*
	 * Internally, we don't reset all the timers when quota enforcement
800
	 * gets turned off. No need to confuse the user level code,
Linus Torvalds's avatar
Linus Torvalds committed
801
802
	 * so return zeroes in that case.
	 */
803
804
805
	if ((!XFS_IS_UQUOTA_ENFORCED(mp) && src->d_flags == XFS_DQ_USER) ||
	    (!XFS_IS_OQUOTA_ENFORCED(mp) &&
			(src->d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) {
Linus Torvalds's avatar
Linus Torvalds committed
806
807
808
809
810
811
		dst->d_btimer = 0;
		dst->d_itimer = 0;
		dst->d_rtbtimer = 0;
	}

#ifdef DEBUG
812
	if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) ||
813
	     (XFS_IS_OQUOTA_ENFORCED(mp) &&
814
			(dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) &&
815
	    dst->d_id != 0) {
816
		if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) &&
Linus Torvalds's avatar
Linus Torvalds committed
817
818
819
		    (dst->d_blk_softlimit > 0)) {
			ASSERT(dst->d_btimer != 0);
		}
820
		if (((int) dst->d_icount > (int) dst->d_ino_softlimit) &&
Linus Torvalds's avatar
Linus Torvalds committed
821
822
823
824
825
826
827
828
829
830
831
832
		    (dst->d_ino_softlimit > 0)) {
			ASSERT(dst->d_itimer != 0);
		}
	}
#endif
}

STATIC uint
xfs_qm_export_qtype_flags(
	uint flags)
{
	/*
833
	 * Can't be more than one, or none.
Linus Torvalds's avatar
Linus Torvalds committed
834
	 */
835
836
837
838
839
840
841
	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
842
843

	return (flags & XFS_DQ_USER) ?
844
845
		FS_USER_QUOTA : (flags & XFS_DQ_PROJ) ?
			FS_PROJ_QUOTA : FS_GROUP_QUOTA;
Linus Torvalds's avatar
Linus Torvalds committed
846
847
848
849
850
851
852
853
854
855
}

STATIC uint
xfs_qm_export_flags(
	uint flags)
{
	uint uflags;

	uflags = 0;
	if (flags & XFS_UQUOTA_ACCT)
856
		uflags |= FS_QUOTA_UDQ_ACCT;
857
	if (flags & XFS_PQUOTA_ACCT)
858
		uflags |= FS_QUOTA_PDQ_ACCT;
Linus Torvalds's avatar
Linus Torvalds committed
859
	if (flags & XFS_GQUOTA_ACCT)
860
		uflags |= FS_QUOTA_GDQ_ACCT;
Linus Torvalds's avatar
Linus Torvalds committed
861
	if (flags & XFS_UQUOTA_ENFD)
862
		uflags |= FS_QUOTA_UDQ_ENFD;
863
864
	if (flags & (XFS_OQUOTA_ENFD)) {
		uflags |= (flags & XFS_GQUOTA_ACCT) ?
865
			FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD;
866
	}
Linus Torvalds's avatar
Linus Torvalds committed
867
868
869
870
	return (uflags);
}


871
872
873
874
875
STATIC int
xfs_dqrele_inode(
	struct xfs_inode	*ip,
	struct xfs_perag	*pag,
	int			flags)
Linus Torvalds's avatar
Linus Torvalds committed
876
{
877
	/* skip quota inodes */
878
879
	if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
	    ip == ip->i_mount->m_quotainfo->qi_gquotaip) {
880
881
882
883
		ASSERT(ip->i_udquot == NULL);
		ASSERT(ip->i_gdquot == NULL);
		return 0;
	}
884

885
886
887
888
889
890
891
892
893
	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
894
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
895
	return 0;
896
897
}

898

899
900
/*
 * Go thru all the inodes in the file system, releasing their dquots.
901
 *
902
 * Note that the mount structure gets modified to indicate that quotas are off
903
 * AFTER this, in the case of quotaoff.
904
905
906
907
908
909
910
 */
void
xfs_qm_dqrele_all_inodes(
	struct xfs_mount *mp,
	uint		 flags)
{
	ASSERT(mp->m_quotainfo);
911
	xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags);
Linus Torvalds's avatar
Linus Torvalds committed
912
}