Blame view
fs/ext4/ioctl.c
10.1 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> |
ac27a0ec1
|
13 14 |
#include <linux/time.h> #include <linux/compat.h> |
42a74f206
|
15 |
#include <linux/mount.h> |
748de6736
|
16 |
#include <linux/file.h> |
ac27a0ec1
|
17 |
#include <asm/uaccess.h> |
3dcf54515
|
18 19 |
#include "ext4_jbd2.h" #include "ext4.h" |
ac27a0ec1
|
20 |
|
5cdd7b2d7
|
21 |
long ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
ac27a0ec1
|
22 |
{ |
5cdd7b2d7
|
23 |
struct inode *inode = filp->f_dentry->d_inode; |
bab08ab96
|
24 |
struct super_block *sb = inode->i_sb; |
617ba13b3
|
25 |
struct ext4_inode_info *ei = EXT4_I(inode); |
ac27a0ec1
|
26 |
unsigned int flags; |
ac27a0ec1
|
27 |
|
af5bc92dd
|
28 29 |
ext4_debug("cmd = %u, arg = %lu ", cmd, arg); |
ac27a0ec1
|
30 31 |
switch (cmd) { |
617ba13b3
|
32 |
case EXT4_IOC_GETFLAGS: |
ff9ddf7e8
|
33 |
ext4_get_inode_flags(ei); |
617ba13b3
|
34 |
flags = ei->i_flags & EXT4_FL_USER_VISIBLE; |
ac27a0ec1
|
35 |
return put_user(flags, (int __user *) arg); |
617ba13b3
|
36 |
case EXT4_IOC_SETFLAGS: { |
ac27a0ec1
|
37 |
handle_t *handle = NULL; |
4db46fc26
|
38 |
int err, migrate = 0; |
617ba13b3
|
39 |
struct ext4_iloc iloc; |
ac27a0ec1
|
40 41 |
unsigned int oldflags; unsigned int jflag; |
2e1496707
|
42 |
if (!inode_owner_or_capable(inode)) |
ac27a0ec1
|
43 44 45 46 |
return -EACCES; if (get_user(flags, (int __user *) arg)) return -EFAULT; |
42a74f206
|
47 48 49 |
err = mnt_want_write(filp->f_path.mnt); if (err) return err; |
2dc6b0d48
|
50 |
flags = ext4_mask_flags(inode->i_mode, flags); |
ac27a0ec1
|
51 |
|
42a74f206
|
52 |
err = -EPERM; |
ac27a0ec1
|
53 |
mutex_lock(&inode->i_mutex); |
e47776a0a
|
54 |
/* Is it quota file? Do not allow user to mess with it */ |
42a74f206
|
55 56 |
if (IS_NOQUOTA(inode)) goto flags_out; |
ac27a0ec1
|
57 58 59 |
oldflags = ei->i_flags; /* The JOURNAL_DATA flag is modifiable only by root */ |
617ba13b3
|
60 |
jflag = flags & EXT4_JOURNAL_DATA_FL; |
ac27a0ec1
|
61 62 63 64 65 66 67 |
/* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. * * This test looks nicer. Thanks to Pauline Middelink */ |
617ba13b3
|
68 |
if ((flags ^ oldflags) & (EXT4_APPEND_FL | EXT4_IMMUTABLE_FL)) { |
42a74f206
|
69 70 |
if (!capable(CAP_LINUX_IMMUTABLE)) goto flags_out; |
ac27a0ec1
|
71 72 73 74 75 76 |
} /* * The JOURNAL_DATA flag can only be changed by * the relevant capability. */ |
617ba13b3
|
77 |
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) { |
42a74f206
|
78 79 |
if (!capable(CAP_SYS_RESOURCE)) goto flags_out; |
ac27a0ec1
|
80 |
} |
4db46fc26
|
81 82 83 84 85 86 87 88 89 90 91 |
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; } |
ac27a0ec1
|
92 |
|
c8d46e41b
|
93 94 95 96 97 98 99 100 |
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); |
617ba13b3
|
101 |
handle = ext4_journal_start(inode, 1); |
ac27a0ec1
|
102 |
if (IS_ERR(handle)) { |
42a74f206
|
103 104 |
err = PTR_ERR(handle); goto flags_out; |
ac27a0ec1
|
105 106 |
} if (IS_SYNC(inode)) |
0390131ba
|
107 |
ext4_handle_sync(handle); |
617ba13b3
|
108 |
err = ext4_reserve_inode_write(handle, inode, &iloc); |
ac27a0ec1
|
109 110 |
if (err) goto flags_err; |
617ba13b3
|
111 112 |
flags = flags & EXT4_FL_USER_MODIFIABLE; flags |= oldflags & ~EXT4_FL_USER_MODIFIABLE; |
ac27a0ec1
|
113 |
ei->i_flags = flags; |
617ba13b3
|
114 |
ext4_set_inode_flags(inode); |
ef7f38359
|
115 |
inode->i_ctime = ext4_current_time(inode); |
ac27a0ec1
|
116 |
|
617ba13b3
|
117 |
err = ext4_mark_iloc_dirty(handle, inode, &iloc); |
ac27a0ec1
|
118 |
flags_err: |
617ba13b3
|
119 |
ext4_journal_stop(handle); |
42a74f206
|
120 121 |
if (err) goto flags_out; |
ac27a0ec1
|
122 |
|
617ba13b3
|
123 124 |
if ((jflag ^ oldflags) & (EXT4_JOURNAL_DATA_FL)) err = ext4_change_inode_journal_flag(inode, jflag); |
4db46fc26
|
125 126 127 128 |
if (err) goto flags_out; if (migrate) err = ext4_ext_migrate(inode); |
42a74f206
|
129 |
flags_out: |
ac27a0ec1
|
130 |
mutex_unlock(&inode->i_mutex); |
42a74f206
|
131 |
mnt_drop_write(filp->f_path.mnt); |
ac27a0ec1
|
132 133 |
return err; } |
617ba13b3
|
134 135 |
case EXT4_IOC_GETVERSION: case EXT4_IOC_GETVERSION_OLD: |
ac27a0ec1
|
136 |
return put_user(inode->i_generation, (int __user *) arg); |
617ba13b3
|
137 138 |
case EXT4_IOC_SETVERSION: case EXT4_IOC_SETVERSION_OLD: { |
ac27a0ec1
|
139 |
handle_t *handle; |
617ba13b3
|
140 |
struct ext4_iloc iloc; |
ac27a0ec1
|
141 142 |
__u32 generation; int err; |
2e1496707
|
143 |
if (!inode_owner_or_capable(inode)) |
ac27a0ec1
|
144 |
return -EPERM; |
42a74f206
|
145 146 147 148 149 150 151 152 |
err = mnt_want_write(filp->f_path.mnt); if (err) return err; if (get_user(generation, (int __user *) arg)) { err = -EFAULT; goto setversion_out; } |
ac27a0ec1
|
153 |
|
617ba13b3
|
154 |
handle = ext4_journal_start(inode, 1); |
42a74f206
|
155 156 157 158 |
if (IS_ERR(handle)) { err = PTR_ERR(handle); goto setversion_out; } |
617ba13b3
|
159 |
err = ext4_reserve_inode_write(handle, inode, &iloc); |
ac27a0ec1
|
160 |
if (err == 0) { |
ef7f38359
|
161 |
inode->i_ctime = ext4_current_time(inode); |
ac27a0ec1
|
162 |
inode->i_generation = generation; |
617ba13b3
|
163 |
err = ext4_mark_iloc_dirty(handle, inode, &iloc); |
ac27a0ec1
|
164 |
} |
617ba13b3
|
165 |
ext4_journal_stop(handle); |
42a74f206
|
166 167 |
setversion_out: mnt_drop_write(filp->f_path.mnt); |
ac27a0ec1
|
168 169 |
return err; } |
617ba13b3
|
170 171 |
case EXT4_IOC_GROUP_EXTEND: { ext4_fsblk_t n_blocks_count; |
ac046f1d6
|
172 |
int err, err2=0; |
ac27a0ec1
|
173 |
|
8f82f840e
|
174 175 176 |
err = ext4_resize_begin(sb); if (err) return err; |
ac27a0ec1
|
177 |
|
ac27a0ec1
|
178 179 |
if (get_user(n_blocks_count, (__u32 __user *)arg)) return -EFAULT; |
bab08ab96
|
180 181 182 183 184 185 |
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc"); return -EOPNOTSUPP; } |
42a74f206
|
186 187 188 |
err = mnt_want_write(filp->f_path.mnt); if (err) return err; |
617ba13b3
|
189 |
err = ext4_group_extend(sb, EXT4_SB(sb)->s_es, n_blocks_count); |
ac046f1d6
|
190 191 192 193 194 |
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); } |
7ffe1ea89
|
195 196 |
if (err == 0) err = err2; |
42a74f206
|
197 |
mnt_drop_write(filp->f_path.mnt); |
8f82f840e
|
198 |
ext4_resize_end(sb); |
ac27a0ec1
|
199 200 201 |
return err; } |
748de6736
|
202 203 204 205 206 |
case EXT4_IOC_MOVE_EXT: { struct move_extent me; struct file *donor_filp; int err; |
4a58579b9
|
207 208 209 |
if (!(filp->f_mode & FMODE_READ) || !(filp->f_mode & FMODE_WRITE)) return -EBADF; |
748de6736
|
210 211 212 |
if (copy_from_user(&me, (struct move_extent __user *)arg, sizeof(me))) return -EFAULT; |
4a58579b9
|
213 |
me.moved_len = 0; |
748de6736
|
214 215 216 217 |
donor_filp = fget(me.donor_fd); if (!donor_filp) return -EBADF; |
4a58579b9
|
218 219 220 |
if (!(donor_filp->f_mode & FMODE_WRITE)) { err = -EBADF; goto mext_out; |
748de6736
|
221 |
} |
bab08ab96
|
222 223 224 225 226 227 |
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; } |
4a58579b9
|
228 229 230 |
err = mnt_want_write(filp->f_path.mnt); if (err) goto mext_out; |
748de6736
|
231 232 |
err = ext4_move_extents(filp, donor_filp, me.orig_start, me.donor_start, me.len, &me.moved_len); |
4a58579b9
|
233 234 235 |
mnt_drop_write(filp->f_path.mnt); if (me.moved_len > 0) file_remove_suid(donor_filp); |
748de6736
|
236 |
|
60e6679e2
|
237 |
if (copy_to_user((struct move_extent __user *)arg, |
c437b2733
|
238 |
&me, sizeof(me))) |
4a58579b9
|
239 240 241 |
err = -EFAULT; mext_out: fput(donor_filp); |
748de6736
|
242 243 |
return err; } |
617ba13b3
|
244 245 |
case EXT4_IOC_GROUP_ADD: { struct ext4_new_group_data input; |
ac046f1d6
|
246 |
int err, err2=0; |
ac27a0ec1
|
247 |
|
8f82f840e
|
248 249 250 |
err = ext4_resize_begin(sb); if (err) return err; |
ac27a0ec1
|
251 |
|
617ba13b3
|
252 |
if (copy_from_user(&input, (struct ext4_new_group_input __user *)arg, |
ac27a0ec1
|
253 254 |
sizeof(input))) return -EFAULT; |
bab08ab96
|
255 256 257 258 259 260 |
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc"); return -EOPNOTSUPP; } |
42a74f206
|
261 262 263 |
err = mnt_want_write(filp->f_path.mnt); if (err) return err; |
617ba13b3
|
264 |
err = ext4_group_add(sb, &input); |
ac046f1d6
|
265 266 267 268 269 |
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); } |
7ffe1ea89
|
270 271 |
if (err == 0) err = err2; |
42a74f206
|
272 |
mnt_drop_write(filp->f_path.mnt); |
8f82f840e
|
273 |
ext4_resize_end(sb); |
ac27a0ec1
|
274 275 276 |
return err; } |
c14c6fd5c
|
277 |
case EXT4_IOC_MIGRATE: |
2a43a8780
|
278 279 |
{ int err; |
2e1496707
|
280 |
if (!inode_owner_or_capable(inode)) |
2a43a8780
|
281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
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; } |
c14c6fd5c
|
298 |
|
ccd2506bd
|
299 300 301 |
case EXT4_IOC_ALLOC_DA_BLKS: { int err; |
2e1496707
|
302 |
if (!inode_owner_or_capable(inode)) |
ccd2506bd
|
303 304 305 306 307 308 309 310 311 |
return -EACCES; err = mnt_want_write(filp->f_path.mnt); if (err) return err; err = ext4_alloc_da_blocks(inode); mnt_drop_write(filp->f_path.mnt); return err; } |
e681c047e
|
312 313 |
case FITRIM: { |
414317921
|
314 |
struct request_queue *q = bdev_get_queue(sb->s_bdev); |
e681c047e
|
315 316 317 318 319 |
struct fstrim_range range; int ret = 0; if (!capable(CAP_SYS_ADMIN)) return -EPERM; |
414317921
|
320 321 |
if (!blk_queue_discard(q)) return -EOPNOTSUPP; |
bab08ab96
|
322 323 324 325 326 327 |
if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC)) { ext4_msg(sb, KERN_ERR, "FITRIM not supported with bigalloc"); return -EOPNOTSUPP; } |
e6705f7c2
|
328 |
if (copy_from_user(&range, (struct fstrim_range __user *)arg, |
e681c047e
|
329 330 |
sizeof(range))) return -EFAULT; |
5c2ed62fd
|
331 332 |
range.minlen = max((unsigned int)range.minlen, q->limits.discard_granularity); |
e681c047e
|
333 334 335 |
ret = ext4_trim_fs(sb, &range); if (ret < 0) return ret; |
e6705f7c2
|
336 |
if (copy_to_user((struct fstrim_range __user *)arg, &range, |
e681c047e
|
337 338 339 340 341 |
sizeof(range))) return -EFAULT; return 0; } |
ac27a0ec1
|
342 343 344 345 346 347 |
default: return -ENOTTY; } } #ifdef CONFIG_COMPAT |
617ba13b3
|
348 |
long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
ac27a0ec1
|
349 |
{ |
ac27a0ec1
|
350 351 |
/* These are just misnamed, they actually get/put from/to user an int */ switch (cmd) { |
617ba13b3
|
352 353 |
case EXT4_IOC32_GETFLAGS: cmd = EXT4_IOC_GETFLAGS; |
ac27a0ec1
|
354 |
break; |
617ba13b3
|
355 356 |
case EXT4_IOC32_SETFLAGS: cmd = EXT4_IOC_SETFLAGS; |
ac27a0ec1
|
357 |
break; |
617ba13b3
|
358 359 |
case EXT4_IOC32_GETVERSION: cmd = EXT4_IOC_GETVERSION; |
ac27a0ec1
|
360 |
break; |
617ba13b3
|
361 362 |
case EXT4_IOC32_SETVERSION: cmd = EXT4_IOC_SETVERSION; |
ac27a0ec1
|
363 |
break; |
617ba13b3
|
364 365 |
case EXT4_IOC32_GROUP_EXTEND: cmd = EXT4_IOC_GROUP_EXTEND; |
ac27a0ec1
|
366 |
break; |
617ba13b3
|
367 368 |
case EXT4_IOC32_GETVERSION_OLD: cmd = EXT4_IOC_GETVERSION_OLD; |
ac27a0ec1
|
369 |
break; |
617ba13b3
|
370 371 |
case EXT4_IOC32_SETVERSION_OLD: cmd = EXT4_IOC_SETVERSION_OLD; |
ac27a0ec1
|
372 |
break; |
617ba13b3
|
373 374 |
case EXT4_IOC32_GETRSVSZ: cmd = EXT4_IOC_GETRSVSZ; |
ac27a0ec1
|
375 |
break; |
617ba13b3
|
376 377 |
case EXT4_IOC32_SETRSVSZ: cmd = EXT4_IOC_SETRSVSZ; |
ac27a0ec1
|
378 |
break; |
4d92dc0f0
|
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
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; } |
b684b2ee9
|
402 |
case EXT4_IOC_MOVE_EXT: |
a56e69c28
|
403 |
case FITRIM: |
b684b2ee9
|
404 |
break; |
ac27a0ec1
|
405 406 407 |
default: return -ENOIOCTLCMD; } |
5cdd7b2d7
|
408 |
return ext4_ioctl(file, cmd, (unsigned long) compat_ptr(arg)); |
ac27a0ec1
|
409 410 |
} #endif |