quota.c 12.4 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * Quota code necessary even when VFS quota support is not compiled
 * into the kernel.  The interesting stuff is over in dquot.c, here
 * we have symbols for initial quotactl(2) handling, the sysctl(2)
 * variables, etc - things needed even when quota support disabled.
 */

#include <linux/fs.h>
#include <linux/namei.h>
#include <linux/slab.h>
#include <asm/current.h>
#include <asm/uaccess.h>
13
#include <linux/compat.h>
L
Linus Torvalds 已提交
14 15 16 17
#include <linux/kernel.h>
#include <linux/security.h>
#include <linux/syscalls.h>
#include <linux/buffer_head.h>
18
#include <linux/capability.h>
A
Adrian Bunk 已提交
19
#include <linux/quotaops.h>
20
#include <linux/types.h>
L
Linus Torvalds 已提交
21 22

/* Check validity of generic quotactl commands */
J
Jan Kara 已提交
23 24
static int generic_quotactl_valid(struct super_block *sb, int type, int cmd,
				  qid_t id)
L
Linus Torvalds 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
{
	if (type >= MAXQUOTAS)
		return -EINVAL;
	if (!sb && cmd != Q_SYNC)
		return -ENODEV;
	/* Is operation supported? */
	if (sb && !sb->s_qcop)
		return -ENOSYS;

	switch (cmd) {
		case Q_GETFMT:
			break;
		case Q_QUOTAON:
			if (!sb->s_qcop->quota_on)
				return -ENOSYS;
			break;
		case Q_QUOTAOFF:
			if (!sb->s_qcop->quota_off)
				return -ENOSYS;
			break;
		case Q_SETINFO:
			if (!sb->s_qcop->set_info)
				return -ENOSYS;
			break;
		case Q_GETINFO:
			if (!sb->s_qcop->get_info)
				return -ENOSYS;
			break;
		case Q_SETQUOTA:
			if (!sb->s_qcop->set_dqblk)
				return -ENOSYS;
			break;
		case Q_GETQUOTA:
			if (!sb->s_qcop->get_dqblk)
				return -ENOSYS;
			break;
		case Q_SYNC:
			if (sb && !sb->s_qcop->quota_sync)
				return -ENOSYS;
			break;
		default:
			return -EINVAL;
	}

	/* Is quota turned on for commands which need it? */
	switch (cmd) {
		case Q_GETFMT:
		case Q_GETINFO:
		case Q_SETINFO:
		case Q_SETQUOTA:
		case Q_GETQUOTA:
J
Jan Kara 已提交
76 77
			/* This is just an informative test so we are satisfied
			 * without the lock */
78
			if (!sb_has_quota_active(sb, type))
L
Linus Torvalds 已提交
79 80 81 82 83
				return -ESRCH;
	}

	/* Check privileges */
	if (cmd == Q_GETQUOTA) {
84
		if (((type == USRQUOTA && current_euid() != id) ||
L
Linus Torvalds 已提交
85 86 87 88 89 90 91 92 93 94 95 96
		     (type == GRPQUOTA && !in_egroup_p(id))) &&
		    !capable(CAP_SYS_ADMIN))
			return -EPERM;
	}
	else if (cmd != Q_GETFMT && cmd != Q_SYNC && cmd != Q_GETINFO)
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

	return 0;
}

/* Check validity of XFS Quota Manager commands */
J
Jan Kara 已提交
97 98
static int xqm_quotactl_valid(struct super_block *sb, int type, int cmd,
			      qid_t id)
L
Linus Torvalds 已提交
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
{
	if (type >= XQM_MAXQUOTAS)
		return -EINVAL;
	if (!sb)
		return -ENODEV;
	if (!sb->s_qcop)
		return -ENOSYS;

	switch (cmd) {
		case Q_XQUOTAON:
		case Q_XQUOTAOFF:
		case Q_XQUOTARM:
			if (!sb->s_qcop->set_xstate)
				return -ENOSYS;
			break;
		case Q_XGETQSTAT:
			if (!sb->s_qcop->get_xstate)
				return -ENOSYS;
			break;
		case Q_XSETQLIM:
			if (!sb->s_qcop->set_xquota)
				return -ENOSYS;
			break;
		case Q_XGETQUOTA:
			if (!sb->s_qcop->get_xquota)
				return -ENOSYS;
			break;
126 127 128 129
		case Q_XQUOTASYNC:
			if (!sb->s_qcop->quota_sync)
				return -ENOSYS;
			break;
L
Linus Torvalds 已提交
130 131 132 133 134 135
		default:
			return -EINVAL;
	}

	/* Check privileges */
	if (cmd == Q_XGETQUOTA) {
136
		if (((type == XQM_USRQUOTA && current_euid() != id) ||
L
Linus Torvalds 已提交
137 138 139
		     (type == XQM_GRPQUOTA && !in_egroup_p(id))) &&
		     !capable(CAP_SYS_ADMIN))
			return -EPERM;
140
	} else if (cmd != Q_XGETQSTAT && cmd != Q_XQUOTASYNC) {
L
Linus Torvalds 已提交
141 142 143 144 145 146 147
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;
	}

	return 0;
}

J
Jan Kara 已提交
148 149
static int check_quotactl_valid(struct super_block *sb, int type, int cmd,
				qid_t id)
L
Linus Torvalds 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
{
	int error;

	if (XQM_COMMAND(cmd))
		error = xqm_quotactl_valid(sb, type, cmd, id);
	else
		error = generic_quotactl_valid(sb, type, cmd, id);
	if (!error)
		error = security_quotactl(cmd, type, id, sb);
	return error;
}

static void quota_sync_sb(struct super_block *sb, int type)
{
	int cnt;

	sb->s_qcop->quota_sync(sb, type);
167 168 169

	if (sb_dqopt(sb)->flags & DQUOT_QUOTA_SYS_FILE)
		return;
L
Linus Torvalds 已提交
170 171 172 173 174 175 176
	/* This is not very clever (and fast) but currently I don't know about
	 * any other simple way of getting quota data to disk and we must get
	 * them there for userspace to be visible... */
	if (sb->s_op->sync_fs)
		sb->s_op->sync_fs(sb, 1);
	sync_blockdev(sb->s_bdev);

177 178 179 180
	/*
	 * Now when everything is written we can discard the pagecache so
	 * that userspace sees the changes.
	 */
I
Ingo Molnar 已提交
181
	mutex_lock(&sb_dqopt(sb)->dqonoff_mutex);
L
Linus Torvalds 已提交
182 183 184
	for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
		if (type != -1 && cnt != type)
			continue;
185
		if (!sb_has_quota_active(sb, cnt))
L
Linus Torvalds 已提交
186
			continue;
J
Jan Kara 已提交
187 188
		mutex_lock_nested(&sb_dqopt(sb)->files[cnt]->i_mutex,
				  I_MUTEX_QUOTA);
189 190
		truncate_inode_pages(&sb_dqopt(sb)->files[cnt]->i_data, 0);
		mutex_unlock(&sb_dqopt(sb)->files[cnt]->i_mutex);
L
Linus Torvalds 已提交
191
	}
I
Ingo Molnar 已提交
192
	mutex_unlock(&sb_dqopt(sb)->dqonoff_mutex);
L
Linus Torvalds 已提交
193 194 195 196
}

void sync_dquots(struct super_block *sb, int type)
{
J
Jan Kara 已提交
197
	int cnt;
198

L
Linus Torvalds 已提交
199 200 201
	if (sb) {
		if (sb->s_qcop->quota_sync)
			quota_sync_sb(sb, type);
202
		return;
L
Linus Torvalds 已提交
203
	}
204 205 206 207

	spin_lock(&sb_lock);
restart:
	list_for_each_entry(sb, &super_blocks, s_list) {
J
Jan Kara 已提交
208 209
		/* This test just improves performance so it needn't be
		 * reliable... */
J
Jan Kara 已提交
210 211 212
		for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
			if (type != -1 && type != cnt)
				continue;
213
			if (!sb_has_quota_active(sb, cnt))
J
Jan Kara 已提交
214 215
				continue;
			if (!info_dirty(&sb_dqopt(sb)->info[cnt]) &&
J
Jan Kara 已提交
216
			   list_empty(&sb_dqopt(sb)->info[cnt].dqi_dirty_list))
J
Jan Kara 已提交
217 218 219 220
				continue;
			break;
		}
		if (cnt == MAXQUOTAS)
221 222 223 224 225 226 227 228 229 230
			continue;
		sb->s_count++;
		spin_unlock(&sb_lock);
		down_read(&sb->s_umount);
		if (sb->s_root && sb->s_qcop->quota_sync)
			quota_sync_sb(sb, type);
		up_read(&sb->s_umount);
		spin_lock(&sb_lock);
		if (__put_super_and_need_restart(sb))
			goto restart;
L
Linus Torvalds 已提交
231
	}
232
	spin_unlock(&sb_lock);
L
Linus Torvalds 已提交
233 234 235
}

/* Copy parameters and call proper function */
J
Jan Kara 已提交
236 237
static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
		       void __user *addr)
L
Linus Torvalds 已提交
238 239 240 241 242 243 244
{
	int ret;

	switch (cmd) {
		case Q_QUOTAON: {
			char *pathname;

J
Jan Kara 已提交
245 246
			pathname = getname(addr);
			if (IS_ERR(pathname))
L
Linus Torvalds 已提交
247
				return PTR_ERR(pathname);
248
			ret = sb->s_qcop->quota_on(sb, type, id, pathname, 0);
L
Linus Torvalds 已提交
249 250 251 252
			putname(pathname);
			return ret;
		}
		case Q_QUOTAOFF:
253
			return sb->s_qcop->quota_off(sb, type, 0);
L
Linus Torvalds 已提交
254 255 256 257 258

		case Q_GETFMT: {
			__u32 fmt;

			down_read(&sb_dqopt(sb)->dqptr_sem);
259
			if (!sb_has_quota_active(sb, type)) {
L
Linus Torvalds 已提交
260 261 262 263 264 265 266 267 268 269 270 271
				up_read(&sb_dqopt(sb)->dqptr_sem);
				return -ESRCH;
			}
			fmt = sb_dqopt(sb)->info[type].dqi_format->qf_fmt_id;
			up_read(&sb_dqopt(sb)->dqptr_sem);
			if (copy_to_user(addr, &fmt, sizeof(fmt)))
				return -EFAULT;
			return 0;
		}
		case Q_GETINFO: {
			struct if_dqinfo info;

J
Jan Kara 已提交
272 273
			ret = sb->s_qcop->get_info(sb, type, &info);
			if (ret)
L
Linus Torvalds 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288
				return ret;
			if (copy_to_user(addr, &info, sizeof(info)))
				return -EFAULT;
			return 0;
		}
		case Q_SETINFO: {
			struct if_dqinfo info;

			if (copy_from_user(&info, addr, sizeof(info)))
				return -EFAULT;
			return sb->s_qcop->set_info(sb, type, &info);
		}
		case Q_GETQUOTA: {
			struct if_dqblk idq;

J
Jan Kara 已提交
289 290
			ret = sb->s_qcop->get_dqblk(sb, type, id, &idq);
			if (ret)
L
Linus Torvalds 已提交
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
				return ret;
			if (copy_to_user(addr, &idq, sizeof(idq)))
				return -EFAULT;
			return 0;
		}
		case Q_SETQUOTA: {
			struct if_dqblk idq;

			if (copy_from_user(&idq, addr, sizeof(idq)))
				return -EFAULT;
			return sb->s_qcop->set_dqblk(sb, type, id, &idq);
		}
		case Q_SYNC:
			sync_dquots(sb, type);
			return 0;

		case Q_XQUOTAON:
		case Q_XQUOTAOFF:
		case Q_XQUOTARM: {
			__u32 flags;

			if (copy_from_user(&flags, addr, sizeof(flags)))
				return -EFAULT;
			return sb->s_qcop->set_xstate(sb, flags, cmd);
		}
		case Q_XGETQSTAT: {
			struct fs_quota_stat fqs;
		
			if ((ret = sb->s_qcop->get_xstate(sb, &fqs)))
				return ret;
			if (copy_to_user(addr, &fqs, sizeof(fqs)))
				return -EFAULT;
			return 0;
		}
		case Q_XSETQLIM: {
			struct fs_disk_quota fdq;

			if (copy_from_user(&fdq, addr, sizeof(fdq)))
				return -EFAULT;
		       return sb->s_qcop->set_xquota(sb, type, id, &fdq);
		}
		case Q_XGETQUOTA: {
			struct fs_disk_quota fdq;

J
Jan Kara 已提交
335 336
			ret = sb->s_qcop->get_xquota(sb, type, id, &fdq);
			if (ret)
L
Linus Torvalds 已提交
337 338 339 340 341
				return ret;
			if (copy_to_user(addr, &fdq, sizeof(fdq)))
				return -EFAULT;
			return 0;
		}
342 343
		case Q_XQUOTASYNC:
			return sb->s_qcop->quota_sync(sb, type);
L
Linus Torvalds 已提交
344 345 346 347 348 349 350
		/* We never reach here unless validity check is broken */
		default:
			BUG();
	}
	return 0;
}

351 352 353 354
/*
 * look up a superblock on which quota ops will be performed
 * - use the name of a block device to find the superblock thereon
 */
J
Jan Kara 已提交
355
static struct super_block *quotactl_block(const char __user *special)
356 357 358 359 360 361 362
{
#ifdef CONFIG_BLOCK
	struct block_device *bdev;
	struct super_block *sb;
	char *tmp = getname(special);

	if (IS_ERR(tmp))
363
		return ERR_CAST(tmp);
364 365 366
	bdev = lookup_bdev(tmp);
	putname(tmp);
	if (IS_ERR(bdev))
367
		return ERR_CAST(bdev);
368 369 370 371 372 373 374 375 376 377 378
	sb = get_super(bdev);
	bdput(bdev);
	if (!sb)
		return ERR_PTR(-ENODEV);

	return sb;
#else
	return ERR_PTR(-ENODEV);
#endif
}

L
Linus Torvalds 已提交
379 380 381 382 383 384
/*
 * This is the system call interface. This communicates with
 * the user-level programs. Currently this only supports diskquota
 * calls. Maybe we need to add the process quotas etc. in the future,
 * but we probably should use rlimits for that.
 */
385 386
SYSCALL_DEFINE4(quotactl, unsigned int, cmd, const char __user *, special,
		qid_t, id, void __user *, addr)
L
Linus Torvalds 已提交
387 388 389 390 391 392 393 394 395
{
	uint cmds, type;
	struct super_block *sb = NULL;
	int ret;

	cmds = cmd >> SUBCMDSHIFT;
	type = cmd & SUBCMDMASK;

	if (cmds != Q_SYNC || special) {
396 397 398
		sb = quotactl_block(special);
		if (IS_ERR(sb))
			return PTR_ERR(sb);
L
Linus Torvalds 已提交
399 400 401 402 403 404 405 406 407 408
	}

	ret = check_quotactl_valid(sb, type, cmds, id);
	if (ret >= 0)
		ret = do_quotactl(sb, type, cmds, id, addr);
	if (sb)
		drop_super(sb);

	return ret;
}
409

410
#if defined(CONFIG_COMPAT_FOR_U64_ALIGNMENT)
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524
/*
 * This code works only for 32 bit quota tools over 64 bit OS (x86_64, ia64)
 * and is necessary due to alignment problems.
 */
struct compat_if_dqblk {
	compat_u64 dqb_bhardlimit;
	compat_u64 dqb_bsoftlimit;
	compat_u64 dqb_curspace;
	compat_u64 dqb_ihardlimit;
	compat_u64 dqb_isoftlimit;
	compat_u64 dqb_curinodes;
	compat_u64 dqb_btime;
	compat_u64 dqb_itime;
	compat_uint_t dqb_valid;
};

/* XFS structures */
struct compat_fs_qfilestat {
	compat_u64 dqb_bhardlimit;
	compat_u64 qfs_nblks;
	compat_uint_t qfs_nextents;
};

struct compat_fs_quota_stat {
	__s8		qs_version;
	__u16		qs_flags;
	__s8		qs_pad;
	struct compat_fs_qfilestat	qs_uquota;
	struct compat_fs_qfilestat	qs_gquota;
	compat_uint_t	qs_incoredqs;
	compat_int_t	qs_btimelimit;
	compat_int_t	qs_itimelimit;
	compat_int_t	qs_rtbtimelimit;
	__u16		qs_bwarnlimit;
	__u16		qs_iwarnlimit;
};

asmlinkage long sys32_quotactl(unsigned int cmd, const char __user *special,
						qid_t id, void __user *addr)
{
	unsigned int cmds;
	struct if_dqblk __user *dqblk;
	struct compat_if_dqblk __user *compat_dqblk;
	struct fs_quota_stat __user *fsqstat;
	struct compat_fs_quota_stat __user *compat_fsqstat;
	compat_uint_t data;
	u16 xdata;
	long ret;

	cmds = cmd >> SUBCMDSHIFT;

	switch (cmds) {
	case Q_GETQUOTA:
		dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
		compat_dqblk = addr;
		ret = sys_quotactl(cmd, special, id, dqblk);
		if (ret)
			break;
		if (copy_in_user(compat_dqblk, dqblk, sizeof(*compat_dqblk)) ||
			get_user(data, &dqblk->dqb_valid) ||
			put_user(data, &compat_dqblk->dqb_valid))
			ret = -EFAULT;
		break;
	case Q_SETQUOTA:
		dqblk = compat_alloc_user_space(sizeof(struct if_dqblk));
		compat_dqblk = addr;
		ret = -EFAULT;
		if (copy_in_user(dqblk, compat_dqblk, sizeof(*compat_dqblk)) ||
			get_user(data, &compat_dqblk->dqb_valid) ||
			put_user(data, &dqblk->dqb_valid))
			break;
		ret = sys_quotactl(cmd, special, id, dqblk);
		break;
	case Q_XGETQSTAT:
		fsqstat = compat_alloc_user_space(sizeof(struct fs_quota_stat));
		compat_fsqstat = addr;
		ret = sys_quotactl(cmd, special, id, fsqstat);
		if (ret)
			break;
		ret = -EFAULT;
		/* Copying qs_version, qs_flags, qs_pad */
		if (copy_in_user(compat_fsqstat, fsqstat,
			offsetof(struct compat_fs_quota_stat, qs_uquota)))
			break;
		/* Copying qs_uquota */
		if (copy_in_user(&compat_fsqstat->qs_uquota,
			&fsqstat->qs_uquota,
			sizeof(compat_fsqstat->qs_uquota)) ||
			get_user(data, &fsqstat->qs_uquota.qfs_nextents) ||
			put_user(data, &compat_fsqstat->qs_uquota.qfs_nextents))
			break;
		/* Copying qs_gquota */
		if (copy_in_user(&compat_fsqstat->qs_gquota,
			&fsqstat->qs_gquota,
			sizeof(compat_fsqstat->qs_gquota)) ||
			get_user(data, &fsqstat->qs_gquota.qfs_nextents) ||
			put_user(data, &compat_fsqstat->qs_gquota.qfs_nextents))
			break;
		/* Copying the rest */
		if (copy_in_user(&compat_fsqstat->qs_incoredqs,
			&fsqstat->qs_incoredqs,
			sizeof(struct compat_fs_quota_stat) -
			offsetof(struct compat_fs_quota_stat, qs_incoredqs)) ||
			get_user(xdata, &fsqstat->qs_iwarnlimit) ||
			put_user(xdata, &compat_fsqstat->qs_iwarnlimit))
			break;
		ret = 0;
		break;
	default:
		ret = sys_quotactl(cmd, special, id, addr);
	}
	return ret;
}
#endif