.. | .. |
---|
| 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 | +} |
---|