diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 54b2e3c5bd04..7a43d840878f 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -2352,6 +2352,9 @@ static int fuse_file_mmap(struct file *file, struct vm_area_struct *vma) if (FUSE_IS_DAX(file_inode(file))) return fuse_dax_mmap(file, vma); + if (ff->passthrough.filp) + return fuse_passthrough_mmap(file, vma); + if (ff->open_flags & FOPEN_DIRECT_IO) { /* Can't provide the coherency needed for MAP_SHARED */ if (vma->vm_flags & VM_MAYSHARE) diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 1b0be1b598ba..66e17893f3e0 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1254,5 +1254,6 @@ int fuse_passthrough_setup(struct fuse_conn *fc, struct fuse_file *ff, void fuse_passthrough_release(struct fuse_passthrough *passthrough); ssize_t fuse_passthrough_read_iter(struct kiocb *iocb, struct iov_iter *to); ssize_t fuse_passthrough_write_iter(struct kiocb *iocb, struct iov_iter *from); +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma); #endif /* _FS_FUSE_I_H */ diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index 24866c5fe7e2..284979f87747 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -135,6 +135,47 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, return ret; } +ssize_t fuse_passthrough_mmap(struct file *file, struct vm_area_struct *vma) +{ + int ret; + const struct cred *old_cred; + struct fuse_file *ff = file->private_data; + struct inode *fuse_inode = file_inode(file); + struct file *passthrough_filp = ff->passthrough.filp; + struct inode *passthrough_inode = file_inode(passthrough_filp); + + if (!passthrough_filp->f_op->mmap) + return -ENODEV; + + if (WARN_ON(file != vma->vm_file)) + return -EIO; + + vma->vm_file = get_file(passthrough_filp); + + old_cred = override_creds(ff->passthrough.cred); + ret = call_mmap(vma->vm_file, vma); + revert_creds(old_cred); + + if (ret) + fput(passthrough_filp); + else + fput(file); + + if (file->f_flags & O_NOATIME) + return ret; + + if ((!timespec64_equal(&fuse_inode->i_mtime, + &passthrough_inode->i_mtime) || + !timespec64_equal(&fuse_inode->i_ctime, + &passthrough_inode->i_ctime))) { + fuse_inode->i_mtime = passthrough_inode->i_mtime; + fuse_inode->i_ctime = passthrough_inode->i_ctime; + } + touch_atime(&file->f_path); + + return ret; +} + int fuse_passthrough_open(struct fuse_dev *fud, struct fuse_passthrough_out *pto) {