OSDN Git Service

coda: pass the host file in vma->vm_file on mmap
[android-x86/kernel.git] / fs / coda / file.c
index 6e0154e..649d17e 100644 (file)
@@ -60,6 +60,41 @@ coda_file_write_iter(struct kiocb *iocb, struct iov_iter *to)
        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 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
        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 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
        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 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
         * 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 @@ coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
        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)