Blame view
fs/ioctl.c
24 KB
b24413180 License cleanup: ... |
1 |
// SPDX-License-Identifier: GPL-2.0 |
1da177e4c Linux-2.6.12-rc2 |
2 3 4 5 6 |
/* * linux/fs/ioctl.c * * Copyright (C) 1991, 1992 Linus Torvalds */ |
1da177e4c Linux-2.6.12-rc2 |
7 8 |
#include <linux/syscalls.h> #include <linux/mm.h> |
16f7e0fe2 [PATCH] capable/c... |
9 |
#include <linux/capability.h> |
2952db0fd compat_ioctl: add... |
10 |
#include <linux/compat.h> |
1da177e4c Linux-2.6.12-rc2 |
11 12 13 |
#include <linux/file.h> #include <linux/fs.h> #include <linux/security.h> |
630d9c472 fs: reduce the us... |
14 |
#include <linux/export.h> |
c9845ff1d VFS: apply coding... |
15 |
#include <linux/uaccess.h> |
68c9d702b generic block bas... |
16 17 |
#include <linux/writeback.h> #include <linux/buffer_head.h> |
3e63cbb1e fs: Add new pre-a... |
18 |
#include <linux/falloc.h> |
f361bf4a6 sched/headers: Pr... |
19 |
#include <linux/sched/signal.h> |
10c5db286 fs: move the fiem... |
20 |
#include <linux/fiemap.h> |
4c5b47997 vfs: add fileattr... |
21 22 23 |
#include <linux/mount.h> #include <linux/fscrypt.h> #include <linux/fileattr.h> |
f361bf4a6 sched/headers: Pr... |
24 |
|
66cf191f3 compat_ioctl: don... |
25 |
#include "internal.h" |
1da177e4c Linux-2.6.12-rc2 |
26 |
|
1da177e4c Linux-2.6.12-rc2 |
27 |
#include <asm/ioctls.h> |
c4b929b85 vfs: vfs-level fi... |
28 29 |
/* So that the fiemap access checks can't overflow on 32 bit machines. */ #define FIEMAP_MAX_EXTENTS (UINT_MAX / sizeof(struct fiemap_extent)) |
deb21db77 VFS: swap do_ioct... |
30 31 |
/** * vfs_ioctl - call filesystem specific ioctl methods |
f6a4c8bdb fix up kerneldoc ... |
32 33 34 |
* @filp: open file to invoke ioctl method on * @cmd: ioctl command to execute * @arg: command-specific argument for ioctl |
deb21db77 VFS: swap do_ioct... |
35 36 |
* * Invokes filesystem specific ->unlocked_ioctl, if one exists; otherwise |
deb21db77 VFS: swap do_ioct... |
37 38 39 40 |
* returns -ENOTTY. * * Returns 0 on success, -errno on error. */ |
66cf191f3 compat_ioctl: don... |
41 |
long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
42 43 |
{ int error = -ENOTTY; |
72c2d5319 file->f_op is nev... |
44 |
if (!filp->f_op->unlocked_ioctl) |
1da177e4c Linux-2.6.12-rc2 |
45 |
goto out; |
b19dd42fa bkl: Remove locke... |
46 47 |
error = filp->f_op->unlocked_ioctl(filp, cmd, arg); if (error == -ENOIOCTLCMD) |
07d106d0a vfs: fix up ENOIO... |
48 |
error = -ENOTTY; |
1da177e4c Linux-2.6.12-rc2 |
49 50 51 |
out: return error; } |
9df6702ad vfs: export vfs_i... |
52 |
EXPORT_SYMBOL(vfs_ioctl); |
1da177e4c Linux-2.6.12-rc2 |
53 |
|
aa81a7c71 VFS: factor out t... |
54 55 |
static int ioctl_fibmap(struct file *filp, int __user *p) { |
0d89fdae2 fibmap: Use bmap ... |
56 |
struct inode *inode = file_inode(filp); |
b75dfde12 fibmap: Warn and ... |
57 |
struct super_block *sb = inode->i_sb; |
0d89fdae2 fibmap: Use bmap ... |
58 59 |
int error, ur_block; sector_t block; |
aa81a7c71 VFS: factor out t... |
60 |
|
aa81a7c71 VFS: factor out t... |
61 62 |
if (!capable(CAP_SYS_RAWIO)) return -EPERM; |
0d89fdae2 fibmap: Use bmap ... |
63 64 65 66 |
error = get_user(ur_block, p); if (error) return error; |
324282c02 fibmap: Reject ne... |
67 68 |
if (ur_block < 0) return -EINVAL; |
0d89fdae2 fibmap: Use bmap ... |
69 70 |
block = ur_block; error = bmap(inode, &block); |
b75dfde12 fibmap: Warn and ... |
71 72 73 74 75 76 77 |
if (block > INT_MAX) { error = -ERANGE; pr_warn_ratelimited("[%s/%d] FS: %s File: %pD4 would truncate fibmap result ", current->comm, task_pid_nr(current), sb->s_id, filp); } |
0d89fdae2 fibmap: Use bmap ... |
78 79 80 81 82 83 84 85 86 |
if (error) ur_block = 0; else ur_block = block; if (put_user(ur_block, p)) error = -EFAULT; return error; |
aa81a7c71 VFS: factor out t... |
87 |
} |
c4b929b85 vfs: vfs-level fi... |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
/** * fiemap_fill_next_extent - Fiemap helper function * @fieinfo: Fiemap context passed into ->fiemap * @logical: Extent logical start offset, in bytes * @phys: Extent physical start offset, in bytes * @len: Extent length, in bytes * @flags: FIEMAP_EXTENT flags that describe this extent * * Called from file system ->fiemap callback. Will populate extent * info as passed in via arguments and copy to user memory. On * success, extent count on fieinfo is incremented. * * Returns 0 on success, -errno on error, 1 if this was the last * extent that will fit in user array. */ #define SET_UNKNOWN_FLAGS (FIEMAP_EXTENT_DELALLOC) #define SET_NO_UNMOUNTED_IO_FLAGS (FIEMAP_EXTENT_DATA_ENCRYPTED) #define SET_NOT_ALIGNED_FLAGS (FIEMAP_EXTENT_DATA_TAIL|FIEMAP_EXTENT_DATA_INLINE) int fiemap_fill_next_extent(struct fiemap_extent_info *fieinfo, u64 logical, u64 phys, u64 len, u32 flags) { struct fiemap_extent extent; |
ecf5632dd fs: fix address s... |
110 |
struct fiemap_extent __user *dest = fieinfo->fi_extents_start; |
c4b929b85 vfs: vfs-level fi... |
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
/* only count the extents */ if (fieinfo->fi_extents_max == 0) { fieinfo->fi_extents_mapped++; return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; } if (fieinfo->fi_extents_mapped >= fieinfo->fi_extents_max) return 1; if (flags & SET_UNKNOWN_FLAGS) flags |= FIEMAP_EXTENT_UNKNOWN; if (flags & SET_NO_UNMOUNTED_IO_FLAGS) flags |= FIEMAP_EXTENT_ENCODED; if (flags & SET_NOT_ALIGNED_FLAGS) flags |= FIEMAP_EXTENT_NOT_ALIGNED; memset(&extent, 0, sizeof(extent)); extent.fe_logical = logical; extent.fe_physical = phys; extent.fe_length = len; extent.fe_flags = flags; dest += fieinfo->fi_extents_mapped; if (copy_to_user(dest, &extent, sizeof(extent))) return -EFAULT; fieinfo->fi_extents_mapped++; if (fieinfo->fi_extents_mapped == fieinfo->fi_extents_max) return 1; return (flags & FIEMAP_EXTENT_LAST) ? 1 : 0; } EXPORT_SYMBOL(fiemap_fill_next_extent); /** |
cddf8a2c4 fs: move fiemap r... |
146 147 |
* fiemap_prep - check validity of requested flags for fiemap * @inode: Inode to operate on |
c4b929b85 vfs: vfs-level fi... |
148 |
* @fieinfo: Fiemap context passed into ->fiemap |
cddf8a2c4 fs: move fiemap r... |
149 150 151 |
* @start: Start of the mapped range * @len: Length of the mapped range, can be truncated by this function. * @supported_flags: Set of fiemap flags that the file system understands |
c4b929b85 vfs: vfs-level fi... |
152 |
* |
cddf8a2c4 fs: move fiemap r... |
153 154 |
* This function must be called from each ->fiemap instance to validate the * fiemap request against the file system parameters. |
c4b929b85 vfs: vfs-level fi... |
155 |
* |
cddf8a2c4 fs: move fiemap r... |
156 |
* Returns 0 on success, or a negative error on failure. |
c4b929b85 vfs: vfs-level fi... |
157 |
*/ |
cddf8a2c4 fs: move fiemap r... |
158 159 |
int fiemap_prep(struct inode *inode, struct fiemap_extent_info *fieinfo, u64 start, u64 *len, u32 supported_flags) |
c4b929b85 vfs: vfs-level fi... |
160 |
{ |
cddf8a2c4 fs: move fiemap r... |
161 |
u64 maxbytes = inode->i_sb->s_maxbytes; |
c4b929b85 vfs: vfs-level fi... |
162 |
u32 incompat_flags; |
45dd052e6 fs: handle FIEMAP... |
163 |
int ret = 0; |
c4b929b85 vfs: vfs-level fi... |
164 |
|
cddf8a2c4 fs: move fiemap r... |
165 |
if (*len == 0) |
c4b929b85 vfs: vfs-level fi... |
166 |
return -EINVAL; |
9a8657486 fs: fix an infini... |
167 |
if (start >= maxbytes) |
c4b929b85 vfs: vfs-level fi... |
168 169 170 171 172 |
return -EFBIG; /* * Shrink request scope to what the fs can actually handle. */ |
cddf8a2c4 fs: move fiemap r... |
173 174 |
if (*len > maxbytes || (maxbytes - *len) < start) *len = maxbytes - start; |
c4b929b85 vfs: vfs-level fi... |
175 |
|
45dd052e6 fs: handle FIEMAP... |
176 |
supported_flags |= FIEMAP_FLAG_SYNC; |
cddf8a2c4 fs: move fiemap r... |
177 178 179 180 181 182 |
supported_flags &= FIEMAP_FLAGS_COMPAT; incompat_flags = fieinfo->fi_flags & ~supported_flags; if (incompat_flags) { fieinfo->fi_flags = incompat_flags; return -EBADR; } |
45dd052e6 fs: handle FIEMAP... |
183 184 185 186 |
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC) ret = filemap_write_and_wait(inode->i_mapping); return ret; |
c4b929b85 vfs: vfs-level fi... |
187 |
} |
cddf8a2c4 fs: move fiemap r... |
188 |
EXPORT_SYMBOL(fiemap_prep); |
c4b929b85 vfs: vfs-level fi... |
189 |
|
34d3d0e65 do_vfs_ioctl(): u... |
190 |
static int ioctl_fiemap(struct file *filp, struct fiemap __user *ufiemap) |
c4b929b85 vfs: vfs-level fi... |
191 192 193 |
{ struct fiemap fiemap; struct fiemap_extent_info fieinfo = { 0, }; |
496ad9aa8 new helper: file_... |
194 |
struct inode *inode = file_inode(filp); |
c4b929b85 vfs: vfs-level fi... |
195 196 197 198 |
int error; if (!inode->i_op->fiemap) return -EOPNOTSUPP; |
ecf5632dd fs: fix address s... |
199 |
if (copy_from_user(&fiemap, ufiemap, sizeof(fiemap))) |
c4b929b85 vfs: vfs-level fi... |
200 201 202 203 |
return -EFAULT; if (fiemap.fm_extent_count > FIEMAP_MAX_EXTENTS) return -EINVAL; |
c4b929b85 vfs: vfs-level fi... |
204 205 |
fieinfo.fi_flags = fiemap.fm_flags; fieinfo.fi_extents_max = fiemap.fm_extent_count; |
ecf5632dd fs: fix address s... |
206 |
fieinfo.fi_extents_start = ufiemap->fm_extents; |
c4b929b85 vfs: vfs-level fi... |
207 |
|
cddf8a2c4 fs: move fiemap r... |
208 209 |
error = inode->i_op->fiemap(inode, &fieinfo, fiemap.fm_start, fiemap.fm_length); |
c7d216e8c fs: remove the ac... |
210 |
|
c4b929b85 vfs: vfs-level fi... |
211 212 |
fiemap.fm_flags = fieinfo.fi_flags; fiemap.fm_mapped_extents = fieinfo.fi_extents_mapped; |
ecf5632dd fs: fix address s... |
213 |
if (copy_to_user(ufiemap, &fiemap, sizeof(fiemap))) |
c4b929b85 vfs: vfs-level fi... |
214 215 216 217 |
error = -EFAULT; return error; } |
04b38d601 vfs: pull btrfs c... |
218 219 220 221 |
static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd, u64 off, u64 olen, u64 destoff) { struct fd src_file = fdget(srcfd); |
42ec3d4c0 vfs: make remap_f... |
222 |
loff_t cloned; |
04b38d601 vfs: pull btrfs c... |
223 224 225 226 |
int ret; if (!src_file.file) return -EBADF; |
913b86e92 vfs: allow vfs_cl... |
227 228 229 |
ret = -EXDEV; if (src_file.file->f_path.mnt != dst_file->f_path.mnt) goto fdput; |
42ec3d4c0 vfs: make remap_f... |
230 |
cloned = vfs_clone_file_range(src_file.file, off, dst_file, destoff, |
452ce6595 vfs: plumb remap ... |
231 |
olen, 0); |
42ec3d4c0 vfs: make remap_f... |
232 233 234 235 236 237 |
if (cloned < 0) ret = cloned; else if (olen && cloned != olen) ret = -EINVAL; else ret = 0; |
913b86e92 vfs: allow vfs_cl... |
238 |
fdput: |
04b38d601 vfs: pull btrfs c... |
239 240 241 |
fdput(src_file); return ret; } |
34d3d0e65 do_vfs_ioctl(): u... |
242 243 |
static long ioctl_file_clone_range(struct file *file, struct file_clone_range __user *argp) |
04b38d601 vfs: pull btrfs c... |
244 245 246 247 248 249 250 251 |
{ struct file_clone_range args; if (copy_from_user(&args, argp, sizeof(args))) return -EFAULT; return ioctl_file_clone(file, args.src_fd, args.src_offset, args.src_length, args.dest_offset); } |
3e63cbb1e fs: Add new pre-a... |
252 253 254 255 256 257 258 |
/* * This provides compatibility with legacy XFS pre-allocation ioctls * which predate the fallocate syscall. * * Only the l_start, l_len and l_whence fields of the 'struct space_resv' * are used here, rest are ignored. */ |
77b904019 compat_ioctl: sim... |
259 |
static int ioctl_preallocate(struct file *filp, int mode, void __user *argp) |
3e63cbb1e fs: Add new pre-a... |
260 |
{ |
496ad9aa8 new helper: file_... |
261 |
struct inode *inode = file_inode(filp); |
3e63cbb1e fs: Add new pre-a... |
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
struct space_resv sr; if (copy_from_user(&sr, argp, sizeof(sr))) return -EFAULT; switch (sr.l_whence) { case SEEK_SET: break; case SEEK_CUR: sr.l_start += filp->f_pos; break; case SEEK_END: sr.l_start += i_size_read(inode); break; default: return -EINVAL; } |
837a6e7f5 fs: add generic U... |
279 280 |
return vfs_fallocate(filp, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len); |
3e63cbb1e fs: Add new pre-a... |
281 |
} |
011da44bc compat: move FS_I... |
282 283 284 |
/* on ia32 l_start is on a 32-bit boundary */ #if defined CONFIG_COMPAT && defined(CONFIG_X86_64) /* just account for different alignment */ |
77b904019 compat_ioctl: sim... |
285 286 |
static int compat_ioctl_preallocate(struct file *file, int mode, struct space_resv_32 __user *argp) |
011da44bc compat: move FS_I... |
287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
{ struct inode *inode = file_inode(file); struct space_resv_32 sr; if (copy_from_user(&sr, argp, sizeof(sr))) return -EFAULT; switch (sr.l_whence) { case SEEK_SET: break; case SEEK_CUR: sr.l_start += file->f_pos; break; case SEEK_END: sr.l_start += i_size_read(inode); break; default: return -EINVAL; } |
97eeb4d9d Merge tag 'xfs-5.... |
306 |
return vfs_fallocate(file, mode | FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len); |
011da44bc compat: move FS_I... |
307 308 |
} #endif |
77b904019 compat_ioctl: sim... |
309 |
static int file_ioctl(struct file *filp, unsigned int cmd, int __user *p) |
1da177e4c Linux-2.6.12-rc2 |
310 |
{ |
1da177e4c Linux-2.6.12-rc2 |
311 |
switch (cmd) { |
c9845ff1d VFS: apply coding... |
312 |
case FIBMAP: |
aa81a7c71 VFS: factor out t... |
313 |
return ioctl_fibmap(filp, p); |
3e63cbb1e fs: Add new pre-a... |
314 315 |
case FS_IOC_RESVSP: case FS_IOC_RESVSP64: |
837a6e7f5 fs: add generic U... |
316 317 318 319 320 321 |
return ioctl_preallocate(filp, 0, p); case FS_IOC_UNRESVSP: case FS_IOC_UNRESVSP64: return ioctl_preallocate(filp, FALLOC_FL_PUNCH_HOLE, p); case FS_IOC_ZERO_RANGE: return ioctl_preallocate(filp, FALLOC_FL_ZERO_RANGE, p); |
1da177e4c Linux-2.6.12-rc2 |
322 |
} |
77b904019 compat_ioctl: sim... |
323 |
return -ENOIOCTLCMD; |
1da177e4c Linux-2.6.12-rc2 |
324 |
} |
aa81a7c71 VFS: factor out t... |
325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
static int ioctl_fionbio(struct file *filp, int __user *argp) { unsigned int flag; int on, error; error = get_user(on, argp); if (error) return error; flag = O_NONBLOCK; #ifdef __sparc__ /* SunOS compatibility item. */ if (O_NONBLOCK != O_NDELAY) flag |= O_NDELAY; #endif |
db1dd4d37 Use f_lock to pro... |
339 |
spin_lock(&filp->f_lock); |
aa81a7c71 VFS: factor out t... |
340 341 342 343 |
if (on) filp->f_flags |= flag; else filp->f_flags &= ~flag; |
db1dd4d37 Use f_lock to pro... |
344 |
spin_unlock(&filp->f_lock); |
aa81a7c71 VFS: factor out t... |
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
return error; } static int ioctl_fioasync(unsigned int fd, struct file *filp, int __user *argp) { unsigned int flag; int on, error; error = get_user(on, argp); if (error) return error; flag = on ? FASYNC : 0; /* Did FASYNC state change ? */ if ((flag ^ filp->f_flags) & FASYNC) { |
72c2d5319 file->f_op is nev... |
361 |
if (filp->f_op->fasync) |
76398425b Move FASYNC bit h... |
362 |
/* fasync() adjusts filp->f_flags */ |
aa81a7c71 VFS: factor out t... |
363 |
error = filp->f_op->fasync(fd, filp, on); |
218d11a8b Fix a race condit... |
364 |
else |
aa81a7c71 VFS: factor out t... |
365 366 |
error = -ENOTTY; } |
60aa49243 Rationalize fasyn... |
367 |
return error < 0 ? error : 0; |
aa81a7c71 VFS: factor out t... |
368 |
} |
fcccf5025 filesystem freeze... |
369 370 |
static int ioctl_fsfreeze(struct file *filp) { |
496ad9aa8 new helper: file_... |
371 |
struct super_block *sb = file_inode(filp)->i_sb; |
fcccf5025 filesystem freeze... |
372 |
|
f3f1a1833 fs: Allow CAP_SYS... |
373 |
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) |
fcccf5025 filesystem freeze... |
374 375 376 |
return -EPERM; /* If filesystem doesn't support freeze feature, return. */ |
48b6bca6b fs: add freeze_su... |
377 |
if (sb->s_op->freeze_fs == NULL && sb->s_op->freeze_super == NULL) |
fcccf5025 filesystem freeze... |
378 |
return -EOPNOTSUPP; |
fcccf5025 filesystem freeze... |
379 |
/* Freeze */ |
48b6bca6b fs: add freeze_su... |
380 381 |
if (sb->s_op->freeze_super) return sb->s_op->freeze_super(sb); |
18e9e5104 Introduce freeze_... |
382 |
return freeze_super(sb); |
fcccf5025 filesystem freeze... |
383 384 385 386 |
} static int ioctl_fsthaw(struct file *filp) { |
496ad9aa8 new helper: file_... |
387 |
struct super_block *sb = file_inode(filp)->i_sb; |
fcccf5025 filesystem freeze... |
388 |
|
f3f1a1833 fs: Allow CAP_SYS... |
389 |
if (!ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) |
fcccf5025 filesystem freeze... |
390 |
return -EPERM; |
fcccf5025 filesystem freeze... |
391 |
/* Thaw */ |
48b6bca6b fs: add freeze_su... |
392 393 |
if (sb->s_op->thaw_super) return sb->s_op->thaw_super(sb); |
18e9e5104 Introduce freeze_... |
394 |
return thaw_super(sb); |
fcccf5025 filesystem freeze... |
395 |
} |
34d3d0e65 do_vfs_ioctl(): u... |
396 397 |
static int ioctl_file_dedupe_range(struct file *file, struct file_dedupe_range __user *argp) |
54dbc1517 vfs: hoist the bt... |
398 |
{ |
54dbc1517 vfs: hoist the bt... |
399 400 401 402 403 404 405 406 407 408 409 |
struct file_dedupe_range *same = NULL; int ret; unsigned long size; u16 count; if (get_user(count, &argp->dest_count)) { ret = -EFAULT; goto out; } size = offsetof(struct file_dedupe_range __user, info[count]); |
b71dbf103 vfs: cap dedupe r... |
410 411 412 413 |
if (size > PAGE_SIZE) { ret = -ENOMEM; goto out; } |
54dbc1517 vfs: hoist the bt... |
414 415 416 417 418 419 420 |
same = memdup_user(argp, size); if (IS_ERR(same)) { ret = PTR_ERR(same); same = NULL; goto out; } |
10eec60ce vfs: ioctl: preve... |
421 |
same->dest_count = count; |
54dbc1517 vfs: hoist the bt... |
422 423 424 425 426 427 428 429 430 431 432 433 |
ret = vfs_dedupe_file_range(file, same); if (ret) goto out; ret = copy_to_user(argp, same, size); if (ret) ret = -EFAULT; out: kfree(same); return ret; } |
4c5b47997 vfs: add fileattr... |
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
/** * fileattr_fill_xflags - initialize fileattr with xflags * @fa: fileattr pointer * @xflags: FS_XFLAG_* flags * * Set ->fsx_xflags, ->fsx_valid and ->flags (translated xflags). All * other fields are zeroed. */ void fileattr_fill_xflags(struct fileattr *fa, u32 xflags) { memset(fa, 0, sizeof(*fa)); fa->fsx_valid = true; fa->fsx_xflags = xflags; if (fa->fsx_xflags & FS_XFLAG_IMMUTABLE) fa->flags |= FS_IMMUTABLE_FL; if (fa->fsx_xflags & FS_XFLAG_APPEND) fa->flags |= FS_APPEND_FL; if (fa->fsx_xflags & FS_XFLAG_SYNC) fa->flags |= FS_SYNC_FL; if (fa->fsx_xflags & FS_XFLAG_NOATIME) fa->flags |= FS_NOATIME_FL; if (fa->fsx_xflags & FS_XFLAG_NODUMP) fa->flags |= FS_NODUMP_FL; if (fa->fsx_xflags & FS_XFLAG_DAX) fa->flags |= FS_DAX_FL; if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT) fa->flags |= FS_PROJINHERIT_FL; } EXPORT_SYMBOL(fileattr_fill_xflags); /** * fileattr_fill_flags - initialize fileattr with flags * @fa: fileattr pointer * @flags: FS_*_FL flags * * Set ->flags, ->flags_valid and ->fsx_xflags (translated flags). * All other fields are zeroed. */ void fileattr_fill_flags(struct fileattr *fa, u32 flags) { memset(fa, 0, sizeof(*fa)); fa->flags_valid = true; fa->flags = flags; if (fa->flags & FS_SYNC_FL) fa->fsx_xflags |= FS_XFLAG_SYNC; if (fa->flags & FS_IMMUTABLE_FL) fa->fsx_xflags |= FS_XFLAG_IMMUTABLE; if (fa->flags & FS_APPEND_FL) fa->fsx_xflags |= FS_XFLAG_APPEND; if (fa->flags & FS_NODUMP_FL) fa->fsx_xflags |= FS_XFLAG_NODUMP; if (fa->flags & FS_NOATIME_FL) fa->fsx_xflags |= FS_XFLAG_NOATIME; if (fa->flags & FS_DAX_FL) fa->fsx_xflags |= FS_XFLAG_DAX; if (fa->flags & FS_PROJINHERIT_FL) fa->fsx_xflags |= FS_XFLAG_PROJINHERIT; } EXPORT_SYMBOL(fileattr_fill_flags); /** * vfs_fileattr_get - retrieve miscellaneous file attributes * @dentry: the object to retrieve from * @fa: fileattr pointer * * Call i_op->fileattr_get() callback, if exists. * * Return: 0 on success, or a negative error on failure. */ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) { struct inode *inode = d_inode(dentry); if (!inode->i_op->fileattr_get) return -ENOIOCTLCMD; return inode->i_op->fileattr_get(dentry, fa); } EXPORT_SYMBOL(vfs_fileattr_get); /** * copy_fsxattr_to_user - copy fsxattr to userspace. * @fa: fileattr pointer * @ufa: fsxattr user pointer * * Return: 0 on success, or -EFAULT on failure. */ int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa) { struct fsxattr xfa; memset(&xfa, 0, sizeof(xfa)); xfa.fsx_xflags = fa->fsx_xflags; xfa.fsx_extsize = fa->fsx_extsize; xfa.fsx_nextents = fa->fsx_nextents; xfa.fsx_projid = fa->fsx_projid; xfa.fsx_cowextsize = fa->fsx_cowextsize; if (copy_to_user(ufa, &xfa, sizeof(xfa))) return -EFAULT; return 0; } EXPORT_SYMBOL(copy_fsxattr_to_user); static int copy_fsxattr_from_user(struct fileattr *fa, struct fsxattr __user *ufa) { struct fsxattr xfa; if (copy_from_user(&xfa, ufa, sizeof(xfa))) return -EFAULT; fileattr_fill_xflags(fa, xfa.fsx_xflags); fa->fsx_extsize = xfa.fsx_extsize; fa->fsx_nextents = xfa.fsx_nextents; fa->fsx_projid = xfa.fsx_projid; fa->fsx_cowextsize = xfa.fsx_cowextsize; return 0; } /* * Generic function to check FS_IOC_FSSETXATTR/FS_IOC_SETFLAGS values and reject * any invalid configurations. * * Note: must be called with inode lock held. */ static int fileattr_set_prepare(struct inode *inode, const struct fileattr *old_ma, struct fileattr *fa) { int err; /* * The IMMUTABLE and APPEND_ONLY flags can only be changed by * the relevant capability. */ if ((fa->flags ^ old_ma->flags) & (FS_APPEND_FL | FS_IMMUTABLE_FL) && !capable(CAP_LINUX_IMMUTABLE)) return -EPERM; err = fscrypt_prepare_setflags(inode, old_ma->flags, fa->flags); if (err) return err; /* * Project Quota ID state is only allowed to change from within the init * namespace. Enforce that restriction only if we are trying to change * the quota ID state. Everything else is allowed in user namespaces. */ if (current_user_ns() != &init_user_ns) { if (old_ma->fsx_projid != fa->fsx_projid) return -EINVAL; if ((old_ma->fsx_xflags ^ fa->fsx_xflags) & FS_XFLAG_PROJINHERIT) return -EINVAL; |
d03ef4daf fs: forbid invali... |
591 592 593 594 595 596 597 598 |
} else { /* * Caller is allowed to change the project ID. If it is being * changed, make sure that the new value is valid. */ if (old_ma->fsx_projid != fa->fsx_projid && !projid_valid(make_kprojid(&init_user_ns, fa->fsx_projid))) return -EINVAL; |
4c5b47997 vfs: add fileattr... |
599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 |
} /* Check extent size hints. */ if ((fa->fsx_xflags & FS_XFLAG_EXTSIZE) && !S_ISREG(inode->i_mode)) return -EINVAL; if ((fa->fsx_xflags & FS_XFLAG_EXTSZINHERIT) && !S_ISDIR(inode->i_mode)) return -EINVAL; if ((fa->fsx_xflags & FS_XFLAG_COWEXTSIZE) && !S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) return -EINVAL; /* * It is only valid to set the DAX flag on regular files and * directories on filesystems. */ if ((fa->fsx_xflags & FS_XFLAG_DAX) && !(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))) return -EINVAL; /* Extent size hints of zero turn off the flags. */ if (fa->fsx_extsize == 0) fa->fsx_xflags &= ~(FS_XFLAG_EXTSIZE | FS_XFLAG_EXTSZINHERIT); if (fa->fsx_cowextsize == 0) fa->fsx_xflags &= ~FS_XFLAG_COWEXTSIZE; return 0; } /** * vfs_fileattr_set - change miscellaneous file attributes * @mnt_userns: user namespace of the mount * @dentry: the object to change * @fa: fileattr pointer * * After verifying permissions, call i_op->fileattr_set() callback, if * exists. * * Verifying attributes involves retrieving current attributes with * i_op->fileattr_get(), this also allows initializing attributes that have * not been set by the caller to current values. Inode lock is held * thoughout to prevent racing with another instance. * * Return: 0 on success, or a negative error on failure. */ int vfs_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa) { struct inode *inode = d_inode(dentry); struct fileattr old_ma = {}; int err; if (!inode->i_op->fileattr_set) return -ENOIOCTLCMD; if (!inode_owner_or_capable(mnt_userns, inode)) return -EPERM; inode_lock(inode); err = vfs_fileattr_get(dentry, &old_ma); if (!err) { /* initialize missing bits from old_ma */ if (fa->flags_valid) { fa->fsx_xflags |= old_ma.fsx_xflags & ~FS_XFLAG_COMMON; fa->fsx_extsize = old_ma.fsx_extsize; fa->fsx_nextents = old_ma.fsx_nextents; fa->fsx_projid = old_ma.fsx_projid; fa->fsx_cowextsize = old_ma.fsx_cowextsize; } else { fa->flags |= old_ma.flags & ~FS_COMMON_FL; } err = fileattr_set_prepare(inode, &old_ma, fa); if (!err) err = inode->i_op->fileattr_set(mnt_userns, dentry, fa); } inode_unlock(inode); return err; } EXPORT_SYMBOL(vfs_fileattr_set); static int ioctl_getflags(struct file *file, unsigned int __user *argp) { struct fileattr fa = { .flags_valid = true }; /* hint only */ int err; err = vfs_fileattr_get(file->f_path.dentry, &fa); if (!err) err = put_user(fa.flags, argp); return err; } static int ioctl_setflags(struct file *file, unsigned int __user *argp) { struct user_namespace *mnt_userns = file_mnt_user_ns(file); struct dentry *dentry = file->f_path.dentry; struct fileattr fa; unsigned int flags; int err; err = get_user(flags, argp); if (!err) { err = mnt_want_write_file(file); if (!err) { fileattr_fill_flags(&fa, flags); err = vfs_fileattr_set(mnt_userns, dentry, &fa); mnt_drop_write_file(file); } } return err; } static int ioctl_fsgetxattr(struct file *file, void __user *argp) { struct fileattr fa = { .fsx_valid = true }; /* hint only */ int err; err = vfs_fileattr_get(file->f_path.dentry, &fa); if (!err) err = copy_fsxattr_to_user(&fa, argp); return err; } static int ioctl_fssetxattr(struct file *file, void __user *argp) { struct user_namespace *mnt_userns = file_mnt_user_ns(file); struct dentry *dentry = file->f_path.dentry; struct fileattr fa; int err; err = copy_fsxattr_from_user(&fa, argp); if (!err) { err = mnt_want_write_file(file); if (!err) { err = vfs_fileattr_set(mnt_userns, dentry, &fa); mnt_drop_write_file(file); } } return err; } |
1da177e4c Linux-2.6.12-rc2 |
742 |
/* |
deb21db77 VFS: swap do_ioct... |
743 |
* do_vfs_ioctl() is not for drivers and not intended to be EXPORT_SYMBOL()'d. |
1da177e4c Linux-2.6.12-rc2 |
744 |
* It's just a simple helper for sys_ioctl and compat_sys_ioctl. |
77b904019 compat_ioctl: sim... |
745 746 747 |
* * When you add any new common ioctls to the switches above and below, * please ensure they have compatible arguments in compat mode. |
1da177e4c Linux-2.6.12-rc2 |
748 |
*/ |
77b904019 compat_ioctl: sim... |
749 750 |
static int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg) |
1da177e4c Linux-2.6.12-rc2 |
751 |
{ |
34d3d0e65 do_vfs_ioctl(): u... |
752 |
void __user *argp = (void __user *)arg; |
496ad9aa8 new helper: file_... |
753 |
struct inode *inode = file_inode(filp); |
1da177e4c Linux-2.6.12-rc2 |
754 755 |
switch (cmd) { |
c9845ff1d VFS: apply coding... |
756 757 |
case FIOCLEX: set_close_on_exec(fd, 1); |
77b904019 compat_ioctl: sim... |
758 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
759 |
|
c9845ff1d VFS: apply coding... |
760 761 |
case FIONCLEX: set_close_on_exec(fd, 0); |
77b904019 compat_ioctl: sim... |
762 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
763 |
|
c9845ff1d VFS: apply coding... |
764 |
case FIONBIO: |
77b904019 compat_ioctl: sim... |
765 |
return ioctl_fionbio(filp, argp); |
c9845ff1d VFS: apply coding... |
766 767 |
case FIOASYNC: |
77b904019 compat_ioctl: sim... |
768 |
return ioctl_fioasync(fd, filp, argp); |
c9845ff1d VFS: apply coding... |
769 770 |
case FIOQSIZE: |
27a4f7e61 vfs: cleanup do_v... |
771 772 773 |
if (S_ISDIR(inode->i_mode) || S_ISREG(inode->i_mode) || S_ISLNK(inode->i_mode)) { loff_t res = inode_get_bytes(inode); |
77b904019 compat_ioctl: sim... |
774 775 776 777 778 |
return copy_to_user(argp, &res, sizeof(res)) ? -EFAULT : 0; } return -ENOTTY; |
fcccf5025 filesystem freeze... |
779 780 |
case FIFREEZE: |
77b904019 compat_ioctl: sim... |
781 |
return ioctl_fsfreeze(filp); |
fcccf5025 filesystem freeze... |
782 783 |
case FITHAW: |
77b904019 compat_ioctl: sim... |
784 |
return ioctl_fsthaw(filp); |
fcccf5025 filesystem freeze... |
785 |
|
19ba0559f vfs: Enable FS_IO... |
786 |
case FS_IOC_FIEMAP: |
34d3d0e65 do_vfs_ioctl(): u... |
787 |
return ioctl_fiemap(filp, argp); |
19ba0559f vfs: Enable FS_IO... |
788 789 |
case FIGETBSZ: |
8f97d1e99 vfs: fix FIGETBSZ... |
790 791 792 |
/* anon_bdev filesystems may not have a block size */ if (!inode->i_sb->s_blocksize) return -EINVAL; |
77b904019 compat_ioctl: sim... |
793 |
|
34d3d0e65 do_vfs_ioctl(): u... |
794 |
return put_user(inode->i_sb->s_blocksize, (int __user *)argp); |
19ba0559f vfs: Enable FS_IO... |
795 |
|
04b38d601 vfs: pull btrfs c... |
796 797 798 799 800 |
case FICLONE: return ioctl_file_clone(filp, arg, 0, 0, 0); case FICLONERANGE: return ioctl_file_clone_range(filp, argp); |
54dbc1517 vfs: hoist the bt... |
801 802 |
case FIDEDUPERANGE: return ioctl_file_dedupe_range(filp, argp); |
0a061743a compat_ioctl: fix... |
803 804 805 806 807 808 |
case FIONREAD: if (!S_ISREG(inode->i_mode)) return vfs_ioctl(filp, cmd, arg); return put_user(i_size_read(inode) - filp->f_pos, (int __user *)argp); |
4c5b47997 vfs: add fileattr... |
809 810 811 812 813 814 815 816 817 818 819 |
case FS_IOC_GETFLAGS: return ioctl_getflags(filp, argp); case FS_IOC_SETFLAGS: return ioctl_setflags(filp, argp); case FS_IOC_FSGETXATTR: return ioctl_fsgetxattr(filp, argp); case FS_IOC_FSSETXATTR: return ioctl_fssetxattr(filp, argp); |
c9845ff1d VFS: apply coding... |
820 |
default: |
27a4f7e61 vfs: cleanup do_v... |
821 |
if (S_ISREG(inode->i_mode)) |
77b904019 compat_ioctl: sim... |
822 |
return file_ioctl(filp, cmd, argp); |
c9845ff1d VFS: apply coding... |
823 |
break; |
1da177e4c Linux-2.6.12-rc2 |
824 |
} |
77b904019 compat_ioctl: sim... |
825 826 |
return -ENOIOCTLCMD; |
1da177e4c Linux-2.6.12-rc2 |
827 |
} |
863b67e15 fs: remove ksys_i... |
828 |
SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) |
1da177e4c Linux-2.6.12-rc2 |
829 |
{ |
2903ff019 switch simple cas... |
830 |
struct fd f = fdget(fd); |
77b904019 compat_ioctl: sim... |
831 |
int error; |
2903ff019 switch simple cas... |
832 833 834 |
if (!f.file) return -EBADF; |
77b904019 compat_ioctl: sim... |
835 |
|
2903ff019 switch simple cas... |
836 |
error = security_file_ioctl(f.file, cmd, arg); |
77b904019 compat_ioctl: sim... |
837 838 839 840 841 842 843 844 |
if (error) goto out; error = do_vfs_ioctl(f.file, fd, cmd, arg); if (error == -ENOIOCTLCMD) error = vfs_ioctl(f.file, cmd, arg); out: |
2903ff019 switch simple cas... |
845 |
fdput(f); |
1da177e4c Linux-2.6.12-rc2 |
846 847 |
return error; } |
cbb60b924 fs: add ksys_ioct... |
848 |
|
2952db0fd compat_ioctl: add... |
849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 |
#ifdef CONFIG_COMPAT /** * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation * * This is not normally called as a function, but instead set in struct * file_operations as * * .compat_ioctl = compat_ptr_ioctl, * * On most architectures, the compat_ptr_ioctl() just passes all arguments * to the corresponding ->ioctl handler. The exception is arch/s390, where * compat_ptr() clears the top bit of a 32-bit pointer value, so user space * pointers to the second 2GB alias the first 2GB, as is the case for * native 32-bit s390 user space. * * The compat_ptr_ioctl() function must therefore be used only with ioctl * functions that either ignore the argument or pass a pointer to a * compatible data type. * * If any ioctl command handled by fops->unlocked_ioctl passes a plain * integer instead of a pointer, or any of the passed data types * is incompatible between 32-bit and 64-bit architectures, a proper * handler is required instead of compat_ptr_ioctl. */ long compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { if (!file->f_op->unlocked_ioctl) return -ENOIOCTLCMD; return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg)); } EXPORT_SYMBOL(compat_ptr_ioctl); |
2af563d07 compat_ioctl: mov... |
881 882 |
COMPAT_SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, |
77b904019 compat_ioctl: sim... |
883 |
compat_ulong_t, arg) |
2af563d07 compat_ioctl: mov... |
884 |
{ |
2af563d07 compat_ioctl: mov... |
885 |
struct fd f = fdget(fd); |
77b904019 compat_ioctl: sim... |
886 |
int error; |
2af563d07 compat_ioctl: mov... |
887 |
if (!f.file) |
77b904019 compat_ioctl: sim... |
888 |
return -EBADF; |
2af563d07 compat_ioctl: mov... |
889 890 891 892 |
/* RED-PEN how should LSM module know it's handling 32bit? */ error = security_file_ioctl(f.file, cmd, arg); if (error) |
77b904019 compat_ioctl: sim... |
893 |
goto out; |
2af563d07 compat_ioctl: mov... |
894 895 |
switch (cmd) { |
77b904019 compat_ioctl: sim... |
896 |
/* FICLONE takes an int argument, so don't use compat_ptr() */ |
2af563d07 compat_ioctl: mov... |
897 |
case FICLONE: |
77b904019 compat_ioctl: sim... |
898 899 |
error = ioctl_file_clone(f.file, arg, 0, 0, 0); break; |
2af563d07 compat_ioctl: mov... |
900 |
#if defined(CONFIG_X86_64) |
77b904019 compat_ioctl: sim... |
901 |
/* these get messy on amd64 due to alignment differences */ |
2af563d07 compat_ioctl: mov... |
902 903 904 |
case FS_IOC_RESVSP_32: case FS_IOC_RESVSP64_32: error = compat_ioctl_preallocate(f.file, 0, compat_ptr(arg)); |
77b904019 compat_ioctl: sim... |
905 |
break; |
2af563d07 compat_ioctl: mov... |
906 907 908 909 |
case FS_IOC_UNRESVSP_32: case FS_IOC_UNRESVSP64_32: error = compat_ioctl_preallocate(f.file, FALLOC_FL_PUNCH_HOLE, compat_ptr(arg)); |
77b904019 compat_ioctl: sim... |
910 |
break; |
2af563d07 compat_ioctl: mov... |
911 912 913 |
case FS_IOC_ZERO_RANGE_32: error = compat_ioctl_preallocate(f.file, FALLOC_FL_ZERO_RANGE, compat_ptr(arg)); |
77b904019 compat_ioctl: sim... |
914 |
break; |
2af563d07 compat_ioctl: mov... |
915 |
#endif |
77b904019 compat_ioctl: sim... |
916 |
/* |
4c5b47997 vfs: add fileattr... |
917 918 919 920 921 922 923 924 925 |
* These access 32-bit values anyway so no further handling is * necessary. */ case FS_IOC32_GETFLAGS: case FS_IOC32_SETFLAGS: cmd = (cmd == FS_IOC32_GETFLAGS) ? FS_IOC_GETFLAGS : FS_IOC_SETFLAGS; fallthrough; /* |
77b904019 compat_ioctl: sim... |
926 927 928 929 |
* everything else in do_vfs_ioctl() takes either a compatible * pointer argument or no argument -- call it with a modified * argument. */ |
2af563d07 compat_ioctl: mov... |
930 |
default: |
77b904019 compat_ioctl: sim... |
931 932 933 934 935 936 |
error = do_vfs_ioctl(f.file, fd, cmd, (unsigned long)compat_ptr(arg)); if (error != -ENOIOCTLCMD) break; if (f.file->f_op->compat_ioctl) |
2af563d07 compat_ioctl: mov... |
937 |
error = f.file->f_op->compat_ioctl(f.file, cmd, arg); |
77b904019 compat_ioctl: sim... |
938 939 940 |
if (error == -ENOIOCTLCMD) error = -ENOTTY; break; |
2af563d07 compat_ioctl: mov... |
941 |
} |
2af563d07 compat_ioctl: mov... |
942 |
out: |
77b904019 compat_ioctl: sim... |
943 |
fdput(f); |
2af563d07 compat_ioctl: mov... |
944 945 |
return error; } |
2952db0fd compat_ioctl: add... |
946 |
#endif |