ioctl.c 16.8 KB
Newer Older
1
/*
2
 * linux/fs/ext4/ioctl.c
3 4 5 6 7 8 9 10
 *
 * Copyright (C) 1993, 1994, 1995
 * Remy Card (card@masi.ibp.fr)
 * Laboratoire MASI - Institut Blaise Pascal
 * Universite Pierre et Marie Curie (Paris VI)
 */

#include <linux/fs.h>
11
#include <linux/jbd2.h>
12 13 14
#include <linux/capability.h>
#include <linux/time.h>
#include <linux/compat.h>
15
#include <linux/mount.h>
16
#include <linux/file.h>
17
#include <asm/uaccess.h>
18 19
#include "ext4_jbd2.h"
#include "ext4.h"
20

21 22
#define MAX_32_NUM ((((unsigned long long) 1) << 32) - 1)

23 24 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 76 77 78
/**
 * Swap memory between @a and @b for @len bytes.
 *
 * @a:          pointer to first memory area
 * @b:          pointer to second memory area
 * @len:        number of bytes to swap
 *
 */
static void memswap(void *a, void *b, size_t len)
{
	unsigned char *ap, *bp;
	unsigned char tmp;

	ap = (unsigned char *)a;
	bp = (unsigned char *)b;
	while (len-- > 0) {
		tmp = *ap;
		*ap = *bp;
		*bp = tmp;
		ap++;
		bp++;
	}
}

/**
 * Swap i_data and associated attributes between @inode1 and @inode2.
 * This function is used for the primary swap between inode1 and inode2
 * and also to revert this primary swap in case of errors.
 *
 * Therefore you have to make sure, that calling this method twice
 * will revert all changes.
 *
 * @inode1:     pointer to first inode
 * @inode2:     pointer to second inode
 */
static void swap_inode_data(struct inode *inode1, struct inode *inode2)
{
	loff_t isize;
	struct ext4_inode_info *ei1;
	struct ext4_inode_info *ei2;

	ei1 = EXT4_I(inode1);
	ei2 = EXT4_I(inode2);

	memswap(&inode1->i_flags, &inode2->i_flags, sizeof(inode1->i_flags));
	memswap(&inode1->i_version, &inode2->i_version,
		  sizeof(inode1->i_version));
	memswap(&inode1->i_blocks, &inode2->i_blocks,
		  sizeof(inode1->i_blocks));
	memswap(&inode1->i_bytes, &inode2->i_bytes, sizeof(inode1->i_bytes));
	memswap(&inode1->i_atime, &inode2->i_atime, sizeof(inode1->i_atime));
	memswap(&inode1->i_mtime, &inode2->i_mtime, sizeof(inode1->i_mtime));

	memswap(ei1->i_data, ei2->i_data, sizeof(ei1->i_data));
	memswap(&ei1->i_flags, &ei2->i_flags, sizeof(ei1->i_flags));
	memswap(&ei1->i_disksize, &ei2->i_disksize, sizeof(ei1->i_disksize));
79 80 81 82
	ext4_es_remove_extent(inode1, 0, EXT_MAX_BLOCKS);
	ext4_es_remove_extent(inode2, 0, EXT_MAX_BLOCKS);
	ext4_es_lru_del(inode1);
	ext4_es_lru_del(inode2);
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 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 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215

	isize = i_size_read(inode1);
	i_size_write(inode1, i_size_read(inode2));
	i_size_write(inode2, isize);
}

/**
 * Swap the information from the given @inode and the inode
 * EXT4_BOOT_LOADER_INO. It will basically swap i_data and all other
 * important fields of the inodes.
 *
 * @sb:         the super block of the filesystem
 * @inode:      the inode to swap with EXT4_BOOT_LOADER_INO
 *
 */
static long swap_inode_boot_loader(struct super_block *sb,
				struct inode *inode)
{
	handle_t *handle;
	int err;
	struct inode *inode_bl;
	struct ext4_inode_info *ei;
	struct ext4_inode_info *ei_bl;
	struct ext4_sb_info *sbi;

	if (inode->i_nlink != 1 || !S_ISREG(inode->i_mode)) {
		err = -EINVAL;
		goto swap_boot_out;
	}

	if (!inode_owner_or_capable(inode) || !capable(CAP_SYS_ADMIN)) {
		err = -EPERM;
		goto swap_boot_out;
	}

	sbi = EXT4_SB(sb);
	ei = EXT4_I(inode);

	inode_bl = ext4_iget(sb, EXT4_BOOT_LOADER_INO);
	if (IS_ERR(inode_bl)) {
		err = PTR_ERR(inode_bl);
		goto swap_boot_out;
	}
	ei_bl = EXT4_I(inode_bl);

	filemap_flush(inode->i_mapping);
	filemap_flush(inode_bl->i_mapping);

	/* Protect orig inodes against a truncate and make sure,
	 * that only 1 swap_inode_boot_loader is running. */
	ext4_inode_double_lock(inode, inode_bl);

	truncate_inode_pages(&inode->i_data, 0);
	truncate_inode_pages(&inode_bl->i_data, 0);

	/* Wait for all existing dio workers */
	ext4_inode_block_unlocked_dio(inode);
	ext4_inode_block_unlocked_dio(inode_bl);
	inode_dio_wait(inode);
	inode_dio_wait(inode_bl);

	handle = ext4_journal_start(inode_bl, EXT4_HT_MOVE_EXTENTS, 2);
	if (IS_ERR(handle)) {
		err = -EINVAL;
		goto swap_boot_out;
	}

	/* Protect extent tree against block allocations via delalloc */
	ext4_double_down_write_data_sem(inode, inode_bl);

	if (inode_bl->i_nlink == 0) {
		/* this inode has never been used as a BOOT_LOADER */
		set_nlink(inode_bl, 1);
		i_uid_write(inode_bl, 0);
		i_gid_write(inode_bl, 0);
		inode_bl->i_flags = 0;
		ei_bl->i_flags = 0;
		inode_bl->i_version = 1;
		i_size_write(inode_bl, 0);
		inode_bl->i_mode = S_IFREG;
		if (EXT4_HAS_INCOMPAT_FEATURE(sb,
					      EXT4_FEATURE_INCOMPAT_EXTENTS)) {
			ext4_set_inode_flag(inode_bl, EXT4_INODE_EXTENTS);
			ext4_ext_tree_init(handle, inode_bl);
		} else
			memset(ei_bl->i_data, 0, sizeof(ei_bl->i_data));
	}

	swap_inode_data(inode, inode_bl);

	inode->i_ctime = inode_bl->i_ctime = ext4_current_time(inode);

	spin_lock(&sbi->s_next_gen_lock);
	inode->i_generation = sbi->s_next_generation++;
	inode_bl->i_generation = sbi->s_next_generation++;
	spin_unlock(&sbi->s_next_gen_lock);

	ext4_discard_preallocations(inode);

	err = ext4_mark_inode_dirty(handle, inode);
	if (err < 0) {
		ext4_warning(inode->i_sb,
			"couldn't mark inode #%lu dirty (err %d)",
			inode->i_ino, err);
		/* Revert all changes: */
		swap_inode_data(inode, inode_bl);
	} else {
		err = ext4_mark_inode_dirty(handle, inode_bl);
		if (err < 0) {
			ext4_warning(inode_bl->i_sb,
				"couldn't mark inode #%lu dirty (err %d)",
				inode_bl->i_ino, err);
			/* Revert all changes: */
			swap_inode_data(inode, inode_bl);
			ext4_mark_inode_dirty(handle, inode);
		}
	}

	ext4_journal_stop(handle);

	ext4_double_up_write_data_sem(inode, inode_bl);

	ext4_inode_resume_unlocked_dio(inode);
	ext4_inode_resume_unlocked_dio(inode_bl);

	ext4_inode_double_unlock(inode, inode_bl);

	iput(inode_bl);

swap_boot_out:
	return err;
}

A
Andi Kleen 已提交
216
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
217
{
A
Al Viro 已提交
218
	struct inode *inode = file_inode(filp);
219
	struct super_block *sb = inode->i_sb;
220
	struct ext4_inode_info *ei = EXT4_I(inode);
221 222
	unsigned int flags;

223
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
224 225

	switch (cmd) {
226
	case EXT4_IOC_GETFLAGS:
227
		ext4_get_inode_flags(ei);
228
		flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
229
		return put_user(flags, (int __user *) arg);
230
	case EXT4_IOC_SETFLAGS: {
231
		handle_t *handle = NULL;
232
		int err, migrate = 0;
233
		struct ext4_iloc iloc;
234
		unsigned int oldflags, mask, i;
235 236
		unsigned int jflag;

237
		if (!inode_owner_or_capable(inode))
238 239 240 241 242
			return -EACCES;

		if (get_user(flags, (int __user *) arg))
			return -EFAULT;

243
		err = mnt_want_write_file(filp);
244 245 246
		if (err)
			return err;

247
		flags = ext4_mask_flags(inode->i_mode, flags);
248

249
		err = -EPERM;
250
		mutex_lock(&inode->i_mutex);
251
		/* Is it quota file? Do not allow user to mess with it */
252 253 254
		if (IS_NOQUOTA(inode))
			goto flags_out;

255 256 257
		oldflags = ei->i_flags;

		/* The JOURNAL_DATA flag is modifiable only by root */
258
		jflag = flags & EXT4_JOURNAL_DATA_FL;
259 260 261 262 263 264 265

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 *
		 * This test looks nicer. Thanks to Pauline Middelink
		 */
266
		if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
267 268
			if (!capable(CAP_LINUX_IMMUTABLE))
				goto flags_out;
269 270 271 272 273 274
		}

		/*
		 * The JOURNAL_DATA flag can only be changed by
		 * the relevant capability.
		 */
275
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
276 277
			if (!capable(CAP_SYS_RESOURCE))
				goto flags_out;
278
		}
279
		if ((flags ^ oldflags) & EXT4_EXTENTS_FL)
280
			migrate = 1;
281

282 283 284 285 286 287 288 289 290
		if (flags & EXT4_EOFBLOCKS_FL) {
			/* we don't support adding EOFBLOCKS flag */
			if (!(oldflags & EXT4_EOFBLOCKS_FL)) {
				err = -EOPNOTSUPP;
				goto flags_out;
			}
		} else if (oldflags & EXT4_EOFBLOCKS_FL)
			ext4_truncate(inode);

291
		handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
292
		if (IS_ERR(handle)) {
293 294
			err = PTR_ERR(handle);
			goto flags_out;
295 296
		}
		if (IS_SYNC(inode))
297
			ext4_handle_sync(handle);
298
		err = ext4_reserve_inode_write(handle, inode, &iloc);
299 300 301
		if (err)
			goto flags_err;

302 303 304 305 306 307 308 309
		for (i = 0, mask = 1; i < 32; i++, mask <<= 1) {
			if (!(mask & EXT4_FL_USER_MODIFIABLE))
				continue;
			if (mask & flags)
				ext4_set_inode_flag(inode, i);
			else
				ext4_clear_inode_flag(inode, i);
		}
310

311
		ext4_set_inode_flags(inode);
K
Kalpak Shah 已提交
312
		inode->i_ctime = ext4_current_time(inode);
313

314
		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
315
flags_err:
316
		ext4_journal_stop(handle);
317 318
		if (err)
			goto flags_out;
319

320 321
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
			err = ext4_change_inode_journal_flag(inode, jflag);
322 323
		if (err)
			goto flags_out;
324 325 326 327 328 329 330
		if (migrate) {
			if (flags & EXT4_EXTENTS_FL)
				err = ext4_ext_migrate(inode);
			else
				err = ext4_ind_migrate(inode);
		}

331
flags_out:
332
		mutex_unlock(&inode->i_mutex);
A
Al Viro 已提交
333
		mnt_drop_write_file(filp);
334 335
		return err;
	}
336 337
	case EXT4_IOC_GETVERSION:
	case EXT4_IOC_GETVERSION_OLD:
338
		return put_user(inode->i_generation, (int __user *) arg);
339 340
	case EXT4_IOC_SETVERSION:
	case EXT4_IOC_SETVERSION_OLD: {
341
		handle_t *handle;
342
		struct ext4_iloc iloc;
343 344 345
		__u32 generation;
		int err;

346
		if (!inode_owner_or_capable(inode))
347
			return -EPERM;
348

349 350 351 352 353 354 355
		if (EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb,
				EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
			ext4_warning(sb, "Setting inode version is not "
				     "supported with metadata_csum enabled.");
			return -ENOTTY;
		}

356
		err = mnt_want_write_file(filp);
357 358 359 360 361 362
		if (err)
			return err;
		if (get_user(generation, (int __user *) arg)) {
			err = -EFAULT;
			goto setversion_out;
		}
363

364
		mutex_lock(&inode->i_mutex);
365
		handle = ext4_journal_start(inode, EXT4_HT_INODE, 1);
366 367
		if (IS_ERR(handle)) {
			err = PTR_ERR(handle);
368
			goto unlock_out;
369
		}
370
		err = ext4_reserve_inode_write(handle, inode, &iloc);
371
		if (err == 0) {
K
Kalpak Shah 已提交
372
			inode->i_ctime = ext4_current_time(inode);
373
			inode->i_generation = generation;
374
			err = ext4_mark_iloc_dirty(handle, inode, &iloc);
375
		}
376
		ext4_journal_stop(handle);
377 378 379

unlock_out:
		mutex_unlock(&inode->i_mutex);
380
setversion_out:
A
Al Viro 已提交
381
		mnt_drop_write_file(filp);
382 383
		return err;
	}
384 385
	case EXT4_IOC_GROUP_EXTEND: {
		ext4_fsblk_t n_blocks_count;
386
		int err, err2=0;
387

388 389 390
		err = ext4_resize_begin(sb);
		if (err)
			return err;
391

392 393 394 395
		if (get_user(n_blocks_count, (__u32 __user *)arg)) {
			err = -EFAULT;
			goto group_extend_out;
		}
396

397 398 399 400
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
401 402
			err = -EOPNOTSUPP;
			goto group_extend_out;
403 404
		}

405
		err = mnt_want_write_file(filp);
406
		if (err)
407
			goto group_extend_out;
408

409
		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
410 411 412 413 414
		if (EXT4_SB(sb)->s_journal) {
			jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
			err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
			jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
		}
415 416
		if (err == 0)
			err = err2;
A
Al Viro 已提交
417
		mnt_drop_write_file(filp);
418
group_extend_out:
419
		ext4_resize_end(sb);
420 421
		return err;
	}
422 423 424

	case EXT4_IOC_MOVE_EXT: {
		struct move_extent me;
425 426
		struct fd donor;
		int err;
427

428 429 430 431
		if (!(filp->f_mode & FMODE_READ) ||
		    !(filp->f_mode & FMODE_WRITE))
			return -EBADF;

432 433 434
		if (copy_from_user(&me,
			(struct move_extent __user *)arg, sizeof(me)))
			return -EFAULT;
435
		me.moved_len = 0;
436

437 438
		donor = fdget(me.donor_fd);
		if (!donor.file)
439 440
			return -EBADF;

441
		if (!(donor.file->f_mode & FMODE_WRITE)) {
442 443
			err = -EBADF;
			goto mext_out;
444 445
		}

446 447 448 449
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online defrag not supported with bigalloc");
450 451
			err = -EOPNOTSUPP;
			goto mext_out;
452 453
		}

454
		err = mnt_want_write_file(filp);
455 456 457
		if (err)
			goto mext_out;

458
		err = ext4_move_extents(filp, donor.file, me.orig_start,
459
					me.donor_start, me.len, &me.moved_len);
A
Al Viro 已提交
460
		mnt_drop_write_file(filp);
461

462
		if (copy_to_user((struct move_extent __user *)arg,
463
				 &me, sizeof(me)))
464 465
			err = -EFAULT;
mext_out:
466
		fdput(donor);
467 468 469
		return err;
	}

470 471
	case EXT4_IOC_GROUP_ADD: {
		struct ext4_new_group_data input;
472
		int err, err2=0;
473

474 475 476
		err = ext4_resize_begin(sb);
		if (err)
			return err;
477

478
		if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
479 480 481 482
				sizeof(input))) {
			err = -EFAULT;
			goto group_add_out;
		}
483

484 485 486 487
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
488 489
			err = -EOPNOTSUPP;
			goto group_add_out;
490 491
		}

492
		err = mnt_want_write_file(filp);
493
		if (err)
494
			goto group_add_out;
495

496
		err = ext4_group_add(sb, &input);
497 498 499 500 501
		if (EXT4_SB(sb)->s_journal) {
			jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
			err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
			jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
		}
502 503
		if (err == 0)
			err = err2;
A
Al Viro 已提交
504
		mnt_drop_write_file(filp);
505 506 507
		if (!err && ext4_has_group_desc_csum(sb) &&
		    test_opt(sb, INIT_INODE_TABLE))
			err = ext4_register_li_request(sb, input.group);
508
group_add_out:
509
		ext4_resize_end(sb);
510 511 512
		return err;
	}

513
	case EXT4_IOC_MIGRATE:
514 515
	{
		int err;
516
		if (!inode_owner_or_capable(inode))
517 518
			return -EACCES;

519
		err = mnt_want_write_file(filp);
520 521 522 523 524 525 526 527 528 529 530
		if (err)
			return err;
		/*
		 * inode_mutex prevent write and truncate on the file.
		 * Read still goes through. We take i_data_sem in
		 * ext4_ext_swap_inode_data before we switch the
		 * inode format to prevent read.
		 */
		mutex_lock(&(inode->i_mutex));
		err = ext4_ext_migrate(inode);
		mutex_unlock(&(inode->i_mutex));
A
Al Viro 已提交
531
		mnt_drop_write_file(filp);
532 533
		return err;
	}
534

535 536 537
	case EXT4_IOC_ALLOC_DA_BLKS:
	{
		int err;
538
		if (!inode_owner_or_capable(inode))
539 540
			return -EACCES;

541
		err = mnt_want_write_file(filp);
542 543 544
		if (err)
			return err;
		err = ext4_alloc_da_blocks(inode);
A
Al Viro 已提交
545
		mnt_drop_write_file(filp);
546 547 548
		return err;
	}

549 550 551 552 553
	case EXT4_IOC_SWAP_BOOT:
		if (!(filp->f_mode & FMODE_WRITE))
			return -EBADF;
		return swap_inode_boot_loader(sb, inode);

554 555 556
	case EXT4_IOC_RESIZE_FS: {
		ext4_fsblk_t n_blocks_count;
		int err = 0, err2 = 0;
557
		ext4_group_t o_group = EXT4_SB(sb)->s_groups_count;
558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574

		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not (yet) supported with bigalloc");
			return -EOPNOTSUPP;
		}

		if (copy_from_user(&n_blocks_count, (__u64 __user *)arg,
				   sizeof(__u64))) {
			return -EFAULT;
		}

		err = ext4_resize_begin(sb);
		if (err)
			return err;

575
		err = mnt_want_write_file(filp);
576 577 578 579 580 581 582 583 584 585 586
		if (err)
			goto resizefs_out;

		err = ext4_resize_fs(sb, n_blocks_count);
		if (EXT4_SB(sb)->s_journal) {
			jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
			err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal);
			jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
		}
		if (err == 0)
			err = err2;
587
		mnt_drop_write_file(filp);
588 589 590 591 592
		if (!err && (o_group > EXT4_SB(sb)->s_groups_count) &&
		    ext4_has_group_desc_csum(sb) &&
		    test_opt(sb, INIT_INODE_TABLE))
			err = ext4_register_li_request(sb, o_group);

593 594 595 596 597
resizefs_out:
		ext4_resize_end(sb);
		return err;
	}

598 599
	case FITRIM:
	{
600
		struct request_queue *q = bdev_get_queue(sb->s_bdev);
601 602 603 604 605 606
		struct fstrim_range range;
		int ret = 0;

		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;

607 608 609
		if (!blk_queue_discard(q))
			return -EOPNOTSUPP;

610
		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
611 612 613
		    sizeof(range)))
			return -EFAULT;

614 615
		range.minlen = max((unsigned int)range.minlen,
				   q->limits.discard_granularity);
616 617 618 619
		ret = ext4_trim_fs(sb, &range);
		if (ret < 0)
			return ret;

620
		if (copy_to_user((struct fstrim_range __user *)arg, &range,
621 622 623 624 625
		    sizeof(range)))
			return -EFAULT;

		return 0;
	}
626 627
	case EXT4_IOC_PRECACHE_EXTENTS:
		return ext4_ext_precache(inode);
628

629 630 631 632 633 634
	default:
		return -ENOTTY;
	}
}

#ifdef CONFIG_COMPAT
635
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
636 637 638
{
	/* These are just misnamed, they actually get/put from/to user an int */
	switch (cmd) {
639 640
	case EXT4_IOC32_GETFLAGS:
		cmd = EXT4_IOC_GETFLAGS;
641
		break;
642 643
	case EXT4_IOC32_SETFLAGS:
		cmd = EXT4_IOC_SETFLAGS;
644
		break;
645 646
	case EXT4_IOC32_GETVERSION:
		cmd = EXT4_IOC_GETVERSION;
647
		break;
648 649
	case EXT4_IOC32_SETVERSION:
		cmd = EXT4_IOC_SETVERSION;
650
		break;
651 652
	case EXT4_IOC32_GROUP_EXTEND:
		cmd = EXT4_IOC_GROUP_EXTEND;
653
		break;
654 655
	case EXT4_IOC32_GETVERSION_OLD:
		cmd = EXT4_IOC_GETVERSION_OLD;
656
		break;
657 658
	case EXT4_IOC32_SETVERSION_OLD:
		cmd = EXT4_IOC_SETVERSION_OLD;
659
		break;
660 661
	case EXT4_IOC32_GETRSVSZ:
		cmd = EXT4_IOC_GETRSVSZ;
662
		break;
663 664
	case EXT4_IOC32_SETRSVSZ:
		cmd = EXT4_IOC_SETRSVSZ;
665
		break;
666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688
	case EXT4_IOC32_GROUP_ADD: {
		struct compat_ext4_new_group_input __user *uinput;
		struct ext4_new_group_input input;
		mm_segment_t old_fs;
		int err;

		uinput = compat_ptr(arg);
		err = get_user(input.group, &uinput->group);
		err |= get_user(input.block_bitmap, &uinput->block_bitmap);
		err |= get_user(input.inode_bitmap, &uinput->inode_bitmap);
		err |= get_user(input.inode_table, &uinput->inode_table);
		err |= get_user(input.blocks_count, &uinput->blocks_count);
		err |= get_user(input.reserved_blocks,
				&uinput->reserved_blocks);
		if (err)
			return -EFAULT;
		old_fs = get_fs();
		set_fs(KERNEL_DS);
		err = ext4_ioctl(file, EXT4_IOC_GROUP_ADD,
				 (unsigned long) &input);
		set_fs(old_fs);
		return err;
	}
689
	case EXT4_IOC_MOVE_EXT:
T
Tao Ma 已提交
690
	case FITRIM:
691
	case EXT4_IOC_RESIZE_FS:
692
	case EXT4_IOC_PRECACHE_EXTENTS:
693
		break;
694 695 696
	default:
		return -ENOIOCTLCMD;
	}
A
Andi Kleen 已提交
697
	return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
698 699
}
#endif