1/*
2 * linux/fs/ext3/ioctl.c
3 *
4 * Copyright (C) 1993, 1994, 1995
5 * Remy Card (card@masi.ibp.fr)
6 * Laboratoire MASI - Institut Blaise Pascal
7 * Universite Pierre et Marie Curie (Paris VI)
8 */
9
10#include <linux/mount.h>
11#include <linux/compat.h>
12#include <asm/uaccess.h>
13#include "ext3.h"
14
15long ext3_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
16{
17	struct inode *inode = file_inode(filp);
18	struct ext3_inode_info *ei = EXT3_I(inode);
19	unsigned int flags;
20	unsigned short rsv_window_size;
21
22	ext3_debug ("cmd = %u, arg = %lu\n", cmd, arg);
23
24	switch (cmd) {
25	case EXT3_IOC_GETFLAGS:
26		ext3_get_inode_flags(ei);
27		flags = ei->i_flags & EXT3_FL_USER_VISIBLE;
28		return put_user(flags, (int __user *) arg);
29	case EXT3_IOC_SETFLAGS: {
30		handle_t *handle = NULL;
31		int err;
32		struct ext3_iloc iloc;
33		unsigned int oldflags;
34		unsigned int jflag;
35
36		if (!inode_owner_or_capable(inode))
37			return -EACCES;
38
39		if (get_user(flags, (int __user *) arg))
40			return -EFAULT;
41
42		err = mnt_want_write_file(filp);
43		if (err)
44			return err;
45
46		flags = ext3_mask_flags(inode->i_mode, flags);
47
48		mutex_lock(&inode->i_mutex);
49
50		/* Is it quota file? Do not allow user to mess with it */
51		err = -EPERM;
52		if (IS_NOQUOTA(inode))
53			goto flags_out;
54
55		oldflags = ei->i_flags;
56
57		/* The JOURNAL_DATA flag is modifiable only by root */
58		jflag = flags & EXT3_JOURNAL_DATA_FL;
59
60		/*
61		 * The IMMUTABLE and APPEND_ONLY flags can only be changed by
62		 * the relevant capability.
63		 *
64		 * This test looks nicer. Thanks to Pauline Middelink
65		 */
66		if ((flags ^ oldflags) & (EXT3_APPEND_FL | EXT3_IMMUTABLE_FL)) {
67			if (!capable(CAP_LINUX_IMMUTABLE))
68				goto flags_out;
69		}
70
71		/*
72		 * The JOURNAL_DATA flag can only be changed by
73		 * the relevant capability.
74		 */
75		if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL)) {
76			if (!capable(CAP_SYS_RESOURCE))
77				goto flags_out;
78		}
79
80		handle = ext3_journal_start(inode, 1);
81		if (IS_ERR(handle)) {
82			err = PTR_ERR(handle);
83			goto flags_out;
84		}
85		if (IS_SYNC(inode))
86			handle->h_sync = 1;
87		err = ext3_reserve_inode_write(handle, inode, &iloc);
88		if (err)
89			goto flags_err;
90
91		flags = flags & EXT3_FL_USER_MODIFIABLE;
92		flags |= oldflags & ~EXT3_FL_USER_MODIFIABLE;
93		ei->i_flags = flags;
94
95		ext3_set_inode_flags(inode);
96		inode->i_ctime = CURRENT_TIME_SEC;
97
98		err = ext3_mark_iloc_dirty(handle, inode, &iloc);
99flags_err:
100		ext3_journal_stop(handle);
101		if (err)
102			goto flags_out;
103
104		if ((jflag ^ oldflags) & (EXT3_JOURNAL_DATA_FL))
105			err = ext3_change_inode_journal_flag(inode, jflag);
106flags_out:
107		mutex_unlock(&inode->i_mutex);
108		mnt_drop_write_file(filp);
109		return err;
110	}
111	case EXT3_IOC_GETVERSION:
112	case EXT3_IOC_GETVERSION_OLD:
113		return put_user(inode->i_generation, (int __user *) arg);
114	case EXT3_IOC_SETVERSION:
115	case EXT3_IOC_SETVERSION_OLD: {
116		handle_t *handle;
117		struct ext3_iloc iloc;
118		__u32 generation;
119		int err;
120
121		if (!inode_owner_or_capable(inode))
122			return -EPERM;
123
124		err = mnt_want_write_file(filp);
125		if (err)
126			return err;
127		if (get_user(generation, (int __user *) arg)) {
128			err = -EFAULT;
129			goto setversion_out;
130		}
131
132		mutex_lock(&inode->i_mutex);
133		handle = ext3_journal_start(inode, 1);
134		if (IS_ERR(handle)) {
135			err = PTR_ERR(handle);
136			goto unlock_out;
137		}
138		err = ext3_reserve_inode_write(handle, inode, &iloc);
139		if (err == 0) {
140			inode->i_ctime = CURRENT_TIME_SEC;
141			inode->i_generation = generation;
142			err = ext3_mark_iloc_dirty(handle, inode, &iloc);
143		}
144		ext3_journal_stop(handle);
145
146unlock_out:
147		mutex_unlock(&inode->i_mutex);
148setversion_out:
149		mnt_drop_write_file(filp);
150		return err;
151	}
152	case EXT3_IOC_GETRSVSZ:
153		if (test_opt(inode->i_sb, RESERVATION)
154			&& S_ISREG(inode->i_mode)
155			&& ei->i_block_alloc_info) {
156			rsv_window_size = ei->i_block_alloc_info->rsv_window_node.rsv_goal_size;
157			return put_user(rsv_window_size, (int __user *)arg);
158		}
159		return -ENOTTY;
160	case EXT3_IOC_SETRSVSZ: {
161		int err;
162
163		if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode))
164			return -ENOTTY;
165
166		err = mnt_want_write_file(filp);
167		if (err)
168			return err;
169
170		if (!inode_owner_or_capable(inode)) {
171			err = -EACCES;
172			goto setrsvsz_out;
173		}
174
175		if (get_user(rsv_window_size, (int __user *)arg)) {
176			err = -EFAULT;
177			goto setrsvsz_out;
178		}
179
180		if (rsv_window_size > EXT3_MAX_RESERVE_BLOCKS)
181			rsv_window_size = EXT3_MAX_RESERVE_BLOCKS;
182
183		/*
184		 * need to allocate reservation structure for this inode
185		 * before set the window size
186		 */
187		mutex_lock(&ei->truncate_mutex);
188		if (!ei->i_block_alloc_info)
189			ext3_init_block_alloc_info(inode);
190
191		if (ei->i_block_alloc_info){
192			struct ext3_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node;
193			rsv->rsv_goal_size = rsv_window_size;
194		}
195		mutex_unlock(&ei->truncate_mutex);
196setrsvsz_out:
197		mnt_drop_write_file(filp);
198		return err;
199	}
200	case EXT3_IOC_GROUP_EXTEND: {
201		ext3_fsblk_t n_blocks_count;
202		struct super_block *sb = inode->i_sb;
203		int err, err2;
204
205		if (!capable(CAP_SYS_RESOURCE))
206			return -EPERM;
207
208		err = mnt_want_write_file(filp);
209		if (err)
210			return err;
211
212		if (get_user(n_blocks_count, (__u32 __user *)arg)) {
213			err = -EFAULT;
214			goto group_extend_out;
215		}
216		err = ext3_group_extend(sb, EXT3_SB(sb)->s_es, n_blocks_count);
217		journal_lock_updates(EXT3_SB(sb)->s_journal);
218		err2 = journal_flush(EXT3_SB(sb)->s_journal);
219		journal_unlock_updates(EXT3_SB(sb)->s_journal);
220		if (err == 0)
221			err = err2;
222group_extend_out:
223		mnt_drop_write_file(filp);
224		return err;
225	}
226	case EXT3_IOC_GROUP_ADD: {
227		struct ext3_new_group_data input;
228		struct super_block *sb = inode->i_sb;
229		int err, err2;
230
231		if (!capable(CAP_SYS_RESOURCE))
232			return -EPERM;
233
234		err = mnt_want_write_file(filp);
235		if (err)
236			return err;
237
238		if (copy_from_user(&input, (struct ext3_new_group_input __user *)arg,
239				sizeof(input))) {
240			err = -EFAULT;
241			goto group_add_out;
242		}
243
244		err = ext3_group_add(sb, &input);
245		journal_lock_updates(EXT3_SB(sb)->s_journal);
246		err2 = journal_flush(EXT3_SB(sb)->s_journal);
247		journal_unlock_updates(EXT3_SB(sb)->s_journal);
248		if (err == 0)
249			err = err2;
250group_add_out:
251		mnt_drop_write_file(filp);
252		return err;
253	}
254	case FITRIM: {
255
256		struct super_block *sb = inode->i_sb;
257		struct fstrim_range range;
258		int ret = 0;
259
260		if (!capable(CAP_SYS_ADMIN))
261			return -EPERM;
262
263		if (copy_from_user(&range, (struct fstrim_range __user *)arg,
264				   sizeof(range)))
265			return -EFAULT;
266
267		ret = ext3_trim_fs(sb, &range);
268		if (ret < 0)
269			return ret;
270
271		if (copy_to_user((struct fstrim_range __user *)arg, &range,
272				 sizeof(range)))
273			return -EFAULT;
274
275		return 0;
276	}
277
278	default:
279		return -ENOTTY;
280	}
281}
282
283#ifdef CONFIG_COMPAT
284long ext3_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
285{
286	/* These are just misnamed, they actually get/put from/to user an int */
287	switch (cmd) {
288	case EXT3_IOC32_GETFLAGS:
289		cmd = EXT3_IOC_GETFLAGS;
290		break;
291	case EXT3_IOC32_SETFLAGS:
292		cmd = EXT3_IOC_SETFLAGS;
293		break;
294	case EXT3_IOC32_GETVERSION:
295		cmd = EXT3_IOC_GETVERSION;
296		break;
297	case EXT3_IOC32_SETVERSION:
298		cmd = EXT3_IOC_SETVERSION;
299		break;
300	case EXT3_IOC32_GROUP_EXTEND:
301		cmd = EXT3_IOC_GROUP_EXTEND;
302		break;
303	case EXT3_IOC32_GETVERSION_OLD:
304		cmd = EXT3_IOC_GETVERSION_OLD;
305		break;
306	case EXT3_IOC32_SETVERSION_OLD:
307		cmd = EXT3_IOC_SETVERSION_OLD;
308		break;
309#ifdef CONFIG_JBD_DEBUG
310	case EXT3_IOC32_WAIT_FOR_READONLY:
311		cmd = EXT3_IOC_WAIT_FOR_READONLY;
312		break;
313#endif
314	case EXT3_IOC32_GETRSVSZ:
315		cmd = EXT3_IOC_GETRSVSZ;
316		break;
317	case EXT3_IOC32_SETRSVSZ:
318		cmd = EXT3_IOC_SETRSVSZ;
319		break;
320	case EXT3_IOC_GROUP_ADD:
321		break;
322	default:
323		return -ENOIOCTLCMD;
324	}
325	return ext3_ioctl(file, cmd, (unsigned long) compat_ptr(arg));
326}
327#endif
328