132 lines
4.1 KiB
C
132 lines
4.1 KiB
C
/*
|
|
* Copyright (c) 2003-2014 Erez Zadok
|
|
* Copyright (c) 2003-2006 Charles P. Wright
|
|
* Copyright (c) 2005-2007 Josef 'Jeff' Sipek
|
|
* Copyright (c) 2005-2006 Junjiro Okajima
|
|
* Copyright (c) 2006 Shaya Potter
|
|
* Copyright (c) 2005 Arun M. Krishnakumar
|
|
* Copyright (c) 2004-2006 David P. Quigley
|
|
* Copyright (c) 2003-2004 Mohammad Nayyer Zubair
|
|
* Copyright (c) 2003 Puja Gupta
|
|
* Copyright (c) 2003 Harikesavan Krishnan
|
|
* Copyright (c) 2003-2014 Stony Brook University
|
|
* Copyright (c) 2003-2014 The Research Foundation of SUNY
|
|
*
|
|
* 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 "union.h"
|
|
|
|
|
|
/*
|
|
* XXX: we need a dummy readpage handler because generic_file_mmap (which we
|
|
* use in unionfs_mmap) checks for the existence of
|
|
* mapping->a_ops->readpage, else it returns -ENOEXEC. The VFS will need to
|
|
* be fixed to allow a file system to define vm_ops->fault without any
|
|
* address_space_ops whatsoever.
|
|
*
|
|
* Otherwise, we don't want to use our readpage method at all.
|
|
*/
|
|
static int unionfs_readpage(struct file *file, struct page *page)
|
|
{
|
|
BUG();
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int unionfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
|
{
|
|
int err;
|
|
struct file *file, *lower_file;
|
|
const struct vm_operations_struct *lower_vm_ops;
|
|
struct vm_area_struct lower_vma;
|
|
|
|
BUG_ON(!vma);
|
|
memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
|
|
file = lower_vma.vm_file;
|
|
lower_vm_ops = UNIONFS_F(file)->lower_vm_ops;
|
|
BUG_ON(!lower_vm_ops);
|
|
|
|
lower_file = unionfs_lower_file(file);
|
|
BUG_ON(!lower_file);
|
|
/*
|
|
* XXX: vm_ops->fault may be called in parallel. Because we have to
|
|
* resort to temporarily changing the vma->vm_file to point to the
|
|
* lower file, a concurrent invocation of unionfs_fault could see a
|
|
* different value. In this workaround, we keep a different copy of
|
|
* the vma structure in our stack, so we never expose a different
|
|
* value of the vma->vm_file called to us, even temporarily. A
|
|
* better fix would be to change the calling semantics of ->fault to
|
|
* take an explicit file pointer.
|
|
*/
|
|
lower_vma.vm_file = lower_file;
|
|
err = lower_vm_ops->fault(&lower_vma, vmf);
|
|
return err;
|
|
}
|
|
|
|
static int unionfs_page_mkwrite(struct vm_area_struct *vma,
|
|
struct vm_fault *vmf)
|
|
{
|
|
int err = 0;
|
|
struct file *file, *lower_file;
|
|
const struct vm_operations_struct *lower_vm_ops;
|
|
struct vm_area_struct lower_vma;
|
|
|
|
BUG_ON(!vma);
|
|
memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
|
|
file = lower_vma.vm_file;
|
|
lower_vm_ops = UNIONFS_F(file)->lower_vm_ops;
|
|
BUG_ON(!lower_vm_ops);
|
|
if (!lower_vm_ops->page_mkwrite)
|
|
goto out;
|
|
|
|
lower_file = unionfs_lower_file(file);
|
|
BUG_ON(!lower_file);
|
|
/*
|
|
* XXX: vm_ops->page_mkwrite may be called in parallel.
|
|
* Because we have to resort to temporarily changing the
|
|
* vma->vm_file to point to the lower file, a concurrent
|
|
* invocation of unionfs_page_mkwrite could see a different
|
|
* value. In this workaround, we keep a different copy of the
|
|
* vma structure in our stack, so we never expose a different
|
|
* value of the vma->vm_file called to us, even temporarily.
|
|
* A better fix would be to change the calling semantics of
|
|
* ->page_mkwrite to take an explicit file pointer.
|
|
*/
|
|
lower_vma.vm_file = lower_file;
|
|
err = lower_vm_ops->page_mkwrite(&lower_vma, vmf);
|
|
out:
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* This function should never be called directly.
|
|
* It's here only for the check a_ops->direct_IO during vfs_open.
|
|
*/
|
|
static ssize_t unionfs_direct_IO(int rw, struct kiocb *iocb,
|
|
const struct iovec *iov, loff_t offset,
|
|
unsigned long nr_segs)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
struct address_space_operations unionfs_aops = {
|
|
.direct_IO = unionfs_direct_IO,
|
|
};
|
|
|
|
/*
|
|
* XXX: we need a second, dummy address_space_ops vector, to be used
|
|
* temporarily during unionfs_mmap, because the latter calls
|
|
* generic_file_mmap, which checks if ->readpage exists, else returns
|
|
* -ENOEXEC.
|
|
*/
|
|
struct address_space_operations unionfs_dummy_aops = {
|
|
.readpage = unionfs_readpage,
|
|
};
|
|
|
|
struct vm_operations_struct unionfs_vm_ops = {
|
|
.fault = unionfs_fault,
|
|
.page_mkwrite = unionfs_page_mkwrite,
|
|
};
|