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

This Linux kernel change "coda: pass the host file in vma->vm_file on mmap" is included in the Linux 4.4.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 6b40b97 (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/0[email protected]cs.cmu.edu
Signed-off-by: Jan Harkes <[email protected]>
Cc: Arnd Bergmann <[email protected]>
Cc: Colin Ian King <[email protected]>
Cc: Dan Carpenter <[email protected]>
Cc: David Howells <[email protected]>
Cc: Fabian Frederick <[email protected]>
Cc: Mikko Rapeli <[email protected]>
Cc: Sam Protsenko <[email protected]>
Cc: Yann Droneaud <[email protected]>
Cc: Zhouyang Jia <[email protected]>
Cc: <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
Signed-off-by: Jan Harkes <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>

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 1da3805..cbbd76f 100644
--- a/fs/coda/file.c
+++ b/fs/coda/file.c
@@ -81,6 +81,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)
 {
@@ -88,6 +123,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);
@@ -96,6 +133,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);

@@ -109,6 +153,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;
    }

@@ -117,7 +162,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 *