coda: pass the host file in vma->vm_file on mmap [Linux 4.9.187]

This Linux kernel change "coda: pass the host file in vma->vm_file on mmap" is included in the Linux 4.9.187 release. This change is authored by Jan Harkes <jaharkes [at] cs.cmu.edu> on Tue Jul 16 16:28:04 2019 -0700. The commit for this change in Linux stable tree is c93b747 (patch) which is from upstream commit 7fa0a1d. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 7fa0a1d.

coda: pass the host file in vma->vm_file on mmap

commit 7fa0a1da3dadfd9216df7745a1331fdaa0940d1c upstream.

Patch series "Coda updates".

The following patch series is a collection of various fixes for Coda,
most of which were collected from linux-fsdevel or linux-kernel but
which have as yet not found their way upstream.

This patch (of 22):

Various file systems expect that vma->vm_file points at their own file
handle, several use file_inode(vma->vm_file) to get at their inode or
use vma->vm_file->private_data.  However the way Coda wrapped mmap on a
host file broke this assumption, vm_file was still pointing at the Coda
file and the host file systems would scribble over Coda's inode and
private file data.

This patch fixes the incorrect expectation and wraps vm_ops->open and
vm_ops->close to allow Coda to track when the vm_area_struct is
destroyed so we still release the reference on the Coda file handle at
the right time.

[This patch differs from the original upstream patch because older stable
 kernels do not have the call_mmap vfs helper so we call f_ops->mmap
 directly.]

Link: http://lkml.kernel.org/r/0e850c6e59c0b147dc2dcd51a3af004c948c3697.1558117389.git.jaharkes@cs.cmu.edu
Signed-off-by: Jan Harkes <jaharkes@cs.cmu.edu>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Colin Ian King <colin.king@canonical.com>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Fabian Frederick <fabf@skynet.be>
Cc: Mikko Rapeli <mikko.rapeli@iki.fi>
Cc: Sam Protsenko <semen.protsenko@linaro.org>
Cc: Yann Droneaud <ydroneaud@opteya.com>
Cc: Zhouyang Jia <jiazhouyang09@gmail.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Jan Harkes <jaharkes@cs.cmu.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

There are 69 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 fs/coda/file.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 68 insertions(+), 1 deletion(-)

diff --git a/fs/coda/file.c b/fs/coda/file.c
index 6e0154e..649d17e 100644
--- a/fs/coda/file.c
+++ b/fs/coda/file.c
@@ -60,6 +60,41 @@
    return ret;
 }

+struct coda_vm_ops {
+   atomic_t refcnt;
+   struct file *coda_file;
+   const struct vm_operations_struct *host_vm_ops;
+   struct vm_operations_struct vm_ops;
+};
+
+static void
+coda_vm_open(struct vm_area_struct *vma)
+{
+   struct coda_vm_ops *cvm_ops =
+       container_of(vma->vm_ops, struct coda_vm_ops, vm_ops);
+
+   atomic_inc(&cvm_ops->refcnt);
+
+   if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->open)
+       cvm_ops->host_vm_ops->open(vma);
+}
+
+static void
+coda_vm_close(struct vm_area_struct *vma)
+{
+   struct coda_vm_ops *cvm_ops =
+       container_of(vma->vm_ops, struct coda_vm_ops, vm_ops);
+
+   if (cvm_ops->host_vm_ops && cvm_ops->host_vm_ops->close)
+       cvm_ops->host_vm_ops->close(vma);
+
+   if (atomic_dec_and_test(&cvm_ops->refcnt)) {
+       vma->vm_ops = cvm_ops->host_vm_ops;
+       fput(cvm_ops->coda_file);
+       kfree(cvm_ops);
+   }
+}
+
 static int
 coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
 {
@@ -67,6 +102,8 @@
    struct coda_inode_info *cii;
    struct file *host_file;
    struct inode *coda_inode, *host_inode;
+   struct coda_vm_ops *cvm_ops;
+   int ret;

    cfi = CODA_FTOC(coda_file);
    BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
@@ -75,6 +112,13 @@
    if (!host_file->f_op->mmap)
        return -ENODEV;

+   if (WARN_ON(coda_file != vma->vm_file))
+       return -EIO;
+
+   cvm_ops = kmalloc(sizeof(struct coda_vm_ops), GFP_KERNEL);
+   if (!cvm_ops)
+       return -ENOMEM;
+
    coda_inode = file_inode(coda_file);
    host_inode = file_inode(host_file);

@@ -88,6 +132,7 @@
     * the container file on us! */
    else if (coda_inode->i_mapping != host_inode->i_mapping) {
        spin_unlock(&cii->c_lock);
+       kfree(cvm_ops);
        return -EBUSY;
    }

@@ -96,7 +141,29 @@
    cfi->cfi_mapcount++;
    spin_unlock(&cii->c_lock);

-   return host_file->f_op->mmap(host_file, vma);
+   vma->vm_file = get_file(host_file);
+   ret = host_file->f_op->mmap(host_file, vma);
+
+   if (ret) {
+       /* if call_mmap fails, our caller will put coda_file so we
+        * should drop the reference to the host_file that we got.
+        */
+       fput(host_file);
+       kfree(cvm_ops);
+   } else {
+       /* here we add redirects for the open/close vm_operations */
+       cvm_ops->host_vm_ops = vma->vm_ops;
+       if (vma->vm_ops)
+           cvm_ops->vm_ops = *vma->vm_ops;
+
+       cvm_ops->vm_ops.open = coda_vm_open;
+       cvm_ops->vm_ops.close = coda_vm_close;
+       cvm_ops->coda_file = coda_file;
+       atomic_set(&cvm_ops->refcnt, 1);
+
+       vma->vm_ops = &cvm_ops->vm_ops;
+   }
+   return ret;
 }

 int coda_open(struct inode *coda_inode, struct file *coda_file)

Leave a Reply

Your email address will not be published. Required fields are marked *