xfs_qm_syscalls.c 24.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2 3
 * Copyright (c) 2000-2005 Silicon Graphics, Inc.
 * All Rights Reserved.
L
Linus Torvalds 已提交
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
L
Linus Torvalds 已提交
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.
L
Linus Torvalds 已提交
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
L
Linus Torvalds 已提交
17
 */
18 19 20

#include <linux/capability.h>

L
Linus Torvalds 已提交
21 22
#include "xfs.h"
#include "xfs_fs.h"
23
#include "xfs_bit.h"
L
Linus Torvalds 已提交
24
#include "xfs_log.h"
25
#include "xfs_inum.h"
L
Linus Torvalds 已提交
26 27
#include "xfs_trans.h"
#include "xfs_sb.h"
28
#include "xfs_ag.h"
L
Linus Torvalds 已提交
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"
L
Linus Torvalds 已提交
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"
C
Christoph Hellwig 已提交
43
#include "xfs_trace.h"
L
Linus Torvalds 已提交
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
L
Linus Torvalds 已提交
60 61
xfs_qm_scall_quotaoff(
	xfs_mount_t		*mp,
62
	uint			flags)
L
Linus Torvalds 已提交
63
{
64
	struct xfs_quotainfo	*q = mp->m_quotainfo;
L
Linus Torvalds 已提交
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
	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.
	 */
88 89
	ASSERT(q);
	mutex_lock(&q->qi_quotaofflock);
L
Linus Torvalds 已提交
90 91 92 93 94 95 96

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

E
Eric Sandeen 已提交
97
		spin_lock(&mp->m_sb_lock);
L
Linus Torvalds 已提交
98
		mp->m_sb.sb_qflags = mp->m_qflags;
E
Eric Sandeen 已提交
99
		spin_unlock(&mp->m_sb_lock);
100
		mutex_unlock(&q->qi_quotaofflock);
L
Linus Torvalds 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120

		/* 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;
121
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
L
Linus Torvalds 已提交
122
		inactivate_flags |= XFS_GQUOTA_ACTIVE;
123 124 125 126
	} else if (flags & XFS_PQUOTA_ACCT) {
		dqtype |= XFS_QMOPT_PQUOTA;
		flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD);
		inactivate_flags |= XFS_PQUOTA_ACTIVE;
L
Linus Torvalds 已提交
127 128 129 130 131 132
	}

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

	/*
	 * Write the LI_QUOTAOFF log record, and do SB changes atomically,
138 139
	 * and synchronously. If we fail to write, we should abort the
	 * operation as it cannot be recovered safely if we crash.
L
Linus Torvalds 已提交
140
	 */
141 142
	error = xfs_qm_log_quotaoff(mp, &qoffstart, flags);
	if (error)
143
		goto out_unlock;
L
Linus Torvalds 已提交
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 172

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

	/*
	 * If quotas is completely disabled, close shop.
	 */
209 210
	if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) ||
	    ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) {
211
		mutex_unlock(&q->qi_quotaofflock);
L
Linus Torvalds 已提交
212 213 214 215 216
		xfs_qm_destroy_quotainfo(mp);
		return (0);
	}

	/*
217
	 * Release our quotainode references if we don't need them anymore.
L
Linus Torvalds 已提交
218
	 */
219 220 221
	if ((dqtype & XFS_QMOPT_UQUOTA) && q->qi_uquotaip) {
		IRELE(q->qi_uquotaip);
		q->qi_uquotaip = NULL;
L
Linus Torvalds 已提交
222
	}
223 224 225
	if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && q->qi_gquotaip) {
		IRELE(q->qi_gquotaip);
		q->qi_gquotaip = NULL;
L
Linus Torvalds 已提交
226 227
	}

228 229 230
out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
	return error;
L
Linus Torvalds 已提交
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 256 257 258 259 260 261
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);
262
	xfs_trans_ijoin(tp, ip, 0);
263

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

274 275
	ASSERT(ip->i_d.di_nextents == 0);

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

286
int
L
Linus Torvalds 已提交
287 288 289 290
xfs_qm_scall_trunc_qfiles(
	xfs_mount_t	*mp,
	uint		flags)
{
291
	int		error = 0, error2 = 0;
L
Linus Torvalds 已提交
292

293
	if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) {
294 295
		xfs_debug(mp, "%s: flags=%x m_qflags=%x\n",
			__func__, flags, mp->m_qflags);
L
Linus Torvalds 已提交
296 297 298
		return XFS_ERROR(EINVAL);
	}

299 300 301 302
	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);
L
Linus Torvalds 已提交
303

304
	return error ? error : error2;
L
Linus Torvalds 已提交
305 306 307 308 309 310 311
}

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

	/*
	 * 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)) ||
386 387 388 389
	     ((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)) ||
L
Linus Torvalds 已提交
390 391 392 393 394 395 396 397 398
	    (flags & XFS_ALL_QUOTA_ENFD) == 0)
		return (0);

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

	/*
	 * Switch on quota enforcement in core.
	 */
399
	mutex_lock(&mp->m_quotainfo->qi_quotaofflock);
L
Linus Torvalds 已提交
400
	mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD);
401
	mutex_unlock(&mp->m_quotainfo->qi_quotaofflock);
L
Linus Torvalds 已提交
402 403 404 405 406 407 408 409

	return (0);
}


/*
 * Return quota status information, such as uquota-off, enforcements, etc.
 */
410
int
L
Linus Torvalds 已提交
411
xfs_qm_scall_getqstat(
412 413
	struct xfs_mount	*mp,
	struct fs_quota_stat	*out)
L
Linus Torvalds 已提交
414
{
415 416 417
	struct xfs_quotainfo	*q = mp->m_quotainfo;
	struct xfs_inode	*uip, *gip;
	boolean_t		tempuqip, tempgqip;
L
Linus Torvalds 已提交
418 419 420 421 422 423

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

	out->qs_version = FS_QSTAT_VERSION;
424
	if (!xfs_sb_version_hasquota(&mp->m_sb)) {
L
Linus Torvalds 已提交
425 426 427 428 429 430 431 432 433 434 435
		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;

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

C
Christoph Hellwig 已提交
473 474 475
#define XFS_DQ_MASK \
	(FS_DQ_LIMIT_MASK | FS_DQ_TIMER_MASK | FS_DQ_WARNS_MASK)

L
Linus Torvalds 已提交
476 477 478
/*
 * Adjust quota limits, and start/stop timers accordingly.
 */
479
int
L
Linus Torvalds 已提交
480 481 482 483 484 485
xfs_qm_scall_setqlim(
	xfs_mount_t		*mp,
	xfs_dqid_t		id,
	uint			type,
	fs_disk_quota_t		*newlim)
{
486
	struct xfs_quotainfo	*q = mp->m_quotainfo;
L
Linus Torvalds 已提交
487 488 489 490 491 492
	xfs_disk_dquot_t	*ddq;
	xfs_dquot_t		*dqp;
	xfs_trans_t		*tp;
	int			error;
	xfs_qcnt_t		hard, soft;

C
Christoph Hellwig 已提交
493 494 495 496
	if (newlim->d_fieldmask & ~XFS_DQ_MASK)
		return EINVAL;
	if ((newlim->d_fieldmask & XFS_DQ_MASK) == 0)
		return 0;
L
Linus Torvalds 已提交
497 498 499 500 501 502 503 504 505 506 507 508 509 510

	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).
	 */
511
	mutex_lock(&q->qi_quotaofflock);
L
Linus Torvalds 已提交
512 513 514 515 516 517 518 519

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

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

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

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

627
	error = xfs_trans_commit(tp, 0);
L
Linus Torvalds 已提交
628 629
	xfs_qm_dqrele(dqp);

630 631
 out_unlock:
	mutex_unlock(&q->qi_quotaofflock);
632
	return error;
L
Linus Torvalds 已提交
633 634 635 636 637 638 639 640
}

STATIC int
xfs_qm_log_quotaoff_end(
	xfs_mount_t		*mp,
	xfs_qoff_logitem_t	*startqoff,
	uint			flags)
{
641
	xfs_trans_t		*tp;
L
Linus Torvalds 已提交
642
	int			error;
643
	xfs_qoff_logitem_t	*qoffi;
L
Linus Torvalds 已提交
644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662

	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);
663
	error = xfs_trans_commit(tp, 0);
L
Linus Torvalds 已提交
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
	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);

E
Eric Sandeen 已提交
692
	spin_lock(&mp->m_sb_lock);
L
Linus Torvalds 已提交
693 694
	oldsbqflag = mp->m_sb.sb_qflags;
	mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL;
E
Eric Sandeen 已提交
695
	spin_unlock(&mp->m_sb_lock);
L
Linus Torvalds 已提交
696 697 698 699 700 701 702 703 704

	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);
705
	error = xfs_trans_commit(tp, 0);
L
Linus Torvalds 已提交
706 707 708 709 710 711 712 713

error0:
	if (error) {
		xfs_trans_cancel(tp, 0);
		/*
		 * No one else is modifying sb_qflags, so this is OK.
		 * We still hold the quotaofflock.
		 */
E
Eric Sandeen 已提交
714
		spin_lock(&mp->m_sb_lock);
L
Linus Torvalds 已提交
715
		mp->m_sb.sb_qflags = oldsbqflag;
E
Eric Sandeen 已提交
716
		spin_unlock(&mp->m_sb_lock);
L
Linus Torvalds 已提交
717 718 719 720 721 722
	}
	*qoffstartp = qoffi;
	return (error);
}


723 724 725 726 727
int
xfs_qm_scall_getquota(
	struct xfs_mount	*mp,
	xfs_dqid_t		id,
	uint			type,
L
Linus Torvalds 已提交
728 729
	struct fs_disk_quota	*dst)
{
730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750
	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;
	}

L
Linus Torvalds 已提交
751
	memset(dst, 0, sizeof(*dst));
752 753 754
	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);
755
	dst->d_blk_hardlimit =
756
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit));
757
	dst->d_blk_softlimit =
758 759 760
		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);
761 762
	dst->d_bcount = XFS_FSB_TO_BB(mp, dqp->q_res_bcount);
	dst->d_icount = dqp->q_res_icount;
763 764 765 766
	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);
767
	dst->d_rtb_hardlimit =
768
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit));
769
	dst->d_rtb_softlimit =
770
		XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit));
771
	dst->d_rtbcount = XFS_FSB_TO_BB(mp, dqp->q_res_rtbcount);
772 773
	dst->d_rtbtimer = be32_to_cpu(dqp->q_core.d_rtbtimer);
	dst->d_rtbwarns = be16_to_cpu(dqp->q_core.d_rtbwarns);
L
Linus Torvalds 已提交
774 775 776

	/*
	 * Internally, we don't reset all the timers when quota enforcement
777
	 * gets turned off. No need to confuse the user level code,
L
Linus Torvalds 已提交
778 779
	 * so return zeroes in that case.
	 */
780
	if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) ||
781
	    (!XFS_IS_OQUOTA_ENFORCED(mp) &&
782
			(dqp->q_core.d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) {
L
Linus Torvalds 已提交
783 784 785 786 787 788
		dst->d_btimer = 0;
		dst->d_itimer = 0;
		dst->d_rtbtimer = 0;
	}

#ifdef DEBUG
789
	if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) ||
790
	     (XFS_IS_OQUOTA_ENFORCED(mp) &&
791
			(dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) &&
792
	    dst->d_id != 0) {
793
		if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) &&
L
Linus Torvalds 已提交
794 795 796
		    (dst->d_blk_softlimit > 0)) {
			ASSERT(dst->d_btimer != 0);
		}
797
		if (((int) dst->d_icount > (int) dst->d_ino_softlimit) &&
L
Linus Torvalds 已提交
798 799 800 801 802
		    (dst->d_ino_softlimit > 0)) {
			ASSERT(dst->d_itimer != 0);
		}
	}
#endif
803 804 805
out_put:
	xfs_qm_dqput(dqp);
	return error;
L
Linus Torvalds 已提交
806 807 808 809 810 811 812
}

STATIC uint
xfs_qm_export_qtype_flags(
	uint flags)
{
	/*
813
	 * Can't be more than one, or none.
L
Linus Torvalds 已提交
814
	 */
815 816 817 818 819 820 821
	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);
L
Linus Torvalds 已提交
822 823

	return (flags & XFS_DQ_USER) ?
824 825
		FS_USER_QUOTA : (flags & XFS_DQ_PROJ) ?
			FS_PROJ_QUOTA : FS_GROUP_QUOTA;
L
Linus Torvalds 已提交
826 827 828 829 830 831 832 833 834 835
}

STATIC uint
xfs_qm_export_flags(
	uint flags)
{
	uint uflags;

	uflags = 0;
	if (flags & XFS_UQUOTA_ACCT)
836
		uflags |= FS_QUOTA_UDQ_ACCT;
837
	if (flags & XFS_PQUOTA_ACCT)
838
		uflags |= FS_QUOTA_PDQ_ACCT;
L
Linus Torvalds 已提交
839
	if (flags & XFS_GQUOTA_ACCT)
840
		uflags |= FS_QUOTA_GDQ_ACCT;
L
Linus Torvalds 已提交
841
	if (flags & XFS_UQUOTA_ENFD)
842
		uflags |= FS_QUOTA_UDQ_ENFD;
843 844
	if (flags & (XFS_OQUOTA_ENFD)) {
		uflags |= (flags & XFS_GQUOTA_ACCT) ?
845
			FS_QUOTA_GDQ_ENFD : FS_QUOTA_PDQ_ENFD;
846
	}
L
Linus Torvalds 已提交
847 848 849 850
	return (uflags);
}


851 852 853 854 855
STATIC int
xfs_dqrele_inode(
	struct xfs_inode	*ip,
	struct xfs_perag	*pag,
	int			flags)
L
Linus Torvalds 已提交
856
{
857
	/* skip quota inodes */
858 859
	if (ip == ip->i_mount->m_quotainfo->qi_uquotaip ||
	    ip == ip->i_mount->m_quotainfo->qi_gquotaip) {
860 861 862 863
		ASSERT(ip->i_udquot == NULL);
		ASSERT(ip->i_gdquot == NULL);
		return 0;
	}
864

865 866 867 868 869 870 871 872 873
	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;
	}
C
Christoph Hellwig 已提交
874
	xfs_iunlock(ip, XFS_ILOCK_EXCL);
875
	return 0;
876 877
}

878

879 880
/*
 * Go thru all the inodes in the file system, releasing their dquots.
881
 *
882
 * Note that the mount structure gets modified to indicate that quotas are off
883
 * AFTER this, in the case of quotaoff.
884 885 886 887 888 889 890
 */
void
xfs_qm_dqrele_all_inodes(
	struct xfs_mount *mp,
	uint		 flags)
{
	ASSERT(mp->m_quotainfo);
891
	xfs_inode_ag_iterator(mp, xfs_dqrele_inode, flags);
L
Linus Torvalds 已提交
892
}