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