ioctl.c 11.6 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 42 43
		unsigned int oldflags;
		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
		flags = flags & EXT4_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE;
120 121
		ei->i_flags = flags;

122
		ext4_set_inode_flags(inode);
K
Kalpak Shah 已提交
123
		inode->i_ctime = ext4_current_time(inode);
124

125
		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
126
flags_err:
127
		ext4_journal_stop(handle);
128 129
		if (err)
			goto flags_out;
130

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

152
		if (!inode_owner_or_capable(inode))
153
			return -EPERM;
154

155
		err = mnt_want_write_file(filp);
156 157 158 159 160 161
		if (err)
			return err;
		if (get_user(generation, (int __user *) arg)) {
			err = -EFAULT;
			goto setversion_out;
		}
162

163
		mutex_lock(&inode->i_mutex);
164
		handle = ext4_journal_start(inode, 1);
165 166
		if (IS_ERR(handle)) {
			err = PTR_ERR(handle);
167
			goto unlock_out;
168
		}
169
		err = ext4_reserve_inode_write(handle, inode, &iloc);
170
		if (err == 0) {
K
Kalpak Shah 已提交
171
			inode->i_ctime = ext4_current_time(inode);
172
			inode->i_generation = generation;
173
			err = ext4_mark_iloc_dirty(handle, inode, &iloc);
174
		}
175
		ext4_journal_stop(handle);
176 177 178

unlock_out:
		mutex_unlock(&inode->i_mutex);
179
setversion_out:
A
Al Viro 已提交
180
		mnt_drop_write_file(filp);
181 182
		return err;
	}
183 184
	case EXT4_IOC_GROUP_EXTEND: {
		ext4_fsblk_t n_blocks_count;
185
		int err, err2=0;
186

187 188 189
		err = ext4_resize_begin(sb);
		if (err)
			return err;
190

191 192 193 194
		if (get_user(n_blocks_count, (__u32 __user *)arg)) {
			err = -EFAULT;
			goto group_extend_out;
		}
195

196 197 198 199
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
200 201
			err = -EOPNOTSUPP;
			goto group_extend_out;
202 203
		}

204
		err = mnt_want_write_file(filp);
205
		if (err)
206
			goto group_extend_out;
207

208
		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
209 210 211 212 213
		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);
		}
214 215
		if (err == 0)
			err = err2;
A
Al Viro 已提交
216
		mnt_drop_write_file(filp);
217
group_extend_out:
218
		ext4_resize_end(sb);
219 220
		return err;
	}
221 222 223 224 225 226

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

227 228 229 230
		if (!(filp->f_mode & FMODE_READ) ||
		    !(filp->f_mode & FMODE_WRITE))
			return -EBADF;

231 232 233
		if (copy_from_user(&me,
			(struct move_extent __user *)arg, sizeof(me)))
			return -EFAULT;
234
		me.moved_len = 0;
235 236 237 238 239

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

240 241 242
		if (!(donor_filp->f_mode & FMODE_WRITE)) {
			err = -EBADF;
			goto mext_out;
243 244
		}

245 246 247 248 249 250 251
		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;
		}

252
		err = mnt_want_write_file(filp);
253 254 255
		if (err)
			goto mext_out;

256 257
		err = ext4_move_extents(filp, donor_filp, me.orig_start,
					me.donor_start, me.len, &me.moved_len);
A
Al Viro 已提交
258
		mnt_drop_write_file(filp);
259
		mnt_drop_write(filp->f_path.mnt);
260

261
		if (copy_to_user((struct move_extent __user *)arg,
262
				 &me, sizeof(me)))
263 264 265
			err = -EFAULT;
mext_out:
		fput(donor_filp);
266 267 268
		return err;
	}

269 270
	case EXT4_IOC_GROUP_ADD: {
		struct ext4_new_group_data input;
271
		int err, err2=0;
272

273 274 275
		err = ext4_resize_begin(sb);
		if (err)
			return err;
276

277
		if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
278 279 280 281
				sizeof(input))) {
			err = -EFAULT;
			goto group_add_out;
		}
282

283 284 285 286
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "Online resizing not supported with bigalloc");
287 288
			err = -EOPNOTSUPP;
			goto group_add_out;
289 290
		}

291
		err = mnt_want_write_file(filp);
292
		if (err)
293
			goto group_add_out;
294

295
		err = ext4_group_add(sb, &input);
296 297 298 299 300
		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);
		}
301 302
		if (err == 0)
			err = err2;
A
Al Viro 已提交
303
		mnt_drop_write_file(filp);
304
group_add_out:
305
		ext4_resize_end(sb);
306 307 308
		return err;
	}

309
	case EXT4_IOC_MIGRATE:
310 311
	{
		int err;
312
		if (!inode_owner_or_capable(inode))
313 314
			return -EACCES;

315
		err = mnt_want_write_file(filp);
316 317 318 319 320 321 322 323 324 325 326
		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 已提交
327
		mnt_drop_write_file(filp);
328 329
		return err;
	}
330

331 332 333
	case EXT4_IOC_ALLOC_DA_BLKS:
	{
		int err;
334
		if (!inode_owner_or_capable(inode))
335 336
			return -EACCES;

337
		err = mnt_want_write_file(filp);
338 339 340
		if (err)
			return err;
		err = ext4_alloc_da_blocks(inode);
A
Al Viro 已提交
341
		mnt_drop_write_file(filp);
342 343 344
		return err;
	}

345 346 347 348 349 350 351 352 353 354 355 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 392 393 394 395 396 397 398
	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;

		err = mnt_want_write(filp->f_path.mnt);
		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;
		mnt_drop_write(filp->f_path.mnt);
resizefs_out:
		ext4_resize_end(sb);
		return err;
	}

399 400
	case FITRIM:
	{
401
		struct request_queue *q = bdev_get_queue(sb->s_bdev);
402 403 404 405 406 407
		struct fstrim_range range;
		int ret = 0;

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

408 409 410
		if (!blk_queue_discard(q))
			return -EOPNOTSUPP;

411 412 413 414 415 416 417
		if (EXT4_HAS_RO_COMPAT_FEATURE(sb,
			       EXT4_FEATURE_RO_COMPAT_BIGALLOC)) {
			ext4_msg(sb, KERN_ERR,
				 "FITRIM not supported with bigalloc");
			return -EOPNOTSUPP;
		}

418
		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
419 420 421
		    sizeof(range)))
			return -EFAULT;

422 423
		range.minlen = max((unsigned int)range.minlen,
				   q->limits.discard_granularity);
424 425 426 427
		ret = ext4_trim_fs(sb, &range);
		if (ret < 0)
			return ret;

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

		return 0;
	}

435 436 437 438 439 440
	default:
		return -ENOTTY;
	}
}

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