ioctl.c 8.1 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 15
#include <linux/capability.h>
#include <linux/time.h>
#include <linux/compat.h>
#include <linux/smp_lock.h>
16
#include <linux/mount.h>
17
#include <asm/uaccess.h>
18 19
#include "ext4_jbd2.h"
#include "ext4.h"
20

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

28
	ext4_debug("cmd = %u, arg = %lu\n", cmd, arg);
29 30

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

42
		if (!is_owner_or_cap(inode))
43 44 45 46 47
			return -EACCES;

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

48 49 50 51
		err = mnt_want_write(filp->f_path.mnt);
		if (err)
			return err;

52
		if (!S_ISDIR(inode->i_mode))
53
			flags &= ~EXT4_DIRSYNC_FL;
54

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

61 62 63
		oldflags = ei->i_flags;

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

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

		/*
		 * The JOURNAL_DATA flag can only be changed by
		 * the relevant capability.
		 */
81
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) {
82 83
			if (!capable(CAP_SYS_RESOURCE))
				goto flags_out;
84 85
		}

86
		handle = ext4_journal_start(inode, 1);
87
		if (IS_ERR(handle)) {
88 89
			err = PTR_ERR(handle);
			goto flags_out;
90 91 92
		}
		if (IS_SYNC(inode))
			handle->h_sync = 1;
93
		err = ext4_reserve_inode_write(handle, inode, &iloc);
94 95 96
		if (err)
			goto flags_err;

97 98
		flags = flags & EXT4_FL_USER_MODIFIABLE;
		flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE;
99 100
		ei->i_flags = flags;

101
		ext4_set_inode_flags(inode);
K
Kalpak Shah 已提交
102
		inode->i_ctime = ext4_current_time(inode);
103

104
		err = ext4_mark_iloc_dirty(handle, inode, &iloc);
105
flags_err:
106
		ext4_journal_stop(handle);
107 108
		if (err)
			goto flags_out;
109

110 111
		if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL))
			err = ext4_change_inode_journal_flag(inode, jflag);
112
flags_out:
113
		mutex_unlock(&inode->i_mutex);
114
		mnt_drop_write(filp->f_path.mnt);
115 116
		return err;
	}
117 118
	case EXT4_IOC_GETVERSION:
	case EXT4_IOC_GETVERSION_OLD:
119
		return put_user(inode->i_generation, (int __user *) arg);
120 121
	case EXT4_IOC_SETVERSION:
	case EXT4_IOC_SETVERSION_OLD: {
122
		handle_t *handle;
123
		struct ext4_iloc iloc;
124 125 126
		__u32 generation;
		int err;

127
		if (!is_owner_or_cap(inode))
128
			return -EPERM;
129 130 131 132 133 134 135 136

		err = mnt_want_write(filp->f_path.mnt);
		if (err)
			return err;
		if (get_user(generation, (int __user *) arg)) {
			err = -EFAULT;
			goto setversion_out;
		}
137

138
		handle = ext4_journal_start(inode, 1);
139 140 141 142
		if (IS_ERR(handle)) {
			err = PTR_ERR(handle);
			goto setversion_out;
		}
143
		err = ext4_reserve_inode_write(handle, inode, &iloc);
144
		if (err == 0) {
K
Kalpak Shah 已提交
145
			inode->i_ctime = ext4_current_time(inode);
146
			inode->i_generation = generation;
147
			err = ext4_mark_iloc_dirty(handle, inode, &iloc);
148
		}
149
		ext4_journal_stop(handle);
150 151
setversion_out:
		mnt_drop_write(filp->f_path.mnt);
152 153
		return err;
	}
154
#ifdef CONFIG_JBD2_DEBUG
155
	case EXT4_IOC_WAIT_FOR_READONLY:
156 157 158 159 160 161 162 163 164 165 166 167 168
		/*
		 * This is racy - by the time we're woken up and running,
		 * the superblock could be released.  And the module could
		 * have been unloaded.  So sue me.
		 *
		 * Returns 1 if it slept, else zero.
		 */
		{
			struct super_block *sb = inode->i_sb;
			DECLARE_WAITQUEUE(wait, current);
			int ret = 0;

			set_current_state(TASK_INTERRUPTIBLE);
169 170
			add_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait);
			if (timer_pending(&EXT4_SB(sb)->turn_ro_timer)) {
171 172 173
				schedule();
				ret = 1;
			}
174
			remove_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait);
175 176 177
			return ret;
		}
#endif
178
	case EXT4_IOC_GETRSVSZ:
179 180 181 182 183 184 185
		if (test_opt(inode->i_sb, RESERVATION)
			&& S_ISREG(inode->i_mode)
			&& ei->i_block_alloc_info) {
			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
			return put_user(rsv_window_size, (int __user *)arg);
		}
		return -ENOTTY;
186
	case EXT4_IOC_SETRSVSZ: {
187
		int err;
188

189
		if (!test_opt(inode->i_sb, RESERVATION) || !S_ISREG(inode->i_mode))
190 191
			return -ENOTTY;

192
		if (!is_owner_or_cap(inode))
193 194 195 196 197
			return -EACCES;

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

198 199 200 201
		err = mnt_want_write(filp->f_path.mnt);
		if (err)
			return err;

202 203
		if (rsv_window_size > EXT4_MAX_RESERVE_BLOCKS)
			rsv_window_size = EXT4_MAX_RESERVE_BLOCKS;
204 205 206 207 208

		/*
		 * need to allocate reservation structure for this inode
		 * before set the window size
		 */
209
		down_write(&ei->i_data_sem);
210
		if (!ei->i_block_alloc_info)
211
			ext4_init_block_alloc_info(inode);
212 213

		if (ei->i_block_alloc_info){
214
			struct ext4_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
215 216
			rsv->rsv_goal_size = rsv_window_size;
		}
217
		up_write(&ei->i_data_sem);
218
		mnt_drop_write(filp->f_path.mnt);
219 220
		return 0;
	}
221 222
	case EXT4_IOC_GROUP_EXTEND: {
		ext4_fsblk_t n_blocks_count;
223 224 225 226 227 228 229 230 231
		struct super_block *sb = inode->i_sb;
		int err;

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

		if (get_user(n_blocks_count, (__u32 __user *)arg))
			return -EFAULT;

232 233 234 235
		err = mnt_want_write(filp->f_path.mnt);
		if (err)
			return err;

236
		err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count);
237 238 239
		jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
		jbd2_journal_flush(EXT4_SB(sb)->s_journal);
		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
240
		mnt_drop_write(filp->f_path.mnt);
241 242 243

		return err;
	}
244 245
	case EXT4_IOC_GROUP_ADD: {
		struct ext4_new_group_data input;
246 247 248 249 250 251
		struct super_block *sb = inode->i_sb;
		int err;

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

252
		if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg,
253 254 255
				sizeof(input)))
			return -EFAULT;

256 257 258 259
		err = mnt_want_write(filp->f_path.mnt);
		if (err)
			return err;

260
		err = ext4_group_add(sb, &input);
261 262 263
		jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
		jbd2_journal_flush(EXT4_SB(sb)->s_journal);
		jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);
264
		mnt_drop_write(filp->f_path.mnt);
265 266 267 268

		return err;
	}

269
	case EXT4_IOC_MIGRATE:
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
	{
		int err;
		if (!is_owner_or_cap(inode))
			return -EACCES;

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

291 292 293 294 295 296
	default:
		return -ENOTTY;
	}
}

#ifdef CONFIG_COMPAT
297
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
298 299 300
{
	/* These are just misnamed, they actually get/put from/to user an int */
	switch (cmd) {
301 302
	case EXT4_IOC32_GETFLAGS:
		cmd = EXT4_IOC_GETFLAGS;
303
		break;
304 305
	case EXT4_IOC32_SETFLAGS:
		cmd = EXT4_IOC_SETFLAGS;
306
		break;
307 308
	case EXT4_IOC32_GETVERSION:
		cmd = EXT4_IOC_GETVERSION;
309
		break;
310 311
	case EXT4_IOC32_SETVERSION:
		cmd = EXT4_IOC_SETVERSION;
312
		break;
313 314
	case EXT4_IOC32_GROUP_EXTEND:
		cmd = EXT4_IOC_GROUP_EXTEND;
315
		break;
316 317
	case EXT4_IOC32_GETVERSION_OLD:
		cmd = EXT4_IOC_GETVERSION_OLD;
318
		break;
319 320
	case EXT4_IOC32_SETVERSION_OLD:
		cmd = EXT4_IOC_SETVERSION_OLD;
321
		break;
322
#ifdef CONFIG_JBD2_DEBUG
323 324
	case EXT4_IOC32_WAIT_FOR_READONLY:
		cmd = EXT4_IOC_WAIT_FOR_READONLY;
325 326
		break;
#endif
327 328
	case EXT4_IOC32_GETRSVSZ:
		cmd = EXT4_IOC_GETRSVSZ;
329
		break;
330 331
	case EXT4_IOC32_SETRSVSZ:
		cmd = EXT4_IOC_SETRSVSZ;
332
		break;
333
	case EXT4_IOC_GROUP_ADD:
334 335 336 337
		break;
	default:
		return -ENOIOCTLCMD;
	}
A
Andi Kleen 已提交
338
	return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
339 340
}
#endif