ioctl.c 12.0 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

	case EXT4_IOC_MOVE_EXT: {
		struct move_extent me;
		struct file *donor_filp;
237
		int err, fput_needed;
238

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
		donor_filp = fget_light(me.donor_fd, &fput_needed);
249 250 251
		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
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online defrag not supported with bigalloc");
261 262
			err = -EOPNOTSUPP;
			goto mext_out;
263 264
		}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		return 0;
	}

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

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