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

	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_bl;
105
	struct ext4_sb_info *sbi = EXT4_SB(sb);
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128

	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;
	}

	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. */
129
	lock_two_nondirectories(inode, inode_bl);
130 131 132 133 134 135 136 137 138 139 140 141 142

	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;
143
		goto journal_err_out;
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
	}

	/* 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);

201
journal_err_out:
202 203 204
	ext4_inode_resume_unlocked_dio(inode);
	ext4_inode_resume_unlocked_dio(inode_bl);

205
	unlock_two_nondirectories(inode, inode_bl);
206 207 208 209 210 211 212

	iput(inode_bl);

swap_boot_out:
	return err;
}

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

220
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
221 222

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

234
		if (!inode_owner_or_capable(inode))
235 236 237 238 239
			return -EACCES;

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

240
		err = mnt_want_write_file(filp);
241 242 243
		if (err)
			return err;

244
		flags = ext4_mask_flags(inode->i_mode, flags);
245

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

252 253 254
		oldflags = ei->i_flags;

		/* The JOURNAL_DATA flag is modifiable only by root */
255
		jflag = flags & EXT4_JOURNAL_DATA_FL;
256 257 258 259 260 261 262

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

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

279 280 281 282 283 284 285 286 287
		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);

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

299 300 301 302 303 304 305 306
		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);
		}
307

308
		ext4_set_inode_flags(inode);
K
Kalpak Shah 已提交
309
		inode->i_ctime = ext4_current_time(inode);
310

311
		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
312
flags_err:
313
		ext4_journal_stop(handle);
314 315
		if (err)
			goto flags_out;
316

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

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

343
		if (!inode_owner_or_capable(inode))
344
			return -EPERM;
345

346 347 348 349 350 351 352
		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;
		}

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

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

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

385 386 387
		err = ext4_resize_begin(sb);
		if (err)
			return err;
388

389 390 391 392
		if (get_user(n_blocks_count, (__u32 __user *)arg)) {
			err = -EFAULT;
			goto group_extend_out;
		}
393

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

402
		err = mnt_want_write_file(filp);
403
		if (err)
404
			goto group_extend_out;
405

406
		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
407 408 409 410 411
		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);
		}
412 413
		if (err == 0)
			err = err2;
A
Al Viro 已提交
414
		mnt_drop_write_file(filp);
415
group_extend_out:
416
		ext4_resize_end(sb);
417 418
		return err;
	}
419 420 421

	case EXT4_IOC_MOVE_EXT: {
		struct move_extent me;
422 423
		struct fd donor;
		int err;
424

425 426 427 428
		if (!(filp->f_mode & FMODE_READ) ||
		    !(filp->f_mode & FMODE_WRITE))
			return -EBADF;

429 430 431
		if (copy_from_user(&me,
			(struct move_extent __user *)arg, sizeof(me)))
			return -EFAULT;
432
		me.moved_len = 0;
433

434 435
		donor = fdget(me.donor_fd);
		if (!donor.file)
436 437
			return -EBADF;

438
		if (!(donor.file->f_mode & FMODE_WRITE)) {
439 440
			err = -EBADF;
			goto mext_out;
441 442
		}

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

451
		err = mnt_want_write_file(filp);
452 453 454
		if (err)
			goto mext_out;

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

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

467 468
	case EXT4_IOC_GROUP_ADD: {
		struct ext4_new_group_data input;
469
		int err, err2=0;
470

471 472 473
		err = ext4_resize_begin(sb);
		if (err)
			return err;
474

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

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

489
		err = mnt_want_write_file(filp);
490
		if (err)
491
			goto group_add_out;
492

493
		err = ext4_group_add(sb, &input);
494 495 496 497 498
		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);
		}
499 500
		if (err == 0)
			err = err2;
A
Al Viro 已提交
501
		mnt_drop_write_file(filp);
502 503 504
		if (!err && ext4_has_group_desc_csum(sb) &&
		    test_opt(sb, INIT_INODE_TABLE))
			err = ext4_register_li_request(sb, input.group);
505
group_add_out:
506
		ext4_resize_end(sb);
507 508 509
		return err;
	}

510
	case EXT4_IOC_MIGRATE:
511 512
	{
		int err;
513
		if (!inode_owner_or_capable(inode))
514 515
			return -EACCES;

516
		err = mnt_want_write_file(filp);
517 518 519 520 521 522 523 524 525 526 527
		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 已提交
528
		mnt_drop_write_file(filp);
529 530
		return err;
	}
531

532 533 534
	case EXT4_IOC_ALLOC_DA_BLKS:
	{
		int err;
535
		if (!inode_owner_or_capable(inode))
536 537
			return -EACCES;

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

546 547 548 549 550
	case EXT4_IOC_SWAP_BOOT:
		if (!(filp->f_mode & FMODE_WRITE))
			return -EBADF;
		return swap_inode_boot_loader(sb, inode);

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

		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;

572
		err = mnt_want_write_file(filp);
573 574 575 576 577 578 579 580 581 582 583
		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;
584
		mnt_drop_write_file(filp);
585 586 587 588 589
		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);

590 591 592 593 594
resizefs_out:
		ext4_resize_end(sb);
		return err;
	}

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

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

604 605 606
		if (!blk_queue_discard(q))
			return -EOPNOTSUPP;

607
		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
608 609 610
		    sizeof(range)))
			return -EFAULT;

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

617
		if (copy_to_user((struct fstrim_range __user *)arg, &range,
618 619 620 621 622
		    sizeof(range)))
			return -EFAULT;

		return 0;
	}
623 624
	case EXT4_IOC_PRECACHE_EXTENTS:
		return ext4_ext_precache(inode);
625

626 627 628 629 630 631
	default:
		return -ENOTTY;
	}
}

#ifdef CONFIG_COMPAT
632
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
633 634 635
{
	/* These are just misnamed, they actually get/put from/to user an int */
	switch (cmd) {
636 637
	case EXT4_IOC32_GETFLAGS:
		cmd = EXT4_IOC_GETFLAGS;
638
		break;
639 640
	case EXT4_IOC32_SETFLAGS:
		cmd = EXT4_IOC_SETFLAGS;
641
		break;
642 643
	case EXT4_IOC32_GETVERSION:
		cmd = EXT4_IOC_GETVERSION;
644
		break;
645 646
	case EXT4_IOC32_SETVERSION:
		cmd = EXT4_IOC_SETVERSION;
647
		break;
648 649
	case EXT4_IOC32_GROUP_EXTEND:
		cmd = EXT4_IOC_GROUP_EXTEND;
650
		break;
651 652
	case EXT4_IOC32_GETVERSION_OLD:
		cmd = EXT4_IOC_GETVERSION_OLD;
653
		break;
654 655
	case EXT4_IOC32_SETVERSION_OLD:
		cmd = EXT4_IOC_SETVERSION_OLD;
656
		break;
657 658
	case EXT4_IOC32_GETRSVSZ:
		cmd = EXT4_IOC_GETRSVSZ;
659
		break;
660 661
	case EXT4_IOC32_SETRSVSZ:
		cmd = EXT4_IOC_SETRSVSZ;
662
		break;
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685
	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;
	}
686
	case EXT4_IOC_MOVE_EXT:
T
Tao Ma 已提交
687
	case FITRIM:
688
	case EXT4_IOC_RESIZE_FS:
689
	case EXT4_IOC_PRECACHE_EXTENTS:
690
		break;
691 692 693
	default:
		return -ENOIOCTLCMD;
	}
A
Andi Kleen 已提交
694
	return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
695 696
}
#endif