| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2017 Red Hat, Inc. |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 5 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 6 | | - * the Free Software Foundation. |
|---|
| 7 | 4 | */ |
|---|
| 8 | 5 | |
|---|
| 9 | 6 | #include <linux/cred.h> |
|---|
| .. | .. |
|---|
| 12 | 9 | #include <linux/xattr.h> |
|---|
| 13 | 10 | #include <linux/uio.h> |
|---|
| 14 | 11 | #include <linux/uaccess.h> |
|---|
| 12 | +#include <linux/splice.h> |
|---|
| 13 | +#include <linux/security.h> |
|---|
| 14 | +#include <linux/mm.h> |
|---|
| 15 | +#include <linux/fs.h> |
|---|
| 15 | 16 | #include "overlayfs.h" |
|---|
| 17 | + |
|---|
| 18 | +#define OVL_IOCB_MASK (IOCB_DSYNC | IOCB_HIPRI | IOCB_NOWAIT | IOCB_SYNC) |
|---|
| 19 | + |
|---|
| 20 | +struct ovl_aio_req { |
|---|
| 21 | + struct kiocb iocb; |
|---|
| 22 | + refcount_t ref; |
|---|
| 23 | + struct kiocb *orig_iocb; |
|---|
| 24 | + struct fd fd; |
|---|
| 25 | +}; |
|---|
| 26 | + |
|---|
| 27 | +static struct kmem_cache *ovl_aio_request_cachep; |
|---|
| 16 | 28 | |
|---|
| 17 | 29 | static char ovl_whatisit(struct inode *inode, struct inode *realinode) |
|---|
| 18 | 30 | { |
|---|
| .. | .. |
|---|
| 34 | 46 | struct file *realfile; |
|---|
| 35 | 47 | const struct cred *old_cred; |
|---|
| 36 | 48 | int flags = file->f_flags | OVL_OPEN_FLAGS; |
|---|
| 49 | + int acc_mode = ACC_MODE(flags); |
|---|
| 50 | + int err; |
|---|
| 51 | + |
|---|
| 52 | + if (flags & O_APPEND) |
|---|
| 53 | + acc_mode |= MAY_APPEND; |
|---|
| 37 | 54 | |
|---|
| 38 | 55 | old_cred = ovl_override_creds(inode->i_sb); |
|---|
| 39 | | - realfile = open_with_fake_path(&file->f_path, flags, realinode, |
|---|
| 40 | | - current_cred()); |
|---|
| 41 | | - ovl_revert_creds(old_cred); |
|---|
| 56 | + err = inode_permission(realinode, MAY_OPEN | acc_mode); |
|---|
| 57 | + if (err) { |
|---|
| 58 | + realfile = ERR_PTR(err); |
|---|
| 59 | + } else if (old_cred && !inode_owner_or_capable(realinode)) { |
|---|
| 60 | + realfile = ERR_PTR(-EPERM); |
|---|
| 61 | + } else { |
|---|
| 62 | + realfile = open_with_fake_path(&file->f_path, flags, realinode, |
|---|
| 63 | + current_cred()); |
|---|
| 64 | + } |
|---|
| 65 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 42 | 66 | |
|---|
| 43 | 67 | pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", |
|---|
| 44 | 68 | file, file, ovl_whatisit(inode, realinode), file->f_flags, |
|---|
| .. | .. |
|---|
| 115 | 139 | |
|---|
| 116 | 140 | static int ovl_real_fdget(const struct file *file, struct fd *real) |
|---|
| 117 | 141 | { |
|---|
| 142 | + if (d_is_dir(file_dentry(file))) { |
|---|
| 143 | + real->flags = 0; |
|---|
| 144 | + real->file = ovl_dir_real_file(file, false); |
|---|
| 145 | + |
|---|
| 146 | + return PTR_ERR_OR_ZERO(real->file); |
|---|
| 147 | + } |
|---|
| 148 | + |
|---|
| 118 | 149 | return ovl_real_fdget_meta(file, real, false); |
|---|
| 119 | 150 | } |
|---|
| 120 | 151 | |
|---|
| .. | .. |
|---|
| 176 | 207 | * limitations that are more strict than ->s_maxbytes for specific |
|---|
| 177 | 208 | * files, so we use the real file to perform seeks. |
|---|
| 178 | 209 | */ |
|---|
| 179 | | - inode_lock(inode); |
|---|
| 210 | + ovl_inode_lock(inode); |
|---|
| 180 | 211 | real.file->f_pos = file->f_pos; |
|---|
| 181 | 212 | |
|---|
| 182 | 213 | old_cred = ovl_override_creds(inode->i_sb); |
|---|
| 183 | 214 | ret = vfs_llseek(real.file, offset, whence); |
|---|
| 184 | | - ovl_revert_creds(old_cred); |
|---|
| 215 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 185 | 216 | |
|---|
| 186 | 217 | file->f_pos = real.file->f_pos; |
|---|
| 187 | | - inode_unlock(inode); |
|---|
| 218 | + ovl_inode_unlock(inode); |
|---|
| 188 | 219 | |
|---|
| 189 | 220 | fdput(real); |
|---|
| 190 | 221 | |
|---|
| .. | .. |
|---|
| 213 | 244 | touch_atime(&file->f_path); |
|---|
| 214 | 245 | } |
|---|
| 215 | 246 | |
|---|
| 216 | | -static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb) |
|---|
| 247 | +static inline void ovl_aio_put(struct ovl_aio_req *aio_req) |
|---|
| 217 | 248 | { |
|---|
| 218 | | - int ifl = iocb->ki_flags; |
|---|
| 219 | | - rwf_t flags = 0; |
|---|
| 249 | + if (refcount_dec_and_test(&aio_req->ref)) { |
|---|
| 250 | + fdput(aio_req->fd); |
|---|
| 251 | + kmem_cache_free(ovl_aio_request_cachep, aio_req); |
|---|
| 252 | + } |
|---|
| 253 | +} |
|---|
| 220 | 254 | |
|---|
| 221 | | - if (ifl & IOCB_NOWAIT) |
|---|
| 222 | | - flags |= RWF_NOWAIT; |
|---|
| 223 | | - if (ifl & IOCB_HIPRI) |
|---|
| 224 | | - flags |= RWF_HIPRI; |
|---|
| 225 | | - if (ifl & IOCB_DSYNC) |
|---|
| 226 | | - flags |= RWF_DSYNC; |
|---|
| 227 | | - if (ifl & IOCB_SYNC) |
|---|
| 228 | | - flags |= RWF_SYNC; |
|---|
| 255 | +static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req) |
|---|
| 256 | +{ |
|---|
| 257 | + struct kiocb *iocb = &aio_req->iocb; |
|---|
| 258 | + struct kiocb *orig_iocb = aio_req->orig_iocb; |
|---|
| 229 | 259 | |
|---|
| 230 | | - return flags; |
|---|
| 260 | + if (iocb->ki_flags & IOCB_WRITE) { |
|---|
| 261 | + struct inode *inode = file_inode(orig_iocb->ki_filp); |
|---|
| 262 | + |
|---|
| 263 | + /* Actually acquired in ovl_write_iter() */ |
|---|
| 264 | + __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, |
|---|
| 265 | + SB_FREEZE_WRITE); |
|---|
| 266 | + file_end_write(iocb->ki_filp); |
|---|
| 267 | + ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 268 | + } |
|---|
| 269 | + |
|---|
| 270 | + orig_iocb->ki_pos = iocb->ki_pos; |
|---|
| 271 | + ovl_aio_put(aio_req); |
|---|
| 272 | +} |
|---|
| 273 | + |
|---|
| 274 | +static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2) |
|---|
| 275 | +{ |
|---|
| 276 | + struct ovl_aio_req *aio_req = container_of(iocb, |
|---|
| 277 | + struct ovl_aio_req, iocb); |
|---|
| 278 | + struct kiocb *orig_iocb = aio_req->orig_iocb; |
|---|
| 279 | + |
|---|
| 280 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 281 | + orig_iocb->ki_complete(orig_iocb, res, res2); |
|---|
| 231 | 282 | } |
|---|
| 232 | 283 | |
|---|
| 233 | 284 | static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) |
|---|
| .. | .. |
|---|
| 244 | 295 | if (ret) |
|---|
| 245 | 296 | return ret; |
|---|
| 246 | 297 | |
|---|
| 298 | + ret = -EINVAL; |
|---|
| 299 | + if (iocb->ki_flags & IOCB_DIRECT && |
|---|
| 300 | + (!real.file->f_mapping->a_ops || |
|---|
| 301 | + !real.file->f_mapping->a_ops->direct_IO)) |
|---|
| 302 | + goto out_fdput; |
|---|
| 303 | + |
|---|
| 247 | 304 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 248 | | - ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, |
|---|
| 249 | | - ovl_iocb_to_rwf(iocb)); |
|---|
| 250 | | - ovl_revert_creds(old_cred); |
|---|
| 305 | + if (is_sync_kiocb(iocb)) { |
|---|
| 306 | + ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, |
|---|
| 307 | + iocb_to_rw_flags(iocb->ki_flags, |
|---|
| 308 | + OVL_IOCB_MASK)); |
|---|
| 309 | + } else { |
|---|
| 310 | + struct ovl_aio_req *aio_req; |
|---|
| 311 | + |
|---|
| 312 | + ret = -ENOMEM; |
|---|
| 313 | + aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); |
|---|
| 314 | + if (!aio_req) |
|---|
| 315 | + goto out; |
|---|
| 316 | + |
|---|
| 317 | + aio_req->fd = real; |
|---|
| 318 | + real.flags = 0; |
|---|
| 319 | + aio_req->orig_iocb = iocb; |
|---|
| 320 | + kiocb_clone(&aio_req->iocb, iocb, real.file); |
|---|
| 321 | + aio_req->iocb.ki_complete = ovl_aio_rw_complete; |
|---|
| 322 | + refcount_set(&aio_req->ref, 2); |
|---|
| 323 | + ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); |
|---|
| 324 | + ovl_aio_put(aio_req); |
|---|
| 325 | + if (ret != -EIOCBQUEUED) |
|---|
| 326 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 327 | + } |
|---|
| 328 | +out: |
|---|
| 329 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 251 | 330 | |
|---|
| 252 | 331 | ovl_file_accessed(file); |
|---|
| 253 | | - |
|---|
| 332 | +out_fdput: |
|---|
| 254 | 333 | fdput(real); |
|---|
| 255 | 334 | |
|---|
| 256 | 335 | return ret; |
|---|
| .. | .. |
|---|
| 263 | 342 | struct fd real; |
|---|
| 264 | 343 | const struct cred *old_cred; |
|---|
| 265 | 344 | ssize_t ret; |
|---|
| 345 | + int ifl = iocb->ki_flags; |
|---|
| 266 | 346 | |
|---|
| 267 | 347 | if (!iov_iter_count(iter)) |
|---|
| 268 | 348 | return 0; |
|---|
| .. | .. |
|---|
| 278 | 358 | if (ret) |
|---|
| 279 | 359 | goto out_unlock; |
|---|
| 280 | 360 | |
|---|
| 361 | + ret = -EINVAL; |
|---|
| 362 | + if (iocb->ki_flags & IOCB_DIRECT && |
|---|
| 363 | + (!real.file->f_mapping->a_ops || |
|---|
| 364 | + !real.file->f_mapping->a_ops->direct_IO)) |
|---|
| 365 | + goto out_fdput; |
|---|
| 366 | + |
|---|
| 367 | + if (!ovl_should_sync(OVL_FS(inode->i_sb))) |
|---|
| 368 | + ifl &= ~(IOCB_DSYNC | IOCB_SYNC); |
|---|
| 369 | + |
|---|
| 281 | 370 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 371 | + if (is_sync_kiocb(iocb)) { |
|---|
| 372 | + file_start_write(real.file); |
|---|
| 373 | + ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, |
|---|
| 374 | + iocb_to_rw_flags(ifl, OVL_IOCB_MASK)); |
|---|
| 375 | + file_end_write(real.file); |
|---|
| 376 | + /* Update size */ |
|---|
| 377 | + ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 378 | + } else { |
|---|
| 379 | + struct ovl_aio_req *aio_req; |
|---|
| 380 | + |
|---|
| 381 | + ret = -ENOMEM; |
|---|
| 382 | + aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); |
|---|
| 383 | + if (!aio_req) |
|---|
| 384 | + goto out; |
|---|
| 385 | + |
|---|
| 386 | + file_start_write(real.file); |
|---|
| 387 | + /* Pacify lockdep, same trick as done in aio_write() */ |
|---|
| 388 | + __sb_writers_release(file_inode(real.file)->i_sb, |
|---|
| 389 | + SB_FREEZE_WRITE); |
|---|
| 390 | + aio_req->fd = real; |
|---|
| 391 | + real.flags = 0; |
|---|
| 392 | + aio_req->orig_iocb = iocb; |
|---|
| 393 | + kiocb_clone(&aio_req->iocb, iocb, real.file); |
|---|
| 394 | + aio_req->iocb.ki_flags = ifl; |
|---|
| 395 | + aio_req->iocb.ki_complete = ovl_aio_rw_complete; |
|---|
| 396 | + refcount_set(&aio_req->ref, 2); |
|---|
| 397 | + ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter); |
|---|
| 398 | + ovl_aio_put(aio_req); |
|---|
| 399 | + if (ret != -EIOCBQUEUED) |
|---|
| 400 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 401 | + } |
|---|
| 402 | +out: |
|---|
| 403 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 404 | +out_fdput: |
|---|
| 405 | + fdput(real); |
|---|
| 406 | + |
|---|
| 407 | +out_unlock: |
|---|
| 408 | + inode_unlock(inode); |
|---|
| 409 | + |
|---|
| 410 | + return ret; |
|---|
| 411 | +} |
|---|
| 412 | + |
|---|
| 413 | +/* |
|---|
| 414 | + * Calling iter_file_splice_write() directly from overlay's f_op may deadlock |
|---|
| 415 | + * due to lock order inversion between pipe->mutex in iter_file_splice_write() |
|---|
| 416 | + * and file_start_write(real.file) in ovl_write_iter(). |
|---|
| 417 | + * |
|---|
| 418 | + * So do everything ovl_write_iter() does and call iter_file_splice_write() on |
|---|
| 419 | + * the real file. |
|---|
| 420 | + */ |
|---|
| 421 | +static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, |
|---|
| 422 | + loff_t *ppos, size_t len, unsigned int flags) |
|---|
| 423 | +{ |
|---|
| 424 | + struct fd real; |
|---|
| 425 | + const struct cred *old_cred; |
|---|
| 426 | + struct inode *inode = file_inode(out); |
|---|
| 427 | + struct inode *realinode = ovl_inode_real(inode); |
|---|
| 428 | + ssize_t ret; |
|---|
| 429 | + |
|---|
| 430 | + inode_lock(inode); |
|---|
| 431 | + /* Update mode */ |
|---|
| 432 | + ovl_copyattr(realinode, inode); |
|---|
| 433 | + ret = file_remove_privs(out); |
|---|
| 434 | + if (ret) |
|---|
| 435 | + goto out_unlock; |
|---|
| 436 | + |
|---|
| 437 | + ret = ovl_real_fdget(out, &real); |
|---|
| 438 | + if (ret) |
|---|
| 439 | + goto out_unlock; |
|---|
| 440 | + |
|---|
| 441 | + old_cred = ovl_override_creds(inode->i_sb); |
|---|
| 282 | 442 | file_start_write(real.file); |
|---|
| 283 | | - ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, |
|---|
| 284 | | - ovl_iocb_to_rwf(iocb)); |
|---|
| 443 | + |
|---|
| 444 | + ret = iter_file_splice_write(pipe, real.file, ppos, len, flags); |
|---|
| 445 | + |
|---|
| 285 | 446 | file_end_write(real.file); |
|---|
| 286 | | - ovl_revert_creds(old_cred); |
|---|
| 287 | | - |
|---|
| 288 | 447 | /* Update size */ |
|---|
| 289 | | - ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 290 | | - |
|---|
| 448 | + ovl_copyattr(realinode, inode); |
|---|
| 449 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 291 | 450 | fdput(real); |
|---|
| 292 | 451 | |
|---|
| 293 | 452 | out_unlock: |
|---|
| .. | .. |
|---|
| 302 | 461 | const struct cred *old_cred; |
|---|
| 303 | 462 | int ret; |
|---|
| 304 | 463 | |
|---|
| 464 | + ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb)); |
|---|
| 465 | + if (ret <= 0) |
|---|
| 466 | + return ret; |
|---|
| 467 | + |
|---|
| 305 | 468 | ret = ovl_real_fdget_meta(file, &real, !datasync); |
|---|
| 306 | 469 | if (ret) |
|---|
| 307 | 470 | return ret; |
|---|
| .. | .. |
|---|
| 310 | 473 | if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) { |
|---|
| 311 | 474 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 312 | 475 | ret = vfs_fsync_range(real.file, start, end, datasync); |
|---|
| 313 | | - ovl_revert_creds(old_cred); |
|---|
| 476 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 314 | 477 | } |
|---|
| 315 | 478 | |
|---|
| 316 | 479 | fdput(real); |
|---|
| .. | .. |
|---|
| 334 | 497 | |
|---|
| 335 | 498 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 336 | 499 | ret = call_mmap(vma->vm_file, vma); |
|---|
| 337 | | - ovl_revert_creds(old_cred); |
|---|
| 500 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 338 | 501 | |
|---|
| 339 | 502 | if (ret) { |
|---|
| 340 | 503 | /* Drop reference count from new vm_file value */ |
|---|
| .. | .. |
|---|
| 362 | 525 | |
|---|
| 363 | 526 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 364 | 527 | ret = vfs_fallocate(real.file, mode, offset, len); |
|---|
| 365 | | - ovl_revert_creds(old_cred); |
|---|
| 528 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 366 | 529 | |
|---|
| 367 | 530 | /* Update size */ |
|---|
| 368 | 531 | ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| .. | .. |
|---|
| 384 | 547 | |
|---|
| 385 | 548 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 386 | 549 | ret = vfs_fadvise(real.file, offset, len, advice); |
|---|
| 387 | | - ovl_revert_creds(old_cred); |
|---|
| 550 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 388 | 551 | |
|---|
| 389 | 552 | fdput(real); |
|---|
| 390 | 553 | |
|---|
| .. | .. |
|---|
| 395 | 558 | unsigned long arg) |
|---|
| 396 | 559 | { |
|---|
| 397 | 560 | struct fd real; |
|---|
| 398 | | - const struct cred *old_cred; |
|---|
| 399 | 561 | long ret; |
|---|
| 400 | 562 | |
|---|
| 401 | 563 | ret = ovl_real_fdget(file, &real); |
|---|
| 402 | 564 | if (ret) |
|---|
| 403 | 565 | return ret; |
|---|
| 404 | 566 | |
|---|
| 405 | | - old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 406 | | - ret = vfs_ioctl(real.file, cmd, arg); |
|---|
| 407 | | - ovl_revert_creds(old_cred); |
|---|
| 567 | + ret = security_file_ioctl(real.file, cmd, arg); |
|---|
| 568 | + if (!ret) { |
|---|
| 569 | + /* |
|---|
| 570 | + * Don't override creds, since we currently can't safely check |
|---|
| 571 | + * permissions before doing so. |
|---|
| 572 | + */ |
|---|
| 573 | + ret = vfs_ioctl(real.file, cmd, arg); |
|---|
| 574 | + } |
|---|
| 408 | 575 | |
|---|
| 409 | 576 | fdput(real); |
|---|
| 410 | 577 | |
|---|
| .. | .. |
|---|
| 412 | 579 | } |
|---|
| 413 | 580 | |
|---|
| 414 | 581 | static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, |
|---|
| 415 | | - unsigned long arg, unsigned int iflags) |
|---|
| 582 | + unsigned long arg) |
|---|
| 416 | 583 | { |
|---|
| 417 | 584 | long ret; |
|---|
| 418 | 585 | struct inode *inode = file_inode(file); |
|---|
| 419 | | - unsigned int old_iflags; |
|---|
| 420 | 586 | |
|---|
| 421 | 587 | if (!inode_owner_or_capable(inode)) |
|---|
| 422 | 588 | return -EACCES; |
|---|
| .. | .. |
|---|
| 427 | 593 | |
|---|
| 428 | 594 | inode_lock(inode); |
|---|
| 429 | 595 | |
|---|
| 430 | | - /* Check the capability before cred override */ |
|---|
| 596 | + /* |
|---|
| 597 | + * Prevent copy up if immutable and has no CAP_LINUX_IMMUTABLE |
|---|
| 598 | + * capability. |
|---|
| 599 | + */ |
|---|
| 431 | 600 | ret = -EPERM; |
|---|
| 432 | | - old_iflags = READ_ONCE(inode->i_flags); |
|---|
| 433 | | - if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) && |
|---|
| 601 | + if (!ovl_has_upperdata(inode) && IS_IMMUTABLE(inode) && |
|---|
| 434 | 602 | !capable(CAP_LINUX_IMMUTABLE)) |
|---|
| 435 | 603 | goto unlock; |
|---|
| 436 | 604 | |
|---|
| .. | .. |
|---|
| 450 | 618 | |
|---|
| 451 | 619 | } |
|---|
| 452 | 620 | |
|---|
| 453 | | -static unsigned int ovl_fsflags_to_iflags(unsigned int flags) |
|---|
| 454 | | -{ |
|---|
| 455 | | - unsigned int iflags = 0; |
|---|
| 456 | | - |
|---|
| 457 | | - if (flags & FS_SYNC_FL) |
|---|
| 458 | | - iflags |= S_SYNC; |
|---|
| 459 | | - if (flags & FS_APPEND_FL) |
|---|
| 460 | | - iflags |= S_APPEND; |
|---|
| 461 | | - if (flags & FS_IMMUTABLE_FL) |
|---|
| 462 | | - iflags |= S_IMMUTABLE; |
|---|
| 463 | | - if (flags & FS_NOATIME_FL) |
|---|
| 464 | | - iflags |= S_NOATIME; |
|---|
| 465 | | - |
|---|
| 466 | | - return iflags; |
|---|
| 467 | | -} |
|---|
| 468 | | - |
|---|
| 469 | | -static long ovl_ioctl_set_fsflags(struct file *file, unsigned int cmd, |
|---|
| 470 | | - unsigned long arg) |
|---|
| 471 | | -{ |
|---|
| 472 | | - unsigned int flags; |
|---|
| 473 | | - |
|---|
| 474 | | - if (get_user(flags, (int __user *) arg)) |
|---|
| 475 | | - return -EFAULT; |
|---|
| 476 | | - |
|---|
| 477 | | - return ovl_ioctl_set_flags(file, cmd, arg, |
|---|
| 478 | | - ovl_fsflags_to_iflags(flags)); |
|---|
| 479 | | -} |
|---|
| 480 | | - |
|---|
| 481 | | -static unsigned int ovl_fsxflags_to_iflags(unsigned int xflags) |
|---|
| 482 | | -{ |
|---|
| 483 | | - unsigned int iflags = 0; |
|---|
| 484 | | - |
|---|
| 485 | | - if (xflags & FS_XFLAG_SYNC) |
|---|
| 486 | | - iflags |= S_SYNC; |
|---|
| 487 | | - if (xflags & FS_XFLAG_APPEND) |
|---|
| 488 | | - iflags |= S_APPEND; |
|---|
| 489 | | - if (xflags & FS_XFLAG_IMMUTABLE) |
|---|
| 490 | | - iflags |= S_IMMUTABLE; |
|---|
| 491 | | - if (xflags & FS_XFLAG_NOATIME) |
|---|
| 492 | | - iflags |= S_NOATIME; |
|---|
| 493 | | - |
|---|
| 494 | | - return iflags; |
|---|
| 495 | | -} |
|---|
| 496 | | - |
|---|
| 497 | | -static long ovl_ioctl_set_fsxflags(struct file *file, unsigned int cmd, |
|---|
| 498 | | - unsigned long arg) |
|---|
| 499 | | -{ |
|---|
| 500 | | - struct fsxattr fa; |
|---|
| 501 | | - |
|---|
| 502 | | - memset(&fa, 0, sizeof(fa)); |
|---|
| 503 | | - if (copy_from_user(&fa, (void __user *) arg, sizeof(fa))) |
|---|
| 504 | | - return -EFAULT; |
|---|
| 505 | | - |
|---|
| 506 | | - return ovl_ioctl_set_flags(file, cmd, arg, |
|---|
| 507 | | - ovl_fsxflags_to_iflags(fa.fsx_xflags)); |
|---|
| 508 | | -} |
|---|
| 509 | | - |
|---|
| 510 | | -static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
|---|
| 621 | +long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
|---|
| 511 | 622 | { |
|---|
| 512 | 623 | long ret; |
|---|
| 513 | 624 | |
|---|
| .. | .. |
|---|
| 517 | 628 | ret = ovl_real_ioctl(file, cmd, arg); |
|---|
| 518 | 629 | break; |
|---|
| 519 | 630 | |
|---|
| 520 | | - case FS_IOC_SETFLAGS: |
|---|
| 521 | | - ret = ovl_ioctl_set_fsflags(file, cmd, arg); |
|---|
| 522 | | - break; |
|---|
| 523 | | - |
|---|
| 524 | 631 | case FS_IOC_FSSETXATTR: |
|---|
| 525 | | - ret = ovl_ioctl_set_fsxflags(file, cmd, arg); |
|---|
| 632 | + case FS_IOC_SETFLAGS: |
|---|
| 633 | + ret = ovl_ioctl_set_flags(file, cmd, arg); |
|---|
| 526 | 634 | break; |
|---|
| 527 | 635 | |
|---|
| 528 | 636 | default: |
|---|
| .. | .. |
|---|
| 532 | 640 | return ret; |
|---|
| 533 | 641 | } |
|---|
| 534 | 642 | |
|---|
| 535 | | -static long ovl_compat_ioctl(struct file *file, unsigned int cmd, |
|---|
| 536 | | - unsigned long arg) |
|---|
| 643 | +#ifdef CONFIG_COMPAT |
|---|
| 644 | +long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
|---|
| 537 | 645 | { |
|---|
| 538 | 646 | switch (cmd) { |
|---|
| 539 | 647 | case FS_IOC32_GETFLAGS: |
|---|
| .. | .. |
|---|
| 550 | 658 | |
|---|
| 551 | 659 | return ovl_ioctl(file, cmd, arg); |
|---|
| 552 | 660 | } |
|---|
| 661 | +#endif |
|---|
| 553 | 662 | |
|---|
| 554 | 663 | enum ovl_copyop { |
|---|
| 555 | 664 | OVL_COPY, |
|---|
| .. | .. |
|---|
| 557 | 666 | OVL_DEDUPE, |
|---|
| 558 | 667 | }; |
|---|
| 559 | 668 | |
|---|
| 560 | | -static ssize_t ovl_copyfile(struct file *file_in, loff_t pos_in, |
|---|
| 669 | +static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, |
|---|
| 561 | 670 | struct file *file_out, loff_t pos_out, |
|---|
| 562 | | - u64 len, unsigned int flags, enum ovl_copyop op) |
|---|
| 671 | + loff_t len, unsigned int flags, enum ovl_copyop op) |
|---|
| 563 | 672 | { |
|---|
| 564 | 673 | struct inode *inode_out = file_inode(file_out); |
|---|
| 565 | 674 | struct fd real_in, real_out; |
|---|
| 566 | 675 | const struct cred *old_cred; |
|---|
| 567 | | - ssize_t ret; |
|---|
| 676 | + loff_t ret; |
|---|
| 568 | 677 | |
|---|
| 569 | 678 | ret = ovl_real_fdget(file_out, &real_out); |
|---|
| 570 | 679 | if (ret) |
|---|
| .. | .. |
|---|
| 585 | 694 | |
|---|
| 586 | 695 | case OVL_CLONE: |
|---|
| 587 | 696 | ret = vfs_clone_file_range(real_in.file, pos_in, |
|---|
| 588 | | - real_out.file, pos_out, len); |
|---|
| 697 | + real_out.file, pos_out, len, flags); |
|---|
| 589 | 698 | break; |
|---|
| 590 | 699 | |
|---|
| 591 | 700 | case OVL_DEDUPE: |
|---|
| 592 | 701 | ret = vfs_dedupe_file_range_one(real_in.file, pos_in, |
|---|
| 593 | | - real_out.file, pos_out, len); |
|---|
| 702 | + real_out.file, pos_out, len, |
|---|
| 703 | + flags); |
|---|
| 594 | 704 | break; |
|---|
| 595 | 705 | } |
|---|
| 596 | | - ovl_revert_creds(old_cred); |
|---|
| 706 | + ovl_revert_creds(file_inode(file_out)->i_sb, old_cred); |
|---|
| 597 | 707 | |
|---|
| 598 | 708 | /* Update size */ |
|---|
| 599 | 709 | ovl_copyattr(ovl_inode_real(inode_out), inode_out); |
|---|
| .. | .. |
|---|
| 612 | 722 | OVL_COPY); |
|---|
| 613 | 723 | } |
|---|
| 614 | 724 | |
|---|
| 615 | | -static int ovl_clone_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 616 | | - struct file *file_out, loff_t pos_out, u64 len) |
|---|
| 725 | +static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 726 | + struct file *file_out, loff_t pos_out, |
|---|
| 727 | + loff_t len, unsigned int remap_flags) |
|---|
| 617 | 728 | { |
|---|
| 618 | | - return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0, |
|---|
| 619 | | - OVL_CLONE); |
|---|
| 620 | | -} |
|---|
| 729 | + enum ovl_copyop op; |
|---|
| 621 | 730 | |
|---|
| 622 | | -static int ovl_dedupe_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 623 | | - struct file *file_out, loff_t pos_out, u64 len) |
|---|
| 624 | | -{ |
|---|
| 731 | + if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) |
|---|
| 732 | + return -EINVAL; |
|---|
| 733 | + |
|---|
| 734 | + if (remap_flags & REMAP_FILE_DEDUP) |
|---|
| 735 | + op = OVL_DEDUPE; |
|---|
| 736 | + else |
|---|
| 737 | + op = OVL_CLONE; |
|---|
| 738 | + |
|---|
| 625 | 739 | /* |
|---|
| 626 | 740 | * Don't copy up because of a dedupe request, this wouldn't make sense |
|---|
| 627 | 741 | * most of the time (data would be duplicated instead of deduplicated). |
|---|
| 628 | 742 | */ |
|---|
| 629 | | - if (!ovl_inode_upper(file_inode(file_in)) || |
|---|
| 630 | | - !ovl_inode_upper(file_inode(file_out))) |
|---|
| 743 | + if (op == OVL_DEDUPE && |
|---|
| 744 | + (!ovl_inode_upper(file_inode(file_in)) || |
|---|
| 745 | + !ovl_inode_upper(file_inode(file_out)))) |
|---|
| 631 | 746 | return -EPERM; |
|---|
| 632 | 747 | |
|---|
| 633 | | - return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0, |
|---|
| 634 | | - OVL_DEDUPE); |
|---|
| 748 | + return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, |
|---|
| 749 | + remap_flags, op); |
|---|
| 635 | 750 | } |
|---|
| 636 | 751 | |
|---|
| 637 | 752 | const struct file_operations ovl_file_operations = { |
|---|
| .. | .. |
|---|
| 645 | 760 | .fallocate = ovl_fallocate, |
|---|
| 646 | 761 | .fadvise = ovl_fadvise, |
|---|
| 647 | 762 | .unlocked_ioctl = ovl_ioctl, |
|---|
| 763 | +#ifdef CONFIG_COMPAT |
|---|
| 648 | 764 | .compat_ioctl = ovl_compat_ioctl, |
|---|
| 765 | +#endif |
|---|
| 766 | + .splice_read = generic_file_splice_read, |
|---|
| 767 | + .splice_write = ovl_splice_write, |
|---|
| 649 | 768 | |
|---|
| 650 | 769 | .copy_file_range = ovl_copy_file_range, |
|---|
| 651 | | - .clone_file_range = ovl_clone_file_range, |
|---|
| 652 | | - .dedupe_file_range = ovl_dedupe_file_range, |
|---|
| 770 | + .remap_file_range = ovl_remap_file_range, |
|---|
| 653 | 771 | }; |
|---|
| 772 | + |
|---|
| 773 | +int __init ovl_aio_request_cache_init(void) |
|---|
| 774 | +{ |
|---|
| 775 | + ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req", |
|---|
| 776 | + sizeof(struct ovl_aio_req), |
|---|
| 777 | + 0, SLAB_HWCACHE_ALIGN, NULL); |
|---|
| 778 | + if (!ovl_aio_request_cachep) |
|---|
| 779 | + return -ENOMEM; |
|---|
| 780 | + |
|---|
| 781 | + return 0; |
|---|
| 782 | +} |
|---|
| 783 | + |
|---|
| 784 | +void ovl_aio_request_cache_destroy(void) |
|---|
| 785 | +{ |
|---|
| 786 | + kmem_cache_destroy(ovl_aio_request_cachep); |
|---|
| 787 | +} |
|---|