From: Alin Dobre <alinmd@xxxxxxxxx> wrapfs renamed to idmapfs, with minimum changes. Signed-off-by: Alin Dobre <alinmd@xxxxxxxxx> --- fs/Kconfig | 2 +- fs/Makefile | 2 +- fs/idmapfs/Kconfig | 8 + fs/idmapfs/Makefile | 7 + fs/idmapfs/dentry.c | 49 ++++++ fs/idmapfs/file.c | 295 +++++++++++++++++++++++++++++++++ fs/idmapfs/inode.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++ fs/idmapfs/lookup.c | 332 +++++++++++++++++++++++++++++++++++++ fs/idmapfs/main.c | 174 ++++++++++++++++++++ fs/idmapfs/mmap.c | 53 ++++++ fs/idmapfs/super.c | 168 +++++++++++++++++++ fs/idmapfs/wrapfs.h | 205 +++++++++++++++++++++++ fs/wrapfs/Kconfig | 8 - fs/wrapfs/Makefile | 7 - fs/wrapfs/dentry.c | 49 ------ fs/wrapfs/file.c | 295 --------------------------------- fs/wrapfs/inode.c | 463 ---------------------------------------------------- fs/wrapfs/lookup.c | 332 ------------------------------------- fs/wrapfs/main.c | 174 -------------------- fs/wrapfs/mmap.c | 53 ------ fs/wrapfs/super.c | 168 ------------------- fs/wrapfs/wrapfs.h | 205 ----------------------- 22 files changed, 1756 insertions(+), 1756 deletions(-) create mode 100644 fs/idmapfs/Kconfig create mode 100644 fs/idmapfs/Makefile create mode 100644 fs/idmapfs/dentry.c create mode 100644 fs/idmapfs/file.c create mode 100644 fs/idmapfs/inode.c create mode 100644 fs/idmapfs/lookup.c create mode 100644 fs/idmapfs/main.c create mode 100644 fs/idmapfs/mmap.c create mode 100644 fs/idmapfs/super.c create mode 100644 fs/idmapfs/wrapfs.h delete mode 100644 fs/wrapfs/Kconfig delete mode 100644 fs/wrapfs/Makefile delete mode 100644 fs/wrapfs/dentry.c delete mode 100644 fs/wrapfs/file.c delete mode 100644 fs/wrapfs/inode.c delete mode 100644 fs/wrapfs/lookup.c delete mode 100644 fs/wrapfs/main.c delete mode 100644 fs/wrapfs/mmap.c delete mode 100644 fs/wrapfs/super.c delete mode 100644 fs/wrapfs/wrapfs.h diff --git a/fs/Kconfig b/fs/Kconfig index ef2b6c0..f9d0167 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -188,7 +188,7 @@ if MISC_FILESYSTEMS source "fs/adfs/Kconfig" source "fs/affs/Kconfig" source "fs/ecryptfs/Kconfig" -source "fs/wrapfs/Kconfig" +source "fs/idmapfs/Kconfig" source "fs/hfs/Kconfig" source "fs/hfsplus/Kconfig" source "fs/befs/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index b0baa95..a6d13fc 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -82,7 +82,7 @@ obj-$(CONFIG_ISO9660_FS) += isofs/ obj-$(CONFIG_HFSPLUS_FS) += hfsplus/ # Before hfs to find wrapped HFS+ obj-$(CONFIG_HFS_FS) += hfs/ obj-$(CONFIG_ECRYPT_FS) += ecryptfs/ -obj-$(CONFIG_WRAP_FS) += wrapfs/ +obj-$(CONFIG_IDMAP_FS) += idmapfs/ obj-$(CONFIG_VXFS_FS) += freevxfs/ obj-$(CONFIG_NFS_FS) += nfs/ obj-$(CONFIG_EXPORTFS) += exportfs/ diff --git a/fs/idmapfs/Kconfig b/fs/idmapfs/Kconfig new file mode 100644 index 0000000..c4c7dd2 --- /dev/null +++ b/fs/idmapfs/Kconfig @@ -0,0 +1,8 @@ +config IDMAP_FS + tristate "Idmapfs stackable file system (EXPERIMENTAL)" + help + Wrapfs is a stackable file system which simply passes its + operations to the lower layer. It is designed as a useful + template for developing or debugging other stackable file systems, + and more (see Documentation/filesystems/wrapfs.txt). See + <http://wrapfs.filesystems.org/> for details. diff --git a/fs/idmapfs/Makefile b/fs/idmapfs/Makefile new file mode 100644 index 0000000..bf88f3c --- /dev/null +++ b/fs/idmapfs/Makefile @@ -0,0 +1,7 @@ +WRAPFS_VERSION="0.1" + +EXTRA_CFLAGS += -DWRAPFS_VERSION=\"$(WRAPFS_VERSION)\" + +obj-$(CONFIG_IDMAP_FS) += idmapfs.o + +idmapfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o diff --git a/fs/idmapfs/dentry.c b/fs/idmapfs/dentry.c new file mode 100644 index 0000000..ed1a0cb --- /dev/null +++ b/fs/idmapfs/dentry.c @@ -0,0 +1,49 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +/* + * returns: -ERRNO if error (returned to user) + * 0: tell VFS to invalidate dentry + * 1: dentry is valid + */ +static int wrapfs_d_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct path lower_path; + struct dentry *lower_dentry; + int err = 1; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) + goto out; + err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); +out: + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static void wrapfs_d_release(struct dentry *dentry) +{ + /* release and reset the lower paths */ + wrapfs_put_reset_lower_path(dentry); + free_dentry_private_data(dentry); + return; +} + +const struct dentry_operations wrapfs_dops = { + .d_revalidate = wrapfs_d_revalidate, + .d_release = wrapfs_d_release, +}; diff --git a/fs/idmapfs/file.c b/fs/idmapfs/file.c new file mode 100644 index 0000000..9825816 --- /dev/null +++ b/fs/idmapfs/file.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +static ssize_t wrapfs_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int err; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + + lower_file = wrapfs_lower_file(file); + err = vfs_read(lower_file, buf, count, ppos); + /* update our inode atime upon a successful lower read */ + if (err >= 0) + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + + return err; +} + +static ssize_t wrapfs_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + int err = 0; + struct file *lower_file; + struct dentry *dentry = file->f_path.dentry; + + lower_file = wrapfs_lower_file(file); + err = vfs_write(lower_file, buf, count, ppos); + /* update our inode times+sizes upon a successful lower write */ + if (err >= 0) { + fsstack_copy_inode_size(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + fsstack_copy_attr_times(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + } + + return err; +} + +static int wrapfs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err = 0; + struct file *lower_file = NULL; + struct dentry *dentry = file->f_path.dentry; + + lower_file = wrapfs_lower_file(file); + err = vfs_readdir(lower_file, filldir, dirent); + file->f_pos = lower_file->f_pos; + if (err >= 0) /* copy the atime */ + fsstack_copy_attr_atime(dentry->d_inode, + lower_file->f_path.dentry->d_inode); + return err; +} + +static long wrapfs_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = wrapfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->unlocked_ioctl) + err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); + + /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ + if (!err) + fsstack_copy_attr_all(file->f_path.dentry->d_inode, + lower_file->f_path.dentry->d_inode); +out: + return err; +} + +#ifdef CONFIG_COMPAT +static long wrapfs_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + long err = -ENOTTY; + struct file *lower_file; + + lower_file = wrapfs_lower_file(file); + + /* XXX: use vfs_ioctl if/when VFS exports it */ + if (!lower_file || !lower_file->f_op) + goto out; + if (lower_file->f_op->compat_ioctl) + err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); + +out: + return err; +} +#endif + +static int wrapfs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err = 0; + bool willwrite; + struct file *lower_file; + const struct vm_operations_struct *saved_vm_ops = NULL; + + /* this might be deferred to mmap's writepage */ + willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); + + /* + * File systems which do not implement ->writepage may use + * generic_file_readonly_mmap as their ->mmap op. If you call + * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. + * But we cannot call the lower ->mmap op, so we can't tell that + * writeable mappings won't work. Therefore, our only choice is to + * check if the lower file system supports the ->writepage, and if + * not, return EINVAL (the same error that + * generic_file_readonly_mmap returns in that case). + */ + lower_file = wrapfs_lower_file(file); + if (willwrite && !lower_file->f_mapping->a_ops->writepage) { + err = -EINVAL; + printk(KERN_ERR "wrapfs: lower file system does not " + "support writeable mmap\n"); + goto out; + } + + /* + * find and save lower vm_ops. + * + * XXX: the VFS should have a cleaner way of finding the lower vm_ops + */ + if (!WRAPFS_F(file)->lower_vm_ops) { + err = lower_file->f_op->mmap(lower_file, vma); + if (err) { + printk(KERN_ERR "wrapfs: lower mmap failed %d\n", err); + goto out; + } + saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ + } + + /* + * Next 3 lines are all I need from generic_file_mmap. I definitely + * don't want its test for ->readpage which returns -ENOEXEC. + */ + file_accessed(file); + vma->vm_ops = &wrapfs_vm_ops; + + file->f_mapping->a_ops = &wrapfs_aops; /* set our aops */ + if (!WRAPFS_F(file)->lower_vm_ops) /* save for our ->fault */ + WRAPFS_F(file)->lower_vm_ops = saved_vm_ops; + +out: + return err; +} + +static int wrapfs_open(struct inode *inode, struct file *file) +{ + int err = 0; + struct file *lower_file = NULL; + struct path lower_path; + + /* don't open unhashed/deleted files */ + if (d_unhashed(file->f_path.dentry)) { + err = -ENOENT; + goto out_err; + } + + file->private_data = + kzalloc(sizeof(struct wrapfs_file_info), GFP_KERNEL); + if (!WRAPFS_F(file)) { + err = -ENOMEM; + goto out_err; + } + + /* open lower object and link wrapfs's file struct to lower's */ + wrapfs_get_lower_path(file->f_path.dentry, &lower_path); + lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); + path_put(&lower_path); + if (IS_ERR(lower_file)) { + err = PTR_ERR(lower_file); + lower_file = wrapfs_lower_file(file); + if (lower_file) { + wrapfs_set_lower_file(file, NULL); + fput(lower_file); /* fput calls dput for lower_dentry */ + } + } else { + wrapfs_set_lower_file(file, lower_file); + } + + if (err) + kfree(WRAPFS_F(file)); + else + fsstack_copy_attr_all(inode, wrapfs_lower_inode(inode)); +out_err: + return err; +} + +static int wrapfs_flush(struct file *file, fl_owner_t id) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = wrapfs_lower_file(file); + if (lower_file && lower_file->f_op && lower_file->f_op->flush) + err = lower_file->f_op->flush(lower_file, id); + + return err; +} + +/* release all lower object references & free the file info structure */ +static int wrapfs_file_release(struct inode *inode, struct file *file) +{ + struct file *lower_file; + + lower_file = wrapfs_lower_file(file); + if (lower_file) { + wrapfs_set_lower_file(file, NULL); + fput(lower_file); + } + + kfree(WRAPFS_F(file)); + return 0; +} + +static int wrapfs_fsync(struct file *file, loff_t start, loff_t end, + int datasync) +{ + int err; + struct file *lower_file; + struct path lower_path; + struct dentry *dentry = file->f_path.dentry; + + err = generic_file_fsync(file, start, end, datasync); + if (err) + goto out; + lower_file = wrapfs_lower_file(file); + wrapfs_get_lower_path(dentry, &lower_path); + err = vfs_fsync_range(lower_file, start, end, datasync); + wrapfs_put_lower_path(dentry, &lower_path); +out: + return err; +} + +static int wrapfs_fasync(int fd, struct file *file, int flag) +{ + int err = 0; + struct file *lower_file = NULL; + + lower_file = wrapfs_lower_file(file); + if (lower_file->f_op && lower_file->f_op->fasync) + err = lower_file->f_op->fasync(fd, lower_file, flag); + + return err; +} + +const struct file_operations wrapfs_main_fops = { + .llseek = generic_file_llseek, + .read = wrapfs_read, + .write = wrapfs_write, + .unlocked_ioctl = wrapfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = wrapfs_compat_ioctl, +#endif + .mmap = wrapfs_mmap, + .open = wrapfs_open, + .flush = wrapfs_flush, + .release = wrapfs_file_release, + .fsync = wrapfs_fsync, + .fasync = wrapfs_fasync, +}; + +/* trimmed directory options */ +const struct file_operations wrapfs_dir_fops = { + .llseek = generic_file_llseek, + .read = generic_read_dir, + .readdir = wrapfs_readdir, + .unlocked_ioctl = wrapfs_unlocked_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = wrapfs_compat_ioctl, +#endif + .open = wrapfs_open, + .release = wrapfs_file_release, + .flush = wrapfs_flush, + .fsync = wrapfs_fsync, + .fasync = wrapfs_fasync, +}; diff --git a/fs/idmapfs/inode.c b/fs/idmapfs/inode.c new file mode 100644 index 0000000..d7fe145 --- /dev/null +++ b/fs/idmapfs/inode.c @@ -0,0 +1,463 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +static int wrapfs_create(struct inode *dir, struct dentry *dentry, + umode_t mode, bool want_excl) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, + want_excl); + if (err) + goto out; + err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + unlock_dir(lower_parent_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static int wrapfs_link(struct dentry *old_dentry, struct inode *dir, + struct dentry *new_dentry) +{ + struct dentry *lower_old_dentry; + struct dentry *lower_new_dentry; + struct dentry *lower_dir_dentry; + u64 file_size_save; + int err; + struct path lower_old_path, lower_new_path; + + file_size_save = i_size_read(old_dentry->d_inode); + wrapfs_get_lower_path(old_dentry, &lower_old_path); + wrapfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_dir_dentry = lock_parent(lower_new_dentry); + + err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, + lower_new_dentry); + if (err || !lower_new_dentry->d_inode) + goto out; + + err = wrapfs_interpose(new_dentry, dir->i_sb, &lower_new_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); + set_nlink(old_dentry->d_inode, + wrapfs_lower_inode(old_dentry->d_inode)->i_nlink); + i_size_write(new_dentry->d_inode, file_size_save); +out: + unlock_dir(lower_dir_dentry); + wrapfs_put_lower_path(old_dentry, &lower_old_path); + wrapfs_put_lower_path(new_dentry, &lower_new_path); + return err; +} + +static int wrapfs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + struct dentry *lower_dentry; + struct inode *lower_dir_inode = wrapfs_lower_inode(dir); + struct dentry *lower_dir_dentry; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + dget(lower_dentry); + lower_dir_dentry = lock_parent(lower_dentry); + + err = vfs_unlink(lower_dir_inode, lower_dentry); + + /* + * Note: unlinking on top of NFS can cause silly-renamed files. + * Trying to delete such files results in EBUSY from NFS + * below. Silly-renamed files will get deleted by NFS later on, so + * we just need to detect them here and treat such EBUSY errors as + * if the upper file was successfully deleted. + */ + if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) + err = 0; + if (err) + goto out; + fsstack_copy_attr_times(dir, lower_dir_inode); + fsstack_copy_inode_size(dir, lower_dir_inode); + set_nlink(dentry->d_inode, + wrapfs_lower_inode(dentry->d_inode)->i_nlink); + dentry->d_inode->i_ctime = dir->i_ctime; + d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ +out: + unlock_dir(lower_dir_dentry); + dput(lower_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static int wrapfs_symlink(struct inode *dir, struct dentry *dentry, + const char *symname) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); + if (err) + goto out; + err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + unlock_dir(lower_parent_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static int wrapfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); + if (err) + goto out; + + err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + + fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + /* update number of links on parent directory */ + set_nlink(dir, wrapfs_lower_inode(dir)->i_nlink); + +out: + unlock_dir(lower_parent_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static int wrapfs_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct dentry *lower_dentry; + struct dentry *lower_dir_dentry; + int err; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_dir_dentry = lock_parent(lower_dentry); + + err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); + if (err) + goto out; + + d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ + if (dentry->d_inode) + clear_nlink(dentry->d_inode); + fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); + fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); + set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); + +out: + unlock_dir(lower_dir_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static int wrapfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, + dev_t dev) +{ + int err = 0; + struct dentry *lower_dentry; + struct dentry *lower_parent_dentry = NULL; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_parent_dentry = lock_parent(lower_dentry); + + err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); + if (err) + goto out; + + err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); + if (err) + goto out; + fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); + fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); + +out: + unlock_dir(lower_parent_dentry); + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +/* + * The locking rules in wrapfs_rename are complex. We could use a simpler + * superblock-level name-space lock for renames and copy-ups. + */ +static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + int err = 0; + struct dentry *lower_old_dentry = NULL; + struct dentry *lower_new_dentry = NULL; + struct dentry *lower_old_dir_dentry = NULL; + struct dentry *lower_new_dir_dentry = NULL; + struct dentry *trap = NULL; + struct path lower_old_path, lower_new_path; + + wrapfs_get_lower_path(old_dentry, &lower_old_path); + wrapfs_get_lower_path(new_dentry, &lower_new_path); + lower_old_dentry = lower_old_path.dentry; + lower_new_dentry = lower_new_path.dentry; + lower_old_dir_dentry = dget_parent(lower_old_dentry); + lower_new_dir_dentry = dget_parent(lower_new_dentry); + + trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + /* source should not be ancestor of target */ + if (trap == lower_old_dentry) { + err = -EINVAL; + goto out; + } + /* target should not be ancestor of source */ + if (trap == lower_new_dentry) { + err = -ENOTEMPTY; + goto out; + } + + err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, + lower_new_dir_dentry->d_inode, lower_new_dentry); + if (err) + goto out; + + fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); + fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); + if (new_dir != old_dir) { + fsstack_copy_attr_all(old_dir, + lower_old_dir_dentry->d_inode); + fsstack_copy_inode_size(old_dir, + lower_old_dir_dentry->d_inode); + } + +out: + unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); + dput(lower_old_dir_dentry); + dput(lower_new_dir_dentry); + wrapfs_put_lower_path(old_dentry, &lower_old_path); + wrapfs_put_lower_path(new_dentry, &lower_new_path); + return err; +} + +static int wrapfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) +{ + int err; + struct dentry *lower_dentry; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + if (!lower_dentry->d_inode->i_op || + !lower_dentry->d_inode->i_op->readlink) { + err = -EINVAL; + goto out; + } + + err = lower_dentry->d_inode->i_op->readlink(lower_dentry, + buf, bufsiz); + if (err < 0) + goto out; + fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); + +out: + wrapfs_put_lower_path(dentry, &lower_path); + return err; +} + +static void *wrapfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + char *buf; + int len = PAGE_SIZE, err; + mm_segment_t old_fs; + + /* This is freed by the put_link method assuming a successful call. */ + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + buf = ERR_PTR(-ENOMEM); + goto out; + } + + /* read the symlink, and then we will follow it */ + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = wrapfs_readlink(dentry, buf, len); + set_fs(old_fs); + if (err < 0) { + kfree(buf); + buf = ERR_PTR(err); + } else { + buf[err] = '\0'; + } +out: + nd_set_link(nd, buf); + return NULL; +} + +/* this @nd *IS* still used */ +static void wrapfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + char *buf = nd_get_link(nd); + if (!IS_ERR(buf)) /* free the char* */ + kfree(buf); +} + +static int wrapfs_permission(struct inode *inode, int mask) +{ + struct inode *lower_inode; + int err; + + lower_inode = wrapfs_lower_inode(inode); + err = inode_permission(lower_inode, mask); + return err; +} + +static int wrapfs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err = 0; + struct dentry *lower_dentry; + struct inode *inode; + struct inode *lower_inode; + struct path lower_path; + struct iattr lower_ia; + + inode = dentry->d_inode; + + /* + * Check if user has permission to change inode. We don't check if + * this user can change the lower inode: that should happen when + * calling notify_change on the lower inode. + */ + err = inode_change_ok(inode, ia); + if (err) + goto out_err; + + wrapfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = wrapfs_lower_inode(inode); + + /* prepare our own lower struct iattr (with the lower file) */ + memcpy(&lower_ia, ia, sizeof(lower_ia)); + if (ia->ia_valid & ATTR_FILE) + lower_ia.ia_file = wrapfs_lower_file(ia->ia_file); + + /* + * If shrinking, first truncate upper level to cancel writing dirty + * pages beyond the new eof; and also if its' maxbytes is more + * limiting (fail with -EFBIG before making any change to the lower + * level). There is no need to vmtruncate the upper level + * afterwards in the other cases: we fsstack_copy_inode_size from + * the lower level. + */ + if (ia->ia_valid & ATTR_SIZE) { + err = inode_newsize_ok(inode, ia->ia_size); + if (err) + goto out; + truncate_setsize(inode, ia->ia_size); + } + + /* + * mode change is for clearing setuid/setgid bits. Allow lower fs + * to interpret this in its own way. + */ + if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + lower_ia.ia_valid &= ~ATTR_MODE; + + /* notify the (possibly copied-up) lower inode */ + /* + * Note: we use lower_dentry->d_inode, because lower_inode may be + * unlinked (no inode->i_sb and i_ino==0. This happens if someone + * tries to open(), unlink(), then ftruncate() a file. + */ + mutex_lock(&lower_dentry->d_inode->i_mutex); + err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ + mutex_unlock(&lower_dentry->d_inode->i_mutex); + if (err) + goto out; + + /* get attributes from the lower inode */ + fsstack_copy_attr_all(inode, lower_inode); + /* + * Not running fsstack_copy_inode_size(inode, lower_inode), because + * VFS should update our inode size, and notify_change on + * lower_inode should update its size. + */ + +out: + wrapfs_put_lower_path(dentry, &lower_path); +out_err: + return err; +} + +const struct inode_operations wrapfs_symlink_iops = { + .readlink = wrapfs_readlink, + .permission = wrapfs_permission, + .follow_link = wrapfs_follow_link, + .setattr = wrapfs_setattr, + .put_link = wrapfs_put_link, +}; + +const struct inode_operations wrapfs_dir_iops = { + .create = wrapfs_create, + .lookup = wrapfs_lookup, + .link = wrapfs_link, + .unlink = wrapfs_unlink, + .symlink = wrapfs_symlink, + .mkdir = wrapfs_mkdir, + .rmdir = wrapfs_rmdir, + .mknod = wrapfs_mknod, + .rename = wrapfs_rename, + .permission = wrapfs_permission, + .setattr = wrapfs_setattr, +}; + +const struct inode_operations wrapfs_main_iops = { + .permission = wrapfs_permission, + .setattr = wrapfs_setattr, +}; diff --git a/fs/idmapfs/lookup.c b/fs/idmapfs/lookup.c new file mode 100644 index 0000000..fa6e019 --- /dev/null +++ b/fs/idmapfs/lookup.c @@ -0,0 +1,332 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +/* The dentry cache is just so we have properly sized dentries */ +static struct kmem_cache *wrapfs_dentry_cachep; + +int wrapfs_init_dentry_cache(void) +{ + wrapfs_dentry_cachep = + kmem_cache_create("wrapfs_dentry", + sizeof(struct wrapfs_dentry_info), + 0, SLAB_RECLAIM_ACCOUNT, NULL); + + return wrapfs_dentry_cachep ? 0 : -ENOMEM; +} + +void wrapfs_destroy_dentry_cache(void) +{ + if (wrapfs_dentry_cachep) + kmem_cache_destroy(wrapfs_dentry_cachep); +} + +void free_dentry_private_data(struct dentry *dentry) +{ + if (!dentry || !dentry->d_fsdata) + return; + kmem_cache_free(wrapfs_dentry_cachep, dentry->d_fsdata); + dentry->d_fsdata = NULL; +} + +/* allocate new dentry private data */ +int new_dentry_private_data(struct dentry *dentry) +{ + struct wrapfs_dentry_info *info = WRAPFS_D(dentry); + + /* use zalloc to init dentry_info.lower_path */ + info = kmem_cache_zalloc(wrapfs_dentry_cachep, GFP_ATOMIC); + if (!info) + return -ENOMEM; + + spin_lock_init(&info->lock); + dentry->d_fsdata = info; + + return 0; +} + +/* + * Initialize a nameidata structure (the intent part) we can pass to a lower + * file system. Returns 0 on success or -error (only -ENOMEM possible). + */ +int init_lower_nd(struct nameidata *nd, unsigned int flags) +{ + int err = 0; + + memset(nd, 0, sizeof(struct nameidata)); + if (!flags) + goto out; + + switch (flags) { + case LOOKUP_CREATE: + case LOOKUP_OPEN: + nd->flags = flags; + break; + default: + /* We should never get here, for now */ + pr_debug("wrapfs: unknown nameidata flag 0x%x\n", flags); + BUG(); + break; + } + +out: + return err; +} + +static int wrapfs_inode_test(struct inode *inode, void *candidate_lower_inode) +{ + struct inode *current_lower_inode = wrapfs_lower_inode(inode); + if (current_lower_inode == (struct inode *)candidate_lower_inode) + return 1; /* found a match */ + else + return 0; /* no match */ +} + +static int wrapfs_inode_set(struct inode *inode, void *lower_inode) +{ + /* we do actual inode initialization in wrapfs_iget */ + return 0; +} + +struct inode *wrapfs_iget(struct super_block *sb, struct inode *lower_inode) +{ + struct wrapfs_inode_info *info; + struct inode *inode; /* the new inode to return */ + int err; + + inode = iget5_locked(sb, /* our superblock */ + /* + * hashval: we use inode number, but we can + * also use "(unsigned long)lower_inode" + * instead. + */ + lower_inode->i_ino, /* hashval */ + wrapfs_inode_test, /* inode comparison function */ + wrapfs_inode_set, /* inode init function */ + lower_inode); /* data passed to test+set fxns */ + if (!inode) { + err = -EACCES; + iput(lower_inode); + return ERR_PTR(err); + } + /* if found a cached inode, then just return it */ + if (!(inode->i_state & I_NEW)) + return inode; + + /* initialize new inode */ + info = WRAPFS_I(inode); + + inode->i_ino = lower_inode->i_ino; + if (!igrab(lower_inode)) { + err = -ESTALE; + return ERR_PTR(err); + } + wrapfs_set_lower_inode(inode, lower_inode); + + inode->i_version++; + + /* use different set of inode ops for symlinks & directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_op = &wrapfs_dir_iops; + else if (S_ISLNK(lower_inode->i_mode)) + inode->i_op = &wrapfs_symlink_iops; + else + inode->i_op = &wrapfs_main_iops; + + /* use different set of file ops for directories */ + if (S_ISDIR(lower_inode->i_mode)) + inode->i_fop = &wrapfs_dir_fops; + else + inode->i_fop = &wrapfs_main_fops; + + inode->i_mapping->a_ops = &wrapfs_aops; + + inode->i_atime.tv_sec = 0; + inode->i_atime.tv_nsec = 0; + inode->i_mtime.tv_sec = 0; + inode->i_mtime.tv_nsec = 0; + inode->i_ctime.tv_sec = 0; + inode->i_ctime.tv_nsec = 0; + + /* properly initialize special inodes */ + if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || + S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) + init_special_inode(inode, lower_inode->i_mode, + lower_inode->i_rdev); + + /* all well, copy inode attributes */ + fsstack_copy_attr_all(inode, lower_inode); + fsstack_copy_inode_size(inode, lower_inode); + + unlock_new_inode(inode); + return inode; +} + +/* + * Connect a wrapfs inode dentry/inode with several lower ones. This is + * the classic stackable file system "vnode interposition" action. + * + * @dentry: wrapfs's dentry which interposes on lower one + * @sb: wrapfs's super_block + * @lower_path: the lower path (caller does path_get/put) + */ +int wrapfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path) +{ + int err = 0; + struct inode *inode; + struct inode *lower_inode; + struct super_block *lower_sb; + + lower_inode = lower_path->dentry->d_inode; + lower_sb = wrapfs_lower_super(sb); + + /* check that the lower file system didn't cross a mount point */ + if (lower_inode->i_sb != lower_sb) { + err = -EXDEV; + goto out; + } + + /* + * We allocate our new inode below by calling wrapfs_iget, + * which will initialize some of the new inode's fields + */ + + /* inherit lower inode number for wrapfs's inode */ + inode = wrapfs_iget(sb, lower_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out; + } + + d_add(dentry, inode); + +out: + return err; +} + +/* + * Main driver function for wrapfs's lookup. + * + * Returns: NULL (ok), ERR_PTR if an error occurred. + * Fills in lower_parent_path with <dentry,mnt> on success. + */ +static struct dentry *__wrapfs_lookup(struct dentry *dentry, + unsigned int flags, + struct path *lower_parent_path) +{ + int err = 0; + struct vfsmount *lower_dir_mnt; + struct dentry *lower_dir_dentry = NULL; + struct dentry *lower_dentry; + const char *name; + struct path lower_path; + struct qstr this; + + /* must initialize dentry operations */ + d_set_d_op(dentry, &wrapfs_dops); + + if (IS_ROOT(dentry)) + goto out; + + name = dentry->d_name.name; + + /* now start the actual lookup procedure */ + lower_dir_dentry = lower_parent_path->dentry; + lower_dir_mnt = lower_parent_path->mnt; + + /* Use vfs_path_lookup to check if the dentry exists or not */ + err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, + &lower_path); + + /* no error: handle positive dentries */ + if (!err) { + wrapfs_set_lower_path(dentry, &lower_path); + err = wrapfs_interpose(dentry, dentry->d_sb, &lower_path); + if (err) /* path_put underlying path on error */ + wrapfs_put_reset_lower_path(dentry); + goto out; + } + + /* + * We don't consider ENOENT an error, and we want to return a + * negative dentry. + */ + if (err && err != -ENOENT) + goto out; + + /* instatiate a new negative dentry */ + this.name = name; + this.len = strlen(name); + this.hash = full_name_hash(this.name, this.len); + lower_dentry = d_lookup(lower_dir_dentry, &this); + if (lower_dentry) + goto setup_lower; + + lower_dentry = d_alloc(lower_dir_dentry, &this); + if (!lower_dentry) { + err = -ENOMEM; + goto out; + } + d_add(lower_dentry, NULL); /* instantiate and hash */ + +setup_lower: + lower_path.dentry = lower_dentry; + lower_path.mnt = mntget(lower_dir_mnt); + wrapfs_set_lower_path(dentry, &lower_path); + + /* + * If the intent is to create a file, then don't return an error, so + * the VFS will continue the process of making this negative dentry + * into a positive one. + */ + if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) + err = 0; + +out: + return ERR_PTR(err); +} + +struct dentry *wrapfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct dentry *ret, *parent; + struct path lower_parent_path; + int err = 0; + + parent = dget_parent(dentry); + + wrapfs_get_lower_path(parent, &lower_parent_path); + + /* allocate dentry private data. We free it in ->d_release */ + err = new_dentry_private_data(dentry); + if (err) { + ret = ERR_PTR(err); + goto out; + } + ret = __wrapfs_lookup(dentry, flags, &lower_parent_path); + if (IS_ERR(ret)) + goto out; + if (ret) + dentry = ret; + if (dentry->d_inode) + fsstack_copy_attr_times(dentry->d_inode, + wrapfs_lower_inode(dentry->d_inode)); + /* update parent directory's atime */ + fsstack_copy_attr_atime(parent->d_inode, + wrapfs_lower_inode(parent->d_inode)); + +out: + wrapfs_put_lower_path(parent, &lower_parent_path); + dput(parent); + return ret; +} diff --git a/fs/idmapfs/main.c b/fs/idmapfs/main.c new file mode 100644 index 0000000..73153fd --- /dev/null +++ b/fs/idmapfs/main.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" +#include <linux/module.h> + +/* + * There is no need to lock the wrapfs_super_info's rwsem as there is no + * way anyone can have a reference to the superblock at this point in time. + */ +static int wrapfs_read_super(struct super_block *sb, void *raw_data, int silent) +{ + int err = 0; + struct super_block *lower_sb; + struct path lower_path; + char *dev_name = (char *) raw_data; + struct inode *inode; + + if (!dev_name) { + printk(KERN_ERR + "wrapfs: read_super: missing dev_name argument\n"); + err = -EINVAL; + goto out; + } + + /* parse lower path */ + err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, + &lower_path); + if (err) { + printk(KERN_ERR "wrapfs: error accessing " + "lower directory '%s'\n", dev_name); + goto out; + } + + /* allocate superblock private data */ + sb->s_fs_info = kzalloc(sizeof(struct wrapfs_sb_info), GFP_KERNEL); + if (!WRAPFS_SB(sb)) { + printk(KERN_CRIT "wrapfs: read_super: out of memory\n"); + err = -ENOMEM; + goto out_free; + } + + /* set the lower superblock field of upper superblock */ + lower_sb = lower_path.dentry->d_sb; + atomic_inc(&lower_sb->s_active); + wrapfs_set_lower_super(sb, lower_sb); + + /* inherit maxbytes from lower file system */ + sb->s_maxbytes = lower_sb->s_maxbytes; + + /* + * Our c/m/atime granularity is 1 ns because we may stack on file + * systems whose granularity is as good. + */ + sb->s_time_gran = 1; + + sb->s_op = &wrapfs_sops; + + /* get a new inode and allocate our root dentry */ + inode = wrapfs_iget(sb, lower_path.dentry->d_inode); + if (IS_ERR(inode)) { + err = PTR_ERR(inode); + goto out_sput; + } + sb->s_root = d_make_root(inode); + if (!sb->s_root) { + err = -ENOMEM; + goto out_iput; + } + d_set_d_op(sb->s_root, &wrapfs_dops); + + /* link the upper and lower dentries */ + sb->s_root->d_fsdata = NULL; + err = new_dentry_private_data(sb->s_root); + if (err) + goto out_freeroot; + + /* if get here: cannot have error */ + + /* set the lower dentries for s_root */ + wrapfs_set_lower_path(sb->s_root, &lower_path); + + /* + * No need to call interpose because we already have a positive + * dentry, which was instantiated by d_make_root. Just need to + * d_rehash it. + */ + d_rehash(sb->s_root); + if (!silent) + printk(KERN_INFO + "wrapfs: mounted on top of %s type %s\n", + dev_name, lower_sb->s_type->name); + goto out; /* all is well */ + + /* no longer needed: free_dentry_private_data(sb->s_root); */ +out_freeroot: + dput(sb->s_root); +out_iput: + iput(inode); +out_sput: + /* drop refs we took earlier */ + atomic_dec(&lower_sb->s_active); + kfree(WRAPFS_SB(sb)); + sb->s_fs_info = NULL; +out_free: + path_put(&lower_path); + +out: + return err; +} + +struct dentry *wrapfs_mount(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data) +{ + void *lower_path_name = (void *) dev_name; + + return mount_nodev(fs_type, flags, lower_path_name, + wrapfs_read_super); +} + +static struct file_system_type wrapfs_fs_type = { + .owner = THIS_MODULE, + .name = WRAPFS_NAME, + .mount = wrapfs_mount, + .kill_sb = generic_shutdown_super, + .fs_flags = 0, +}; +MODULE_ALIAS_FS(WRAPFS_NAME); + +static int __init init_wrapfs_fs(void) +{ + int err; + + pr_info("Registering wrapfs " WRAPFS_VERSION "\n"); + + err = wrapfs_init_inode_cache(); + if (err) + goto out; + err = wrapfs_init_dentry_cache(); + if (err) + goto out; + err = register_filesystem(&wrapfs_fs_type); +out: + if (err) { + wrapfs_destroy_inode_cache(); + wrapfs_destroy_dentry_cache(); + } + return err; +} + +static void __exit exit_wrapfs_fs(void) +{ + wrapfs_destroy_inode_cache(); + wrapfs_destroy_dentry_cache(); + unregister_filesystem(&wrapfs_fs_type); + pr_info("Completed wrapfs module unload\n"); +} + +MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" + " (http://www.fsl.cs.sunysb.edu/)"); +MODULE_DESCRIPTION("Wrapfs " WRAPFS_VERSION + " (http://wrapfs.filesystems.org/)"); +MODULE_LICENSE("GPL"); + +module_init(init_wrapfs_fs); +module_exit(exit_wrapfs_fs); diff --git a/fs/idmapfs/mmap.c b/fs/idmapfs/mmap.c new file mode 100644 index 0000000..c224fc3 --- /dev/null +++ b/fs/idmapfs/mmap.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +static int wrapfs_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; + + memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); + file = lower_vma.vm_file; + lower_vm_ops = WRAPFS_F(file)->lower_vm_ops; + BUG_ON(!lower_vm_ops); + + lower_file = wrapfs_lower_file(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 wrapfs_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; +} + +/* + * XXX: the default address_space_ops for wrapfs is empty. We cannot set + * our inode->i_mapping->a_ops to NULL because too many code paths expect + * the a_ops vector to be non-NULL. + */ +const struct address_space_operations wrapfs_aops = { + /* empty on purpose */ +}; + +const struct vm_operations_struct wrapfs_vm_ops = { + .fault = wrapfs_fault, +}; diff --git a/fs/idmapfs/super.c b/fs/idmapfs/super.c new file mode 100644 index 0000000..aec2093 --- /dev/null +++ b/fs/idmapfs/super.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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 "wrapfs.h" + +/* + * The inode cache is used with alloc_inode for both our inode info and the + * vfs inode. + */ +static struct kmem_cache *wrapfs_inode_cachep; + +/* final actions when unmounting a file system */ +static void wrapfs_put_super(struct super_block *sb) +{ + struct wrapfs_sb_info *spd; + struct super_block *s; + + spd = WRAPFS_SB(sb); + if (!spd) + return; + + /* decrement lower super references */ + s = wrapfs_lower_super(sb); + wrapfs_set_lower_super(sb, NULL); + atomic_dec(&s->s_active); + + kfree(spd); + sb->s_fs_info = NULL; +} + +static int wrapfs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + struct path lower_path; + + wrapfs_get_lower_path(dentry, &lower_path); + err = vfs_statfs(&lower_path, buf); + wrapfs_put_lower_path(dentry, &lower_path); + + /* set return buf to our f/s to avoid confusing user-level utils */ + buf->f_type = WRAPFS_SUPER_MAGIC; + + return err; +} + +/* + * @flags: numeric mount options + * @options: mount options string + */ +static int wrapfs_remount_fs(struct super_block *sb, int *flags, char *options) +{ + int err = 0; + + /* + * The VFS will take care of "ro" and "rw" flags among others. We + * can safely accept a few flags (RDONLY, MANDLOCK), and honor + * SILENT, but anything else left over is an error. + */ + if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { + printk(KERN_ERR + "wrapfs: remount flags 0x%x unsupported\n", *flags); + err = -EINVAL; + } + + return err; +} + +/* + * Called by iput() when the inode reference count reached zero + * and the inode is not hashed anywhere. Used to clear anything + * that needs to be, before the inode is completely destroyed and put + * on the inode free list. + */ +static void wrapfs_evict_inode(struct inode *inode) +{ + struct inode *lower_inode; + + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + /* + * Decrement a reference to a lower_inode, which was incremented + * by our read_inode when it was created initially. + */ + lower_inode = wrapfs_lower_inode(inode); + wrapfs_set_lower_inode(inode, NULL); + iput(lower_inode); +} + +static struct inode *wrapfs_alloc_inode(struct super_block *sb) +{ + struct wrapfs_inode_info *i; + + i = kmem_cache_alloc(wrapfs_inode_cachep, GFP_KERNEL); + if (!i) + return NULL; + + /* memset everything up to the inode to 0 */ + memset(i, 0, offsetof(struct wrapfs_inode_info, vfs_inode)); + + i->vfs_inode.i_version = 1; + return &i->vfs_inode; +} + +static void wrapfs_destroy_inode(struct inode *inode) +{ + kmem_cache_free(wrapfs_inode_cachep, WRAPFS_I(inode)); +} + +/* wrapfs inode cache constructor */ +static void init_once(void *obj) +{ + struct wrapfs_inode_info *i = obj; + + inode_init_once(&i->vfs_inode); +} + +int wrapfs_init_inode_cache(void) +{ + int err = 0; + + wrapfs_inode_cachep = + kmem_cache_create("wrapfs_inode_cache", + sizeof(struct wrapfs_inode_info), 0, + SLAB_RECLAIM_ACCOUNT, init_once); + if (!wrapfs_inode_cachep) + err = -ENOMEM; + return err; +} + +/* wrapfs inode cache destructor */ +void wrapfs_destroy_inode_cache(void) +{ + if (wrapfs_inode_cachep) + kmem_cache_destroy(wrapfs_inode_cachep); +} + +/* + * Used only in nfs, to kill any pending RPC tasks, so that subsequent + * code can actually succeed and won't leave tasks that need handling. + */ +static void wrapfs_umount_begin(struct super_block *sb) +{ + struct super_block *lower_sb; + + lower_sb = wrapfs_lower_super(sb); + if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin) + lower_sb->s_op->umount_begin(lower_sb); +} + +const struct super_operations wrapfs_sops = { + .put_super = wrapfs_put_super, + .statfs = wrapfs_statfs, + .remount_fs = wrapfs_remount_fs, + .evict_inode = wrapfs_evict_inode, + .umount_begin = wrapfs_umount_begin, + .show_options = generic_show_options, + .alloc_inode = wrapfs_alloc_inode, + .destroy_inode = wrapfs_destroy_inode, + .drop_inode = generic_delete_inode, +}; diff --git a/fs/idmapfs/wrapfs.h b/fs/idmapfs/wrapfs.h new file mode 100644 index 0000000..a85b07e --- /dev/null +++ b/fs/idmapfs/wrapfs.h @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1998-2011 Erez Zadok + * Copyright (c) 2009 Shrikar Archak + * Copyright (c) 2003-2011 Stony Brook University + * Copyright (c) 2003-2011 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. + */ + +#ifndef _WRAPFS_H_ +#define _WRAPFS_H_ + +#include <linux/dcache.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/mount.h> +#include <linux/namei.h> +#include <linux/seq_file.h> +#include <linux/statfs.h> +#include <linux/fs_stack.h> +#include <linux/magic.h> +#include <linux/uaccess.h> +#include <linux/slab.h> +#include <linux/sched.h> + +/* the file system name */ +#define WRAPFS_NAME "idmapfs" + +/* wrapfs root inode number */ +#define WRAPFS_ROOT_INO 1 + +/* useful for tracking code reachability */ +#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) + +/* operations vectors defined in specific files */ +extern const struct file_operations wrapfs_main_fops; +extern const struct file_operations wrapfs_dir_fops; +extern const struct inode_operations wrapfs_main_iops; +extern const struct inode_operations wrapfs_dir_iops; +extern const struct inode_operations wrapfs_symlink_iops; +extern const struct super_operations wrapfs_sops; +extern const struct dentry_operations wrapfs_dops; +extern const struct address_space_operations wrapfs_aops, wrapfs_dummy_aops; +extern const struct vm_operations_struct wrapfs_vm_ops; + +extern int wrapfs_init_inode_cache(void); +extern void wrapfs_destroy_inode_cache(void); +extern int wrapfs_init_dentry_cache(void); +extern void wrapfs_destroy_dentry_cache(void); +extern int new_dentry_private_data(struct dentry *dentry); +extern void free_dentry_private_data(struct dentry *dentry); +extern int init_lower_nd(struct nameidata *nd, unsigned int flags); +extern struct dentry *wrapfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags); +extern struct inode *wrapfs_iget(struct super_block *sb, + struct inode *lower_inode); +extern int wrapfs_interpose(struct dentry *dentry, struct super_block *sb, + struct path *lower_path); + +/* file private data */ +struct wrapfs_file_info { + struct file *lower_file; + const struct vm_operations_struct *lower_vm_ops; +}; + +/* wrapfs inode data in memory */ +struct wrapfs_inode_info { + struct inode *lower_inode; + struct inode vfs_inode; +}; + +/* wrapfs dentry data in memory */ +struct wrapfs_dentry_info { + spinlock_t lock; /* protects lower_path */ + struct path lower_path; +}; + +/* wrapfs super-block data in memory */ +struct wrapfs_sb_info { + struct super_block *lower_sb; +}; + +/* + * inode to private data + * + * Since we use containers and the struct inode is _inside_ the + * wrapfs_inode_info structure, WRAPFS_I will always (given a non-NULL + * inode pointer), return a valid non-NULL pointer. + */ +static inline struct wrapfs_inode_info *WRAPFS_I(const struct inode *inode) +{ + return container_of(inode, struct wrapfs_inode_info, vfs_inode); +} + +/* dentry to private data */ +#define WRAPFS_D(dent) ((struct wrapfs_dentry_info *)(dent)->d_fsdata) + +/* superblock to private data */ +#define WRAPFS_SB(super) ((struct wrapfs_sb_info *)(super)->s_fs_info) + +/* file to private Data */ +#define WRAPFS_F(file) ((struct wrapfs_file_info *)((file)->private_data)) + +/* file to lower file */ +static inline struct file *wrapfs_lower_file(const struct file *f) +{ + return WRAPFS_F(f)->lower_file; +} + +static inline void wrapfs_set_lower_file(struct file *f, struct file *val) +{ + WRAPFS_F(f)->lower_file = val; +} + +/* inode to lower inode. */ +static inline struct inode *wrapfs_lower_inode(const struct inode *i) +{ + return WRAPFS_I(i)->lower_inode; +} + +static inline void wrapfs_set_lower_inode(struct inode *i, struct inode *val) +{ + WRAPFS_I(i)->lower_inode = val; +} + +/* superblock to lower superblock */ +static inline struct super_block *wrapfs_lower_super( + const struct super_block *sb) +{ + return WRAPFS_SB(sb)->lower_sb; +} + +static inline void wrapfs_set_lower_super(struct super_block *sb, + struct super_block *val) +{ + WRAPFS_SB(sb)->lower_sb = val; +} + +/* path based (dentry/mnt) macros */ +static inline void pathcpy(struct path *dst, const struct path *src) +{ + dst->dentry = src->dentry; + dst->mnt = src->mnt; +} +/* Returns struct path. Caller must path_put it. */ +static inline void wrapfs_get_lower_path(const struct dentry *dent, + struct path *lower_path) +{ + spin_lock(&WRAPFS_D(dent)->lock); + pathcpy(lower_path, &WRAPFS_D(dent)->lower_path); + path_get(lower_path); + spin_unlock(&WRAPFS_D(dent)->lock); + return; +} +static inline void wrapfs_put_lower_path(const struct dentry *dent, + struct path *lower_path) +{ + path_put(lower_path); + return; +} +static inline void wrapfs_set_lower_path(const struct dentry *dent, + struct path *lower_path) +{ + spin_lock(&WRAPFS_D(dent)->lock); + pathcpy(&WRAPFS_D(dent)->lower_path, lower_path); + spin_unlock(&WRAPFS_D(dent)->lock); + return; +} +static inline void wrapfs_reset_lower_path(const struct dentry *dent) +{ + spin_lock(&WRAPFS_D(dent)->lock); + WRAPFS_D(dent)->lower_path.dentry = NULL; + WRAPFS_D(dent)->lower_path.mnt = NULL; + spin_unlock(&WRAPFS_D(dent)->lock); + return; +} +static inline void wrapfs_put_reset_lower_path(const struct dentry *dent) +{ + struct path lower_path; + spin_lock(&WRAPFS_D(dent)->lock); + pathcpy(&lower_path, &WRAPFS_D(dent)->lower_path); + WRAPFS_D(dent)->lower_path.dentry = NULL; + WRAPFS_D(dent)->lower_path.mnt = NULL; + spin_unlock(&WRAPFS_D(dent)->lock); + path_put(&lower_path); + return; +} + +/* locking helpers */ +static inline struct dentry *lock_parent(struct dentry *dentry) +{ + struct dentry *dir = dget_parent(dentry); + mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); + return dir; +} + +static inline void unlock_dir(struct dentry *dir) +{ + mutex_unlock(&dir->d_inode->i_mutex); + dput(dir); +} +#endif /* not _WRAPFS_H_ */ diff --git a/fs/wrapfs/Kconfig b/fs/wrapfs/Kconfig deleted file mode 100644 index a495c7d..0000000 --- a/fs/wrapfs/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config WRAP_FS - tristate "Wrapfs stackable file system (EXPERIMENTAL)" - help - Wrapfs is a stackable file system which simply passes its - operations to the lower layer. It is designed as a useful - template for developing or debugging other stackable file systems, - and more (see Documentation/filesystems/wrapfs.txt). See - <http://wrapfs.filesystems.org/> for details. diff --git a/fs/wrapfs/Makefile b/fs/wrapfs/Makefile deleted file mode 100644 index f318d11..0000000 --- a/fs/wrapfs/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -WRAPFS_VERSION="0.1" - -EXTRA_CFLAGS += -DWRAPFS_VERSION=\"$(WRAPFS_VERSION)\" - -obj-$(CONFIG_WRAP_FS) += wrapfs.o - -wrapfs-y := dentry.o file.o inode.o main.o super.o lookup.o mmap.o diff --git a/fs/wrapfs/dentry.c b/fs/wrapfs/dentry.c deleted file mode 100644 index ed1a0cb..0000000 --- a/fs/wrapfs/dentry.c +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -/* - * returns: -ERRNO if error (returned to user) - * 0: tell VFS to invalidate dentry - * 1: dentry is valid - */ -static int wrapfs_d_revalidate(struct dentry *dentry, unsigned int flags) -{ - struct path lower_path; - struct dentry *lower_dentry; - int err = 1; - - if (flags & LOOKUP_RCU) - return -ECHILD; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - if (!lower_dentry->d_op || !lower_dentry->d_op->d_revalidate) - goto out; - err = lower_dentry->d_op->d_revalidate(lower_dentry, flags); -out: - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static void wrapfs_d_release(struct dentry *dentry) -{ - /* release and reset the lower paths */ - wrapfs_put_reset_lower_path(dentry); - free_dentry_private_data(dentry); - return; -} - -const struct dentry_operations wrapfs_dops = { - .d_revalidate = wrapfs_d_revalidate, - .d_release = wrapfs_d_release, -}; diff --git a/fs/wrapfs/file.c b/fs/wrapfs/file.c deleted file mode 100644 index 9825816..0000000 --- a/fs/wrapfs/file.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -static ssize_t wrapfs_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int err; - struct file *lower_file; - struct dentry *dentry = file->f_path.dentry; - - lower_file = wrapfs_lower_file(file); - err = vfs_read(lower_file, buf, count, ppos); - /* update our inode atime upon a successful lower read */ - if (err >= 0) - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - - return err; -} - -static ssize_t wrapfs_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - int err = 0; - struct file *lower_file; - struct dentry *dentry = file->f_path.dentry; - - lower_file = wrapfs_lower_file(file); - err = vfs_write(lower_file, buf, count, ppos); - /* update our inode times+sizes upon a successful lower write */ - if (err >= 0) { - fsstack_copy_inode_size(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - fsstack_copy_attr_times(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - } - - return err; -} - -static int wrapfs_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - int err = 0; - struct file *lower_file = NULL; - struct dentry *dentry = file->f_path.dentry; - - lower_file = wrapfs_lower_file(file); - err = vfs_readdir(lower_file, filldir, dirent); - file->f_pos = lower_file->f_pos; - if (err >= 0) /* copy the atime */ - fsstack_copy_attr_atime(dentry->d_inode, - lower_file->f_path.dentry->d_inode); - return err; -} - -static long wrapfs_unlocked_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - long err = -ENOTTY; - struct file *lower_file; - - lower_file = wrapfs_lower_file(file); - - /* XXX: use vfs_ioctl if/when VFS exports it */ - if (!lower_file || !lower_file->f_op) - goto out; - if (lower_file->f_op->unlocked_ioctl) - err = lower_file->f_op->unlocked_ioctl(lower_file, cmd, arg); - - /* some ioctls can change inode attributes (EXT2_IOC_SETFLAGS) */ - if (!err) - fsstack_copy_attr_all(file->f_path.dentry->d_inode, - lower_file->f_path.dentry->d_inode); -out: - return err; -} - -#ifdef CONFIG_COMPAT -static long wrapfs_compat_ioctl(struct file *file, unsigned int cmd, - unsigned long arg) -{ - long err = -ENOTTY; - struct file *lower_file; - - lower_file = wrapfs_lower_file(file); - - /* XXX: use vfs_ioctl if/when VFS exports it */ - if (!lower_file || !lower_file->f_op) - goto out; - if (lower_file->f_op->compat_ioctl) - err = lower_file->f_op->compat_ioctl(lower_file, cmd, arg); - -out: - return err; -} -#endif - -static int wrapfs_mmap(struct file *file, struct vm_area_struct *vma) -{ - int err = 0; - bool willwrite; - struct file *lower_file; - const struct vm_operations_struct *saved_vm_ops = NULL; - - /* this might be deferred to mmap's writepage */ - willwrite = ((vma->vm_flags | VM_SHARED | VM_WRITE) == vma->vm_flags); - - /* - * File systems which do not implement ->writepage may use - * generic_file_readonly_mmap as their ->mmap op. If you call - * generic_file_readonly_mmap with VM_WRITE, you'd get an -EINVAL. - * But we cannot call the lower ->mmap op, so we can't tell that - * writeable mappings won't work. Therefore, our only choice is to - * check if the lower file system supports the ->writepage, and if - * not, return EINVAL (the same error that - * generic_file_readonly_mmap returns in that case). - */ - lower_file = wrapfs_lower_file(file); - if (willwrite && !lower_file->f_mapping->a_ops->writepage) { - err = -EINVAL; - printk(KERN_ERR "wrapfs: lower file system does not " - "support writeable mmap\n"); - goto out; - } - - /* - * find and save lower vm_ops. - * - * XXX: the VFS should have a cleaner way of finding the lower vm_ops - */ - if (!WRAPFS_F(file)->lower_vm_ops) { - err = lower_file->f_op->mmap(lower_file, vma); - if (err) { - printk(KERN_ERR "wrapfs: lower mmap failed %d\n", err); - goto out; - } - saved_vm_ops = vma->vm_ops; /* save: came from lower ->mmap */ - } - - /* - * Next 3 lines are all I need from generic_file_mmap. I definitely - * don't want its test for ->readpage which returns -ENOEXEC. - */ - file_accessed(file); - vma->vm_ops = &wrapfs_vm_ops; - - file->f_mapping->a_ops = &wrapfs_aops; /* set our aops */ - if (!WRAPFS_F(file)->lower_vm_ops) /* save for our ->fault */ - WRAPFS_F(file)->lower_vm_ops = saved_vm_ops; - -out: - return err; -} - -static int wrapfs_open(struct inode *inode, struct file *file) -{ - int err = 0; - struct file *lower_file = NULL; - struct path lower_path; - - /* don't open unhashed/deleted files */ - if (d_unhashed(file->f_path.dentry)) { - err = -ENOENT; - goto out_err; - } - - file->private_data = - kzalloc(sizeof(struct wrapfs_file_info), GFP_KERNEL); - if (!WRAPFS_F(file)) { - err = -ENOMEM; - goto out_err; - } - - /* open lower object and link wrapfs's file struct to lower's */ - wrapfs_get_lower_path(file->f_path.dentry, &lower_path); - lower_file = dentry_open(&lower_path, file->f_flags, current_cred()); - path_put(&lower_path); - if (IS_ERR(lower_file)) { - err = PTR_ERR(lower_file); - lower_file = wrapfs_lower_file(file); - if (lower_file) { - wrapfs_set_lower_file(file, NULL); - fput(lower_file); /* fput calls dput for lower_dentry */ - } - } else { - wrapfs_set_lower_file(file, lower_file); - } - - if (err) - kfree(WRAPFS_F(file)); - else - fsstack_copy_attr_all(inode, wrapfs_lower_inode(inode)); -out_err: - return err; -} - -static int wrapfs_flush(struct file *file, fl_owner_t id) -{ - int err = 0; - struct file *lower_file = NULL; - - lower_file = wrapfs_lower_file(file); - if (lower_file && lower_file->f_op && lower_file->f_op->flush) - err = lower_file->f_op->flush(lower_file, id); - - return err; -} - -/* release all lower object references & free the file info structure */ -static int wrapfs_file_release(struct inode *inode, struct file *file) -{ - struct file *lower_file; - - lower_file = wrapfs_lower_file(file); - if (lower_file) { - wrapfs_set_lower_file(file, NULL); - fput(lower_file); - } - - kfree(WRAPFS_F(file)); - return 0; -} - -static int wrapfs_fsync(struct file *file, loff_t start, loff_t end, - int datasync) -{ - int err; - struct file *lower_file; - struct path lower_path; - struct dentry *dentry = file->f_path.dentry; - - err = generic_file_fsync(file, start, end, datasync); - if (err) - goto out; - lower_file = wrapfs_lower_file(file); - wrapfs_get_lower_path(dentry, &lower_path); - err = vfs_fsync_range(lower_file, start, end, datasync); - wrapfs_put_lower_path(dentry, &lower_path); -out: - return err; -} - -static int wrapfs_fasync(int fd, struct file *file, int flag) -{ - int err = 0; - struct file *lower_file = NULL; - - lower_file = wrapfs_lower_file(file); - if (lower_file->f_op && lower_file->f_op->fasync) - err = lower_file->f_op->fasync(fd, lower_file, flag); - - return err; -} - -const struct file_operations wrapfs_main_fops = { - .llseek = generic_file_llseek, - .read = wrapfs_read, - .write = wrapfs_write, - .unlocked_ioctl = wrapfs_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = wrapfs_compat_ioctl, -#endif - .mmap = wrapfs_mmap, - .open = wrapfs_open, - .flush = wrapfs_flush, - .release = wrapfs_file_release, - .fsync = wrapfs_fsync, - .fasync = wrapfs_fasync, -}; - -/* trimmed directory options */ -const struct file_operations wrapfs_dir_fops = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = wrapfs_readdir, - .unlocked_ioctl = wrapfs_unlocked_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = wrapfs_compat_ioctl, -#endif - .open = wrapfs_open, - .release = wrapfs_file_release, - .flush = wrapfs_flush, - .fsync = wrapfs_fsync, - .fasync = wrapfs_fasync, -}; diff --git a/fs/wrapfs/inode.c b/fs/wrapfs/inode.c deleted file mode 100644 index d7fe145..0000000 --- a/fs/wrapfs/inode.c +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -static int wrapfs_create(struct inode *dir, struct dentry *dentry, - umode_t mode, bool want_excl) -{ - int err = 0; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_create(lower_parent_dentry->d_inode, lower_dentry, mode, - want_excl); - if (err) - goto out; - err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); - -out: - unlock_dir(lower_parent_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static int wrapfs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *new_dentry) -{ - struct dentry *lower_old_dentry; - struct dentry *lower_new_dentry; - struct dentry *lower_dir_dentry; - u64 file_size_save; - int err; - struct path lower_old_path, lower_new_path; - - file_size_save = i_size_read(old_dentry->d_inode); - wrapfs_get_lower_path(old_dentry, &lower_old_path); - wrapfs_get_lower_path(new_dentry, &lower_new_path); - lower_old_dentry = lower_old_path.dentry; - lower_new_dentry = lower_new_path.dentry; - lower_dir_dentry = lock_parent(lower_new_dentry); - - err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, - lower_new_dentry); - if (err || !lower_new_dentry->d_inode) - goto out; - - err = wrapfs_interpose(new_dentry, dir->i_sb, &lower_new_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, lower_new_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_new_dentry->d_inode); - set_nlink(old_dentry->d_inode, - wrapfs_lower_inode(old_dentry->d_inode)->i_nlink); - i_size_write(new_dentry->d_inode, file_size_save); -out: - unlock_dir(lower_dir_dentry); - wrapfs_put_lower_path(old_dentry, &lower_old_path); - wrapfs_put_lower_path(new_dentry, &lower_new_path); - return err; -} - -static int wrapfs_unlink(struct inode *dir, struct dentry *dentry) -{ - int err; - struct dentry *lower_dentry; - struct inode *lower_dir_inode = wrapfs_lower_inode(dir); - struct dentry *lower_dir_dentry; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - dget(lower_dentry); - lower_dir_dentry = lock_parent(lower_dentry); - - err = vfs_unlink(lower_dir_inode, lower_dentry); - - /* - * Note: unlinking on top of NFS can cause silly-renamed files. - * Trying to delete such files results in EBUSY from NFS - * below. Silly-renamed files will get deleted by NFS later on, so - * we just need to detect them here and treat such EBUSY errors as - * if the upper file was successfully deleted. - */ - if (err == -EBUSY && lower_dentry->d_flags & DCACHE_NFSFS_RENAMED) - err = 0; - if (err) - goto out; - fsstack_copy_attr_times(dir, lower_dir_inode); - fsstack_copy_inode_size(dir, lower_dir_inode); - set_nlink(dentry->d_inode, - wrapfs_lower_inode(dentry->d_inode)->i_nlink); - dentry->d_inode->i_ctime = dir->i_ctime; - d_drop(dentry); /* this is needed, else LTP fails (VFS won't do it) */ -out: - unlock_dir(lower_dir_dentry); - dput(lower_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static int wrapfs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - int err = 0; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_symlink(lower_parent_dentry->d_inode, lower_dentry, symname); - if (err) - goto out; - err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); - -out: - unlock_dir(lower_parent_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static int wrapfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - int err = 0; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); - if (err) - goto out; - - err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - - fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); - /* update number of links on parent directory */ - set_nlink(dir, wrapfs_lower_inode(dir)->i_nlink); - -out: - unlock_dir(lower_parent_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static int wrapfs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct dentry *lower_dentry; - struct dentry *lower_dir_dentry; - int err; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_dir_dentry = lock_parent(lower_dentry); - - err = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); - if (err) - goto out; - - d_drop(dentry); /* drop our dentry on success (why not VFS's job?) */ - if (dentry->d_inode) - clear_nlink(dentry->d_inode); - fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); - fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); - set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); - -out: - unlock_dir(lower_dir_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static int wrapfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, - dev_t dev) -{ - int err = 0; - struct dentry *lower_dentry; - struct dentry *lower_parent_dentry = NULL; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_parent_dentry = lock_parent(lower_dentry); - - err = vfs_mknod(lower_parent_dentry->d_inode, lower_dentry, mode, dev); - if (err) - goto out; - - err = wrapfs_interpose(dentry, dir->i_sb, &lower_path); - if (err) - goto out; - fsstack_copy_attr_times(dir, wrapfs_lower_inode(dir)); - fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); - -out: - unlock_dir(lower_parent_dentry); - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -/* - * The locking rules in wrapfs_rename are complex. We could use a simpler - * superblock-level name-space lock for renames and copy-ups. - */ -static int wrapfs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - int err = 0; - struct dentry *lower_old_dentry = NULL; - struct dentry *lower_new_dentry = NULL; - struct dentry *lower_old_dir_dentry = NULL; - struct dentry *lower_new_dir_dentry = NULL; - struct dentry *trap = NULL; - struct path lower_old_path, lower_new_path; - - wrapfs_get_lower_path(old_dentry, &lower_old_path); - wrapfs_get_lower_path(new_dentry, &lower_new_path); - lower_old_dentry = lower_old_path.dentry; - lower_new_dentry = lower_new_path.dentry; - lower_old_dir_dentry = dget_parent(lower_old_dentry); - lower_new_dir_dentry = dget_parent(lower_new_dentry); - - trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - /* source should not be ancestor of target */ - if (trap == lower_old_dentry) { - err = -EINVAL; - goto out; - } - /* target should not be ancestor of source */ - if (trap == lower_new_dentry) { - err = -ENOTEMPTY; - goto out; - } - - err = vfs_rename(lower_old_dir_dentry->d_inode, lower_old_dentry, - lower_new_dir_dentry->d_inode, lower_new_dentry); - if (err) - goto out; - - fsstack_copy_attr_all(new_dir, lower_new_dir_dentry->d_inode); - fsstack_copy_inode_size(new_dir, lower_new_dir_dentry->d_inode); - if (new_dir != old_dir) { - fsstack_copy_attr_all(old_dir, - lower_old_dir_dentry->d_inode); - fsstack_copy_inode_size(old_dir, - lower_old_dir_dentry->d_inode); - } - -out: - unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); - dput(lower_old_dir_dentry); - dput(lower_new_dir_dentry); - wrapfs_put_lower_path(old_dentry, &lower_old_path); - wrapfs_put_lower_path(new_dentry, &lower_new_path); - return err; -} - -static int wrapfs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) -{ - int err; - struct dentry *lower_dentry; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - if (!lower_dentry->d_inode->i_op || - !lower_dentry->d_inode->i_op->readlink) { - err = -EINVAL; - goto out; - } - - err = lower_dentry->d_inode->i_op->readlink(lower_dentry, - buf, bufsiz); - if (err < 0) - goto out; - fsstack_copy_attr_atime(dentry->d_inode, lower_dentry->d_inode); - -out: - wrapfs_put_lower_path(dentry, &lower_path); - return err; -} - -static void *wrapfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - char *buf; - int len = PAGE_SIZE, err; - mm_segment_t old_fs; - - /* This is freed by the put_link method assuming a successful call. */ - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - buf = ERR_PTR(-ENOMEM); - goto out; - } - - /* read the symlink, and then we will follow it */ - old_fs = get_fs(); - set_fs(KERNEL_DS); - err = wrapfs_readlink(dentry, buf, len); - set_fs(old_fs); - if (err < 0) { - kfree(buf); - buf = ERR_PTR(err); - } else { - buf[err] = '\0'; - } -out: - nd_set_link(nd, buf); - return NULL; -} - -/* this @nd *IS* still used */ -static void wrapfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *buf = nd_get_link(nd); - if (!IS_ERR(buf)) /* free the char* */ - kfree(buf); -} - -static int wrapfs_permission(struct inode *inode, int mask) -{ - struct inode *lower_inode; - int err; - - lower_inode = wrapfs_lower_inode(inode); - err = inode_permission(lower_inode, mask); - return err; -} - -static int wrapfs_setattr(struct dentry *dentry, struct iattr *ia) -{ - int err = 0; - struct dentry *lower_dentry; - struct inode *inode; - struct inode *lower_inode; - struct path lower_path; - struct iattr lower_ia; - - inode = dentry->d_inode; - - /* - * Check if user has permission to change inode. We don't check if - * this user can change the lower inode: that should happen when - * calling notify_change on the lower inode. - */ - err = inode_change_ok(inode, ia); - if (err) - goto out_err; - - wrapfs_get_lower_path(dentry, &lower_path); - lower_dentry = lower_path.dentry; - lower_inode = wrapfs_lower_inode(inode); - - /* prepare our own lower struct iattr (with the lower file) */ - memcpy(&lower_ia, ia, sizeof(lower_ia)); - if (ia->ia_valid & ATTR_FILE) - lower_ia.ia_file = wrapfs_lower_file(ia->ia_file); - - /* - * If shrinking, first truncate upper level to cancel writing dirty - * pages beyond the new eof; and also if its' maxbytes is more - * limiting (fail with -EFBIG before making any change to the lower - * level). There is no need to vmtruncate the upper level - * afterwards in the other cases: we fsstack_copy_inode_size from - * the lower level. - */ - if (ia->ia_valid & ATTR_SIZE) { - err = inode_newsize_ok(inode, ia->ia_size); - if (err) - goto out; - truncate_setsize(inode, ia->ia_size); - } - - /* - * mode change is for clearing setuid/setgid bits. Allow lower fs - * to interpret this in its own way. - */ - if (lower_ia.ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) - lower_ia.ia_valid &= ~ATTR_MODE; - - /* notify the (possibly copied-up) lower inode */ - /* - * Note: we use lower_dentry->d_inode, because lower_inode may be - * unlinked (no inode->i_sb and i_ino==0. This happens if someone - * tries to open(), unlink(), then ftruncate() a file. - */ - mutex_lock(&lower_dentry->d_inode->i_mutex); - err = notify_change(lower_dentry, &lower_ia); /* note: lower_ia */ - mutex_unlock(&lower_dentry->d_inode->i_mutex); - if (err) - goto out; - - /* get attributes from the lower inode */ - fsstack_copy_attr_all(inode, lower_inode); - /* - * Not running fsstack_copy_inode_size(inode, lower_inode), because - * VFS should update our inode size, and notify_change on - * lower_inode should update its size. - */ - -out: - wrapfs_put_lower_path(dentry, &lower_path); -out_err: - return err; -} - -const struct inode_operations wrapfs_symlink_iops = { - .readlink = wrapfs_readlink, - .permission = wrapfs_permission, - .follow_link = wrapfs_follow_link, - .setattr = wrapfs_setattr, - .put_link = wrapfs_put_link, -}; - -const struct inode_operations wrapfs_dir_iops = { - .create = wrapfs_create, - .lookup = wrapfs_lookup, - .link = wrapfs_link, - .unlink = wrapfs_unlink, - .symlink = wrapfs_symlink, - .mkdir = wrapfs_mkdir, - .rmdir = wrapfs_rmdir, - .mknod = wrapfs_mknod, - .rename = wrapfs_rename, - .permission = wrapfs_permission, - .setattr = wrapfs_setattr, -}; - -const struct inode_operations wrapfs_main_iops = { - .permission = wrapfs_permission, - .setattr = wrapfs_setattr, -}; diff --git a/fs/wrapfs/lookup.c b/fs/wrapfs/lookup.c deleted file mode 100644 index fa6e019..0000000 --- a/fs/wrapfs/lookup.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -/* The dentry cache is just so we have properly sized dentries */ -static struct kmem_cache *wrapfs_dentry_cachep; - -int wrapfs_init_dentry_cache(void) -{ - wrapfs_dentry_cachep = - kmem_cache_create("wrapfs_dentry", - sizeof(struct wrapfs_dentry_info), - 0, SLAB_RECLAIM_ACCOUNT, NULL); - - return wrapfs_dentry_cachep ? 0 : -ENOMEM; -} - -void wrapfs_destroy_dentry_cache(void) -{ - if (wrapfs_dentry_cachep) - kmem_cache_destroy(wrapfs_dentry_cachep); -} - -void free_dentry_private_data(struct dentry *dentry) -{ - if (!dentry || !dentry->d_fsdata) - return; - kmem_cache_free(wrapfs_dentry_cachep, dentry->d_fsdata); - dentry->d_fsdata = NULL; -} - -/* allocate new dentry private data */ -int new_dentry_private_data(struct dentry *dentry) -{ - struct wrapfs_dentry_info *info = WRAPFS_D(dentry); - - /* use zalloc to init dentry_info.lower_path */ - info = kmem_cache_zalloc(wrapfs_dentry_cachep, GFP_ATOMIC); - if (!info) - return -ENOMEM; - - spin_lock_init(&info->lock); - dentry->d_fsdata = info; - - return 0; -} - -/* - * Initialize a nameidata structure (the intent part) we can pass to a lower - * file system. Returns 0 on success or -error (only -ENOMEM possible). - */ -int init_lower_nd(struct nameidata *nd, unsigned int flags) -{ - int err = 0; - - memset(nd, 0, sizeof(struct nameidata)); - if (!flags) - goto out; - - switch (flags) { - case LOOKUP_CREATE: - case LOOKUP_OPEN: - nd->flags = flags; - break; - default: - /* We should never get here, for now */ - pr_debug("wrapfs: unknown nameidata flag 0x%x\n", flags); - BUG(); - break; - } - -out: - return err; -} - -static int wrapfs_inode_test(struct inode *inode, void *candidate_lower_inode) -{ - struct inode *current_lower_inode = wrapfs_lower_inode(inode); - if (current_lower_inode == (struct inode *)candidate_lower_inode) - return 1; /* found a match */ - else - return 0; /* no match */ -} - -static int wrapfs_inode_set(struct inode *inode, void *lower_inode) -{ - /* we do actual inode initialization in wrapfs_iget */ - return 0; -} - -struct inode *wrapfs_iget(struct super_block *sb, struct inode *lower_inode) -{ - struct wrapfs_inode_info *info; - struct inode *inode; /* the new inode to return */ - int err; - - inode = iget5_locked(sb, /* our superblock */ - /* - * hashval: we use inode number, but we can - * also use "(unsigned long)lower_inode" - * instead. - */ - lower_inode->i_ino, /* hashval */ - wrapfs_inode_test, /* inode comparison function */ - wrapfs_inode_set, /* inode init function */ - lower_inode); /* data passed to test+set fxns */ - if (!inode) { - err = -EACCES; - iput(lower_inode); - return ERR_PTR(err); - } - /* if found a cached inode, then just return it */ - if (!(inode->i_state & I_NEW)) - return inode; - - /* initialize new inode */ - info = WRAPFS_I(inode); - - inode->i_ino = lower_inode->i_ino; - if (!igrab(lower_inode)) { - err = -ESTALE; - return ERR_PTR(err); - } - wrapfs_set_lower_inode(inode, lower_inode); - - inode->i_version++; - - /* use different set of inode ops for symlinks & directories */ - if (S_ISDIR(lower_inode->i_mode)) - inode->i_op = &wrapfs_dir_iops; - else if (S_ISLNK(lower_inode->i_mode)) - inode->i_op = &wrapfs_symlink_iops; - else - inode->i_op = &wrapfs_main_iops; - - /* use different set of file ops for directories */ - if (S_ISDIR(lower_inode->i_mode)) - inode->i_fop = &wrapfs_dir_fops; - else - inode->i_fop = &wrapfs_main_fops; - - inode->i_mapping->a_ops = &wrapfs_aops; - - inode->i_atime.tv_sec = 0; - inode->i_atime.tv_nsec = 0; - inode->i_mtime.tv_sec = 0; - inode->i_mtime.tv_nsec = 0; - inode->i_ctime.tv_sec = 0; - inode->i_ctime.tv_nsec = 0; - - /* properly initialize special inodes */ - if (S_ISBLK(lower_inode->i_mode) || S_ISCHR(lower_inode->i_mode) || - S_ISFIFO(lower_inode->i_mode) || S_ISSOCK(lower_inode->i_mode)) - init_special_inode(inode, lower_inode->i_mode, - lower_inode->i_rdev); - - /* all well, copy inode attributes */ - fsstack_copy_attr_all(inode, lower_inode); - fsstack_copy_inode_size(inode, lower_inode); - - unlock_new_inode(inode); - return inode; -} - -/* - * Connect a wrapfs inode dentry/inode with several lower ones. This is - * the classic stackable file system "vnode interposition" action. - * - * @dentry: wrapfs's dentry which interposes on lower one - * @sb: wrapfs's super_block - * @lower_path: the lower path (caller does path_get/put) - */ -int wrapfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path) -{ - int err = 0; - struct inode *inode; - struct inode *lower_inode; - struct super_block *lower_sb; - - lower_inode = lower_path->dentry->d_inode; - lower_sb = wrapfs_lower_super(sb); - - /* check that the lower file system didn't cross a mount point */ - if (lower_inode->i_sb != lower_sb) { - err = -EXDEV; - goto out; - } - - /* - * We allocate our new inode below by calling wrapfs_iget, - * which will initialize some of the new inode's fields - */ - - /* inherit lower inode number for wrapfs's inode */ - inode = wrapfs_iget(sb, lower_inode); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - goto out; - } - - d_add(dentry, inode); - -out: - return err; -} - -/* - * Main driver function for wrapfs's lookup. - * - * Returns: NULL (ok), ERR_PTR if an error occurred. - * Fills in lower_parent_path with <dentry,mnt> on success. - */ -static struct dentry *__wrapfs_lookup(struct dentry *dentry, - unsigned int flags, - struct path *lower_parent_path) -{ - int err = 0; - struct vfsmount *lower_dir_mnt; - struct dentry *lower_dir_dentry = NULL; - struct dentry *lower_dentry; - const char *name; - struct path lower_path; - struct qstr this; - - /* must initialize dentry operations */ - d_set_d_op(dentry, &wrapfs_dops); - - if (IS_ROOT(dentry)) - goto out; - - name = dentry->d_name.name; - - /* now start the actual lookup procedure */ - lower_dir_dentry = lower_parent_path->dentry; - lower_dir_mnt = lower_parent_path->mnt; - - /* Use vfs_path_lookup to check if the dentry exists or not */ - err = vfs_path_lookup(lower_dir_dentry, lower_dir_mnt, name, 0, - &lower_path); - - /* no error: handle positive dentries */ - if (!err) { - wrapfs_set_lower_path(dentry, &lower_path); - err = wrapfs_interpose(dentry, dentry->d_sb, &lower_path); - if (err) /* path_put underlying path on error */ - wrapfs_put_reset_lower_path(dentry); - goto out; - } - - /* - * We don't consider ENOENT an error, and we want to return a - * negative dentry. - */ - if (err && err != -ENOENT) - goto out; - - /* instatiate a new negative dentry */ - this.name = name; - this.len = strlen(name); - this.hash = full_name_hash(this.name, this.len); - lower_dentry = d_lookup(lower_dir_dentry, &this); - if (lower_dentry) - goto setup_lower; - - lower_dentry = d_alloc(lower_dir_dentry, &this); - if (!lower_dentry) { - err = -ENOMEM; - goto out; - } - d_add(lower_dentry, NULL); /* instantiate and hash */ - -setup_lower: - lower_path.dentry = lower_dentry; - lower_path.mnt = mntget(lower_dir_mnt); - wrapfs_set_lower_path(dentry, &lower_path); - - /* - * If the intent is to create a file, then don't return an error, so - * the VFS will continue the process of making this negative dentry - * into a positive one. - */ - if (flags & (LOOKUP_CREATE|LOOKUP_RENAME_TARGET)) - err = 0; - -out: - return ERR_PTR(err); -} - -struct dentry *wrapfs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct dentry *ret, *parent; - struct path lower_parent_path; - int err = 0; - - parent = dget_parent(dentry); - - wrapfs_get_lower_path(parent, &lower_parent_path); - - /* allocate dentry private data. We free it in ->d_release */ - err = new_dentry_private_data(dentry); - if (err) { - ret = ERR_PTR(err); - goto out; - } - ret = __wrapfs_lookup(dentry, flags, &lower_parent_path); - if (IS_ERR(ret)) - goto out; - if (ret) - dentry = ret; - if (dentry->d_inode) - fsstack_copy_attr_times(dentry->d_inode, - wrapfs_lower_inode(dentry->d_inode)); - /* update parent directory's atime */ - fsstack_copy_attr_atime(parent->d_inode, - wrapfs_lower_inode(parent->d_inode)); - -out: - wrapfs_put_lower_path(parent, &lower_parent_path); - dput(parent); - return ret; -} diff --git a/fs/wrapfs/main.c b/fs/wrapfs/main.c deleted file mode 100644 index 73153fd..0000000 --- a/fs/wrapfs/main.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" -#include <linux/module.h> - -/* - * There is no need to lock the wrapfs_super_info's rwsem as there is no - * way anyone can have a reference to the superblock at this point in time. - */ -static int wrapfs_read_super(struct super_block *sb, void *raw_data, int silent) -{ - int err = 0; - struct super_block *lower_sb; - struct path lower_path; - char *dev_name = (char *) raw_data; - struct inode *inode; - - if (!dev_name) { - printk(KERN_ERR - "wrapfs: read_super: missing dev_name argument\n"); - err = -EINVAL; - goto out; - } - - /* parse lower path */ - err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, - &lower_path); - if (err) { - printk(KERN_ERR "wrapfs: error accessing " - "lower directory '%s'\n", dev_name); - goto out; - } - - /* allocate superblock private data */ - sb->s_fs_info = kzalloc(sizeof(struct wrapfs_sb_info), GFP_KERNEL); - if (!WRAPFS_SB(sb)) { - printk(KERN_CRIT "wrapfs: read_super: out of memory\n"); - err = -ENOMEM; - goto out_free; - } - - /* set the lower superblock field of upper superblock */ - lower_sb = lower_path.dentry->d_sb; - atomic_inc(&lower_sb->s_active); - wrapfs_set_lower_super(sb, lower_sb); - - /* inherit maxbytes from lower file system */ - sb->s_maxbytes = lower_sb->s_maxbytes; - - /* - * Our c/m/atime granularity is 1 ns because we may stack on file - * systems whose granularity is as good. - */ - sb->s_time_gran = 1; - - sb->s_op = &wrapfs_sops; - - /* get a new inode and allocate our root dentry */ - inode = wrapfs_iget(sb, lower_path.dentry->d_inode); - if (IS_ERR(inode)) { - err = PTR_ERR(inode); - goto out_sput; - } - sb->s_root = d_make_root(inode); - if (!sb->s_root) { - err = -ENOMEM; - goto out_iput; - } - d_set_d_op(sb->s_root, &wrapfs_dops); - - /* link the upper and lower dentries */ - sb->s_root->d_fsdata = NULL; - err = new_dentry_private_data(sb->s_root); - if (err) - goto out_freeroot; - - /* if get here: cannot have error */ - - /* set the lower dentries for s_root */ - wrapfs_set_lower_path(sb->s_root, &lower_path); - - /* - * No need to call interpose because we already have a positive - * dentry, which was instantiated by d_make_root. Just need to - * d_rehash it. - */ - d_rehash(sb->s_root); - if (!silent) - printk(KERN_INFO - "wrapfs: mounted on top of %s type %s\n", - dev_name, lower_sb->s_type->name); - goto out; /* all is well */ - - /* no longer needed: free_dentry_private_data(sb->s_root); */ -out_freeroot: - dput(sb->s_root); -out_iput: - iput(inode); -out_sput: - /* drop refs we took earlier */ - atomic_dec(&lower_sb->s_active); - kfree(WRAPFS_SB(sb)); - sb->s_fs_info = NULL; -out_free: - path_put(&lower_path); - -out: - return err; -} - -struct dentry *wrapfs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *raw_data) -{ - void *lower_path_name = (void *) dev_name; - - return mount_nodev(fs_type, flags, lower_path_name, - wrapfs_read_super); -} - -static struct file_system_type wrapfs_fs_type = { - .owner = THIS_MODULE, - .name = WRAPFS_NAME, - .mount = wrapfs_mount, - .kill_sb = generic_shutdown_super, - .fs_flags = 0, -}; -MODULE_ALIAS_FS(WRAPFS_NAME); - -static int __init init_wrapfs_fs(void) -{ - int err; - - pr_info("Registering wrapfs " WRAPFS_VERSION "\n"); - - err = wrapfs_init_inode_cache(); - if (err) - goto out; - err = wrapfs_init_dentry_cache(); - if (err) - goto out; - err = register_filesystem(&wrapfs_fs_type); -out: - if (err) { - wrapfs_destroy_inode_cache(); - wrapfs_destroy_dentry_cache(); - } - return err; -} - -static void __exit exit_wrapfs_fs(void) -{ - wrapfs_destroy_inode_cache(); - wrapfs_destroy_dentry_cache(); - unregister_filesystem(&wrapfs_fs_type); - pr_info("Completed wrapfs module unload\n"); -} - -MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" - " (http://www.fsl.cs.sunysb.edu/)"); -MODULE_DESCRIPTION("Wrapfs " WRAPFS_VERSION - " (http://wrapfs.filesystems.org/)"); -MODULE_LICENSE("GPL"); - -module_init(init_wrapfs_fs); -module_exit(exit_wrapfs_fs); diff --git a/fs/wrapfs/mmap.c b/fs/wrapfs/mmap.c deleted file mode 100644 index c224fc3..0000000 --- a/fs/wrapfs/mmap.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -static int wrapfs_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; - - memcpy(&lower_vma, vma, sizeof(struct vm_area_struct)); - file = lower_vma.vm_file; - lower_vm_ops = WRAPFS_F(file)->lower_vm_ops; - BUG_ON(!lower_vm_ops); - - lower_file = wrapfs_lower_file(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 wrapfs_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; -} - -/* - * XXX: the default address_space_ops for wrapfs is empty. We cannot set - * our inode->i_mapping->a_ops to NULL because too many code paths expect - * the a_ops vector to be non-NULL. - */ -const struct address_space_operations wrapfs_aops = { - /* empty on purpose */ -}; - -const struct vm_operations_struct wrapfs_vm_ops = { - .fault = wrapfs_fault, -}; diff --git a/fs/wrapfs/super.c b/fs/wrapfs/super.c deleted file mode 100644 index aec2093..0000000 --- a/fs/wrapfs/super.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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 "wrapfs.h" - -/* - * The inode cache is used with alloc_inode for both our inode info and the - * vfs inode. - */ -static struct kmem_cache *wrapfs_inode_cachep; - -/* final actions when unmounting a file system */ -static void wrapfs_put_super(struct super_block *sb) -{ - struct wrapfs_sb_info *spd; - struct super_block *s; - - spd = WRAPFS_SB(sb); - if (!spd) - return; - - /* decrement lower super references */ - s = wrapfs_lower_super(sb); - wrapfs_set_lower_super(sb, NULL); - atomic_dec(&s->s_active); - - kfree(spd); - sb->s_fs_info = NULL; -} - -static int wrapfs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - int err; - struct path lower_path; - - wrapfs_get_lower_path(dentry, &lower_path); - err = vfs_statfs(&lower_path, buf); - wrapfs_put_lower_path(dentry, &lower_path); - - /* set return buf to our f/s to avoid confusing user-level utils */ - buf->f_type = WRAPFS_SUPER_MAGIC; - - return err; -} - -/* - * @flags: numeric mount options - * @options: mount options string - */ -static int wrapfs_remount_fs(struct super_block *sb, int *flags, char *options) -{ - int err = 0; - - /* - * The VFS will take care of "ro" and "rw" flags among others. We - * can safely accept a few flags (RDONLY, MANDLOCK), and honor - * SILENT, but anything else left over is an error. - */ - if ((*flags & ~(MS_RDONLY | MS_MANDLOCK | MS_SILENT)) != 0) { - printk(KERN_ERR - "wrapfs: remount flags 0x%x unsupported\n", *flags); - err = -EINVAL; - } - - return err; -} - -/* - * Called by iput() when the inode reference count reached zero - * and the inode is not hashed anywhere. Used to clear anything - * that needs to be, before the inode is completely destroyed and put - * on the inode free list. - */ -static void wrapfs_evict_inode(struct inode *inode) -{ - struct inode *lower_inode; - - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); - /* - * Decrement a reference to a lower_inode, which was incremented - * by our read_inode when it was created initially. - */ - lower_inode = wrapfs_lower_inode(inode); - wrapfs_set_lower_inode(inode, NULL); - iput(lower_inode); -} - -static struct inode *wrapfs_alloc_inode(struct super_block *sb) -{ - struct wrapfs_inode_info *i; - - i = kmem_cache_alloc(wrapfs_inode_cachep, GFP_KERNEL); - if (!i) - return NULL; - - /* memset everything up to the inode to 0 */ - memset(i, 0, offsetof(struct wrapfs_inode_info, vfs_inode)); - - i->vfs_inode.i_version = 1; - return &i->vfs_inode; -} - -static void wrapfs_destroy_inode(struct inode *inode) -{ - kmem_cache_free(wrapfs_inode_cachep, WRAPFS_I(inode)); -} - -/* wrapfs inode cache constructor */ -static void init_once(void *obj) -{ - struct wrapfs_inode_info *i = obj; - - inode_init_once(&i->vfs_inode); -} - -int wrapfs_init_inode_cache(void) -{ - int err = 0; - - wrapfs_inode_cachep = - kmem_cache_create("wrapfs_inode_cache", - sizeof(struct wrapfs_inode_info), 0, - SLAB_RECLAIM_ACCOUNT, init_once); - if (!wrapfs_inode_cachep) - err = -ENOMEM; - return err; -} - -/* wrapfs inode cache destructor */ -void wrapfs_destroy_inode_cache(void) -{ - if (wrapfs_inode_cachep) - kmem_cache_destroy(wrapfs_inode_cachep); -} - -/* - * Used only in nfs, to kill any pending RPC tasks, so that subsequent - * code can actually succeed and won't leave tasks that need handling. - */ -static void wrapfs_umount_begin(struct super_block *sb) -{ - struct super_block *lower_sb; - - lower_sb = wrapfs_lower_super(sb); - if (lower_sb && lower_sb->s_op && lower_sb->s_op->umount_begin) - lower_sb->s_op->umount_begin(lower_sb); -} - -const struct super_operations wrapfs_sops = { - .put_super = wrapfs_put_super, - .statfs = wrapfs_statfs, - .remount_fs = wrapfs_remount_fs, - .evict_inode = wrapfs_evict_inode, - .umount_begin = wrapfs_umount_begin, - .show_options = generic_show_options, - .alloc_inode = wrapfs_alloc_inode, - .destroy_inode = wrapfs_destroy_inode, - .drop_inode = generic_delete_inode, -}; diff --git a/fs/wrapfs/wrapfs.h b/fs/wrapfs/wrapfs.h deleted file mode 100644 index ed49521..0000000 --- a/fs/wrapfs/wrapfs.h +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright (c) 1998-2011 Erez Zadok - * Copyright (c) 2009 Shrikar Archak - * Copyright (c) 2003-2011 Stony Brook University - * Copyright (c) 2003-2011 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. - */ - -#ifndef _WRAPFS_H_ -#define _WRAPFS_H_ - -#include <linux/dcache.h> -#include <linux/file.h> -#include <linux/fs.h> -#include <linux/mm.h> -#include <linux/mount.h> -#include <linux/namei.h> -#include <linux/seq_file.h> -#include <linux/statfs.h> -#include <linux/fs_stack.h> -#include <linux/magic.h> -#include <linux/uaccess.h> -#include <linux/slab.h> -#include <linux/sched.h> - -/* the file system name */ -#define WRAPFS_NAME "wrapfs" - -/* wrapfs root inode number */ -#define WRAPFS_ROOT_INO 1 - -/* useful for tracking code reachability */ -#define UDBG printk(KERN_DEFAULT "DBG:%s:%s:%d\n", __FILE__, __func__, __LINE__) - -/* operations vectors defined in specific files */ -extern const struct file_operations wrapfs_main_fops; -extern const struct file_operations wrapfs_dir_fops; -extern const struct inode_operations wrapfs_main_iops; -extern const struct inode_operations wrapfs_dir_iops; -extern const struct inode_operations wrapfs_symlink_iops; -extern const struct super_operations wrapfs_sops; -extern const struct dentry_operations wrapfs_dops; -extern const struct address_space_operations wrapfs_aops, wrapfs_dummy_aops; -extern const struct vm_operations_struct wrapfs_vm_ops; - -extern int wrapfs_init_inode_cache(void); -extern void wrapfs_destroy_inode_cache(void); -extern int wrapfs_init_dentry_cache(void); -extern void wrapfs_destroy_dentry_cache(void); -extern int new_dentry_private_data(struct dentry *dentry); -extern void free_dentry_private_data(struct dentry *dentry); -extern int init_lower_nd(struct nameidata *nd, unsigned int flags); -extern struct dentry *wrapfs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags); -extern struct inode *wrapfs_iget(struct super_block *sb, - struct inode *lower_inode); -extern int wrapfs_interpose(struct dentry *dentry, struct super_block *sb, - struct path *lower_path); - -/* file private data */ -struct wrapfs_file_info { - struct file *lower_file; - const struct vm_operations_struct *lower_vm_ops; -}; - -/* wrapfs inode data in memory */ -struct wrapfs_inode_info { - struct inode *lower_inode; - struct inode vfs_inode; -}; - -/* wrapfs dentry data in memory */ -struct wrapfs_dentry_info { - spinlock_t lock; /* protects lower_path */ - struct path lower_path; -}; - -/* wrapfs super-block data in memory */ -struct wrapfs_sb_info { - struct super_block *lower_sb; -}; - -/* - * inode to private data - * - * Since we use containers and the struct inode is _inside_ the - * wrapfs_inode_info structure, WRAPFS_I will always (given a non-NULL - * inode pointer), return a valid non-NULL pointer. - */ -static inline struct wrapfs_inode_info *WRAPFS_I(const struct inode *inode) -{ - return container_of(inode, struct wrapfs_inode_info, vfs_inode); -} - -/* dentry to private data */ -#define WRAPFS_D(dent) ((struct wrapfs_dentry_info *)(dent)->d_fsdata) - -/* superblock to private data */ -#define WRAPFS_SB(super) ((struct wrapfs_sb_info *)(super)->s_fs_info) - -/* file to private Data */ -#define WRAPFS_F(file) ((struct wrapfs_file_info *)((file)->private_data)) - -/* file to lower file */ -static inline struct file *wrapfs_lower_file(const struct file *f) -{ - return WRAPFS_F(f)->lower_file; -} - -static inline void wrapfs_set_lower_file(struct file *f, struct file *val) -{ - WRAPFS_F(f)->lower_file = val; -} - -/* inode to lower inode. */ -static inline struct inode *wrapfs_lower_inode(const struct inode *i) -{ - return WRAPFS_I(i)->lower_inode; -} - -static inline void wrapfs_set_lower_inode(struct inode *i, struct inode *val) -{ - WRAPFS_I(i)->lower_inode = val; -} - -/* superblock to lower superblock */ -static inline struct super_block *wrapfs_lower_super( - const struct super_block *sb) -{ - return WRAPFS_SB(sb)->lower_sb; -} - -static inline void wrapfs_set_lower_super(struct super_block *sb, - struct super_block *val) -{ - WRAPFS_SB(sb)->lower_sb = val; -} - -/* path based (dentry/mnt) macros */ -static inline void pathcpy(struct path *dst, const struct path *src) -{ - dst->dentry = src->dentry; - dst->mnt = src->mnt; -} -/* Returns struct path. Caller must path_put it. */ -static inline void wrapfs_get_lower_path(const struct dentry *dent, - struct path *lower_path) -{ - spin_lock(&WRAPFS_D(dent)->lock); - pathcpy(lower_path, &WRAPFS_D(dent)->lower_path); - path_get(lower_path); - spin_unlock(&WRAPFS_D(dent)->lock); - return; -} -static inline void wrapfs_put_lower_path(const struct dentry *dent, - struct path *lower_path) -{ - path_put(lower_path); - return; -} -static inline void wrapfs_set_lower_path(const struct dentry *dent, - struct path *lower_path) -{ - spin_lock(&WRAPFS_D(dent)->lock); - pathcpy(&WRAPFS_D(dent)->lower_path, lower_path); - spin_unlock(&WRAPFS_D(dent)->lock); - return; -} -static inline void wrapfs_reset_lower_path(const struct dentry *dent) -{ - spin_lock(&WRAPFS_D(dent)->lock); - WRAPFS_D(dent)->lower_path.dentry = NULL; - WRAPFS_D(dent)->lower_path.mnt = NULL; - spin_unlock(&WRAPFS_D(dent)->lock); - return; -} -static inline void wrapfs_put_reset_lower_path(const struct dentry *dent) -{ - struct path lower_path; - spin_lock(&WRAPFS_D(dent)->lock); - pathcpy(&lower_path, &WRAPFS_D(dent)->lower_path); - WRAPFS_D(dent)->lower_path.dentry = NULL; - WRAPFS_D(dent)->lower_path.mnt = NULL; - spin_unlock(&WRAPFS_D(dent)->lock); - path_put(&lower_path); - return; -} - -/* locking helpers */ -static inline struct dentry *lock_parent(struct dentry *dentry) -{ - struct dentry *dir = dget_parent(dentry); - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - return dir; -} - -static inline void unlock_dir(struct dentry *dir) -{ - mutex_unlock(&dir->d_inode->i_mutex); - dput(dir); -} -#endif /* not _WRAPFS_H_ */ -- 1.8.3.2 _______________________________________________ Kernelnewbies mailing list Kernelnewbies@xxxxxxxxxxxxxxxxx http://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies