Blame view
fs/ext4/ioctl.c
7.27 KB
ac27a0ec1
|
1 |
/* |
617ba13b3
|
2 |
* linux/fs/ext4/ioctl.c |
ac27a0ec1
|
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> |
dab291af8
|
11 |
#include <linux/jbd2.h> |
ac27a0ec1
|
12 |
#include <linux/capability.h> |
617ba13b3
|
13 |
#include <linux/ext4_fs.h> |
dab291af8
|
14 |
#include <linux/ext4_jbd2.h> |
ac27a0ec1
|
15 16 17 18 |
#include <linux/time.h> #include <linux/compat.h> #include <linux/smp_lock.h> #include <asm/uaccess.h> |
617ba13b3
|
19 |
int ext4_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, |
ac27a0ec1
|
20 21 |
unsigned long arg) { |
617ba13b3
|
22 |
struct ext4_inode_info *ei = EXT4_I(inode); |
ac27a0ec1
|
23 24 |
unsigned int flags; unsigned short rsv_window_size; |
617ba13b3
|
25 26 |
ext4_debug ("cmd = %u, arg = %lu ", cmd, arg); |
ac27a0ec1
|
27 28 |
switch (cmd) { |
617ba13b3
|
29 |
case EXT4_IOC_GETFLAGS: |
ff9ddf7e8
|
30 |
ext4_get_inode_flags(ei); |
617ba13b3
|
31 |
flags = ei->i_flags & EXT4_FL_USER_VISIBLE; |
ac27a0ec1
|
32 |
return put_user(flags, (int __user *) arg); |
617ba13b3
|
33 |
case EXT4_IOC_SETFLAGS: { |
ac27a0ec1
|
34 35 |
handle_t *handle = NULL; int err; |
617ba13b3
|
36 |
struct ext4_iloc iloc; |
ac27a0ec1
|
37 38 39 40 41 |
unsigned int oldflags; unsigned int jflag; if (IS_RDONLY(inode)) return -EROFS; |
3bd858ab1
|
42 |
if (!is_owner_or_cap(inode)) |
ac27a0ec1
|
43 44 45 46 47 48 |
return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; if (!S_ISDIR(inode->i_mode)) |
617ba13b3
|
49 |
flags &= ~EXT4_DIRSYNC_FL; |
ac27a0ec1
|
50 51 52 53 54 |
mutex_lock(&inode->i_mutex); oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ |
617ba13b3
|
55 |
jflag = flags & EXT4_JOURNAL_DATA_FL; |
ac27a0ec1
|
56 57 58 59 60 61 62 |
/* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ |
617ba13b3
|
63 |
if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { |
ac27a0ec1
|
64 65 66 67 68 69 70 71 72 73 |
if (!capable(CAP_LINUX_IMMUTABLE)) { mutex_unlock(&inode->i_mutex); return -EPERM; } } /* * The JOURNAL_DATA flag can only be changed by * the relevant capability. */ |
617ba13b3
|
74 |
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { |
ac27a0ec1
|
75 76 77 78 79 |
if (!capable(CAP_SYS_RESOURCE)) { mutex_unlock(&inode->i_mutex); return -EPERM; } } |
617ba13b3
|
80 |
handle = ext4_journal_start(inode, 1); |
ac27a0ec1
|
81 82 83 84 85 86 |
if (IS_ERR(handle)) { mutex_unlock(&inode->i_mutex); return PTR_ERR(handle); } if (IS_SYNC(inode)) handle->h_sync = 1; |
617ba13b3
|
87 |
err = ext4_reserve_inode_write(handle, inode, &iloc); |
ac27a0ec1
|
88 89 |
if (err) goto flags_err; |
617ba13b3
|
90 91 |
flags = flags & EXT4_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE; |
ac27a0ec1
|
92 |
ei->i_flags = flags; |
617ba13b3
|
93 |
ext4_set_inode_flags(inode); |
ef7f38359
|
94 |
inode->i_ctime = ext4_current_time(inode); |
ac27a0ec1
|
95 |
|
617ba13b3
|
96 |
err = ext4_mark_iloc_dirty(handle, inode, &iloc); |
ac27a0ec1
|
97 |
flags_err: |
617ba13b3
|
98 |
ext4_journal_stop(handle); |
ac27a0ec1
|
99 100 101 102 |
if (err) { mutex_unlock(&inode->i_mutex); return err; } |
617ba13b3
|
103 104 |
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) err = ext4_change_inode_journal_flag(inode, jflag); |
ac27a0ec1
|
105 106 107 |
mutex_unlock(&inode->i_mutex); return err; } |
617ba13b3
|
108 109 |
case EXT4_IOC_GETVERSION: case EXT4_IOC_GETVERSION_OLD: |
ac27a0ec1
|
110 |
return put_user(inode->i_generation, (int __user *) arg); |
617ba13b3
|
111 112 |
case EXT4_IOC_SETVERSION: case EXT4_IOC_SETVERSION_OLD: { |
ac27a0ec1
|
113 |
handle_t *handle; |
617ba13b3
|
114 |
struct ext4_iloc iloc; |
ac27a0ec1
|
115 116 |
__u32 generation; int err; |
3bd858ab1
|
117 |
if (!is_owner_or_cap(inode)) |
ac27a0ec1
|
118 119 120 121 122 |
return -EPERM; if (IS_RDONLY(inode)) return -EROFS; if (get_user(generation, (int __user *) arg)) return -EFAULT; |
617ba13b3
|
123 |
handle = ext4_journal_start(inode, 1); |
ac27a0ec1
|
124 125 |
if (IS_ERR(handle)) return PTR_ERR(handle); |
617ba13b3
|
126 |
err = ext4_reserve_inode_write(handle, inode, &iloc); |
ac27a0ec1
|
127 |
if (err == 0) { |
ef7f38359
|
128 |
inode->i_ctime = ext4_current_time(inode); |
ac27a0ec1
|
129 |
inode->i_generation = generation; |
617ba13b3
|
130 |
err = ext4_mark_iloc_dirty(handle, inode, &iloc); |
ac27a0ec1
|
131 |
} |
617ba13b3
|
132 |
ext4_journal_stop(handle); |
ac27a0ec1
|
133 134 |
return err; } |
e23291b91
|
135 |
#ifdef CONFIG_JBD2_DEBUG |
617ba13b3
|
136 |
case EXT4_IOC_WAIT_FOR_READONLY: |
ac27a0ec1
|
137 138 139 140 141 142 143 144 145 146 147 148 149 |
/* * 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); |
617ba13b3
|
150 151 |
add_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); if (timer_pending(&EXT4_SB(sb)->turn_ro_timer)) { |
ac27a0ec1
|
152 153 154 |
schedule(); ret = 1; } |
617ba13b3
|
155 |
remove_wait_queue(&EXT4_SB(sb)->ro_wait_queue, &wait); |
ac27a0ec1
|
156 157 158 |
return ret; } #endif |
617ba13b3
|
159 |
case EXT4_IOC_GETRSVSZ: |
ac27a0ec1
|
160 161 162 163 164 165 166 |
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; |
617ba13b3
|
167 |
case EXT4_IOC_SETRSVSZ: { |
ac27a0ec1
|
168 169 170 171 172 173 |
if (!test_opt(inode->i_sb, RESERVATION) ||!S_ISREG(inode->i_mode)) return -ENOTTY; if (IS_RDONLY(inode)) return -EROFS; |
3bd858ab1
|
174 |
if (!is_owner_or_cap(inode)) |
ac27a0ec1
|
175 176 177 178 |
return -EACCES; if (get_user(rsv_window_size, (int __user *)arg)) return -EFAULT; |
617ba13b3
|
179 180 |
if (rsv_window_size > EXT4_MAX_RESERVE_BLOCKS) rsv_window_size = EXT4_MAX_RESERVE_BLOCKS; |
ac27a0ec1
|
181 182 183 184 185 186 187 |
/* * need to allocate reservation structure for this inode * before set the window size */ mutex_lock(&ei->truncate_mutex); if (!ei->i_block_alloc_info) |
617ba13b3
|
188 |
ext4_init_block_alloc_info(inode); |
ac27a0ec1
|
189 190 |
if (ei->i_block_alloc_info){ |
617ba13b3
|
191 |
struct ext4_reserve_window_node *rsv = &ei->i_block_alloc_info->rsv_window_node; |
ac27a0ec1
|
192 193 194 195 196 |
rsv->rsv_goal_size = rsv_window_size; } mutex_unlock(&ei->truncate_mutex); return 0; } |
617ba13b3
|
197 198 |
case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; |
ac27a0ec1
|
199 200 201 202 203 204 205 206 207 208 209 |
struct super_block *sb = inode->i_sb; int err; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; if (get_user(n_blocks_count, (__u32 __user *)arg)) return -EFAULT; |
617ba13b3
|
210 |
err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); |
dab291af8
|
211 212 213 |
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); |
ac27a0ec1
|
214 215 216 |
return err; } |
617ba13b3
|
217 218 |
case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; |
ac27a0ec1
|
219 220 221 222 223 224 225 226 |
struct super_block *sb = inode->i_sb; int err; if (!capable(CAP_SYS_RESOURCE)) return -EPERM; if (IS_RDONLY(inode)) return -EROFS; |
617ba13b3
|
227 |
if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, |
ac27a0ec1
|
228 229 |
sizeof(input))) return -EFAULT; |
617ba13b3
|
230 |
err = ext4_group_add(sb, &input); |
dab291af8
|
231 232 233 |
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); |
ac27a0ec1
|
234 235 236 |
return err; } |
ac27a0ec1
|
237 238 239 240 241 242 |
default: return -ENOTTY; } } #ifdef CONFIG_COMPAT |
617ba13b3
|
243 |
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
ac27a0ec1
|
244 |
{ |
9d549890e
|
245 |
struct inode *inode = file->f_path.dentry->d_inode; |
ac27a0ec1
|
246 247 248 249 |
int ret; /* These are just misnamed, they actually get/put from/to user an int */ switch (cmd) { |
617ba13b3
|
250 251 |
case EXT4_IOC32_GETFLAGS: cmd = EXT4_IOC_GETFLAGS; |
ac27a0ec1
|
252 |
break; |
617ba13b3
|
253 254 |
case EXT4_IOC32_SETFLAGS: cmd = EXT4_IOC_SETFLAGS; |
ac27a0ec1
|
255 |
break; |
617ba13b3
|
256 257 |
case EXT4_IOC32_GETVERSION: cmd = EXT4_IOC_GETVERSION; |
ac27a0ec1
|
258 |
break; |
617ba13b3
|
259 260 |
case EXT4_IOC32_SETVERSION: cmd = EXT4_IOC_SETVERSION; |
ac27a0ec1
|
261 |
break; |
617ba13b3
|
262 263 |
case EXT4_IOC32_GROUP_EXTEND: cmd = EXT4_IOC_GROUP_EXTEND; |
ac27a0ec1
|
264 |
break; |
617ba13b3
|
265 266 |
case EXT4_IOC32_GETVERSION_OLD: cmd = EXT4_IOC_GETVERSION_OLD; |
ac27a0ec1
|
267 |
break; |
617ba13b3
|
268 269 |
case EXT4_IOC32_SETVERSION_OLD: cmd = EXT4_IOC_SETVERSION_OLD; |
ac27a0ec1
|
270 |
break; |
e23291b91
|
271 |
#ifdef CONFIG_JBD2_DEBUG |
617ba13b3
|
272 273 |
case EXT4_IOC32_WAIT_FOR_READONLY: cmd = EXT4_IOC_WAIT_FOR_READONLY; |
ac27a0ec1
|
274 275 |
break; #endif |
617ba13b3
|
276 277 |
case EXT4_IOC32_GETRSVSZ: cmd = EXT4_IOC_GETRSVSZ; |
ac27a0ec1
|
278 |
break; |
617ba13b3
|
279 280 |
case EXT4_IOC32_SETRSVSZ: cmd = EXT4_IOC_SETRSVSZ; |
ac27a0ec1
|
281 |
break; |
617ba13b3
|
282 |
case EXT4_IOC_GROUP_ADD: |
ac27a0ec1
|
283 284 285 286 287 |
break; default: return -ENOIOCTLCMD; } lock_kernel(); |
617ba13b3
|
288 |
ret = ext4_ioctl(inode, file, cmd, (unsigned long) compat_ptr(arg)); |
ac27a0ec1
|
289 290 291 292 |
unlock_kernel(); return ret; } #endif |