diff options
Diffstat (limited to 'fs/overlayfs/file.c')
| -rw-r--r-- | fs/overlayfs/file.c | 645 |
1 files changed, 380 insertions, 265 deletions
diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 84dd957efa24..cbae89457234 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #include <linux/cred.h> @@ -11,6 +8,10 @@ #include <linux/mount.h> #include <linux/xattr.h> #include <linux/uio.h> +#include <linux/uaccess.h> +#include <linux/security.h> +#include <linux/fs.h> +#include <linux/backing-file.h> #include "overlayfs.h" static char ovl_whatisit(struct inode *inode, struct inode *realinode) @@ -24,16 +25,32 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode) } static struct file *ovl_open_realfile(const struct file *file, - struct inode *realinode) + const struct path *realpath) { + struct inode *realinode = d_inode(realpath->dentry); struct inode *inode = file_inode(file); + struct mnt_idmap *real_idmap; struct file *realfile; - const struct cred *old_cred; + int flags = file->f_flags | OVL_OPEN_FLAGS; + int acc_mode = ACC_MODE(flags); + int err; - old_cred = ovl_override_creds(inode->i_sb); - realfile = open_with_fake_path(&file->f_path, file->f_flags | O_NOATIME, - realinode, current_cred()); - revert_creds(old_cred); + if (flags & O_APPEND) + acc_mode |= MAY_APPEND; + + with_ovl_creds(inode->i_sb) { + real_idmap = mnt_idmap(realpath->mnt); + err = inode_permission(real_idmap, realinode, MAY_OPEN | acc_mode); + if (err) { + realfile = ERR_PTR(err); + } else { + if (!inode_owner_or_capable(real_idmap, realinode)) + flags &= ~O_NOATIME; + + realfile = backing_file_open(file_user_path(file), + flags, realpath, current_cred()); + } + } pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n", file, file, ovl_whatisit(inode, realinode), file->f_flags, @@ -49,23 +66,13 @@ static int ovl_change_flags(struct file *file, unsigned int flags) struct inode *inode = file_inode(file); int err; - /* No atime modificaton on underlying */ - flags |= O_NOATIME; - - /* If some flag changed that cannot be changed then something's amiss */ - if (WARN_ON((file->f_flags ^ flags) & ~OVL_SETFL_MASK)) - return -EIO; - flags &= OVL_SETFL_MASK; if (((flags ^ file->f_flags) & O_APPEND) && IS_APPEND(inode)) return -EPERM; - if (flags & O_DIRECT) { - if (!file->f_mapping->a_ops || - !file->f_mapping->a_ops->direct_IO) - return -EINVAL; - } + if ((flags & O_DIRECT) && !(file->f_mode & FMODE_CAN_ODIRECT)) + return -EINVAL; if (file->f_op->check_flags) { err = file->f_op->check_flags(flags); @@ -75,86 +82,220 @@ static int ovl_change_flags(struct file *file, unsigned int flags) spin_lock(&file->f_lock); file->f_flags = (file->f_flags & ~OVL_SETFL_MASK) | flags; + file->f_iocb_flags = iocb_flags(file); spin_unlock(&file->f_lock); return 0; } -static int ovl_real_fdget_meta(const struct file *file, struct fd *real, - bool allow_meta) +struct ovl_file { + struct file *realfile; + struct file *upperfile; +}; + +struct ovl_file *ovl_file_alloc(struct file *realfile) { - struct inode *inode = file_inode(file); - struct inode *realinode; + struct ovl_file *of = kzalloc(sizeof(struct ovl_file), GFP_KERNEL); - real->flags = 0; - real->file = file->private_data; + if (unlikely(!of)) + return NULL; - if (allow_meta) - realinode = ovl_inode_real(inode); - else - realinode = ovl_inode_realdata(inode); + of->realfile = realfile; + return of; +} + +void ovl_file_free(struct ovl_file *of) +{ + fput(of->realfile); + if (of->upperfile) + fput(of->upperfile); + kfree(of); +} + +static bool ovl_is_real_file(const struct file *realfile, + const struct path *realpath) +{ + return file_inode(realfile) == d_inode(realpath->dentry); +} + +static struct file *ovl_real_file_path(const struct file *file, + const struct path *realpath) +{ + struct ovl_file *of = file->private_data; + struct file *realfile = of->realfile; - /* Has it been copied up since we'd opened it? */ - if (unlikely(file_inode(real->file) != realinode)) { - real->flags = FDPUT_FPUT; - real->file = ovl_open_realfile(file, realinode); + if (WARN_ON_ONCE(!realpath->dentry)) + return ERR_PTR(-EIO); - return PTR_ERR_OR_ZERO(real->file); + /* + * If the realfile that we want is not where the data used to be at + * open time, either we'd been copied up, or it's an fsync of a + * metacopied file. We need the upperfile either way, so see if it + * is already opened and if it is not then open and store it. + */ + if (unlikely(!ovl_is_real_file(realfile, realpath))) { + struct file *upperfile = READ_ONCE(of->upperfile); + struct file *old; + + if (!upperfile) { /* Nobody opened upperfile yet */ + upperfile = ovl_open_realfile(file, realpath); + if (IS_ERR(upperfile)) + return upperfile; + + /* Store the upperfile for later */ + old = cmpxchg_release(&of->upperfile, NULL, upperfile); + if (old) { /* Someone opened upperfile before us */ + fput(upperfile); + upperfile = old; + } + } + /* + * Stored file must be from the right inode, unless someone's + * been corrupting the upper layer. + */ + if (WARN_ON_ONCE(!ovl_is_real_file(upperfile, realpath))) + return ERR_PTR(-EIO); + + realfile = upperfile; } /* Did the flags change since open? */ - if (unlikely((file->f_flags ^ real->file->f_flags) & ~O_NOATIME)) - return ovl_change_flags(real->file, file->f_flags); + if (unlikely((file->f_flags ^ realfile->f_flags) & ~OVL_OPEN_FLAGS)) { + int err = ovl_change_flags(realfile, file->f_flags); - return 0; + if (err) + return ERR_PTR(err); + } + + return realfile; } -static int ovl_real_fdget(const struct file *file, struct fd *real) +static struct file *ovl_real_file(const struct file *file) { - return ovl_real_fdget_meta(file, real, false); + struct dentry *dentry = file_dentry(file); + struct path realpath; + int err; + + if (d_is_dir(dentry)) { + struct file *f = ovl_dir_real_file(file, false); + + if (WARN_ON_ONCE(!f)) + return ERR_PTR(-EIO); + return f; + } + + /* lazy lookup and verify of lowerdata */ + err = ovl_verify_lowerdata(dentry); + if (err) + return ERR_PTR(err); + + ovl_path_realdata(dentry, &realpath); + + return ovl_real_file_path(file, &realpath); } static int ovl_open(struct inode *inode, struct file *file) { struct dentry *dentry = file_dentry(file); struct file *realfile; + struct path realpath; + struct ovl_file *of; int err; - err = ovl_open_maybe_copy_up(dentry, file->f_flags); + /* lazy lookup and verify lowerdata */ + err = ovl_verify_lowerdata(dentry); + if (err) + return err; + + err = ovl_maybe_copy_up(dentry, file->f_flags); if (err) return err; /* No longer need these flags, so don't pass them on to underlying fs */ file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); - realfile = ovl_open_realfile(file, ovl_inode_realdata(inode)); + ovl_path_realdata(dentry, &realpath); + if (!realpath.dentry) + return -EIO; + + realfile = ovl_open_realfile(file, &realpath); if (IS_ERR(realfile)) return PTR_ERR(realfile); - file->private_data = realfile; + of = ovl_file_alloc(realfile); + if (!of) { + fput(realfile); + return -ENOMEM; + } + + file->private_data = of; return 0; } static int ovl_release(struct inode *inode, struct file *file) { - fput(file->private_data); - + ovl_file_free(file->private_data); return 0; } static loff_t ovl_llseek(struct file *file, loff_t offset, int whence) { - struct inode *realinode = ovl_inode_real(file_inode(file)); + struct inode *inode = file_inode(file); + struct file *realfile; + loff_t ret; + + /* + * The two special cases below do not need to involve real fs, + * so we can optimizing concurrent callers. + */ + if (offset == 0) { + if (whence == SEEK_CUR) + return file->f_pos; - return generic_file_llseek_size(file, offset, whence, - realinode->i_sb->s_maxbytes, - i_size_read(realinode)); + if (whence == SEEK_SET) + return vfs_setpos(file, 0, 0); + } + + realfile = ovl_real_file(file); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); + + /* + * Overlay file f_pos is the master copy that is preserved + * through copy up and modified on read/write, but only real + * fs knows how to SEEK_HOLE/SEEK_DATA and real fs may impose + * limitations that are more strict than ->s_maxbytes for specific + * files, so we use the real file to perform seeks. + */ + ovl_inode_lock(inode); + realfile->f_pos = file->f_pos; + + with_ovl_creds(inode->i_sb) + ret = vfs_llseek(realfile, offset, whence); + + file->f_pos = realfile->f_pos; + ovl_inode_unlock(inode); + + return ret; +} + +static void ovl_file_modified(struct file *file) +{ + /* Update size/mtime */ + ovl_copyattr(file_inode(file)); +} + +static void ovl_file_end_write(struct kiocb *iocb, ssize_t ret) +{ + ovl_file_modified(iocb->ki_filp); } static void ovl_file_accessed(struct file *file) { struct inode *inode, *upperinode; + struct timespec64 ctime, uctime; + struct timespec64 mtime, umtime; if (file->f_flags & O_NOATIME) return; @@ -165,91 +306,67 @@ static void ovl_file_accessed(struct file *file) if (!upperinode) return; - if ((!timespec64_equal(&inode->i_mtime, &upperinode->i_mtime) || - !timespec64_equal(&inode->i_ctime, &upperinode->i_ctime))) { - inode->i_mtime = upperinode->i_mtime; - inode->i_ctime = upperinode->i_ctime; + ctime = inode_get_ctime(inode); + uctime = inode_get_ctime(upperinode); + mtime = inode_get_mtime(inode); + umtime = inode_get_mtime(upperinode); + if ((!timespec64_equal(&mtime, &umtime)) || + !timespec64_equal(&ctime, &uctime)) { + inode_set_mtime_to_ts(inode, inode_get_mtime(upperinode)); + inode_set_ctime_to_ts(inode, uctime); } touch_atime(&file->f_path); } -static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb) -{ - int ifl = iocb->ki_flags; - rwf_t flags = 0; - - if (ifl & IOCB_NOWAIT) - flags |= RWF_NOWAIT; - if (ifl & IOCB_HIPRI) - flags |= RWF_HIPRI; - if (ifl & IOCB_DSYNC) - flags |= RWF_DSYNC; - if (ifl & IOCB_SYNC) - flags |= RWF_SYNC; - - return flags; -} - static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) { struct file *file = iocb->ki_filp; - struct fd real; - const struct cred *old_cred; - ssize_t ret; + struct file *realfile; + struct backing_file_ctx ctx = { + .cred = ovl_creds(file_inode(file)->i_sb), + .accessed = ovl_file_accessed, + }; if (!iov_iter_count(iter)) return 0; - ret = ovl_real_fdget(file, &real); - if (ret) - return ret; - - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_iter_read(real.file, iter, &iocb->ki_pos, - ovl_iocb_to_rwf(iocb)); - revert_creds(old_cred); - - ovl_file_accessed(file); - - fdput(real); + realfile = ovl_real_file(file); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); - return ret; + return backing_file_read_iter(realfile, iter, iocb, iocb->ki_flags, + &ctx); } static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) { struct file *file = iocb->ki_filp; struct inode *inode = file_inode(file); - struct fd real; - const struct cred *old_cred; + struct file *realfile; ssize_t ret; + int ifl = iocb->ki_flags; + struct backing_file_ctx ctx = { + .cred = ovl_creds(inode->i_sb), + .end_write = ovl_file_end_write, + }; if (!iov_iter_count(iter)) return 0; inode_lock(inode); /* Update mode */ - ovl_copyattr(ovl_inode_real(inode), inode); - ret = file_remove_privs(file); - if (ret) - goto out_unlock; + ovl_copyattr(inode); - ret = ovl_real_fdget(file, &real); - if (ret) + realfile = ovl_real_file(file); + ret = PTR_ERR(realfile); + if (IS_ERR(realfile)) goto out_unlock; - old_cred = ovl_override_creds(file_inode(file)->i_sb); - file_start_write(real.file); - ret = vfs_iter_write(real.file, iter, &iocb->ki_pos, - ovl_iocb_to_rwf(iocb)); - file_end_write(real.file); - revert_creds(old_cred); - - /* Update size */ - ovl_copyattr(ovl_inode_real(inode), inode); + if (!ovl_should_sync(OVL_FS(inode->i_sb))) + ifl &= ~(IOCB_DSYNC | IOCB_SYNC); - fdput(real); + ret = backing_file_write_iter(realfile, iter, iocb, ifl, &ctx); out_unlock: inode_unlock(inode); @@ -257,175 +374,147 @@ out_unlock: return ret; } -static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) -{ - struct fd real; - const struct cred *old_cred; - int ret; - - ret = ovl_real_fdget_meta(file, &real, !datasync); - if (ret) - return ret; - - /* Don't sync lower file for fear of receiving EROFS error */ - if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) { - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fsync_range(real.file, start, end, datasync); - revert_creds(old_cred); - } - - fdput(real); - - return ret; -} - -static int ovl_mmap(struct file *file, struct vm_area_struct *vma) +static ssize_t ovl_splice_read(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) { - struct file *realfile = file->private_data; - const struct cred *old_cred; - int ret; - - if (!realfile->f_op->mmap) - return -ENODEV; - - if (WARN_ON(file != vma->vm_file)) - return -EIO; - - vma->vm_file = get_file(realfile); - - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = call_mmap(vma->vm_file, vma); - revert_creds(old_cred); + struct file *realfile; + ssize_t ret; + struct backing_file_ctx ctx = { + .cred = ovl_creds(file_inode(in)->i_sb), + .accessed = ovl_file_accessed, + }; + struct kiocb iocb; - if (ret) { - /* Drop reference count from new vm_file value */ - fput(realfile); - } else { - /* Drop reference count from previous vm_file value */ - fput(file); - } + realfile = ovl_real_file(in); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); - ovl_file_accessed(file); + init_sync_kiocb(&iocb, in); + iocb.ki_pos = *ppos; + ret = backing_file_splice_read(realfile, &iocb, pipe, len, flags, &ctx); + *ppos = iocb.ki_pos; return ret; } -static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len) +/* + * Calling iter_file_splice_write() directly from overlay's f_op may deadlock + * due to lock order inversion between pipe->mutex in iter_file_splice_write() + * and file_start_write(realfile) in ovl_write_iter(). + * + * So do everything ovl_write_iter() does and call iter_file_splice_write() on + * the real file. + */ +static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) { - struct inode *inode = file_inode(file); - struct fd real; - const struct cred *old_cred; - int ret; + struct file *realfile; + struct inode *inode = file_inode(out); + ssize_t ret; + struct backing_file_ctx ctx = { + .cred = ovl_creds(inode->i_sb), + .end_write = ovl_file_end_write, + }; + struct kiocb iocb; - ret = ovl_real_fdget(file, &real); - if (ret) - return ret; + inode_lock(inode); + /* Update mode */ + ovl_copyattr(inode); - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fallocate(real.file, mode, offset, len); - revert_creds(old_cred); + realfile = ovl_real_file(out); + ret = PTR_ERR(realfile); + if (IS_ERR(realfile)) + goto out_unlock; - /* Update size */ - ovl_copyattr(ovl_inode_real(inode), inode); + init_sync_kiocb(&iocb, out); + iocb.ki_pos = *ppos; + ret = backing_file_splice_write(pipe, realfile, &iocb, len, flags, &ctx); + *ppos = iocb.ki_pos; - fdput(real); +out_unlock: + inode_unlock(inode); return ret; } -static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice) +static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync) { - struct fd real; - const struct cred *old_cred; + struct dentry *dentry = file_dentry(file); + enum ovl_path_type type; + struct path upperpath; + struct file *upperfile; int ret; - ret = ovl_real_fdget(file, &real); - if (ret) + ret = ovl_sync_status(OVL_FS(file_inode(file)->i_sb)); + if (ret <= 0) return ret; - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_fadvise(real.file, offset, len, advice); - revert_creds(old_cred); + /* Don't sync lower file for fear of receiving EROFS error */ + type = ovl_path_type(dentry); + if (!OVL_TYPE_UPPER(type) || (datasync && OVL_TYPE_MERGE(type))) + return 0; - fdput(real); + ovl_path_upper(dentry, &upperpath); + upperfile = ovl_real_file_path(file, &upperpath); + if (IS_ERR(upperfile)) + return PTR_ERR(upperfile); - return ret; + with_ovl_creds(file_inode(file)->i_sb) + return vfs_fsync_range(upperfile, start, end, datasync); } -static long ovl_real_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int ovl_mmap(struct file *file, struct vm_area_struct *vma) { - struct fd real; - const struct cred *old_cred; - long ret; - - ret = ovl_real_fdget(file, &real); - if (ret) - return ret; - - old_cred = ovl_override_creds(file_inode(file)->i_sb); - ret = vfs_ioctl(real.file, cmd, arg); - revert_creds(old_cred); - - fdput(real); + struct ovl_file *of = file->private_data; + struct backing_file_ctx ctx = { + .cred = ovl_creds(file_inode(file)->i_sb), + .accessed = ovl_file_accessed, + }; - return ret; + return backing_file_mmap(of->realfile, vma, &ctx); } -static long ovl_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len) { - long ret; struct inode *inode = file_inode(file); + struct file *realfile; + int ret; - switch (cmd) { - case FS_IOC_GETFLAGS: - ret = ovl_real_ioctl(file, cmd, arg); - break; - - case FS_IOC_SETFLAGS: - if (!inode_owner_or_capable(inode)) - return -EACCES; - - ret = mnt_want_write_file(file); - if (ret) - return ret; + inode_lock(inode); + /* Update mode */ + ovl_copyattr(inode); + ret = file_remove_privs(file); + if (ret) + goto out_unlock; - ret = ovl_copy_up_with_data(file_dentry(file)); - if (!ret) { - ret = ovl_real_ioctl(file, cmd, arg); + realfile = ovl_real_file(file); + ret = PTR_ERR(realfile); + if (IS_ERR(realfile)) + goto out_unlock; - inode_lock(inode); - ovl_copyflags(ovl_inode_real(inode), inode); - inode_unlock(inode); - } + with_ovl_creds(inode->i_sb) + ret = vfs_fallocate(realfile, mode, offset, len); - mnt_drop_write_file(file); - break; + /* Update size */ + ovl_file_modified(file); - default: - ret = -ENOTTY; - } +out_unlock: + inode_unlock(inode); return ret; } -static long ovl_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) +static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice) { - switch (cmd) { - case FS_IOC32_GETFLAGS: - cmd = FS_IOC_GETFLAGS; - break; - - case FS_IOC32_SETFLAGS: - cmd = FS_IOC_SETFLAGS; - break; + struct file *realfile; - default: - return -ENOIOCTLCMD; - } + realfile = ovl_real_file(file); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); - return ovl_ioctl(file, cmd, arg); + with_ovl_creds(file_inode(file)->i_sb) + return vfs_fadvise(realfile, offset, len, advice); } enum ovl_copyop { @@ -439,45 +528,53 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, loff_t len, unsigned int flags, enum ovl_copyop op) { struct inode *inode_out = file_inode(file_out); - struct fd real_in, real_out; - const struct cred *old_cred; + struct file *realfile_in, *realfile_out; loff_t ret; - ret = ovl_real_fdget(file_out, &real_out); - if (ret) - return ret; - - ret = ovl_real_fdget(file_in, &real_in); - if (ret) { - fdput(real_out); - return ret; + inode_lock(inode_out); + if (op != OVL_DEDUPE) { + /* Update mode */ + ovl_copyattr(inode_out); + ret = file_remove_privs(file_out); + if (ret) + goto out_unlock; } - old_cred = ovl_override_creds(file_inode(file_out)->i_sb); - switch (op) { - case OVL_COPY: - ret = vfs_copy_file_range(real_in.file, pos_in, - real_out.file, pos_out, len, flags); - break; - - case OVL_CLONE: - ret = vfs_clone_file_range(real_in.file, pos_in, - real_out.file, pos_out, len, flags); - break; - - case OVL_DEDUPE: - ret = vfs_dedupe_file_range_one(real_in.file, pos_in, - real_out.file, pos_out, len, - flags); - break; + realfile_out = ovl_real_file(file_out); + ret = PTR_ERR(realfile_out); + if (IS_ERR(realfile_out)) + goto out_unlock; + + realfile_in = ovl_real_file(file_in); + ret = PTR_ERR(realfile_in); + if (IS_ERR(realfile_in)) + goto out_unlock; + + with_ovl_creds(file_inode(file_out)->i_sb) { + switch (op) { + case OVL_COPY: + ret = vfs_copy_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; + + case OVL_CLONE: + ret = vfs_clone_file_range(realfile_in, pos_in, + realfile_out, pos_out, len, flags); + break; + + case OVL_DEDUPE: + ret = vfs_dedupe_file_range_one(realfile_in, pos_in, + realfile_out, pos_out, len, + flags); + break; + } } - revert_creds(old_cred); /* Update size */ - ovl_copyattr(ovl_inode_real(inode_out), inode_out); + ovl_file_modified(file_out); - fdput(real_in); - fdput(real_out); +out_unlock: + inode_unlock(inode_out); return ret; } @@ -517,6 +614,23 @@ static loff_t ovl_remap_file_range(struct file *file_in, loff_t pos_in, remap_flags, op); } +static int ovl_flush(struct file *file, fl_owner_t id) +{ + struct file *realfile; + int err = 0; + + realfile = ovl_real_file(file); + if (IS_ERR(realfile)) + return PTR_ERR(realfile); + + if (realfile->f_op->flush) { + with_ovl_creds(file_inode(file)->i_sb) + err = realfile->f_op->flush(realfile, id); + } + + return err; +} + const struct file_operations ovl_file_operations = { .open = ovl_open, .release = ovl_release, @@ -527,8 +641,9 @@ const struct file_operations ovl_file_operations = { .mmap = ovl_mmap, .fallocate = ovl_fallocate, .fadvise = ovl_fadvise, - .unlocked_ioctl = ovl_ioctl, - .compat_ioctl = ovl_compat_ioctl, + .flush = ovl_flush, + .splice_read = ovl_splice_read, + .splice_write = ovl_splice_write, .copy_file_range = ovl_copy_file_range, .remap_file_range = ovl_remap_file_range, |
