ioctl.c 11.9 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)

A
Andi Kleen 已提交
23
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
24
{
A
Andi Kleen 已提交
25
	struct inode *inode = filp->f_dentry->d_inode;
26
	struct super_block *sb = inode->i_sb;
27
	struct ext4_inode_info *ei = EXT4_I(inode);
28 29
	unsigned int flags;

30
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
31 32

	switch (cmd) {
33
	case EXT4_IOC_GETFLAGS:
34
		ext4_get_inode_flags(ei);
35
		flags = ei->i_flags & EXT4_FL_USER_VISIBLE;
36
		return put_user(flags, (int __user *) arg);
37
	case EXT4_IOC_SETFLAGS: {
38
		handle_t *handle = NULL;
39
		int err, migrate = 0;
40
		struct ext4_iloc iloc;
41
		unsigned int oldflags, mask, i;
42 43
		unsigned int jflag;

44
		if (!inode_owner_or_capable(inode))
45 46 47 48 49
			return -EACCES;

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

50
		err = mnt_want_write_file(filp);
51 52 53
		if (err)
			return err;

54
		flags = ext4_mask_flags(inode->i_mode, flags);
55

56
		err = -EPERM;
57
		mutex_lock(&inode->i_mutex);
58
		/* Is it quota file? Do not allow user to mess with it */
59 60 61
		if (IS_NOQUOTA(inode))
			goto flags_out;

62 63 64
		oldflags = ei->i_flags;

		/* The JOURNAL_DATA flag is modifiable only by root */
65
		jflag = flags & EXT4_JOURNAL_DATA_FL;
66 67 68 69 70 71 72

		/*
		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
		 * the relevant capability.
		 *
		 * This test looks nicer. Thanks to Pauline Middelink
		 */
73
		if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) {
74 75
			if (!capable(CAP_LINUX_IMMUTABLE))
				goto flags_out;
76 77 78 79 80 81
		}

		/*
		 * The JOURNAL_DATA flag can only be changed by
		 * the relevant capability.
		 */
82
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
83 84
			if (!capable(CAP_SYS_RESOURCE))
				goto flags_out;
85
		}
86 87 88 89 90 91 92 93 94 95 96
		if (oldflags & EXT4_EXTENTS_FL) {
			/* We don't support clearning extent flags */
			if (!(flags & EXT4_EXTENTS_FL)) {
				err = -EOPNOTSUPP;
				goto flags_out;
			}
		} else if (flags & EXT4_EXTENTS_FL) {
			/* migrate the file */
			migrate = 1;
			flags &= ~EXT4_EXTENTS_FL;
		}
97

98 99 100 101 102 103 104 105 106
		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);

107
		handle = ext4_journal_start(inode, 1);
108
		if (IS_ERR(handle)) {
109 110
			err = PTR_ERR(handle);
			goto flags_out;
111 112
		}
		if (IS_SYNC(inode))
113
			ext4_handle_sync(handle);
114
		err = ext4_reserve_inode_write(handle, inode, &iloc);
115 116 117
		if (err)
			goto flags_err;

118 119 120 121 122 123 124 125
		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);
		}
126

127
		ext4_set_inode_flags(inode);
K
Kalpak Shah 已提交
128
		inode->i_ctime = ext4_current_time(inode);
129

130
		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
131
flags_err:
132
		ext4_journal_stop(handle);
133 134
		if (err)
			goto flags_out;
135

136 137
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
			err = ext4_change_inode_journal_flag(inode, jflag);
138 139 140 141
		if (err)
			goto flags_out;
		if (migrate)
			err = ext4_ext_migrate(inode);
142
flags_out:
143
		mutex_unlock(&inode->i_mutex);
A
Al Viro 已提交
144
		mnt_drop_write_file(filp);
145 146
		return err;
	}
147 148
	case EXT4_IOC_GETVERSION:
	case EXT4_IOC_GETVERSION_OLD:
149
		return put_user(inode->i_generation, (int __user *) arg);
150 151
	case EXT4_IOC_SETVERSION:
	case EXT4_IOC_SETVERSION_OLD: {
152
		handle_t *handle;
153
		struct ext4_iloc iloc;
154 155 156
		__u32 generation;
		int err;

157
		if (!inode_owner_or_capable(inode))
158
			return -EPERM;
159

160 161 162 163 164 165 166
		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;
		}

167
		err = mnt_want_write_file(filp);
168 169 170 171 172 173
		if (err)
			return err;
		if (get_user(generation, (int __user *) arg)) {
			err = -EFAULT;
			goto setversion_out;
		}
174

175
		mutex_lock(&inode->i_mutex);
176
		handle = ext4_journal_start(inode, 1);
177 178
		if (IS_ERR(handle)) {
			err = PTR_ERR(handle);
179
			goto unlock_out;
180
		}
181
		err = ext4_reserve_inode_write(handle, inode, &iloc);
182
		if (err == 0) {
K
Kalpak Shah 已提交
183
			inode->i_ctime = ext4_current_time(inode);
184
			inode->i_generation = generation;
185
			err = ext4_mark_iloc_dirty(handle, inode, &iloc);
186
		}
187
		ext4_journal_stop(handle);
188 189 190

unlock_out:
		mutex_unlock(&inode->i_mutex);
191
setversion_out:
A
Al Viro 已提交
192
		mnt_drop_write_file(filp);
193 194
		return err;
	}
195 196
	case EXT4_IOC_GROUP_EXTEND: {
		ext4_fsblk_t n_blocks_count;
197
		int err, err2=0;
198

199 200 201
		err = ext4_resize_begin(sb);
		if (err)
			return err;
202

203 204 205 206
		if (get_user(n_blocks_count, (__u32 __user *)arg)) {
			err = -EFAULT;
			goto group_extend_out;
		}
207

208 209 210 211
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
212 213
			err = -EOPNOTSUPP;
			goto group_extend_out;
214 215
		}

216
		err = mnt_want_write_file(filp);
217
		if (err)
218
			goto group_extend_out;
219

220
		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
221 222 223 224 225
		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);
		}
226 227
		if (err == 0)
			err = err2;
A
Al Viro 已提交
228
		mnt_drop_write_file(filp);
229
group_extend_out:
230
		ext4_resize_end(sb);
231 232
		return err;
	}
233 234 235 236 237 238

	case EXT4_IOC_MOVE_EXT: {
		struct move_extent me;
		struct file *donor_filp;
		int err;

239 240 241 242
		if (!(filp->f_mode & FMODE_READ) ||
		    !(filp->f_mode & FMODE_WRITE))
			return -EBADF;

243 244 245
		if (copy_from_user(&me,
			(struct move_extent __user *)arg, sizeof(me)))
			return -EFAULT;
246
		me.moved_len = 0;
247 248 249 250 251

		donor_filp = fget(me.donor_fd);
		if (!donor_filp)
			return -EBADF;

252 253 254
		if (!(donor_filp->f_mode & FMODE_WRITE)) {
			err = -EBADF;
			goto mext_out;
255 256
		}

257 258 259 260 261 262 263
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online defrag not supported with bigalloc");
			return -EOPNOTSUPP;
		}

264
		err = mnt_want_write_file(filp);
265 266 267
		if (err)
			goto mext_out;

268 269
		err = ext4_move_extents(filp, donor_filp, me.orig_start,
					me.donor_start, me.len, &me.moved_len);
A
Al Viro 已提交
270
		mnt_drop_write_file(filp);
271

272
		if (copy_to_user((struct move_extent __user *)arg,
273
				 &me, sizeof(me)))
274 275 276
			err = -EFAULT;
mext_out:
		fput(donor_filp);
277 278 279
		return err;
	}

280 281
	case EXT4_IOC_GROUP_ADD: {
		struct ext4_new_group_data input;
282
		int err, err2=0;
283

284 285 286
		err = ext4_resize_begin(sb);
		if (err)
			return err;
287

288
		if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
289 290 291 292
				sizeof(input))) {
			err = -EFAULT;
			goto group_add_out;
		}
293

294 295 296 297
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
298 299
			err = -EOPNOTSUPP;
			goto group_add_out;
300 301
		}

302
		err = mnt_want_write_file(filp);
303
		if (err)
304
			goto group_add_out;
305

306
		err = ext4_group_add(sb, &input);
307 308 309 310 311
		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);
		}
312 313
		if (err == 0)
			err = err2;
A
Al Viro 已提交
314
		mnt_drop_write_file(filp);
315
group_add_out:
316
		ext4_resize_end(sb);
317 318 319
		return err;
	}

320
	case EXT4_IOC_MIGRATE:
321 322
	{
		int err;
323
		if (!inode_owner_or_capable(inode))
324 325
			return -EACCES;

326
		err = mnt_want_write_file(filp);
327 328 329 330 331 332 333 334 335 336 337
		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 已提交
338
		mnt_drop_write_file(filp);
339 340
		return err;
	}
341

342 343 344
	case EXT4_IOC_ALLOC_DA_BLKS:
	{
		int err;
345
		if (!inode_owner_or_capable(inode))
346 347
			return -EACCES;

348
		err = mnt_want_write_file(filp);
349 350 351
		if (err)
			return err;
		err = ext4_alloc_da_blocks(inode);
A
Al Viro 已提交
352
		mnt_drop_write_file(filp);
353 354 355
		return err;
	}

356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
	case EXT4_IOC_RESIZE_FS: {
		ext4_fsblk_t n_blocks_count;
		struct super_block *sb = inode->i_sb;
		int err = 0, err2 = 0;

		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 (EXT4_HAS_INCOMPAT_FEATURE(sb,
			       EXT4_FEATURE_INCOMPAT_META_BG)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not (yet) supported with meta_bg");
			return -EOPNOTSUPP;
		}

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

		if (n_blocks_count > MAX_32_NUM &&
		    !EXT4_HAS_INCOMPAT_FEATURE(sb,
					       EXT4_FEATURE_INCOMPAT_64BIT)) {
			ext4_msg(sb, KERN_ERR,
				 "File system only supports 32-bit block numbers");
			return -EOPNOTSUPP;
		}

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

392
		err = mnt_want_write_file(filp);
393 394 395 396 397 398 399 400 401 402 403
		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;
404
		mnt_drop_write_file(filp);
405 406 407 408 409
resizefs_out:
		ext4_resize_end(sb);
		return err;
	}

410 411
	case FITRIM:
	{
412
		struct request_queue *q = bdev_get_queue(sb->s_bdev);
413 414 415 416 417 418
		struct fstrim_range range;
		int ret = 0;

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

419 420 421
		if (!blk_queue_discard(q))
			return -EOPNOTSUPP;

422 423 424 425 426 427 428
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "FITRIM not supported with bigalloc");
			return -EOPNOTSUPP;
		}

429
		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
430 431 432
		    sizeof(range)))
			return -EFAULT;

433 434
		range.minlen = max((unsigned int)range.minlen,
				   q->limits.discard_granularity);
435 436 437 438
		ret = ext4_trim_fs(sb, &range);
		if (ret < 0)
			return ret;

439
		if (copy_to_user((struct fstrim_range __user *)arg, &range,
440 441 442 443 444 445
		    sizeof(range)))
			return -EFAULT;

		return 0;
	}

446 447 448 449 450 451
	default:
		return -ENOTTY;
	}
}

#ifdef CONFIG_COMPAT
452
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
453 454 455
{
	/* These are just misnamed, they actually get/put from/to user an int */
	switch (cmd) {
456 457
	case EXT4_IOC32_GETFLAGS:
		cmd = EXT4_IOC_GETFLAGS;
458
		break;
459 460
	case EXT4_IOC32_SETFLAGS:
		cmd = EXT4_IOC_SETFLAGS;
461
		break;
462 463
	case EXT4_IOC32_GETVERSION:
		cmd = EXT4_IOC_GETVERSION;
464
		break;
465 466
	case EXT4_IOC32_SETVERSION:
		cmd = EXT4_IOC_SETVERSION;
467
		break;
468 469
	case EXT4_IOC32_GROUP_EXTEND:
		cmd = EXT4_IOC_GROUP_EXTEND;
470
		break;
471 472
	case EXT4_IOC32_GETVERSION_OLD:
		cmd = EXT4_IOC_GETVERSION_OLD;
473
		break;
474 475
	case EXT4_IOC32_SETVERSION_OLD:
		cmd = EXT4_IOC_SETVERSION_OLD;
476
		break;
477 478
	case EXT4_IOC32_GETRSVSZ:
		cmd = EXT4_IOC_GETRSVSZ;
479
		break;
480 481
	case EXT4_IOC32_SETRSVSZ:
		cmd = EXT4_IOC_SETRSVSZ;
482
		break;
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
	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;
	}
506
	case EXT4_IOC_MOVE_EXT:
T
Tao Ma 已提交
507
	case FITRIM:
508
	case EXT4_IOC_RESIZE_FS:
509
		break;
510 511 512
	default:
		return -ENOIOCTLCMD;
	}
A
Andi Kleen 已提交
513
	return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
514 515
}
#endif