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
#include "ext4_extents.h"
21

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

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 79
/**
 * 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));
80 81 82 83
	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);
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 216

	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 已提交
217
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
218
{
A
Al Viro 已提交
219
	struct inode *inode = file_inode(filp);
220
	struct super_block *sb = inode->i_sb;
221
	struct ext4_inode_info *ei = EXT4_I(inode);
222 223
	unsigned int flags;

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

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

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

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

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

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

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

256 257 258
		oldflags = ei->i_flags;

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

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

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

283 284 285 286 287 288 289 290 291
		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);

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

303 304 305 306 307 308 309 310
		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);
		}
311

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

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

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

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

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

350 351 352 353 354 355 356
		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;
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		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;

576
		err = mnt_want_write_file(filp);
577 578 579 580 581 582 583 584 585 586 587
		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;
588
		mnt_drop_write_file(filp);
589 590 591 592 593
		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);

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

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

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

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

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

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

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

		return 0;
	}

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

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