| .. | .. |
|---|
| 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 | +}; |
|---|
| 25 | + |
|---|
| 26 | +static struct kmem_cache *ovl_aio_request_cachep; |
|---|
| 16 | 27 | |
|---|
| 17 | 28 | static char ovl_whatisit(struct inode *inode, struct inode *realinode) |
|---|
| 18 | 29 | { |
|---|
| .. | .. |
|---|
| 34 | 45 | struct file *realfile; |
|---|
| 35 | 46 | const struct cred *old_cred; |
|---|
| 36 | 47 | int flags = file->f_flags | OVL_OPEN_FLAGS; |
|---|
| 48 | + int acc_mode = ACC_MODE(flags); |
|---|
| 49 | + int err; |
|---|
| 50 | + |
|---|
| 51 | + if (flags & O_APPEND) |
|---|
| 52 | + acc_mode |= MAY_APPEND; |
|---|
| 37 | 53 | |
|---|
| 38 | 54 | 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); |
|---|
| 55 | + err = inode_permission(realinode, MAY_OPEN | acc_mode); |
|---|
| 56 | + if (err) { |
|---|
| 57 | + realfile = ERR_PTR(err); |
|---|
| 58 | + } else if (old_cred && !inode_owner_or_capable(realinode)) { |
|---|
| 59 | + realfile = ERR_PTR(-EPERM); |
|---|
| 60 | + } else { |
|---|
| 61 | + realfile = open_with_fake_path(&file->f_path, flags, realinode, |
|---|
| 62 | + current_cred()); |
|---|
| 63 | + } |
|---|
| 64 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 42 | 65 | |
|---|
| 43 | 66 | pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", |
|---|
| 44 | 67 | file, file, ovl_whatisit(inode, realinode), file->f_flags, |
|---|
| .. | .. |
|---|
| 115 | 138 | |
|---|
| 116 | 139 | static int ovl_real_fdget(const struct file *file, struct fd *real) |
|---|
| 117 | 140 | { |
|---|
| 141 | + if (d_is_dir(file_dentry(file))) { |
|---|
| 142 | + real->flags = 0; |
|---|
| 143 | + real->file = ovl_dir_real_file(file, false); |
|---|
| 144 | + |
|---|
| 145 | + return PTR_ERR_OR_ZERO(real->file); |
|---|
| 146 | + } |
|---|
| 147 | + |
|---|
| 118 | 148 | return ovl_real_fdget_meta(file, real, false); |
|---|
| 119 | 149 | } |
|---|
| 120 | 150 | |
|---|
| .. | .. |
|---|
| 176 | 206 | * limitations that are more strict than ->s_maxbytes for specific |
|---|
| 177 | 207 | * files, so we use the real file to perform seeks. |
|---|
| 178 | 208 | */ |
|---|
| 179 | | - inode_lock(inode); |
|---|
| 209 | + ovl_inode_lock(inode); |
|---|
| 180 | 210 | real.file->f_pos = file->f_pos; |
|---|
| 181 | 211 | |
|---|
| 182 | 212 | old_cred = ovl_override_creds(inode->i_sb); |
|---|
| 183 | 213 | ret = vfs_llseek(real.file, offset, whence); |
|---|
| 184 | | - ovl_revert_creds(old_cred); |
|---|
| 214 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 185 | 215 | |
|---|
| 186 | 216 | file->f_pos = real.file->f_pos; |
|---|
| 187 | | - inode_unlock(inode); |
|---|
| 217 | + ovl_inode_unlock(inode); |
|---|
| 188 | 218 | |
|---|
| 189 | 219 | fdput(real); |
|---|
| 190 | 220 | |
|---|
| .. | .. |
|---|
| 213 | 243 | touch_atime(&file->f_path); |
|---|
| 214 | 244 | } |
|---|
| 215 | 245 | |
|---|
| 216 | | -static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb) |
|---|
| 246 | +static inline void ovl_aio_put(struct ovl_aio_req *aio_req) |
|---|
| 217 | 247 | { |
|---|
| 218 | | - int ifl = iocb->ki_flags; |
|---|
| 219 | | - rwf_t flags = 0; |
|---|
| 248 | + if (refcount_dec_and_test(&aio_req->ref)) { |
|---|
| 249 | + fput(aio_req->iocb.ki_filp); |
|---|
| 250 | + kmem_cache_free(ovl_aio_request_cachep, aio_req); |
|---|
| 251 | + } |
|---|
| 252 | +} |
|---|
| 220 | 253 | |
|---|
| 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; |
|---|
| 254 | +static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req) |
|---|
| 255 | +{ |
|---|
| 256 | + struct kiocb *iocb = &aio_req->iocb; |
|---|
| 257 | + struct kiocb *orig_iocb = aio_req->orig_iocb; |
|---|
| 229 | 258 | |
|---|
| 230 | | - return flags; |
|---|
| 259 | + if (iocb->ki_flags & IOCB_WRITE) { |
|---|
| 260 | + struct inode *inode = file_inode(orig_iocb->ki_filp); |
|---|
| 261 | + |
|---|
| 262 | + /* Actually acquired in ovl_write_iter() */ |
|---|
| 263 | + __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, |
|---|
| 264 | + SB_FREEZE_WRITE); |
|---|
| 265 | + file_end_write(iocb->ki_filp); |
|---|
| 266 | + ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 267 | + } |
|---|
| 268 | + |
|---|
| 269 | + orig_iocb->ki_pos = iocb->ki_pos; |
|---|
| 270 | + ovl_aio_put(aio_req); |
|---|
| 271 | +} |
|---|
| 272 | + |
|---|
| 273 | +static void ovl_aio_rw_complete(struct kiocb *iocb, long res, long res2) |
|---|
| 274 | +{ |
|---|
| 275 | + struct ovl_aio_req *aio_req = container_of(iocb, |
|---|
| 276 | + struct ovl_aio_req, iocb); |
|---|
| 277 | + struct kiocb *orig_iocb = aio_req->orig_iocb; |
|---|
| 278 | + |
|---|
| 279 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 280 | + orig_iocb->ki_complete(orig_iocb, res, res2); |
|---|
| 231 | 281 | } |
|---|
| 232 | 282 | |
|---|
| 233 | 283 | static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) |
|---|
| .. | .. |
|---|
| 244 | 294 | if (ret) |
|---|
| 245 | 295 | return ret; |
|---|
| 246 | 296 | |
|---|
| 297 | + ret = -EINVAL; |
|---|
| 298 | + if (iocb->ki_flags & IOCB_DIRECT && |
|---|
| 299 | + (!real.file->f_mapping->a_ops || |
|---|
| 300 | + !real.file->f_mapping->a_ops->direct_IO)) |
|---|
| 301 | + goto out_fdput; |
|---|
| 302 | + |
|---|
| 247 | 303 | 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); |
|---|
| 304 | + if (is_sync_kiocb(iocb)) { |
|---|
| 305 | + ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, |
|---|
| 306 | + iocb_to_rw_flags(iocb->ki_flags, |
|---|
| 307 | + OVL_IOCB_MASK)); |
|---|
| 308 | + } else { |
|---|
| 309 | + struct ovl_aio_req *aio_req; |
|---|
| 310 | + |
|---|
| 311 | + ret = -ENOMEM; |
|---|
| 312 | + aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); |
|---|
| 313 | + if (!aio_req) |
|---|
| 314 | + goto out; |
|---|
| 315 | + |
|---|
| 316 | + real.flags = 0; |
|---|
| 317 | + aio_req->orig_iocb = iocb; |
|---|
| 318 | + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); |
|---|
| 319 | + aio_req->iocb.ki_complete = ovl_aio_rw_complete; |
|---|
| 320 | + refcount_set(&aio_req->ref, 2); |
|---|
| 321 | + ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); |
|---|
| 322 | + ovl_aio_put(aio_req); |
|---|
| 323 | + if (ret != -EIOCBQUEUED) |
|---|
| 324 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 325 | + } |
|---|
| 326 | +out: |
|---|
| 327 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 251 | 328 | |
|---|
| 252 | 329 | ovl_file_accessed(file); |
|---|
| 253 | | - |
|---|
| 330 | +out_fdput: |
|---|
| 254 | 331 | fdput(real); |
|---|
| 255 | 332 | |
|---|
| 256 | 333 | return ret; |
|---|
| .. | .. |
|---|
| 263 | 340 | struct fd real; |
|---|
| 264 | 341 | const struct cred *old_cred; |
|---|
| 265 | 342 | ssize_t ret; |
|---|
| 343 | + int ifl = iocb->ki_flags; |
|---|
| 266 | 344 | |
|---|
| 267 | 345 | if (!iov_iter_count(iter)) |
|---|
| 268 | 346 | return 0; |
|---|
| .. | .. |
|---|
| 278 | 356 | if (ret) |
|---|
| 279 | 357 | goto out_unlock; |
|---|
| 280 | 358 | |
|---|
| 359 | + ret = -EINVAL; |
|---|
| 360 | + if (iocb->ki_flags & IOCB_DIRECT && |
|---|
| 361 | + (!real.file->f_mapping->a_ops || |
|---|
| 362 | + !real.file->f_mapping->a_ops->direct_IO)) |
|---|
| 363 | + goto out_fdput; |
|---|
| 364 | + |
|---|
| 365 | + if (!ovl_should_sync(OVL_FS(inode->i_sb))) |
|---|
| 366 | + ifl &= ~(IOCB_DSYNC | IOCB_SYNC); |
|---|
| 367 | + |
|---|
| 281 | 368 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 369 | + if (is_sync_kiocb(iocb)) { |
|---|
| 370 | + file_start_write(real.file); |
|---|
| 371 | + ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, |
|---|
| 372 | + iocb_to_rw_flags(ifl, OVL_IOCB_MASK)); |
|---|
| 373 | + file_end_write(real.file); |
|---|
| 374 | + /* Update size */ |
|---|
| 375 | + ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 376 | + } else { |
|---|
| 377 | + struct ovl_aio_req *aio_req; |
|---|
| 378 | + |
|---|
| 379 | + ret = -ENOMEM; |
|---|
| 380 | + aio_req = kmem_cache_zalloc(ovl_aio_request_cachep, GFP_KERNEL); |
|---|
| 381 | + if (!aio_req) |
|---|
| 382 | + goto out; |
|---|
| 383 | + |
|---|
| 384 | + file_start_write(real.file); |
|---|
| 385 | + /* Pacify lockdep, same trick as done in aio_write() */ |
|---|
| 386 | + __sb_writers_release(file_inode(real.file)->i_sb, |
|---|
| 387 | + SB_FREEZE_WRITE); |
|---|
| 388 | + real.flags = 0; |
|---|
| 389 | + aio_req->orig_iocb = iocb; |
|---|
| 390 | + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); |
|---|
| 391 | + aio_req->iocb.ki_flags = ifl; |
|---|
| 392 | + aio_req->iocb.ki_complete = ovl_aio_rw_complete; |
|---|
| 393 | + refcount_set(&aio_req->ref, 2); |
|---|
| 394 | + ret = vfs_iocb_iter_write(real.file, &aio_req->iocb, iter); |
|---|
| 395 | + ovl_aio_put(aio_req); |
|---|
| 396 | + if (ret != -EIOCBQUEUED) |
|---|
| 397 | + ovl_aio_cleanup_handler(aio_req); |
|---|
| 398 | + } |
|---|
| 399 | +out: |
|---|
| 400 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 401 | +out_fdput: |
|---|
| 402 | + fdput(real); |
|---|
| 403 | + |
|---|
| 404 | +out_unlock: |
|---|
| 405 | + inode_unlock(inode); |
|---|
| 406 | + |
|---|
| 407 | + return ret; |
|---|
| 408 | +} |
|---|
| 409 | + |
|---|
| 410 | +/* |
|---|
| 411 | + * Calling iter_file_splice_write() directly from overlay's f_op may deadlock |
|---|
| 412 | + * due to lock order inversion between pipe->mutex in iter_file_splice_write() |
|---|
| 413 | + * and file_start_write(real.file) in ovl_write_iter(). |
|---|
| 414 | + * |
|---|
| 415 | + * So do everything ovl_write_iter() does and call iter_file_splice_write() on |
|---|
| 416 | + * the real file. |
|---|
| 417 | + */ |
|---|
| 418 | +static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, |
|---|
| 419 | + loff_t *ppos, size_t len, unsigned int flags) |
|---|
| 420 | +{ |
|---|
| 421 | + struct fd real; |
|---|
| 422 | + const struct cred *old_cred; |
|---|
| 423 | + struct inode *inode = file_inode(out); |
|---|
| 424 | + struct inode *realinode = ovl_inode_real(inode); |
|---|
| 425 | + ssize_t ret; |
|---|
| 426 | + |
|---|
| 427 | + inode_lock(inode); |
|---|
| 428 | + /* Update mode */ |
|---|
| 429 | + ovl_copyattr(realinode, inode); |
|---|
| 430 | + ret = file_remove_privs(out); |
|---|
| 431 | + if (ret) |
|---|
| 432 | + goto out_unlock; |
|---|
| 433 | + |
|---|
| 434 | + ret = ovl_real_fdget(out, &real); |
|---|
| 435 | + if (ret) |
|---|
| 436 | + goto out_unlock; |
|---|
| 437 | + |
|---|
| 438 | + old_cred = ovl_override_creds(inode->i_sb); |
|---|
| 282 | 439 | file_start_write(real.file); |
|---|
| 283 | | - ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, |
|---|
| 284 | | - ovl_iocb_to_rwf(iocb)); |
|---|
| 440 | + |
|---|
| 441 | + ret = iter_file_splice_write(pipe, real.file, ppos, len, flags); |
|---|
| 442 | + |
|---|
| 285 | 443 | file_end_write(real.file); |
|---|
| 286 | | - ovl_revert_creds(old_cred); |
|---|
| 287 | | - |
|---|
| 288 | 444 | /* Update size */ |
|---|
| 289 | | - ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 290 | | - |
|---|
| 445 | + ovl_copyattr(realinode, inode); |
|---|
| 446 | + ovl_revert_creds(inode->i_sb, old_cred); |
|---|
| 291 | 447 | fdput(real); |
|---|
| 292 | 448 | |
|---|
| 293 | 449 | out_unlock: |
|---|
| .. | .. |
|---|
| 302 | 458 | const struct cred *old_cred; |
|---|
| 303 | 459 | int ret; |
|---|
| 304 | 460 | |
|---|
| 461 | + ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb)); |
|---|
| 462 | + if (ret <= 0) |
|---|
| 463 | + return ret; |
|---|
| 464 | + |
|---|
| 305 | 465 | ret = ovl_real_fdget_meta(file, &real, !datasync); |
|---|
| 306 | 466 | if (ret) |
|---|
| 307 | 467 | return ret; |
|---|
| .. | .. |
|---|
| 310 | 470 | if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) { |
|---|
| 311 | 471 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 312 | 472 | ret = vfs_fsync_range(real.file, start, end, datasync); |
|---|
| 313 | | - ovl_revert_creds(old_cred); |
|---|
| 473 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 314 | 474 | } |
|---|
| 315 | 475 | |
|---|
| 316 | 476 | fdput(real); |
|---|
| .. | .. |
|---|
| 334 | 494 | |
|---|
| 335 | 495 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 336 | 496 | ret = call_mmap(vma->vm_file, vma); |
|---|
| 337 | | - ovl_revert_creds(old_cred); |
|---|
| 497 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 338 | 498 | |
|---|
| 339 | 499 | if (ret) { |
|---|
| 340 | 500 | /* Drop reference count from new vm_file value */ |
|---|
| .. | .. |
|---|
| 356 | 516 | const struct cred *old_cred; |
|---|
| 357 | 517 | int ret; |
|---|
| 358 | 518 | |
|---|
| 519 | + inode_lock(inode); |
|---|
| 520 | + /* Update mode */ |
|---|
| 521 | + ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 522 | + ret = file_remove_privs(file); |
|---|
| 523 | + if (ret) |
|---|
| 524 | + goto out_unlock; |
|---|
| 525 | + |
|---|
| 359 | 526 | ret = ovl_real_fdget(file, &real); |
|---|
| 360 | 527 | if (ret) |
|---|
| 361 | | - return ret; |
|---|
| 528 | + goto out_unlock; |
|---|
| 362 | 529 | |
|---|
| 363 | 530 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 364 | 531 | ret = vfs_fallocate(real.file, mode, offset, len); |
|---|
| 365 | | - ovl_revert_creds(old_cred); |
|---|
| 532 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 366 | 533 | |
|---|
| 367 | 534 | /* Update size */ |
|---|
| 368 | 535 | ovl_copyattr(ovl_inode_real(inode), inode); |
|---|
| 369 | 536 | |
|---|
| 370 | 537 | fdput(real); |
|---|
| 538 | + |
|---|
| 539 | +out_unlock: |
|---|
| 540 | + inode_unlock(inode); |
|---|
| 371 | 541 | |
|---|
| 372 | 542 | return ret; |
|---|
| 373 | 543 | } |
|---|
| .. | .. |
|---|
| 384 | 554 | |
|---|
| 385 | 555 | old_cred = ovl_override_creds(file_inode(file)->i_sb); |
|---|
| 386 | 556 | ret = vfs_fadvise(real.file, offset, len, advice); |
|---|
| 387 | | - ovl_revert_creds(old_cred); |
|---|
| 557 | + ovl_revert_creds(file_inode(file)->i_sb, old_cred); |
|---|
| 388 | 558 | |
|---|
| 389 | 559 | fdput(real); |
|---|
| 390 | 560 | |
|---|
| .. | .. |
|---|
| 395 | 565 | unsigned long arg) |
|---|
| 396 | 566 | { |
|---|
| 397 | 567 | struct fd real; |
|---|
| 398 | | - const struct cred *old_cred; |
|---|
| 399 | 568 | long ret; |
|---|
| 400 | 569 | |
|---|
| 401 | 570 | ret = ovl_real_fdget(file, &real); |
|---|
| 402 | 571 | if (ret) |
|---|
| 403 | 572 | return ret; |
|---|
| 404 | 573 | |
|---|
| 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); |
|---|
| 574 | + ret = security_file_ioctl(real.file, cmd, arg); |
|---|
| 575 | + if (!ret) { |
|---|
| 576 | + /* |
|---|
| 577 | + * Don't override creds, since we currently can't safely check |
|---|
| 578 | + * permissions before doing so. |
|---|
| 579 | + */ |
|---|
| 580 | + ret = vfs_ioctl(real.file, cmd, arg); |
|---|
| 581 | + } |
|---|
| 408 | 582 | |
|---|
| 409 | 583 | fdput(real); |
|---|
| 410 | 584 | |
|---|
| .. | .. |
|---|
| 412 | 586 | } |
|---|
| 413 | 587 | |
|---|
| 414 | 588 | static long ovl_ioctl_set_flags(struct file *file, unsigned int cmd, |
|---|
| 415 | | - unsigned long arg, unsigned int iflags) |
|---|
| 589 | + unsigned long arg) |
|---|
| 416 | 590 | { |
|---|
| 417 | 591 | long ret; |
|---|
| 418 | 592 | struct inode *inode = file_inode(file); |
|---|
| 419 | | - unsigned int old_iflags; |
|---|
| 420 | 593 | |
|---|
| 421 | 594 | if (!inode_owner_or_capable(inode)) |
|---|
| 422 | 595 | return -EACCES; |
|---|
| .. | .. |
|---|
| 427 | 600 | |
|---|
| 428 | 601 | inode_lock(inode); |
|---|
| 429 | 602 | |
|---|
| 430 | | - /* Check the capability before cred override */ |
|---|
| 603 | + /* |
|---|
| 604 | + * Prevent copy up if immutable and has no CAP_LINUX_IMMUTABLE |
|---|
| 605 | + * capability. |
|---|
| 606 | + */ |
|---|
| 431 | 607 | ret = -EPERM; |
|---|
| 432 | | - old_iflags = READ_ONCE(inode->i_flags); |
|---|
| 433 | | - if (((iflags ^ old_iflags) & (S_APPEND | S_IMMUTABLE)) && |
|---|
| 608 | + if (!ovl_has_upperdata(inode) && IS_IMMUTABLE(inode) && |
|---|
| 434 | 609 | !capable(CAP_LINUX_IMMUTABLE)) |
|---|
| 435 | 610 | goto unlock; |
|---|
| 436 | 611 | |
|---|
| .. | .. |
|---|
| 450 | 625 | |
|---|
| 451 | 626 | } |
|---|
| 452 | 627 | |
|---|
| 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) |
|---|
| 628 | +long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
|---|
| 511 | 629 | { |
|---|
| 512 | 630 | long ret; |
|---|
| 513 | 631 | |
|---|
| .. | .. |
|---|
| 517 | 635 | ret = ovl_real_ioctl(file, cmd, arg); |
|---|
| 518 | 636 | break; |
|---|
| 519 | 637 | |
|---|
| 520 | | - case FS_IOC_SETFLAGS: |
|---|
| 521 | | - ret = ovl_ioctl_set_fsflags(file, cmd, arg); |
|---|
| 522 | | - break; |
|---|
| 523 | | - |
|---|
| 524 | 638 | case FS_IOC_FSSETXATTR: |
|---|
| 525 | | - ret = ovl_ioctl_set_fsxflags(file, cmd, arg); |
|---|
| 639 | + case FS_IOC_SETFLAGS: |
|---|
| 640 | + ret = ovl_ioctl_set_flags(file, cmd, arg); |
|---|
| 526 | 641 | break; |
|---|
| 527 | 642 | |
|---|
| 528 | 643 | default: |
|---|
| .. | .. |
|---|
| 532 | 647 | return ret; |
|---|
| 533 | 648 | } |
|---|
| 534 | 649 | |
|---|
| 535 | | -static long ovl_compat_ioctl(struct file *file, unsigned int cmd, |
|---|
| 536 | | - unsigned long arg) |
|---|
| 650 | +#ifdef CONFIG_COMPAT |
|---|
| 651 | +long ovl_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
|---|
| 537 | 652 | { |
|---|
| 538 | 653 | switch (cmd) { |
|---|
| 539 | 654 | case FS_IOC32_GETFLAGS: |
|---|
| .. | .. |
|---|
| 550 | 665 | |
|---|
| 551 | 666 | return ovl_ioctl(file, cmd, arg); |
|---|
| 552 | 667 | } |
|---|
| 668 | +#endif |
|---|
| 553 | 669 | |
|---|
| 554 | 670 | enum ovl_copyop { |
|---|
| 555 | 671 | OVL_COPY, |
|---|
| .. | .. |
|---|
| 557 | 673 | OVL_DEDUPE, |
|---|
| 558 | 674 | }; |
|---|
| 559 | 675 | |
|---|
| 560 | | -static ssize_t ovl_copyfile(struct file *file_in, loff_t pos_in, |
|---|
| 676 | +static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, |
|---|
| 561 | 677 | struct file *file_out, loff_t pos_out, |
|---|
| 562 | | - u64 len, unsigned int flags, enum ovl_copyop op) |
|---|
| 678 | + loff_t len, unsigned int flags, enum ovl_copyop op) |
|---|
| 563 | 679 | { |
|---|
| 564 | 680 | struct inode *inode_out = file_inode(file_out); |
|---|
| 565 | 681 | struct fd real_in, real_out; |
|---|
| 566 | 682 | const struct cred *old_cred; |
|---|
| 567 | | - ssize_t ret; |
|---|
| 683 | + loff_t ret; |
|---|
| 684 | + |
|---|
| 685 | + inode_lock(inode_out); |
|---|
| 686 | + if (op != OVL_DEDUPE) { |
|---|
| 687 | + /* Update mode */ |
|---|
| 688 | + ovl_copyattr(ovl_inode_real(inode_out), inode_out); |
|---|
| 689 | + ret = file_remove_privs(file_out); |
|---|
| 690 | + if (ret) |
|---|
| 691 | + goto out_unlock; |
|---|
| 692 | + } |
|---|
| 568 | 693 | |
|---|
| 569 | 694 | ret = ovl_real_fdget(file_out, &real_out); |
|---|
| 570 | 695 | if (ret) |
|---|
| 571 | | - return ret; |
|---|
| 696 | + goto out_unlock; |
|---|
| 572 | 697 | |
|---|
| 573 | 698 | ret = ovl_real_fdget(file_in, &real_in); |
|---|
| 574 | 699 | if (ret) { |
|---|
| 575 | 700 | fdput(real_out); |
|---|
| 576 | | - return ret; |
|---|
| 701 | + goto out_unlock; |
|---|
| 577 | 702 | } |
|---|
| 578 | 703 | |
|---|
| 579 | 704 | old_cred = ovl_override_creds(file_inode(file_out)->i_sb); |
|---|
| .. | .. |
|---|
| 585 | 710 | |
|---|
| 586 | 711 | case OVL_CLONE: |
|---|
| 587 | 712 | ret = vfs_clone_file_range(real_in.file, pos_in, |
|---|
| 588 | | - real_out.file, pos_out, len); |
|---|
| 713 | + real_out.file, pos_out, len, flags); |
|---|
| 589 | 714 | break; |
|---|
| 590 | 715 | |
|---|
| 591 | 716 | case OVL_DEDUPE: |
|---|
| 592 | 717 | ret = vfs_dedupe_file_range_one(real_in.file, pos_in, |
|---|
| 593 | | - real_out.file, pos_out, len); |
|---|
| 718 | + real_out.file, pos_out, len, |
|---|
| 719 | + flags); |
|---|
| 594 | 720 | break; |
|---|
| 595 | 721 | } |
|---|
| 596 | | - ovl_revert_creds(old_cred); |
|---|
| 722 | + ovl_revert_creds(file_inode(file_out)->i_sb, old_cred); |
|---|
| 597 | 723 | |
|---|
| 598 | 724 | /* Update size */ |
|---|
| 599 | 725 | ovl_copyattr(ovl_inode_real(inode_out), inode_out); |
|---|
| 600 | 726 | |
|---|
| 601 | 727 | fdput(real_in); |
|---|
| 602 | 728 | fdput(real_out); |
|---|
| 729 | + |
|---|
| 730 | +out_unlock: |
|---|
| 731 | + inode_unlock(inode_out); |
|---|
| 603 | 732 | |
|---|
| 604 | 733 | return ret; |
|---|
| 605 | 734 | } |
|---|
| .. | .. |
|---|
| 612 | 741 | OVL_COPY); |
|---|
| 613 | 742 | } |
|---|
| 614 | 743 | |
|---|
| 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) |
|---|
| 744 | +static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in, |
|---|
| 745 | + struct file *file_out, loff_t pos_out, |
|---|
| 746 | + loff_t len, unsigned int remap_flags) |
|---|
| 617 | 747 | { |
|---|
| 618 | | - return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0, |
|---|
| 619 | | - OVL_CLONE); |
|---|
| 620 | | -} |
|---|
| 748 | + enum ovl_copyop op; |
|---|
| 621 | 749 | |
|---|
| 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 | | -{ |
|---|
| 750 | + if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY)) |
|---|
| 751 | + return -EINVAL; |
|---|
| 752 | + |
|---|
| 753 | + if (remap_flags & REMAP_FILE_DEDUP) |
|---|
| 754 | + op = OVL_DEDUPE; |
|---|
| 755 | + else |
|---|
| 756 | + op = OVL_CLONE; |
|---|
| 757 | + |
|---|
| 625 | 758 | /* |
|---|
| 626 | 759 | * Don't copy up because of a dedupe request, this wouldn't make sense |
|---|
| 627 | 760 | * most of the time (data would be duplicated instead of deduplicated). |
|---|
| 628 | 761 | */ |
|---|
| 629 | | - if (!ovl_inode_upper(file_inode(file_in)) || |
|---|
| 630 | | - !ovl_inode_upper(file_inode(file_out))) |
|---|
| 762 | + if (op == OVL_DEDUPE && |
|---|
| 763 | + (!ovl_inode_upper(file_inode(file_in)) || |
|---|
| 764 | + !ovl_inode_upper(file_inode(file_out)))) |
|---|
| 631 | 765 | return -EPERM; |
|---|
| 632 | 766 | |
|---|
| 633 | | - return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, 0, |
|---|
| 634 | | - OVL_DEDUPE); |
|---|
| 767 | + return ovl_copyfile(file_in, pos_in, file_out, pos_out, len, |
|---|
| 768 | + remap_flags, op); |
|---|
| 635 | 769 | } |
|---|
| 636 | 770 | |
|---|
| 637 | 771 | const struct file_operations ovl_file_operations = { |
|---|
| .. | .. |
|---|
| 645 | 779 | .fallocate = ovl_fallocate, |
|---|
| 646 | 780 | .fadvise = ovl_fadvise, |
|---|
| 647 | 781 | .unlocked_ioctl = ovl_ioctl, |
|---|
| 782 | +#ifdef CONFIG_COMPAT |
|---|
| 648 | 783 | .compat_ioctl = ovl_compat_ioctl, |
|---|
| 784 | +#endif |
|---|
| 785 | + .splice_read = generic_file_splice_read, |
|---|
| 786 | + .splice_write = ovl_splice_write, |
|---|
| 649 | 787 | |
|---|
| 650 | 788 | .copy_file_range = ovl_copy_file_range, |
|---|
| 651 | | - .clone_file_range = ovl_clone_file_range, |
|---|
| 652 | | - .dedupe_file_range = ovl_dedupe_file_range, |
|---|
| 789 | + .remap_file_range = ovl_remap_file_range, |
|---|
| 653 | 790 | }; |
|---|
| 791 | + |
|---|
| 792 | +int __init ovl_aio_request_cache_init(void) |
|---|
| 793 | +{ |
|---|
| 794 | + ovl_aio_request_cachep = kmem_cache_create("ovl_aio_req", |
|---|
| 795 | + sizeof(struct ovl_aio_req), |
|---|
| 796 | + 0, SLAB_HWCACHE_ALIGN, NULL); |
|---|
| 797 | + if (!ovl_aio_request_cachep) |
|---|
| 798 | + return -ENOMEM; |
|---|
| 799 | + |
|---|
| 800 | + return 0; |
|---|
| 801 | +} |
|---|
| 802 | + |
|---|
| 803 | +void ovl_aio_request_cache_destroy(void) |
|---|
| 804 | +{ |
|---|
| 805 | + kmem_cache_destroy(ovl_aio_request_cachep); |
|---|
| 806 | +} |
|---|