diff options
author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-03-17 19:09:48 +0000 |
---|---|---|
committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2011-03-17 19:09:48 +0000 |
commit | ff367a6a34587f7762ee5fefe714efa0118a8ede (patch) | |
tree | 754e15d20fe6e0bb993ccb01f98ba3895fc69bba /target | |
parent | db0a51c50a6cd698fdd7fbe16dc3af278aaff932 (diff) |
kernel: replace mini_fo with overlayfs for 2.6.37
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@26209 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target')
15 files changed, 2995 insertions, 8490 deletions
diff --git a/target/linux/generic/config-2.6.37 b/target/linux/generic/config-2.6.37 index d225c6a1a..949294720 100644 --- a/target/linux/generic/config-2.6.37 +++ b/target/linux/generic/config-2.6.37 @@ -582,8 +582,9 @@ CONFIG_EXPERIMENTAL=y # CONFIG_EXT3_FS_XATTR is not set # CONFIG_EXT4_DEBUG is not set # CONFIG_EXT4_FS is not set +# CONFIG_EXT4_FS_POSIX_ACL is not set # CONFIG_EXT4_FS_SECURITY is not set -# CONFIG_EXT4_FS_XATTR is not set +CONFIG_EXT4_FS_XATTR=y CONFIG_EXT4_USE_FOR_EXT23=y CONFIG_EXTRA_FIRMWARE="" CONFIG_EXTRA_TARGETS="" @@ -1122,9 +1123,11 @@ CONFIG_JFFS2_CMODE_PRIORITY=y CONFIG_JFFS2_COMPRESSION_OPTIONS=y CONFIG_JFFS2_FS=y CONFIG_JFFS2_FS_DEBUG=0 +# CONFIG_JFFS2_FS_POSIX_ACL is not set +# CONFIG_JFFS2_FS_SECURITY is not set # CONFIG_JFFS2_FS_WBUF_VERIFY is not set CONFIG_JFFS2_FS_WRITEBUFFER=y -# CONFIG_JFFS2_FS_XATTR is not set +CONFIG_JFFS2_FS_XATTR=y CONFIG_JFFS2_LZMA=y # CONFIG_JFFS2_LZO is not set CONFIG_JFFS2_RTIME=y @@ -1301,7 +1304,6 @@ CONFIG_MII=y # CONFIG_MIKROTIK_RB532 is not set # CONFIG_MINIX_FS is not set # CONFIG_MINIX_SUBPARTITION is not set -CONFIG_MINI_FO=y # CONFIG_MIPS_ALCHEMY is not set # CONFIG_MIPS_COBALT is not set # CONFIG_MIPS_FPU_EMU is not set @@ -1715,6 +1717,7 @@ CONFIG_NLS_DEFAULT="iso8859-1" # CONFIG_OMFS_FS is not set # CONFIG_ORION_WATCHDOG is not set # CONFIG_OSF_PARTITION is not set +CONFIG_OVERLAYFS_FS=y # CONFIG_P54_COMMON is not set CONFIG_PACKET=y # CONFIG_PAGE_POISONING is not set diff --git a/target/linux/generic/patches-2.6.37/209-mini_fo.patch b/target/linux/generic/patches-2.6.37/209-mini_fo.patch deleted file mode 100644 index 065f186d5..000000000 --- a/target/linux/generic/patches-2.6.37/209-mini_fo.patch +++ /dev/null @@ -1,7786 +0,0 @@ ---- a/fs/Kconfig -+++ b/fs/Kconfig -@@ -183,6 +183,7 @@ source "fs/logfs/Kconfig" - source "fs/cramfs/Kconfig" - source "fs/squashfs/Kconfig" - source "fs/freevxfs/Kconfig" -+source "fs/mini_fo/Kconfig" - source "fs/minix/Kconfig" - source "fs/omfs/Kconfig" - source "fs/hpfs/Kconfig" ---- a/fs/Makefile -+++ b/fs/Makefile -@@ -74,6 +74,7 @@ obj-$(CONFIG_SQUASHFS) += squashfs/ - obj-y += ramfs/ - obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ - obj-$(CONFIG_CODA_FS) += coda/ -+obj-$(CONFIG_MINI_FO) += mini_fo/ - obj-$(CONFIG_MINIX_FS) += minix/ - obj-$(CONFIG_FAT_FS) += fat/ - obj-$(CONFIG_BFS_FS) += bfs/ ---- /dev/null -+++ b/fs/mini_fo/aux.c -@@ -0,0 +1,577 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+/* check if file exists in storage */ -+int exists_in_storage(dentry_t *dentry) -+{ -+ check_mini_fo_dentry(dentry); -+ if(dtost(dentry) == MODIFIED || dtost(dentry) == CREATED || dtost(dentry) == DEL_REWRITTEN) -+ return 1; -+ return 0; -+} -+ -+/* check if dentry is in an existing state */ -+int is_mini_fo_existant(dentry_t *dentry) -+{ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) == DELETED || dtost(dentry) == NON_EXISTANT) -+ return 0; -+ else -+ return 1; -+} -+ -+/* -+ * This function will create a negative storage dentry for -+ * dentry, what is required for many create like options. -+ * It will create the storage structure if necessary. -+ */ -+int get_neg_sto_dentry(dentry_t *dentry) -+{ -+ int err = 0; -+ unsigned int len; -+ const unsigned char *name; -+ -+ if(!dentry || -+ !dtopd(dentry) || -+ !(dtost(dentry) == UNMODIFIED || -+ dtost(dentry) == NON_EXISTANT || -+ dtost(dentry) == DELETED)) { -+ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: invalid dentry passed.\n"); -+ err = -1; -+ goto out; -+ } -+ /* Have we got a neg. dentry already? */ -+ if(dtohd2(dentry)) { -+ err = 0; -+ goto out; -+ } -+ if(dtost(dentry->d_parent) == UNMODIFIED) { -+ /* build sto struct */ -+ err = build_sto_structure(dentry->d_parent->d_parent, dentry->d_parent); -+ if(err || -+ dtost(dentry->d_parent) != MODIFIED) { -+ printk(KERN_CRIT "mini_fo: get_neg_sto_dentry: ERROR building sto structure.\n"); -+ err = -1; -+ goto out; -+ } -+ } -+ -+ len = dentry->d_name.len; -+ name = dentry->d_name.name; -+ -+ dtohd2(dentry) = -+ lookup_one_len(name, dtohd2(dentry->d_parent), len); -+ -+ out: -+ return err; -+} -+ -+int check_mini_fo_dentry(dentry_t *dentry) -+{ -+ ASSERT(dentry != NULL); -+ ASSERT(dtopd(dentry) != NULL); -+ ASSERT((dtohd(dentry) != NULL) || (dtohd2(dentry) != NULL)); -+ -+/* if(dtost(dentry) == MODIFIED) { */ -+/* ASSERT(dentry->d_inode != NULL); */ -+/* ASSERT(dtohd(dentry) != NULL); */ -+/* ASSERT(dtohd(dentry)->d_inode != NULL); */ -+/* ASSERT(dtohd2(dentry) != NULL); */ -+/* ASSERT(dtohd2(dentry)->d_inode != NULL); */ -+/* } */ -+/* else if(dtost(dentry) == UNMODIFIED) { */ -+/* ASSERT(dentry->d_inode != NULL); */ -+/* ASSERT( */ -+/* } */ -+ return 0; -+} -+ -+int check_mini_fo_file(file_t *file) -+{ -+ ASSERT(file != NULL); -+ ASSERT(ftopd(file) != NULL); -+ ASSERT(file->f_dentry != NULL); -+ -+ /* violent checking, check depending of state and type -+ * if(S_ISDIR(file->f_dentry->d_inode->i_mode)) {} -+ */ -+ ASSERT((ftohf(file) != NULL) || (ftohf2(file) != NULL)); -+ return 0; -+} -+ -+int check_mini_fo_inode(inode_t *inode) -+{ -+ ASSERT(inode != NULL); -+ ASSERT(itopd(inode) != NULL); -+ ASSERT((itohi(inode) != NULL) || (itohi2(inode) != NULL)); -+ return 0; -+} -+ -+/* -+ * will walk a base path as provided by get_mini_fo_bpath and return -+ * the (hopefully ;-) ) positive dentry of the renamed base dir. -+ * -+ * This does some work of path_init. -+ */ -+dentry_t *bpath_walk(super_block_t *sb, char *bpath) -+{ -+ int err; -+ struct vfsmount *mnt; -+ struct nameidata nd; -+ -+ /* be paranoid */ -+ if(!bpath || bpath[0] != '/') { -+ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid string.\n"); -+ return NULL; -+ } -+ if(!sb || !stopd(sb)) { -+ printk(KERN_CRIT "mini_fo: bpath_walk: Invalid sb.\n"); -+ return NULL; -+ } -+ -+ /* fix this: how do I reach this lock? -+ * read_lock(¤t->fs->lock); */ -+ mnt = mntget(stopd(sb)->hidden_mnt); -+ /* read_unlock(¤t->fs->lock); */ -+ -+ err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &nd); -+ -+ /* validate */ -+ if (err || !nd.dentry || !nd.dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); -+ return NULL; -+ } -+ return nd.dentry; -+} -+ -+ -+/* returns the full path of the basefile incl. its name */ -+int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len) -+{ -+ char *buf_walker; -+ int len = 0; -+ dentry_t *sky_walker; -+ -+ if(!dentry || !dtohd(dentry)) { -+ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: invalid dentry passed.\n"); -+ return -1; -+ } -+ sky_walker = dtohd(dentry); -+ -+ do { -+ len += sky_walker->d_name.len + 1 ; /* 1 for '/' */ -+ sky_walker = sky_walker->d_parent; -+ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); -+ -+ /* 1 to oil the loop */ -+ *bpath = (char*) kmalloc(len + 1, GFP_KERNEL); -+ if(!*bpath) { -+ printk(KERN_CRIT "mini_fo: get_mini_fo_bpath: out of mem.\n"); -+ return -1; -+ } -+ buf_walker = *bpath+len; /* put it on last char */ -+ *buf_walker = '\n'; -+ sky_walker = dtohd(dentry); -+ -+ do { -+ buf_walker -= sky_walker->d_name.len; -+ strncpy(buf_walker, -+ sky_walker->d_name.name, -+ sky_walker->d_name.len); -+ *(--buf_walker) = '/'; -+ sky_walker = sky_walker->d_parent; -+ } while(sky_walker != stopd(dentry->d_inode->i_sb)->base_dir_dentry); -+ -+ /* bpath_len doesn't count newline! */ -+ *bpath_len = len; -+ return 0; -+} -+ -+int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, -+ dentry_t *src_dentry, struct vfsmount *src_mnt) -+{ -+ void *buf; -+ mm_segment_t old_fs; -+ file_t *tgt_file; -+ file_t *src_file; -+ int bytes, len, tmp, err; -+ err = 0; -+ -+ if(!(tgt_dentry->d_inode && src_dentry->d_inode)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, neg. dentry passed.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ dget(tgt_dentry); -+ dget(src_dentry); -+ mntget(tgt_mnt); -+ mntget(src_mnt); -+ -+ /* open file write only */ -+ tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1); -+ if(!tgt_file || IS_ERR(tgt_file)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening target file.\n"); -+ err = PTR_ERR(tgt_file); -+ goto out_err; -+ } -+ -+ /* open file read only */ -+ src_file = dentry_open(src_dentry, src_mnt, 0x0); -+ if(!src_file || IS_ERR(src_file)) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening source file.\n"); -+ err = PTR_ERR(src_file); -+ -+ /* close target file */ -+ fput(tgt_file); -+ goto out_err; -+ } -+ -+ /* check if the filesystem(s) support read respective write */ -+ if(!src_file->f_op->read || !tgt_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, no fs read or write support.\n"); -+ err = -EPERM; -+ goto out_close; -+ } -+ -+ /* allocate a page for transfering the data */ -+ buf = (void *) __get_free_page(GFP_KERNEL); -+ if(!buf) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR, out of kernel mem.\n"); -+ goto out_err; -+ } -+ -+ tgt_file->f_pos = 0; -+ src_file->f_pos = 0; -+ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* Doing this I assume that a read operation will return a full -+ * buffer while there is still data to read, and a less than -+ * full buffer when all data has been read. -+ */ -+ bytes = len = PAGE_SIZE; -+ while(bytes == len) { -+ bytes = src_file->f_op->read(src_file, buf, len, -+ &src_file->f_pos); -+ tmp = tgt_file->f_op->write(tgt_file, buf, bytes, -+ &tgt_file->f_pos); -+ if(tmp != bytes) { -+ printk(KERN_CRIT "mini_fo_cp_cont: ERROR writing.\n"); -+ goto out_close_unset; -+ } -+ } -+ -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ fput(tgt_file); -+ fput(src_file); -+ goto out; -+ -+ out_close_unset: -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ -+ out_close: -+ fput(tgt_file); -+ fput(src_file); -+ -+ out_err: -+ dput(tgt_dentry); -+ dput(src_dentry); -+ -+ /* mk: not sure if this need to be done */ -+ mntput(tgt_mnt); -+ mntput(src_mnt); -+ -+ out: -+ return err; -+} -+ -+/* mk: -+ * ndl (no-duplicate list) stuff -+ * This is used in mini_fo_readdir, to save the storage directory contents -+ * and later when reading base, match them against the list in order -+ * to avoid duplicates. -+ */ -+ -+/* add a file specified by name and len to the ndl -+ * Return values: 0 on success, <0 on failure. -+ */ -+int ndl_add_entry(struct readdir_data *rd, const char *name, int len) -+{ -+ struct ndl_entry *tmp_entry; -+ -+ tmp_entry = (struct ndl_entry *) -+ kmalloc(sizeof(struct ndl_entry), GFP_KERNEL); -+ if(!tmp_entry) { -+ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ tmp_entry->name = (char*) kmalloc(len, GFP_KERNEL); -+ if(!tmp_entry->name) { -+ printk(KERN_CRIT "mini_fo: ndl_add_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ strncpy(tmp_entry->name, name, len); -+ tmp_entry->len = len; -+ -+ list_add(&tmp_entry->list, &rd->ndl_list); -+ rd->ndl_size++; -+ return 0; -+} -+ -+/* delete all list entries and free memory */ -+void ndl_put_list(struct readdir_data *rd) -+{ -+ struct list_head *tmp; -+ struct ndl_entry *tmp_entry; -+ -+ if(rd->ndl_size <= 0) -+ return; -+ while(!list_empty(&rd->ndl_list)) { -+ tmp = rd->ndl_list.next; -+ list_del(tmp); -+ tmp_entry = list_entry(tmp, struct ndl_entry, list); -+ kfree(tmp_entry->name); -+ kfree(tmp_entry); -+ } -+ rd->ndl_size = 0; -+} -+ -+/* Check if a file specified by name and len is in the ndl -+ * Return value: 0 if not in list, 1 if file is found in ndl. -+ */ -+int ndl_check_entry(struct readdir_data *rd, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct ndl_entry *tmp_entry; -+ -+ if(rd->ndl_size <= 0) -+ return 0; -+ -+ list_for_each(tmp, &rd->ndl_list) { -+ tmp_entry = list_entry(tmp, struct ndl_entry, list); -+ if(tmp_entry->len != len) -+ continue; -+ if(!strncmp(tmp_entry->name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* mk: -+ * Recursive function to create corresponding directorys in the storage fs. -+ * The function will build the storage directorys up to dentry. -+ */ -+int build_sto_structure(dentry_t *dir, dentry_t *dentry) -+{ -+ int err; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(dentry->d_parent != dir) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [1].\n"); -+ return 1; -+ } -+ -+ if(dtost(dir) != MODIFIED) { -+ err = build_sto_structure(dir->d_parent, dentry->d_parent); -+ if(err) -+ return err; -+ } -+ -+ /* ok, coming back again. */ -+ check_mini_fo_dentry(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(!hidden_sto_dentry) { -+ /* -+ * This is the case after creating the first -+ * hidden_sto_dentry. -+ * After one negative storage_dentry, all pointers to -+ * hidden_storage dentries are set to NULL. We need to -+ * create the negative dentry before we create the storage -+ * file. -+ */ -+ unsigned int len; -+ const unsigned char *name; -+ len = dtohd(dentry)->d_name.len; -+ name = dtohd(dentry)->d_name.name; -+ hidden_sto_dentry = lookup_one_len(name, dtohd2(dir), len); -+ dtohd2(dentry) = hidden_sto_dentry; -+ } -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ /* lets be safe */ -+ if(dtohd2(dir) != hidden_sto_dir_dentry) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: invalid parameter or meta data corruption [2].\n"); -+ return 1; -+ } -+ -+ /* check for errors in lock_parent */ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if(IS_ERR(hidden_sto_dir_dentry)) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: lock_parent failed.\n"); -+ return err; -+ } -+ -+ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dir->d_inode->i_mode); -+ -+ if(err) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [1].\n"); -+ /* was: unlock_dir(dir); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&dir->d_inode->i_mutex); -+#else -+ up(&dir->d_inode->i_sem); -+#endif -+ dput(dir); -+ return err; -+ } -+ -+ /* everything ok! */ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: build_sto_structure: failed to create storage dir [2].\n"); -+ /* was: unlock_dir(dir); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&dir->d_inode->i_mutex); -+#else -+ up(&dir->d_inode->i_sem); -+#endif -+ dput(dir); -+ return 1; -+ } -+ -+ /* interpose the new inode and set new state */ -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ dtopd(dentry)->state = MODIFIED; -+ -+ /* initalize the wol list */ -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ -+ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); -+ fist_copy_attr_timesizes(dir->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ dir->d_inode->i_nlink++; -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ return 0; -+} -+ -+ -+#if 0 /* unused */ -+ -+/* -+ * Read "len" bytes from "filename" into "buf". -+ * "buf" is in kernel space. -+ */ -+int -+mini_fo_read_file(const char *filename, void *buf, int len) -+{ -+ file_t *filp; -+ mm_segment_t oldfs; -+ int bytes; -+ /* Chroot? Maybe NULL isn't right here */ -+ filp = filp_open(filename, O_RDONLY, 0); -+ if (!filp || IS_ERR(filp)) { -+ printk("mini_fo_read_file err %d\n", (int) PTR_ERR(filp)); -+ return -1; /* or do something else */ -+ } -+ -+ if (!filp->f_op->read) -+ return -2; /* file(system) doesn't allow reads */ -+ -+ /* now read len bytes from offset 0 */ -+ filp->f_pos = 0; /* start offset */ -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ bytes = filp->f_op->read(filp, buf, len, &filp->f_pos); -+ set_fs(oldfs); -+ -+ /* close the file */ -+ fput(filp); -+ -+ return bytes; -+} -+ -+ -+ -+/* -+ * Write "len" bytes from "buf" to "filename" -+ * "buf" is in kernel space. -+ */ -+int -+mini_fo_write_file(const char *filename, void *buf, int len) -+{ -+ file_t *filp; -+ mm_segment_t oldfs; -+ int bytes; -+ /* Chroot? Maybe NULL isn't right here */ -+ filp = filp_open(filename, O_RDWR|O_CREAT, 0640); -+ if (!filp || IS_ERR(filp)) { -+ printk("mini_fo_write_file err %d\n", (int) PTR_ERR(filp)); -+ return -1; /* or do something else */ -+ } -+ -+ if (!filp->f_op->write) -+ return -2; /* file(system) doesn't allow writes */ -+ -+ /* now write len bytes from offset 0 */ -+ filp->f_pos = 0; /* start offset */ -+ oldfs = get_fs(); -+ set_fs(KERNEL_DS); -+ bytes = filp->f_op->write(filp, buf, len, &filp->f_pos); -+ set_fs(oldfs); -+ -+ /* close the file */ -+ fput(filp); -+ -+ return bytes; -+} -+ -+#endif /* unused */ -+ ---- /dev/null -+++ b/fs/mini_fo/ChangeLog -@@ -0,0 +1,281 @@ -+2006-01-24 Markus Klotzbuecher <mk@mary.denx.de> -+ -+ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to -+ retain backwards compatibility. -+ -+2006-01-24 Ed L. Cashin <ecashin@coraid.com> -+ -+ * Support for the new mutex infrastructure -+ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) -+ -+2005-10-15 Markus Klotzbuecher <mk@localhost.localdomain> -+ -+ * Bugfix for a serious memory leak in mini_fo_follow_link. -+ -+2005-09-21 Markus Klotzbuecher <mk@mary> -+ -+ * new release 0.6.1 -+ -+ * fix of a compiler warning due to changes in 2.6.13 -+ -+2005-09-21 Klaus Wenninger <klaus.wenninger@siemens.com> -+ -+ * file.c: readdir: fix for a bug that caused directory entries -+ to show up twice when using storage filesystems such as -+ minixfs or pramfs. -+ -+2005-06-30 Eric Lammerts <eric@lammerts.org> -+ -+ * fix for an oops when overwriting a binary thats beeing -+ executed. -+ -+2005-06-09 <mk@mary> -+ -+ * Renamed overlay to mini_fo-overlay. -+ -+ * Added mini_fo-merge script to allow merging of storage and base -+ after making modifications. -+ -+2005-05-22 root <mk@mary> -+ -+ * Added overlay script that allows to easily mount mini_fo ontop -+ of a given base directory -+ -+2005-05-10 <mk@mary> -+ -+ * inode.c: xattr functions return -EOPNOSUPP instead of -+ -ENOSUPP, what confuses "ls -l" -+ -+ * Changed license from LGPL to GPL. -+ -+2005-05-08 root <mk@mary> -+ -+ * Makefile: clean it up and added make install and make -+ uninstall. -+ -+2005-05-06 <mk@mary> -+ -+ * merged devel branch back to main. [v0-6-0-pre3] -+ -+ * removed unused files print.c and fist_ioctl. [devel-0-0-18] -+ -+ * ioctl: removed fist_ioctl stuff, that is not needed for -+ now. -+ -+2005-05-03 <mk@mary> -+ -+ * file.c: simplified mini_fo_open and mini_fo_setattr using -+ new state changing functions. [devel-0-0-17] -+ -+ * inode.c: Fixed getattr state bug (see below) in 2.4 function -+ mini_fo_inode revalidate. -+ -+ * inode.c: found an other bug in mini_fo_getattr. States are not -+ reliable in this function, as a file can be opened, unlinked and -+ the getattr function called. This results in a deleted dentry -+ with an inode. Fix is to ignore states and simply use the inode -+ available. -+ -+2005-04-29 <mk@mary> -+ -+ * file.c: Bugfix and cleanup in fasync and fsync. [devel-0-0-16] -+ -+ * file.c: do not use mini_fo_lock so the generic version is -+ used (I guess). -+ -+ * inode.c: getattr, never call getattr on lower files, as this -+ will cause the inum to change. -+ -+ * inode.c: rename_reg_file renamed to rename_nondir, as it -+ doesn't matter as long it't not a dir. Removed all -+ rename_dev_file etc. -+ -+ * tagged as devel-0-0-15 -+ -+ * inode.c: added support for chosing support for extended -+ attrs at compile time by XATTR define in mini_fo.h . -+ -+ * inode.c: fixed mini_fo_getattr to use mini_fo inode and not -+ lower again, what avoids inode number changes that confused -+ rm again. This is the proper solution. -+ -+2005-04-24 <mk@mary> -+ -+ * all files: updated Copyright notive to 2005. [devel-0-0-14] -+ -+ * inode.c: fixed mini_fo_getattr to not change the inode -+ number, even if lower files change. -+ -+ * super.c: fixed a bug that caused deleted base file to show -+ up suddenly after some time, or after creating a special -+ file. The problem was that after some time or after special -+ file creating sync_sb_inodes is called by the vfs, that -+ called our mini_fo_put_inode. There was (wrongly) called -+ __meta_put_lists, that nuked the lists, although the inode -+ was going to continue its life. Moving __meta_put_lists to -+ mini_fo_clear_inode, where an inode is really destroyed, -+ solved the problem. -+ -+ -+2005-04-23 <mk@mary> -+ -+ * state.c, aux.c: more cleaning up and -+ simplifications. [devel-0-0-13] -+ -+ * inode.c: implemented mini_fo_getattr, that was required for -+ 2.6 because inode_revalidate has been remove there, and the -+ old "du" bug returned. -+ -+ -+2005-04-20 <mk@mary> -+ -+ * aux.c: get_neg_sto_dentry(): allow to be called for dentries -+ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. -+ -+2005-04-19 <mk@mary> -+ -+ * Fixed a bug under 2.6 that caused files deleted via mini_fo -+ not to be deleted properly and therefore the fs filled up -+ untill no memory was left. [devel-0-0-12] -+ -+ * Added basic hard link support. This means that creating -+ hardlinks will work, but existing ones will be treated as -+ individual files. [devel-0-0-11] -+ -+2005-04-17 <mk@mary> -+ -+ * Bugfixes -+ -+2005-04-13 root <mk@mary> -+ -+ * Added file state.c for the state transition -+ functions. Doesn't work very well yet, though... -+ -+2005-04-12 <mk@mary> -+ -+ * Porting to 2.6 started, which is easier than expected, also -+ due to Olivier previous work. -+ -+2005-04-08 <mk@mary> -+ -+ * Fixed the bug that caused du to return invalid sizes of -+ directory trees. The problem was that -+ mini_fo_inode_revalidate didn't always copy the attributes -+ from the base inode properly. -+ -+2005-04-01 Markus Klotzbuecher <mk@chasey> -+ -+ * Merged devel branch back to main trunk and updated the -+ RELEASE notes. This will be 0-6-0-pre1. -+ -+2005-03-31 Markus Klotzbuecher <mk@chasey> -+ -+ * Fixed some bugs in rename_reg_file, that only showed up in -+ the kernel compile test. Kernel compiles cleanly ontop of -+ mini_fo, now also make mrproper etc. work. Seems pretty stable. -+ -+2005-03-28 Markus Klotzbuecher <mk@chasey> -+ -+ * Many, many directory renaming bugfixes and a lot of other -+ cleanup. Dir renaming seems to work relatively stable. -+ -+2005-03-22 Markus Klotzbuecher <mk@chasey> -+ -+ * Finished implementing lightweight directory renaming. Some -+ basic testing indicates it works fine. -+ Next is to implement testcases for the testsuite and confirm -+ everything is really working ok. -+ -+2005-03-18 Markus Klotzbuecher <mk@chasey> -+ -+ * Finished implementing meta.c stuff required for directory -+ renaming. -+ -+2005-03-17 Markus Klotzbuecher <mk@chasey> -+ -+ * Fixed all compile warnings + an extremly old bug that -+ somehow crept in while reworking the wol stuff to the META -+ system. Turning on -Werror again... :-) -+ -+ * Fixed some bugs in the new rename_reg_file function. -+ -+ * Rewrote mini_fo rename and split it into several -+ subfunctions, that handle the different types -+ seperately. Rewrote the regular file function aswell, as it -+ was implemented somewhat inefficient. -+ -+2005-03-16 Markus Klotzbuecher <mk@chasey> -+ -+ * Implemented new META subsystem, removed old WOL stuff in favor -+ if it. -+ -+ * After some basic testing everything seems ok... -+ -+2005-03-11 Markus Klotzbuecher <mk@chasey> -+ -+ * Renaming a non regular file caused trouble because I always -+ tried to copy the contents. Now I only do this for regular -+ files. mini_fo_rename still isn't implemented properly, renaming -+ of device files, symlinks etc. results in a empty regular file -+ instead of the proper type. -+ -+ * Directory renaming suddenly works! What a surprise! I guess -+ this is because renaming is implemented as making a copy and -+ removing the original. Still this might not work -+ everywhere... -+ -+2005-03-09 Markus Klotzbuecher <mk@chasey> -+ -+ * Bugfix, when a mini_fo directory that exists in storage -+ (state: MODIFIED, CREATED and DEL_REWRITTEN) is deleted, a -+ possibly existing WOL file contained in it needs to be -+ deleted too. -+ -+ * Starting cleanup: defined state names in order to get rid of -+ the state numbers. -+ -+2005-03-08 Markus Klotzbuecher <mk@chasey> -+ -+ * Makefile fix, fist_ioctl was built against wrong sources if ARCH=um -+ -+ * Fixed a bug in dentry.c, mini_fo_d_hash. In state 4 = -+ DEL_REWRITTEN the hash was calculated from the base dentry, -+ which was wrong and and caused assertions in -+ __mini_fo_hidden_dentry to fail. -+ -+2005-02-21 <mk@mary> -+ -+ * Implemented directory deleting (inode.c) -+ -+ * main.c: made mini_fo_parse_options a little more robust. -+ -+2004-12-22 <mk@mary> -+ -+ * Makefile cleanup and uml stuff, removed unneccessary files -+ -+ * Created a new and hopefully more informative README -+ -+ * CHANGELOG: created a new CHANGELOG and added old entries reversely -+ -+ -+2004-10-24 Gleb Natapov <gleb@nbase.co.il> -+ -+ * Fix: owner and group where not correctly copied from base to -+ storage. -+ -+ -+2004-10-05 Gleb Natapov <gleb@nbase.co.il> -+ -+ * Implementation of fsync, fasync and lock mini_fo functions. -+ -+ -+2004-09-29 Bob Lee <bob@pantasys.com> -+ -+ * Fix of a serious pointer bug -+ -+ -+2004-09-28 Gleb Natapov <gleb@nbase.co.il> -+ -+ * Implementation of mini_fo_mknod and mini_fo_rename, support -+ for device files. -+ ---- /dev/null -+++ b/fs/mini_fo/dentry.c -@@ -0,0 +1,244 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+/* -+ * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. -+ */ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_d_revalidate(dentry_t *dentry, struct nameidata *nd) -+#else -+mini_fo_d_revalidate(dentry_t *dentry, int flags) -+#endif -+{ -+ int err1 = 1; /* valid = 1, invalid = 0 */ -+ int err2 = 1; -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ -+ check_mini_fo_dentry(dentry); -+ -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_revalidate) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, nd); -+#else -+ err1 = hidden_dentry->d_op->d_revalidate(hidden_dentry, flags); -+#endif -+ } -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_revalidate) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, -+ nd); -+#else -+ err2 = hidden_sto_dentry->d_op->d_revalidate(hidden_sto_dentry, -+ flags); -+#endif -+ } -+ -+ /* mk: if one of the lower level dentries are valid, -+ * the mini_fo dentry is too. -+ */ -+ return (err1 || err2); -+} -+ -+ -+STATIC int -+mini_fo_d_hash(dentry_t *dentry, qstr_t *name) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ /* hidden_dentry = mini_fo_hidden_dentry(dentry); -+ * hidden_sto_dentry = mini_fo_hidden_sto_dentry(dentry); */ -+ -+ /* state 1, 3, 4, 5: build the hash for the storage dentry */ -+ if((dtopd(dentry)->state == MODIFIED) || -+ (dtopd(dentry)->state == CREATED) || -+ (dtopd(dentry)->state == DEL_REWRITTEN) || -+ (dtopd(dentry)->state == DELETED)) { -+ hidden_sto_dentry = dtohd2(dentry); -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_hash) { -+ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); -+ } -+ goto out; -+ } -+ /* state 2: build the hash for the base dentry */ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ hidden_dentry = dtohd(dentry); -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_hash) { -+ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); -+ } -+ goto out; -+ } -+ /* state 6: build hash for the dentry that exists */ -+ if(dtopd(dentry)->state == NON_EXISTANT) { -+ hidden_sto_dentry = dtohd2(dentry); -+ if(hidden_sto_dentry && -+ hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_hash) { -+ err = hidden_sto_dentry->d_op->d_hash(hidden_sto_dentry, name); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ if(hidden_dentry && -+ hidden_dentry->d_op && -+ hidden_dentry->d_op->d_hash) { -+ err = hidden_dentry->d_op->d_hash(hidden_dentry, name); -+ goto out; -+ } -+ } -+ -+ printk(KERN_CRIT "mini_fo: d_hash: invalid state detected.\n"); -+ -+ out: -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_d_compare(dentry_t *dentry, qstr_t *a, qstr_t *b) -+{ -+ int err; -+ dentry_t *hidden_dentry=NULL; -+ -+ /* hidden_dentry = mini_fo_hidden_dentry(dentry); */ -+ if(dtohd2(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else if(dtohd(dentry)) -+ hidden_dentry = dtohd(dentry); -+ -+ if (hidden_dentry && hidden_dentry->d_op && hidden_dentry->d_op->d_compare) { -+ err = hidden_dentry->d_op->d_compare(hidden_dentry, a, b); -+ } else { -+ err = ((a->len != b->len) || memcmp(a->name, b->name, b->len)); -+ } -+ -+ return err; -+} -+ -+ -+int -+mini_fo_d_delete(dentry_t *dentry) -+{ -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ int err = 0; -+ -+ /* this could be a negative dentry, so check first */ -+ if (!dtopd(dentry)) { -+ printk(KERN_CRIT "mini_fo_d_delete: negative dentry passed.\n"); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry) { -+ if(hidden_dentry->d_op && -+ hidden_dentry->d_op->d_delete) { -+ err = hidden_dentry->d_op->d_delete(hidden_dentry); -+ } -+ } -+ if(hidden_sto_dentry) { -+ if(hidden_sto_dentry->d_op && -+ hidden_sto_dentry->d_op->d_delete) { -+ err = hidden_sto_dentry->d_op->d_delete(hidden_sto_dentry); -+ } -+ } -+ -+ out: -+ return err; -+} -+ -+ -+void -+mini_fo_d_release(dentry_t *dentry) -+{ -+ dentry_t *hidden_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ -+ /* this could be a negative dentry, so check first */ -+ if (!dtopd(dentry)) { -+ printk(KERN_CRIT "mini_fo_d_release: no private data.\n"); -+ goto out; -+ } -+ hidden_dentry = dtohd(dentry); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ if(hidden_dentry) { -+ /* decrement hidden dentry's counter and free its inode */ -+ dput(hidden_dentry); -+ } -+ if(hidden_sto_dentry) { -+ /* decrement hidden dentry's counter and free its inode */ -+ dput(hidden_sto_dentry); -+ } -+ -+ /* free private data (mini_fo_dentry_info) here */ -+ kfree(dtopd(dentry)); -+ __dtopd(dentry) = NULL; /* just to be safe */ -+ out: -+ return; -+} -+ -+ -+/* -+ * we don't really need mini_fo_d_iput, because dentry_iput will call iput() if -+ * mini_fo_d_iput is not defined. We left this implemented for ease of -+ * tracing/debugging. -+ */ -+void -+mini_fo_d_iput(dentry_t *dentry, inode_t *inode) -+{ -+ iput(inode); -+} -+ -+ -+struct dentry_operations mini_fo_dops = { -+ d_revalidate: mini_fo_d_revalidate, -+ d_hash: mini_fo_d_hash, -+ d_compare: mini_fo_d_compare, -+ d_release: mini_fo_d_release, -+ d_delete: mini_fo_d_delete, -+ d_iput: mini_fo_d_iput, -+}; ---- /dev/null -+++ b/fs/mini_fo/file.c -@@ -0,0 +1,713 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) -+ -+/******************* -+ * File Operations * -+ *******************/ -+ -+STATIC loff_t -+mini_fo_llseek(file_t *file, loff_t offset, int origin) -+{ -+ loff_t err; -+ file_t *hidden_file = NULL; -+ -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ /* Check if trying to llseek from a directory */ -+ err = -EISDIR; -+ goto out; -+ } -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ /* always set hidden position to this one */ -+ hidden_file->f_pos = file->f_pos; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ memcpy(&(hidden_file->f_ra), -+ &(file->f_ra), -+ sizeof(struct file_ra_state)); -+#else -+ if (file->f_reada) { /* update readahead information if needed */ -+ hidden_file->f_reada = file->f_reada; -+ hidden_file->f_ramax = file->f_ramax; -+ hidden_file->f_raend = file->f_raend; -+ hidden_file->f_ralen = file->f_ralen; -+ hidden_file->f_rawin = file->f_rawin; -+ } -+#endif -+ if (hidden_file->f_op && hidden_file->f_op->llseek) -+ err = hidden_file->f_op->llseek(hidden_file, offset, origin); -+ else -+ err = generic_file_llseek(hidden_file, offset, origin); -+ -+ if (err < 0) -+ goto out; -+ -+ if (err != file->f_pos) { -+ file->f_pos = err; -+ // ION maybe this? -+ // file->f_pos = hidden_file->f_pos; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ file->f_reada = 0; -+#endif -+ file->f_version++; -+ } -+ -+ out: -+ return err; -+} -+ -+ -+/* mk: fanout capable */ -+STATIC ssize_t -+mini_fo_read(file_t *file, char *buf, size_t count, loff_t *ppos) -+{ -+ int err = -EINVAL; -+ file_t *hidden_file = NULL; -+ loff_t pos = *ppos; -+ -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ /* Check if trying to read from a directory */ -+ /* printk(KERN_CRIT "mini_fo_read: ERROR: trying to read data from a directory.\n"); */ -+ err = -EISDIR; -+ goto out; -+ } -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->read) -+ goto out; -+ -+ err = hidden_file->f_op->read(hidden_file, buf, count, &pos); -+ *ppos = pos; -+ -+ if (err >= 0) { -+ /* atime should also be updated for reads of size zero or more */ -+ fist_copy_attr_atime(file->f_dentry->d_inode, -+ hidden_file->f_dentry->d_inode); -+ } -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ /* -+ * MAJOR HACK -+ * because pread() does not have any way to tell us that it is -+ * our caller, then we don't know for sure if we have to update -+ * the file positions. This hack relies on read() having passed us -+ * the "real" pointer of its struct file's f_pos field. -+ */ -+ if (ppos == &file->f_pos) -+ hidden_file->f_pos = *ppos = pos; -+ if (hidden_file->f_reada) { /* update readahead information if needed */ -+ file->f_reada = hidden_file->f_reada; -+ file->f_ramax = hidden_file->f_ramax; -+ file->f_raend = hidden_file->f_raend; -+ file->f_ralen = hidden_file->f_ralen; -+ file->f_rawin = hidden_file->f_rawin; -+ } -+#else -+ memcpy(&(file->f_ra),&(hidden_file->f_ra),sizeof(struct file_ra_state)); -+#endif -+ -+ out: -+ return err; -+} -+ -+ -+/* this mini_fo_write() does not modify data pages! */ -+STATIC ssize_t -+mini_fo_write(file_t *file, const char *buf, size_t count, loff_t *ppos) -+{ -+ int err = -EINVAL; -+ file_t *hidden_file = NULL; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ loff_t pos = *ppos; -+ -+ /* mk: fan out: */ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ /* This is bad! We have no storage file to write to. This -+ * should never happen because if a file is opened for -+ * writing, a copy should have been made earlier. -+ */ -+ printk(KERN_CRIT "mini_fo: write : ERROR, no storage file to write.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ } -+ -+ inode = file->f_dentry->d_inode; -+ hidden_inode = itohi2(inode); -+ if(!hidden_inode) { -+ printk(KERN_CRIT "mini_fo: write: no sto inode found, not good.\n"); -+ goto out; -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->write) -+ goto out; -+ -+ /* adjust for append -- seek to the end of the file */ -+ if (file->f_flags & O_APPEND) -+ pos = inode->i_size; -+ -+ err = hidden_file->f_op->write(hidden_file, buf, count, &pos); -+ -+ /* -+ * copy ctime and mtime from lower layer attributes -+ * atime is unchanged for both layers -+ */ -+ if (err >= 0) -+ fist_copy_attr_times(inode, hidden_inode); -+ -+ *ppos = pos; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ /* -+ * XXX: MAJOR HACK -+ * -+ * because pwrite() does not have any way to tell us that it is -+ * our caller, then we don't know for sure if we have to update -+ * the file positions. This hack relies on write() having passed us -+ * the "real" pointer of its struct file's f_pos field. -+ */ -+ if (ppos == &file->f_pos) -+ hidden_file->f_pos = *ppos = pos; -+#endif -+ /* update this inode's size */ -+ if (pos > inode->i_size) -+ inode->i_size = pos; -+ -+ out: -+ return err; -+} -+ -+/* Global variable to hold a file_t pointer. -+ * This serves to allow mini_fo_filldir function to know which file is -+ * beeing read, which is required for two reasons: -+ * -+ * - be able to call wol functions in order to avoid listing deleted -+ * base files. -+ * - if we're reading a directory which is in state 1, we need to -+ * maintain a list (in mini_fo_filldir) of which files allready -+ * have been copied to userspace,to detect files existing in base -+ * and storage and not list them twice. -+ */ -+filldir_t mini_fo_filldir_orig; -+file_t *mini_fo_filldir_file; -+ -+/* mainly copied from fs/readdir.c */ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, -+ u64 ino, unsigned int d_type) -+#else -+mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, -+ ino_t ino, unsigned int d_type) -+#endif -+{ -+ struct getdents_callback * buf = (struct getdents_callback *) __buf; -+ file_t* file = mini_fo_filldir_file; -+ -+ /* In theses states we filter meta files in storage (WOL) */ -+ if(file && (dtopd(file->f_dentry)->state == MODIFIED || -+ dtopd(file->f_dentry)->state == CREATED || -+ dtopd(file->f_dentry)->state == DEL_REWRITTEN)) { -+ -+ int tmp = strlen(META_FILENAME); -+ if(tmp == namlen) { -+ if(!strncmp(name, META_FILENAME, namlen)) -+ return 0; -+ } -+ } -+ -+ /* check if we are merging the contents of storage and base */ -+ if(file && dtopd(file->f_dentry)->state == MODIFIED) { -+ /* check if we are still reading storage contents, if -+ * yes, we just save the name of the file for duplicate -+ * checking later. */ -+ -+ if(!ftopd(file)->rd.sto_done) { -+ /* put file into ndl list */ -+ if(ndl_add_entry(&ftopd(file)->rd, name, namlen)) -+ printk(KERN_CRIT "mini_fo_filldir: Error adding to ndl.\n"); -+ } else { -+ /* check if file has been deleted */ -+ if(meta_check_d_entry(file->f_dentry, name, namlen)) -+ return 0; -+ -+ /* do duplicate checking */ -+ if(ndl_check_entry(&ftopd(file)->rd, name, namlen)) -+ return 0; -+ } -+ } -+ -+ return mini_fo_filldir_orig(buf, name, namlen, offset, ino, d_type); -+} -+ -+ -+STATIC int -+mini_fo_readdir(file_t *file, void *dirent, filldir_t filldir) -+{ -+ int err = 0;/* mk: ??? -ENOTDIR; */ -+ file_t *hidden_file = NULL; -+ file_t *hidden_sto_file = NULL; -+ inode_t *inode; -+ struct getdents_callback *buf; -+ int oldcount; -+ -+#if defined(FIST_FILTER_NAME) || defined(FIST_FILTER_SCA) -+ struct mini_fo_getdents_callback buf; -+#endif /* FIST_FILTER_NAME || FIST_FILTER_SCA */ -+ -+ buf = (struct getdents_callback *) dirent; -+ oldcount = buf->count; -+ inode = file->f_dentry->d_inode; -+ mini_fo_filldir_file = file; -+ mini_fo_filldir_orig = filldir; -+ -+ ftopd(file)->rd.sto_done = 0; -+ do { -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_sto_file = ftohf2(file); -+ err = vfs_readdir(hidden_sto_file, mini_fo_filldir, dirent); -+ file->f_pos = hidden_sto_file->f_pos; -+ if (err > 0) -+ fist_copy_attr_atime(inode, hidden_sto_file->f_dentry->d_inode); -+ /* not finshed yet, we'll be called again */ -+ if (buf->count != oldcount) -+ break; -+ } -+ -+ ftopd(file)->rd.sto_done = 1; -+ -+ if(ftohf(file)) { -+ hidden_file = ftohf(file); -+ err = vfs_readdir(hidden_file, mini_fo_filldir, dirent); -+ file->f_pos = hidden_file->f_pos; -+ if (err > 0) -+ fist_copy_attr_atime(inode, hidden_file->f_dentry->d_inode); -+ } -+ -+ } -+ } while (0); -+ -+ /* mk: -+ * we need to check if all the directory data has been copied to userspace, -+ * or if we will be called again by userspace to complete the operation. -+ */ -+ if(buf->count == oldcount) { -+ ndl_put_list(&ftopd(file)->rd); -+ } -+ -+ /* unset this, safe */ -+ mini_fo_filldir_file = NULL; -+ return err; -+} -+ -+ -+STATIC unsigned int -+mini_fo_poll(file_t *file, poll_table *wait) -+{ -+ unsigned int mask = DEFAULT_POLLMASK; -+ file_t *hidden_file = NULL; -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ if (!hidden_file->f_op || !hidden_file->f_op->poll) -+ goto out; -+ -+ mask = hidden_file->f_op->poll(hidden_file, wait); -+ -+ out: -+ return mask; -+} -+ -+/* FIST-LITE special version of mmap */ -+STATIC int -+mini_fo_mmap(file_t *file, vm_area_t *vma) -+{ -+ int err = 0; -+ file_t *hidden_file = NULL; -+ -+ /* fanout capability */ -+ if (ftopd(file) != NULL) { -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ } else { -+ hidden_file = ftohf(file); -+ } -+ } -+ -+ ASSERT(hidden_file != NULL); -+ ASSERT(hidden_file->f_op != NULL); -+ ASSERT(hidden_file->f_op->mmap != NULL); -+ -+ vma->vm_file = hidden_file; -+ err = hidden_file->f_op->mmap(hidden_file, vma); -+ get_file(hidden_file); /* make sure it doesn't get freed on us */ -+ fput(file); /* no need to keep extra ref on ours */ -+ -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_open(inode_t *inode, file_t *file) -+{ -+ int err = 0; -+ int hidden_flags; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry = NULL; -+ -+ /* fanout stuff */ -+ file_t *hidden_sto_file = NULL; -+ dentry_t *hidden_sto_dentry = NULL; -+ -+ __ftopd(file) = -+ kmalloc(sizeof(struct mini_fo_file_info), GFP_KERNEL); -+ if (!ftopd(file)) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ /* init the readdir_helper structure */ -+ INIT_LIST_HEAD(&ftopd(file)->rd.ndl_list); -+ ftopd(file)->rd.ndl_size = 0; -+ -+ /* In certain paths this could stay uninitalized and cause trouble */ -+ ftohf(file) = NULL; -+ ftohf2(file) = NULL; -+ hidden_flags = file->f_flags; -+ -+ /* create storage files? */ -+ if(dtost(file->f_dentry) == UNMODIFIED) { -+ if(!IS_WRITE_FLAG(file->f_flags)) { -+ hidden_dentry = dtohd(file->f_dentry); -+ dget(hidden_dentry); -+ /* dentry_open will decrement mnt refcnt if err. -+ * otherwise fput() will do an mntput() for us upon file close. */ -+ mntget(stopd(inode->i_sb)->hidden_mnt); -+ hidden_file = dentry_open(hidden_dentry, -+ stopd(inode->i_sb)->hidden_mnt, -+ hidden_flags); -+ if (IS_ERR(hidden_file)) { -+ err = PTR_ERR(hidden_file); -+ dput(hidden_dentry); -+ goto out; -+ } -+ ftohf(file) = hidden_file; /* link two files */ -+ goto out; -+ } -+ else { -+ if(S_ISDIR(file->f_dentry->d_inode->i_mode)) { -+ err = dir_unmod_to_mod(file->f_dentry); -+ } else -+ err = nondir_unmod_to_mod(file->f_dentry, 1); -+ -+ if (err) { -+ printk("mini_fo_open: ERROR creating storage file.\n"); -+ goto out; -+ } -+ } -+ } -+ hidden_sto_dentry = dtohd2(file->f_dentry); -+ dget(hidden_sto_dentry); -+ -+ if(dtopd(file->f_dentry)->state == MODIFIED) { -+ /* Directorys are special, interpose on both lower level files */ -+ if(S_ISDIR(itohi(inode)->i_mode)) { -+ /* check for invalid file types of lower level files */ -+ if(!(S_ISDIR(itohi(inode)->i_mode) && S_ISDIR(itohi2(inode)->i_mode))) { -+ printk(KERN_CRIT "mini_fo_open: meta data corruption detected.\n"); -+ dput(hidden_sto_dentry); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ /* lower level directorys are ok, open the base file */ -+ hidden_dentry = dtohd(file->f_dentry); -+ dget(hidden_dentry); -+ -+ mntget(stopd(inode->i_sb)->hidden_mnt); -+ hidden_file = dentry_open(hidden_dentry, -+ stopd(inode->i_sb)->hidden_mnt, -+ hidden_flags); -+ if (IS_ERR(hidden_file)) { -+ err = PTR_ERR(hidden_file); -+ dput(hidden_dentry); -+ dput(hidden_sto_dentry); -+ goto out; -+ } -+ ftohf(file) = hidden_file; /* link the two files */ -+ } -+ } -+ -+ if(!exists_in_storage(file->f_dentry)) { -+ printk(KERN_CRIT "mini_fo_open: invalid file state detected.\n"); -+ err = -EINVAL; -+ dput(hidden_sto_dentry); -+ -+ /* If the base file has been opened, we need to close it here */ -+ if(ftohf(file)) { -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ hidden_file->f_op->flush(hidden_file, NULL); -+#else -+ hidden_file->f_op->flush(hidden_file); -+#endif -+ dput(hidden_dentry); -+ } -+ goto out; -+ } -+ -+ /* ok, now we can safely open the storage file */ -+ mntget(stopd(inode->i_sb)->hidden_mnt2); -+ hidden_sto_file = dentry_open(hidden_sto_dentry, -+ stopd(inode->i_sb)->hidden_mnt2, -+ hidden_flags); -+ -+ /* dentry_open dputs the dentry if it fails */ -+ if (IS_ERR(hidden_sto_file)) { -+ err = PTR_ERR(hidden_sto_file); -+ /* close base file if open */ -+ if(ftohf(file)) { -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ hidden_file->f_op->flush(hidden_file, NULL); -+#else -+ hidden_file->f_op->flush(hidden_file); -+#endif -+ dput(hidden_dentry); -+ } -+ goto out; -+ } -+ ftohf2(file) = hidden_sto_file; /* link storage file */ -+ -+ out: -+ if (err < 0 && ftopd(file)) { -+ kfree(ftopd(file)); -+ } -+ return err; -+} -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_flush(file_t *file, fl_owner_t id) -+#else -+mini_fo_flush(file_t *file) -+#endif -+{ -+ int err1 = 0; /* assume ok (see open.c:close_fp) */ -+ int err2 = 0; -+ file_t *hidden_file = NULL; -+ -+ check_mini_fo_file(file); -+ -+ /* mk: we don't do any state checking here, as its not worth the time. -+ * Just flush the lower level files if they exist. -+ */ -+ if(ftopd(file) != NULL) { -+ if(ftohf(file) != NULL) { -+ hidden_file = ftohf(file); -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ err1 = hidden_file->f_op->flush(hidden_file, id); -+#else -+ err1 = hidden_file->f_op->flush(hidden_file); -+#endif -+ } -+ if(ftohf2(file) != NULL) { -+ hidden_file = ftohf2(file); -+ if (hidden_file->f_op && hidden_file->f_op->flush) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ err2 = hidden_file->f_op->flush(hidden_file, id); -+#else -+ err2 = hidden_file->f_op->flush(hidden_file); -+#endif -+ } -+ } -+ return (err1 | err2); -+} -+ -+ -+STATIC int -+mini_fo_release(inode_t *inode, file_t *file) -+{ -+ int err = 0; -+ file_t *hidden_file = NULL; -+ -+ if (ftopd(file) != NULL) { -+ if(ftohf(file)) { -+ hidden_file = ftohf(file); -+ fput(hidden_file); -+ } -+ if(ftohf2(file)) { -+ hidden_file = ftohf2(file); -+ fput(hidden_file); -+ } -+ kfree(ftopd(file)); -+ } -+ return err; -+} -+ -+STATIC int -+mini_fo_fsync(file_t *file, dentry_t *dentry, int datasync) -+{ -+ int err1 = 0; -+ int err2 = 0; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry; -+ -+ check_mini_fo_file(file); -+ -+ if ((hidden_file = ftohf(file)) != NULL) { -+ hidden_dentry = dtohd(dentry); -+ if (hidden_file->f_op && hidden_file->f_op->fsync) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ err1 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ } -+ -+ if ((hidden_file = ftohf2(file)) != NULL) { -+ hidden_dentry = dtohd2(dentry); -+ if (hidden_file->f_op && hidden_file->f_op->fsync) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ err2 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ } -+ else -+ goto err; -+ -+err: -+ return (err1 || err2); -+} -+ -+ -+STATIC int -+mini_fo_fasync(int fd, file_t *file, int flag) -+{ -+ int err1 = 0; -+ int err2 = 0; -+ -+ file_t *hidden_file = NULL; -+ -+ check_mini_fo_file(file); -+ -+ if((hidden_file = ftohf(file)) != NULL) { -+ err1 = hidden_file->f_op->fasync(fd, hidden_file, flag); -+ } -+ if((hidden_file = ftohf2(file)) != NULL) { -+ err2 = hidden_file->f_op->fasync(fd, hidden_file, flag); -+ } -+ -+ return (err1 || err2); -+} -+ -+ -+ -+struct file_operations mini_fo_dir_fops = -+ { -+ read: generic_read_dir, -+ write: mini_fo_write, -+ readdir: mini_fo_readdir, -+ poll: mini_fo_poll, -+ /* ioctl: mini_fo_ioctl, */ -+ mmap: mini_fo_mmap, -+ open: mini_fo_open, -+ flush: mini_fo_flush, -+ release: mini_fo_release, -+ fsync: mini_fo_fsync, -+ fasync: mini_fo_fasync, -+ /* not needed lock: mini_fo_lock, */ -+ /* not needed: readv */ -+ /* not needed: writev */ -+ /* not implemented: sendpage */ -+ /* not implemented: get_unmapped_area */ -+ }; -+ -+struct file_operations mini_fo_main_fops = -+ { -+ llseek: mini_fo_llseek, -+ read: mini_fo_read, -+ write: mini_fo_write, -+ readdir: mini_fo_readdir, -+ poll: mini_fo_poll, -+ /* ioctl: mini_fo_ioctl, */ -+ mmap: mini_fo_mmap, -+ open: mini_fo_open, -+ flush: mini_fo_flush, -+ release: mini_fo_release, -+ fsync: mini_fo_fsync, -+ fasync: mini_fo_fasync, -+ /* not needed: lock: mini_fo_lock, */ -+ /* not needed: readv */ -+ /* not needed: writev */ -+ /* not implemented: sendpage */ -+ /* not implemented: get_unmapped_area */ -+ }; ---- /dev/null -+++ b/fs/mini_fo/fist.h -@@ -0,0 +1,254 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifndef __FIST_H_ -+#define __FIST_H_ -+ -+/* -+ * KERNEL ONLY CODE: -+ */ -+#ifdef __KERNEL__ -+#include <linux/version.h> -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,33) -+#include <generated/autoconf.h> -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) -+#include <linux/autoconf.h> -+#else -+#include <linux/config.h> -+#endif -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+#ifdef CONFIG_MODVERSIONS -+# define MODVERSIONS -+# include <linux/modversions.h> -+#endif /* CONFIG_MODVERSIONS */ -+#endif /* KERNEL_VERSION < 2.6.0 */ -+#include <linux/sched.h> -+#include <linux/kernel.h> -+#include <linux/mm.h> -+#include <linux/string.h> -+#include <linux/stat.h> -+#include <linux/errno.h> -+#include <linux/wait.h> -+#include <linux/limits.h> -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+#include <linux/locks.h> -+#else -+#include <linux/buffer_head.h> -+#include <linux/pagemap.h> -+#include <linux/namei.h> -+#include <linux/module.h> -+#include <linux/mount.h> -+#include <linux/page-flags.h> -+#include <linux/writeback.h> -+#include <linux/statfs.h> -+#endif -+#include <linux/smp.h> -+#include <linux/smp_lock.h> -+#include <linux/file.h> -+#include <linux/slab.h> -+#include <linux/vmalloc.h> -+#include <linux/poll.h> -+#include <linux/list.h> -+#include <linux/init.h> -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) -+#include <linux/xattr.h> -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#include <linux/security.h> -+#endif -+ -+#include <linux/swap.h> -+ -+#include <asm/system.h> -+/* #include <asm/segment.h> */ -+#include <asm/mman.h> -+#include <linux/seq_file.h> -+ -+/* -+ * MACROS: -+ */ -+ -+/* those mapped to ATTR_* were copied from linux/fs.h */ -+#define FA_MODE ATTR_MODE -+#define FA_UID ATTR_UID -+#define FA_GID ATTR_GID -+#define FA_SIZE ATTR_SIZE -+#define FA_ATIME ATTR_ATIME -+#define FA_MTIME ATTR_MTIME -+#define FA_CTIME ATTR_CTIME -+#define FA_ATIME_SET ATTR_ATIME_SET -+#define FA_MTIME_SET ATTR_MTIME_SET -+#define FA_FORCE ATTR_FORCE -+#define FA_ATTR_FLAGS ATTR_ATTR_FLAG -+ -+/* must be greater than all other ATTR_* flags! */ -+#define FA_NLINK 2048 -+#define FA_BLKSIZE 4096 -+#define FA_BLOCKS 8192 -+#define FA_TIMES (FA_ATIME|FA_MTIME|FA_CTIME) -+#define FA_ALL 0 -+ -+/* macros to manage changes between kernels */ -+#define INODE_DATA(i) (&(i)->i_data) -+ -+#define MIN(x,y) ((x < y) ? (x) : (y)) -+#define MAX(x,y) ((x > y) ? (x) : (y)) -+#define MAXPATHLEN PATH_MAX -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) -+# define lookup_one_len(a,b,c) lookup_one(a,b) -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) */ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) -+# define generic_file_llseek default_llseek -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,4,8) */ -+ -+#ifndef SEEK_SET -+# define SEEK_SET 0 -+#endif /* not SEEK_SET */ -+ -+#ifndef SEEK_CUR -+# define SEEK_CUR 1 -+#endif /* not SEEK_CUR */ -+ -+#ifndef SEEK_END -+# define SEEK_END 2 -+#endif /* not SEEK_END */ -+ -+#ifndef DEFAULT_POLLMASK -+# define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) -+#endif /* not DEFAULT_POLLMASK */ -+ -+/* XXX: fix this so fistgen generates kfree() code directly */ -+#define kfree_s(a,b) kfree(a) -+ -+/* -+ * TYPEDEFS: -+ */ -+typedef struct dentry dentry_t; -+typedef struct file file_t; -+typedef struct inode inode_t; -+typedef inode_t vnode_t; -+typedef struct page page_t; -+typedef struct qstr qstr_t; -+typedef struct super_block super_block_t; -+typedef super_block_t vfs_t; -+typedef struct vm_area_struct vm_area_t; -+ -+ -+/* -+ * EXTERNALS: -+ */ -+ -+#define FPPF(str,page) printk("PPF %s 0x%x/%d: Lck:%d Err:%d Ref:%d Upd:%d Other::%d:%d:%d:%d:\n", \ -+ str, \ -+ (int) page, \ -+ (int) page->index, \ -+ (PageLocked(page) ? 1 : 0), \ -+ (PageError(page) ? 1 : 0), \ -+ (PageReferenced(page) ? 1 : 0), \ -+ (Page_Uptodate(page) ? 1 : 0), \ -+ (PageDecrAfter(page) ? 1 : 0), \ -+ (PageSlab(page) ? 1 : 0), \ -+ (PageSwapCache(page) ? 1 : 0), \ -+ (PageReserved(page) ? 1 : 0) \ -+ ) -+#define EZKDBG printk("EZK %s:%d:%s\n",__FILE__,__LINE__,__FUNCTION__) -+#if 0 -+# define EZKDBG1 printk("EZK %s:%d\n",__FILE__,__LINE__) -+#else -+# define EZKDBG1 -+#endif -+ -+extern int fist_get_debug_value(void); -+extern int fist_set_debug_value(int val); -+#if 0 /* mini_fo doesn't need these */ -+extern void fist_dprint_internal(int level, char *str,...); -+extern void fist_print_dentry(char *str, const dentry_t *dentry); -+extern void fist_print_inode(char *str, const inode_t *inode); -+extern void fist_print_file(char *str, const file_t *file); -+extern void fist_print_buffer_flags(char *str, struct buffer_head *buffer); -+extern void fist_print_page_flags(char *str, page_t *page); -+extern void fist_print_page_bytes(char *str, page_t *page); -+extern void fist_print_pte_flags(char *str, const page_t *page); -+extern void fist_checkinode(inode_t *inode, char *msg); -+extern void fist_print_sb(char *str, const super_block_t *sb); -+ -+/* §$% by mk: special debug functions */ -+extern void fist_mk_print_dentry(char *str, const dentry_t *dentry); -+extern void fist_mk_print_inode(char *str, const inode_t *inode); -+ -+extern char *add_indent(void); -+extern char *del_indent(void); -+#endif/* mini_fo doesn't need these */ -+ -+ -+#define STATIC -+#define ASSERT(EX) \ -+do { \ -+ if (!(EX)) { \ -+ printk(KERN_CRIT "ASSERTION FAILED: %s at %s:%d (%s)\n", #EX, \ -+ __FILE__, __LINE__, __FUNCTION__); \ -+ (*((char *)0))=0; \ -+ } \ -+} while (0) -+/* same ASSERT, but tell me who was the caller of the function */ -+#define ASSERT2(EX) \ -+do { \ -+ if (!(EX)) { \ -+ printk(KERN_CRIT "ASSERTION FAILED (caller): %s at %s:%d (%s)\n", #EX, \ -+ file, line, func); \ -+ (*((char *)0))=0; \ -+ } \ -+} while (0) -+ -+#if 0 /* mini_fo doesn't need these */ -+#define dprintk(format, args...) printk(KERN_DEBUG format, ##args) -+#define fist_dprint(level, str, args...) fist_dprint_internal(level, KERN_DEBUG str, ## args) -+#define print_entry_location() fist_dprint(4, "%sIN: %s %s:%d\n", add_indent(), __FUNCTION__, __FILE__, __LINE__) -+#define print_exit_location() fist_dprint(4, "%s OUT: %s %s:%d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__) -+#define print_exit_status(status) fist_dprint(4, "%s OUT: %s %s:%d, STATUS: %d\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, status) -+#define print_exit_pointer(status) \ -+do { \ -+ if (IS_ERR(status)) \ -+ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: %ld\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ -+ else \ -+ fist_dprint(4, "%s OUT: %s %s:%d, RESULT: 0x%x\n", del_indent(), __FUNCTION__, __FILE__, __LINE__, PTR_ERR(status)); \ -+} while (0) -+#endif/* mini_fo doesn't need these */ -+ -+#endif /* __KERNEL__ */ -+ -+ -+/* -+ * DEFINITIONS FOR USER AND KERNEL CODE: -+ * (Note: ioctl numbers 1--9 are reserved for fistgen, the rest -+ * are auto-generated automatically based on the user's .fist file.) -+ */ -+# define FIST_IOCTL_GET_DEBUG_VALUE _IOR(0x15, 1, int) -+# define FIST_IOCTL_SET_DEBUG_VALUE _IOW(0x15, 2, int) -+ -+#endif /* not __FIST_H_ */ ---- /dev/null -+++ b/fs/mini_fo/inode.c -@@ -0,0 +1,1564 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd) -+#else -+mini_fo_create(inode_t *dir, dentry_t *dentry, int mode) -+#endif -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = create_sto_reg_file(dentry, mode, nd); -+#else -+ err = create_sto_reg_file(dentry, mode); -+#endif -+ check_mini_fo_dentry(dentry); -+ return err; -+} -+ -+ -+STATIC dentry_t * -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_lookup(inode_t *dir, dentry_t *dentry, struct nameidata* nd) -+#else -+mini_fo_lookup(inode_t *dir, dentry_t *dentry) -+#endif -+{ -+ int err = 0; -+ dentry_t *hidden_dir_dentry; -+ dentry_t *hidden_dentry = NULL; -+ -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *hidden_sto_dentry = NULL; -+ -+ /* whiteout flag */ -+ int del_flag = 0; -+ char *bpath = NULL; -+ -+ const char *name; -+ unsigned int namelen; -+ -+ /* Don't allow lookups of META-files */ -+ namelen = strlen(META_FILENAME); -+ if(namelen == dentry->d_name.len) { -+ if(!strncmp(dentry->d_name.name, META_FILENAME, namelen)) { -+ err = -ENOENT; -+ goto out; -+ } -+ } -+ -+ hidden_dir_dentry = dtohd(dentry->d_parent); -+ hidden_sto_dir_dentry = dtohd2(dentry->d_parent); -+ -+ name = dentry->d_name.name; -+ namelen = dentry->d_name.len; -+ -+ /* must initialize dentry operations */ -+ dentry->d_op = &mini_fo_dops; -+ -+ /* setup the del_flag */ -+ del_flag = __meta_check_d_entry(dir, name, namelen); -+ bpath = __meta_check_r_entry(dir, name, namelen); -+ -+ /* perform the lookups of base and storage files: -+ * -+ * This caused some serious trouble, as a lookup_one_len passing -+ * a negative dentry oopses. Solution is to only do the lookup -+ * if the dentry is positive, else we set it to NULL -+ * More trouble, who said a *_dir_dentry can't be NULL? -+ */ -+ if(bpath) { -+ /* Cross-Interposing (C), yeah! */ -+ hidden_dentry = bpath_walk(dir->i_sb, bpath); -+ if(!hidden_dentry || !hidden_dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo_lookup: bpath_walk failed.\n"); -+ err= -EINVAL; -+ goto out; -+ } -+ -+ /* this can be set up safely without fear of spaghetti -+ * interposing as it is only used for copying times */ -+ hidden_dir_dentry = hidden_dentry->d_parent; -+ kfree(bpath); -+ } -+ else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) -+ hidden_dentry = -+ lookup_one_len(name, hidden_dir_dentry, namelen); -+ else -+ hidden_dentry = NULL; -+ -+ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) -+ hidden_sto_dentry = -+ lookup_one_len(name, hidden_sto_dir_dentry, namelen); -+ else -+ hidden_sto_dentry = NULL; -+ -+ /* catch error in lookup */ -+ if (IS_ERR(hidden_dentry) || IS_ERR(hidden_sto_dentry)) { -+ /* mk: we need to call dput on the dentry, whose -+ * lookup_one_len operation failed, in order to avoid -+ * unmount trouble. -+ */ -+ if(IS_ERR(hidden_dentry)) { -+ printk(KERN_CRIT "mini_fo_lookup: ERR from base dentry, lookup failed.\n"); -+ err = PTR_ERR(hidden_dentry); -+ } else { -+ dput(hidden_dentry); -+ } -+ if(IS_ERR(hidden_sto_dentry)) { -+ printk(KERN_CRIT "mini_fo_lookup: ERR from storage dentry, lookup failed.\n"); -+ err = PTR_ERR(hidden_sto_dentry); -+ } else { -+ dput(hidden_sto_dentry); -+ } -+ goto out; -+ } -+ -+ /* allocate dentry private data */ -+ __dtopd(dentry) = (struct mini_fo_dentry_info *) -+ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); -+ -+ if (!dtopd(dentry)) { -+ err = -ENOMEM; -+ goto out_dput; -+ } -+ -+ /* check for different states of the mini_fo file to be looked up. */ -+ -+ /* state 1, file has been modified */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && hidden_sto_dentry->d_inode && !del_flag) { -+ -+ /* update parent directory's atime */ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = MODIFIED; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state1).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ /* state 2, file is unmodified */ -+ if(hidden_dentry && hidden_dentry->d_inode && !del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = UNMODIFIED; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; /* could be negative */ -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state2).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ /* state 3, file has been newly created */ -+ if(hidden_sto_dentry && hidden_sto_dentry->d_inode && !del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ dtopd(dentry)->state = CREATED; -+ dtohd(dentry) = hidden_dentry; /* could be negative */ -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(hidden_dentry, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state3).\n"); -+ goto out_free; -+ } -+ goto out; -+ } -+ -+ /* state 4, file has deleted and created again. */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && -+ hidden_sto_dentry->d_inode && del_flag) { -+ -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ dtopd(dentry)->state = DEL_REWRITTEN; -+ dtohd(dentry) = NULL; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ err = mini_fo_tri_interpose(NULL, -+ hidden_sto_dentry, -+ dentry, dir->i_sb, 1); -+ if (err) { -+ printk(KERN_CRIT "mini_fo_lookup: error interposing (state4).\n"); -+ goto out_free; -+ } -+ /* We will never need this dentry again, as the file has been -+ * deleted from base */ -+ dput(hidden_dentry); -+ goto out; -+ } -+ /* state 5, file has been deleted in base */ -+ if(hidden_dentry && hidden_sto_dentry && -+ hidden_dentry->d_inode && -+ !hidden_sto_dentry->d_inode && del_flag) { -+ -+ /* check which parents atime we need for updating */ -+ if(hidden_sto_dir_dentry->d_inode) -+ fist_copy_attr_atime(dir, -+ hidden_sto_dir_dentry->d_inode); -+ else -+ fist_copy_attr_atime(dir, -+ hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = DELETED; -+ dtohd(dentry) = NULL; -+ dtohd2(dentry) = hidden_sto_dentry; -+ -+ /* add negative dentry to dcache to speed up lookups */ -+ d_add(dentry, NULL); -+ dput(hidden_dentry); -+ goto out; -+ } -+ /* state 6, file does not exist */ -+ if(((hidden_dentry && !hidden_dentry->d_inode) || -+ (hidden_sto_dentry && !hidden_sto_dentry->d_inode)) && !del_flag) -+ { -+ /* check which parents atime we need for updating */ -+ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) -+ fist_copy_attr_atime(dir, hidden_sto_dir_dentry->d_inode); -+ else -+ fist_copy_attr_atime(dir, hidden_dir_dentry->d_inode); -+ -+ dtopd(dentry)->state = NON_EXISTANT; -+ dtohd(dentry) = hidden_dentry; -+ dtohd2(dentry) = hidden_sto_dentry; -+ d_add(dentry, NULL); -+ goto out; -+ } -+ -+ /* if we get to here, were in an invalid state. bad. */ -+ printk(KERN_CRIT "mini_fo_lookup: ERROR, meta data corruption detected.\n"); -+ -+ /* end state checking */ -+ out_free: -+ d_drop(dentry); /* so that our bad dentry will get destroyed */ -+ kfree(dtopd(dentry)); -+ __dtopd(dentry) = NULL; /* be safe */ -+ -+ out_dput: -+ if(hidden_dentry) -+ dput(hidden_dentry); -+ if(hidden_sto_dentry) -+ dput(hidden_sto_dentry); /* drops usage count and marks for release */ -+ -+ out: -+ /* initalize wol if file exists and is directory */ -+ if(dentry->d_inode) { -+ if(S_ISDIR(dentry->d_inode->i_mode)) { -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ } -+ } -+ return ERR_PTR(err); -+} -+ -+ -+STATIC int -+mini_fo_link(dentry_t *old_dentry, inode_t *dir, dentry_t *new_dentry) -+{ -+ int err; -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_dir_dentry; -+ -+ -+ check_mini_fo_dentry(old_dentry); -+ check_mini_fo_dentry(new_dentry); -+ check_mini_fo_inode(dir); -+ -+ /* no links to directorys and existing targets target allowed */ -+ if(S_ISDIR(old_dentry->d_inode->i_mode) || -+ is_mini_fo_existant(new_dentry)) { -+ err = -EPERM; -+ goto out; -+ } -+ -+ /* bring it directly from unmod to del_rew */ -+ if(dtost(old_dentry) == UNMODIFIED) { -+ err = nondir_unmod_to_mod(old_dentry, 1); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ err = meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ dput(dtohd(old_dentry)); -+ dtohd(old_dentry) = NULL; -+ dtost(old_dentry) = DEL_REWRITTEN; -+ } -+ -+ err = get_neg_sto_dentry(new_dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ /* was: hidden_dir_dentry = lock_parent(hidden_new_dentry); */ -+ hidden_dir_dentry = dget(hidden_new_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = vfs_link(hidden_old_dentry, -+ hidden_dir_dentry->d_inode, -+ hidden_new_dentry); -+ if (err || !hidden_new_dentry->d_inode) -+ goto out_lock; -+ -+ dtost(new_dentry) = CREATED; -+ err = mini_fo_tri_interpose(NULL, hidden_new_dentry, new_dentry, dir->i_sb, 0); -+ if (err) -+ goto out_lock; -+ -+ fist_copy_attr_timesizes(dir, hidden_new_dentry->d_inode); -+ /* propagate number of hard-links */ -+ old_dentry->d_inode->i_nlink = itohi2(old_dentry->d_inode)->i_nlink; -+ -+ out_lock: -+ /* was: unlock_dir(hidden_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_dir_dentry); -+ -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ if (!new_dentry->d_inode) -+ d_drop(new_dentry); -+ -+ out: -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_unlink(inode_t *dir, dentry_t *dentry) -+{ -+ int err = 0; -+ -+ dget(dentry); -+ if(dtopd(dentry)->state == MODIFIED) { -+ err = nondir_mod_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ err = nondir_unmod_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == CREATED) { -+ err = nondir_creat_to_del(dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == DEL_REWRITTEN) { -+ err = nondir_del_rew_to_del(dentry); -+ goto out; -+ } -+ -+ printk(KERN_CRIT "mini_fo_unlink: ERROR, invalid state detected.\n"); -+ -+ out: -+ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); -+ -+ if(!err) { -+ /* is this causing my pain? d_delete(dentry); */ -+ d_drop(dentry); -+ } -+ -+ dput(dentry); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_symlink(inode_t *dir, dentry_t *dentry, const char *symname) -+{ -+ int err=0; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ umode_t mode; -+#endif -+ -+ /* Fail if the symlink file exists */ -+ if(!(dtost(dentry) == DELETED || -+ dtost(dentry) == NON_EXISTANT)) { -+ err = -EEXIST; -+ goto out; -+ } -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ dget(hidden_sto_dentry); -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ mode = S_IALLUGO; -+ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, symname, mode); -+#else -+ err = vfs_symlink(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ symname); -+#endif -+ if (err || !hidden_sto_dentry->d_inode) -+ goto out_lock; -+ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ dput(hidden_sto_dentry); -+ if (!dentry->d_inode) -+ d_drop(dentry); -+ out: -+ return err; -+} -+ -+STATIC int -+mini_fo_mkdir(inode_t *dir, dentry_t *dentry, int mode) -+{ -+ int err; -+ -+ err = create_sto_dir(dentry, mode); -+ -+ check_mini_fo_dentry(dentry); -+ -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_rmdir(inode_t *dir, dentry_t *dentry) -+{ -+ int err = 0; -+ -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *meta_dentry; -+ inode_t *hidden_sto_dir = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ check_mini_fo_inode(dir); -+ -+ dget(dentry); -+ if(dtopd(dentry)->state == MODIFIED) { -+ /* XXX: disabled, because it does not bother to check files on -+ * the original filesystem - just a hack, but better than simply -+ * removing it without testing */ -+ err = -EINVAL; -+ goto out; -+ -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was:hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ err = vfs_unlink(hidden_sto_dentry->d_inode, meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ dput(dtohd(dentry)); -+ -+ dtohd(dentry) = NULL; -+ dtopd(dentry)->state = DELETED; -+ -+ /* carefull with R files */ -+ if( __meta_is_r_entry(dir, -+ dentry->d_name.name, -+ dentry->d_name.len) == 1) { -+ err = meta_remove_r_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: rmdir: meta_remove_r_entry failed.\n"); -+ goto out; -+ } -+ } -+ else { -+ /* ok, add deleted file to META */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ } -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ /* XXX: simply adding it to the delete list here is fscking dangerous! -+ * as a temporary hack, i will disable rmdir on unmodified directories -+ * for now. -+ */ -+ err = -EINVAL; -+ goto out; -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtopd(dentry)->state = DELETED; -+ -+ /* add deleted file to META-file */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ goto out; -+ } -+ else if(dtopd(dentry)->state == CREATED) { -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ /* is this necessary? dget(meta_dentry); */ -+ err = vfs_unlink(hidden_sto_dentry->d_inode, -+ meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ dtopd(dentry)->state = NON_EXISTANT; -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ goto out; -+ } -+ else if(dtopd(dentry)->state == DEL_REWRITTEN) { -+ hidden_sto_dir = itohi2(dir); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* Delete an old WOL file contained in the storage dir */ -+ meta_dentry = lookup_one_len(META_FILENAME, -+ hidden_sto_dentry, -+ strlen(META_FILENAME)); -+ if(meta_dentry->d_inode) { -+ /* is this necessary? dget(meta_dentry); */ -+ err = vfs_unlink(hidden_sto_dentry->d_inode, -+ meta_dentry); -+ dput(meta_dentry); -+ if(!err) -+ d_delete(meta_dentry); -+ } -+ -+ err = vfs_rmdir(hidden_sto_dir, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ dtopd(dentry)->state = DELETED; -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ goto out; -+ } -+ -+ printk(KERN_CRIT "mini_fo_rmdir: ERROR, invalid state detected.\n"); -+ -+ out: -+ if(!err) { -+ d_drop(dentry); -+ } -+ -+ fist_copy_attr_times(dir, itohi2(dentry->d_parent->d_inode)); -+ dput(dentry); -+ -+ return err; -+} -+ -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, dev_t dev) -+#else -+mini_fo_mknod(inode_t *dir, dentry_t *dentry, int mode, int dev) -+#endif -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ err = create_sto_nod(dentry, mode, dev); -+ if(err) { -+ printk(KERN_CRIT "mini_fo_mknod: creating sto nod failed.\n"); -+ err = -EINVAL; -+ } -+ -+ check_mini_fo_dentry(dentry); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_rename(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ /* dispatch */ -+ if(S_ISDIR(old_dentry->d_inode->i_mode)) -+ return rename_directory(old_dir, old_dentry, new_dir, new_dentry); -+ return rename_nondir(old_dir, old_dentry, new_dir, new_dentry); -+ -+} -+ -+int rename_directory(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ int err, bpath_len; -+ char *bpath; -+ -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_old_dir_dentry; -+ dentry_t *hidden_new_dir_dentry; -+ -+ err = 0; -+ bpath = NULL; -+ bpath_len = 0; -+ -+ /* this is a test, chuck out if it works */ -+ if(!(dtopd(new_dentry)->state == DELETED || -+ dtopd(new_dentry)->state == NON_EXISTANT)) { -+ printk(KERN_CRIT "mini_fo: rename_directory: \ -+ uh, ah, new_dentry not negative.\n"); -+ /* return -1; */ -+ } -+ -+ /* state = UNMODIFIED */ -+ if(dtopd(old_dentry)->state == UNMODIFIED) { -+ err = dir_unmod_to_mod(old_dentry); -+ if (err) -+ goto out; -+ } -+ -+ /* state = MODIFIED */ -+ if(dtopd(old_dentry)->state == MODIFIED) { -+ bpath = meta_check_r_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(bpath) { -+ err = meta_remove_r_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: rename_directory:\ -+ meta_remove_r_entry \ -+ failed.\n"); -+ goto out; -+ } -+ err = meta_add_r_entry(new_dentry->d_parent, -+ bpath, -+ strlen(bpath), -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ kfree(bpath); -+ } -+ else {/* wol it */ -+ err = meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ if (err) -+ goto out; -+ /* put it on rename list */ -+ err = get_mini_fo_bpath(old_dentry, -+ &bpath, -+ &bpath_len); -+ if (err) -+ goto out; -+ err = meta_add_r_entry(new_dentry->d_parent, -+ bpath, bpath_len, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ if (err) -+ goto out; -+ } -+ /* no state change, MODIFIED stays MODIFIED */ -+ } -+ /* state = CREATED */ -+ if(dtopd(old_dentry)->state == CREATED || -+ dtopd(old_dentry)->state == DEL_REWRITTEN) { -+ if(dtohd(old_dentry)) -+ dput(dtohd(old_dentry)); -+ -+ if(dtopd(new_dentry)->state == DELETED) { -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ dtohd(old_dentry) = NULL; -+ } -+ else if(dtopd(new_dentry)->state == NON_EXISTANT) { -+ dtopd(old_dentry)->state = CREATED; -+ /* steal new dentry's neg. base dentry */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ dtohd(new_dentry) = NULL; -+ } -+ } -+ if(dtopd(new_dentry)->state == UNMODIFIED || -+ dtopd(new_dentry)->state == NON_EXISTANT) { -+ err = get_neg_sto_dentry(new_dentry); -+ if(err) -+ goto out; -+ } -+ -+ /* now move sto file */ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); -+ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); -+ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ -+ err = vfs_rename(hidden_old_dir_dentry->d_inode, hidden_old_dentry, -+ hidden_new_dir_dentry->d_inode, hidden_new_dentry); -+ if(err) -+ goto out_lock; -+ -+ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); -+ if (new_dir != old_dir) -+ fist_copy_attr_all(old_dir, -+ hidden_old_dir_dentry->d_inode); -+ -+ out_lock: -+ /* double_unlock will dput the new/old parent dentries -+ * whose refcnts were incremented via get_parent above. */ -+ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ -+ out: -+ return err; -+} -+ -+int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, -+ inode_t *new_dir, dentry_t *new_dentry) -+{ -+ int err=0; -+ -+ check_mini_fo_dentry(old_dentry); -+ check_mini_fo_dentry(new_dentry); -+ check_mini_fo_inode(old_dir); -+ check_mini_fo_inode(new_dir); -+ -+ /* state: UNMODIFIED */ -+ if(dtost(old_dentry) == UNMODIFIED) { -+ err = nondir_unmod_to_mod(old_dentry, 1); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ } -+ -+ /* the easy states */ -+ if(exists_in_storage(old_dentry)) { -+ -+ dentry_t *hidden_old_dentry; -+ dentry_t *hidden_new_dentry; -+ dentry_t *hidden_old_dir_dentry; -+ dentry_t *hidden_new_dir_dentry; -+ -+ /* if old file is MODIFIED, add it to the deleted_list */ -+ if(dtopd(old_dentry)->state == MODIFIED) { -+ meta_add_d_entry(old_dentry->d_parent, -+ old_dentry->d_name.name, -+ old_dentry->d_name.len); -+ -+ dput(dtohd(old_dentry)); -+ } -+ /* if old file is CREATED, we only release the base dentry */ -+ if(dtopd(old_dentry)->state == CREATED) { -+ if(dtohd(old_dentry)) -+ dput(dtohd(old_dentry)); -+ } -+ -+ /* now setup the new states (depends on new_dentry state) */ -+ /* new dentry state = MODIFIED */ -+ if(dtopd(new_dentry)->state == MODIFIED) { -+ meta_add_d_entry(new_dentry->d_parent, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ -+ /* new dentry will be d_put'ed later by the vfs -+ * so don't do it here -+ * dput(dtohd(new_dentry)); -+ */ -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ /* new dentry state = UNMODIFIED */ -+ else if(dtopd(new_dentry)->state == UNMODIFIED) { -+ if(get_neg_sto_dentry(new_dentry)) -+ return -EINVAL; -+ -+ meta_add_d_entry(new_dentry->d_parent, -+ new_dentry->d_name.name, -+ new_dentry->d_name.len); -+ -+ /* is this right??? */ -+ /*dput(dtohd(new_dentry));*/ -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ /* new dentry state = CREATED */ -+ else if(dtopd(new_dentry)->state == CREATED) { -+ /* we keep the neg. base dentry (if exists) */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ /* ...and set it to Null, or we'll get -+ * dcache.c:345 if it gets dput twice... */ -+ dtohd(new_dentry) = NULL; -+ dtopd(old_dentry)->state = CREATED; -+ } -+ /* new dentry state = NON_EXISTANT */ -+ else if(dtopd(new_dentry)->state == NON_EXISTANT) { -+ if(get_neg_sto_dentry(new_dentry)) -+ return -EINVAL; -+ -+ /* we keep the neg. base dentry (if exists) */ -+ dtohd(old_dentry) = dtohd(new_dentry); -+ /* ...and set it to Null, or we'll get -+ * Dr. dcache.c:345 if it gets dput twice... */ -+ dtohd(new_dentry) = NULL; -+ dtopd(old_dentry)->state = CREATED; -+ } -+ /* new dentry state = DEL_REWRITTEN or DELETED */ -+ else if(dtopd(new_dentry)->state == DEL_REWRITTEN || -+ dtopd(new_dentry)->state == DELETED) { -+ dtohd(old_dentry) = NULL; -+ dtopd(old_dentry)->state = DEL_REWRITTEN; -+ } -+ else { /* not possible, uhh, ahh */ -+ printk(KERN_CRIT -+ "mini_fo: rename_reg_file: invalid state detected [1].\n"); -+ return -1; -+ } -+ -+ /* now we definitely have a sto file */ -+ hidden_old_dentry = dtohd2(old_dentry); -+ hidden_new_dentry = dtohd2(new_dentry); -+ -+ dget(hidden_old_dentry); -+ dget(hidden_new_dentry); -+ -+ hidden_old_dir_dentry = dget(hidden_old_dentry->d_parent); -+ hidden_new_dir_dentry = dget(hidden_new_dentry->d_parent); -+ double_lock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ -+ err = vfs_rename(hidden_old_dir_dentry->d_inode, -+ hidden_old_dentry, -+ hidden_new_dir_dentry->d_inode, -+ hidden_new_dentry); -+ if(err) -+ goto out_lock; -+ -+ fist_copy_attr_all(new_dir, hidden_new_dir_dentry->d_inode); -+ if (new_dir != old_dir) -+ fist_copy_attr_all(old_dir, hidden_old_dir_dentry->d_inode); -+ -+ out_lock: -+ /* double_unlock will dput the new/old parent dentries -+ * whose refcnts were incremented via get_parent above. -+ */ -+ double_unlock(hidden_old_dir_dentry, hidden_new_dir_dentry); -+ dput(hidden_new_dentry); -+ dput(hidden_old_dentry); -+ out: -+ return err; -+ } -+ else { /* invalid state */ -+ printk(KERN_CRIT "mini_fo: rename_reg_file: ERROR: invalid state detected [2].\n"); -+ return -1; -+ } -+} -+ -+ -+STATIC int -+mini_fo_readlink(dentry_t *dentry, char *buf, int bufsiz) -+{ -+ int err=0; -+ dentry_t *hidden_dentry = NULL; -+ -+ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { -+ hidden_dentry = dtohd2(dentry); -+ } else if(dtohd(dentry) && dtohd(dentry)->d_inode) { -+ hidden_dentry = dtohd(dentry); -+ } else { -+ goto out; -+ } -+ -+ if (!hidden_dentry->d_inode->i_op || -+ !hidden_dentry->d_inode->i_op->readlink) { -+ err = -EINVAL; goto out; -+ } -+ -+ err = hidden_dentry->d_inode->i_op->readlink(hidden_dentry, -+ buf, -+ bufsiz); -+ if (err > 0) -+ fist_copy_attr_atime(dentry->d_inode, hidden_dentry->d_inode); -+ -+ out: -+ return err; -+} -+ -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+static int mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) -+#else -+static void* mini_fo_follow_link(dentry_t *dentry, struct nameidata *nd) -+#endif -+{ -+ char *buf; -+ int len = PAGE_SIZE, err; -+ mm_segment_t old_fs; -+ -+ /* in 2.6 this is freed by mini_fo_put_link called by __do_follow_link */ -+ buf = kmalloc(len, GFP_KERNEL); -+ if (!buf) { -+ err = -ENOMEM; -+ goto out; -+ } -+ -+ /* read the symlink, and then we will follow it */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ err = dentry->d_inode->i_op->readlink(dentry, buf, len); -+ set_fs(old_fs); -+ if (err < 0) { -+ kfree(buf); -+ buf = NULL; -+ goto out; -+ } -+ buf[err] = 0; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ nd_set_link(nd, buf); -+ err = 0; -+#else -+ err = vfs_follow_link(nd, buf); -+#endif -+ -+ out: -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ kfree(buf); -+#endif -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+ return err; -+#else -+ return ERR_PTR(err); -+#endif -+} -+ -+STATIC -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13) -+void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd) -+#else -+void mini_fo_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) -+#endif -+{ -+ char *link; -+ link = nd_get_link(nd); -+ kfree(link); -+} -+#endif -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) -+#else -+mini_fo_permission(inode_t *inode, int mask) -+#endif -+{ -+ inode_t *hidden_inode; -+ int mode; -+ int err; -+ -+ if(itohi2(inode)) { -+ hidden_inode = itohi2(inode); -+ } else { -+ hidden_inode = itohi(inode); -+ } -+ mode = inode->i_mode; -+ -+ /* not really needed, as permission handles everything: -+ * err = vfs_permission(inode, mask); -+ * if (err) -+ * goto out; -+ */ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = permission(hidden_inode, mask, nd); -+#else -+ err = permission(hidden_inode, mask); -+#endif -+ -+ /* out: */ -+ return err; -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+STATIC int -+mini_fo_inode_revalidate(dentry_t *dentry) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ inode_t *hidden_inode; -+ -+ ASSERT(dentry->d_inode); -+ ASSERT(itopd(dentry->d_inode)); -+ -+ if(itohi2(dentry->d_inode)) { -+ hidden_dentry = dtohd2(dentry); -+ hidden_inode = hidden_dentry->d_inode; -+ } else if(itohi(dentry->d_inode)) { -+ hidden_dentry = dtohd(dentry); -+ hidden_inode = hidden_dentry->d_inode; -+ } else { -+ printk(KERN_CRIT "mini_fo_inode_revalidate: ERROR, invalid state detected.\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ if (hidden_inode && hidden_inode->i_op && hidden_inode->i_op->revalidate){ -+ err = hidden_inode->i_op->revalidate(hidden_dentry); -+ if (err) -+ goto out; -+ } -+ fist_copy_attr_all(dentry->d_inode, hidden_inode); -+ out: -+ return err; -+} -+#endif -+ -+STATIC int -+mini_fo_setattr(dentry_t *dentry, struct iattr *ia) -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(!is_mini_fo_existant(dentry)) { -+ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [1].\n"); -+ goto out; -+ } -+ -+ if(dtost(dentry) == UNMODIFIED) { -+ if(!IS_COPY_FLAG(ia->ia_valid)) -+ goto out; /* we ignore these changes to base */ -+ -+ if(S_ISDIR(dentry->d_inode->i_mode)) { -+ err = dir_unmod_to_mod(dentry); -+ } else { -+ /* we copy contents if file is not beeing truncated */ -+ if(S_ISREG(dentry->d_inode->i_mode) && -+ !(ia->ia_size == 0 && (ia->ia_valid & ATTR_SIZE))) { -+ err = nondir_unmod_to_mod(dentry, 1); -+ } else -+ err = nondir_unmod_to_mod(dentry, 0); -+ } -+ if(err) { -+ err = -EINVAL; -+ printk(KERN_CRIT "mini_fo_setattr: ERROR changing states.\n"); -+ goto out; -+ } -+ } -+ if(!exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo_setattr: ERROR, invalid state detected [2].\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ ASSERT(dentry->d_inode); -+ ASSERT(dtohd2(dentry)); -+ ASSERT(itopd(dentry->d_inode)); -+ ASSERT(itohi2(dentry->d_inode)); -+ -+ err = notify_change(dtohd2(dentry), ia); -+ fist_copy_attr_all(dentry->d_inode, itohi2(dentry->d_inode)); -+ out: -+ return err; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+STATIC int -+mini_fo_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) -+{ -+ int err = 0; -+ dentry_t *hidden_dentry; -+ -+ ASSERT(dentry->d_inode); -+ ASSERT(itopd(dentry->d_inode)); -+ -+ if(itohi2(dentry->d_inode)) { -+ hidden_dentry = dtohd2(dentry); -+ } else if(itohi(dentry->d_inode)) { -+ hidden_dentry = dtohd(dentry); -+ } else { -+ printk(KERN_CRIT "mini_fo_getattr: ERROR, invalid state detected.\n"); -+ err = -ENOENT; -+ goto out; -+ } -+ fist_copy_attr_all(dentry->d_inode, hidden_dentry->d_inode); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ generic_fillattr(dentry->d_inode, stat); -+ if (!stat->blksize) { -+ struct super_block *s = hidden_dentry->d_inode->i_sb; -+ unsigned blocks; -+ blocks = (stat->size+s->s_blocksize-1) >> s->s_blocksize_bits; -+ stat->blocks = (s->s_blocksize / 512) * blocks; -+ stat->blksize = s->s_blocksize; -+ } -+ out: -+ return err; -+} -+#endif -+ -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+#if 0 /* no xattr_alloc() and xattr_free() */ -+/* This is lifted from fs/xattr.c */ -+static void * -+xattr_alloc(size_t size, size_t limit) -+{ -+ void *ptr; -+ -+ if (size > limit) -+ return ERR_PTR(-E2BIG); -+ -+ if (!size) /* size request, no buffer is needed */ -+ return NULL; -+ else if (size <= PAGE_SIZE) -+ ptr = kmalloc((unsigned long) size, GFP_KERNEL); -+ else -+ ptr = vmalloc((unsigned long) size); -+ if (!ptr) -+ return ERR_PTR(-ENOMEM); -+ return ptr; -+} -+ -+static void -+xattr_free(void *ptr, size_t size) -+{ -+ if (!size) /* size request, no buffer was needed */ -+ return; -+ else if (size <= PAGE_SIZE) -+ kfree(ptr); -+ else -+ vfree(ptr); -+} -+#endif /* no xattr_alloc() and xattr_free() */ -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_getxattr(struct dentry *dentry, const char *name, void *value, size_t size) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ /* Define these anyway so we don't need as much ifdef'ed code. */ -+ char *encoded_name = NULL; -+ char *encoded_value = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->getxattr) { -+ encoded_name = (char *)name; -+ encoded_value = (char *)value; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->getxattr(hidden_dentry, encoded_name, encoded_value, size); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,21) \ -+ && LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,23)) \ -+ || LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) -+mini_fo_setxattr(struct dentry *dentry, const char *name, -+ const void *value, size_t size, int flags) -+#else -+mini_fo_setxattr(struct dentry *dentry, const char *name, -+ void *value, size_t size, int flags) -+#endif -+ -+{ -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ -+ /* Define these anyway, so we don't have as much ifdef'ed code. */ -+ char *encoded_value = NULL; -+ char *encoded_name = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->setxattr) { -+ encoded_name = (char *)name; -+ encoded_value = (char *)value; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->setxattr(hidden_dentry, encoded_name, encoded_value, size, flags); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_removexattr(struct dentry *dentry, const char *name) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ char *encoded_name; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->removexattr) { -+ encoded_name = (char *)name; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->removexattr(hidden_dentry, encoded_name); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+ -+/* BKL held by caller. -+ * dentry->d_inode->i_sem down -+ */ -+STATIC int -+mini_fo_listxattr(struct dentry *dentry, char *list, size_t size) { -+ struct dentry *hidden_dentry = NULL; -+ int err = -EOPNOTSUPP; -+ char *encoded_list = NULL; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(exists_in_storage(dentry)) -+ hidden_dentry = dtohd2(dentry); -+ else -+ hidden_dentry = dtohd(dentry); -+ -+ ASSERT(hidden_dentry); -+ ASSERT(hidden_dentry->d_inode); -+ ASSERT(hidden_dentry->d_inode->i_op); -+ -+ if (hidden_dentry->d_inode->i_op->listxattr) { -+ encoded_list = list; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_dentry->d_inode->i_sem); -+#endif -+ /* lock_kernel() already done by caller. */ -+ err = hidden_dentry->d_inode->i_op->listxattr(hidden_dentry, encoded_list, size); -+ /* unlock_kernel() will be done by caller. */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_dentry->d_inode->i_sem); -+#endif -+ } -+ return err; -+} -+# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ -+ -+struct inode_operations mini_fo_symlink_iops = -+ { -+ readlink: mini_fo_readlink, -+ follow_link: mini_fo_follow_link, -+ /* mk: permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+ setattr: mini_fo_setattr, -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+ put_link: mini_fo_put_link, -+#endif -+ -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) */ -+ }; -+ -+struct inode_operations mini_fo_dir_iops = -+ { -+ create: mini_fo_create, -+ lookup: mini_fo_lookup, -+ link: mini_fo_link, -+ unlink: mini_fo_unlink, -+ symlink: mini_fo_symlink, -+ mkdir: mini_fo_mkdir, -+ rmdir: mini_fo_rmdir, -+ mknod: mini_fo_mknod, -+ rename: mini_fo_rename, -+ /* no readlink/follow_link for non-symlinks */ -+ // off because we have setattr -+ // truncate: mini_fo_truncate, -+ /* mk:permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+#endif -+ setattr: mini_fo_setattr, -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ -+ }; -+ -+struct inode_operations mini_fo_main_iops = -+ { -+ /* permission: mini_fo_permission, */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ revalidate: mini_fo_inode_revalidate, -+#endif -+ setattr: mini_fo_setattr, -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ getattr: mini_fo_getattr, -+#endif -+#if defined(XATTR) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20)) -+ setxattr: mini_fo_setxattr, -+ getxattr: mini_fo_getxattr, -+ listxattr: mini_fo_listxattr, -+ removexattr: mini_fo_removexattr -+# endif /* XATTR && LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) */ -+ }; ---- /dev/null -+++ b/fs/mini_fo/main.c -@@ -0,0 +1,423 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+#include <linux/module.h> -+ -+/* This definition must only appear after we include <linux/module.h> */ -+#ifndef MODULE_LICENSE -+# define MODULE_LICENSE(bison) -+#endif /* not MODULE_LICENSE */ -+ -+/* -+ * This is the mini_fo tri interpose function, which extends the -+ * functionality of the regular interpose by interposing a higher -+ * level inode on top of two lower level ones: the base filesystem -+ * inode and the storage filesystem inode. -+ * -+ * sb we pass is mini_fo's super_block -+ */ -+int -+mini_fo_tri_interpose(dentry_t *hidden_dentry, -+ dentry_t *hidden_sto_dentry, -+ dentry_t *dentry, super_block_t *sb, int flag) -+{ -+ inode_t *hidden_inode = NULL; -+ inode_t *hidden_sto_inode = NULL; /* store corresponding storage inode */ -+ int err = 0; -+ inode_t *inode; -+ -+ /* Pointer to hidden_sto_inode if exists, else to hidden_inode. -+ * This is used to copy the attributes of the correct inode. */ -+ inode_t *master_inode; -+ -+ if(hidden_dentry) -+ hidden_inode = hidden_dentry->d_inode; -+ if(hidden_sto_dentry) -+ hidden_sto_inode = hidden_sto_dentry->d_inode; -+ -+ ASSERT(dentry->d_inode == NULL); -+ -+ /* mk: One of the inodes associated with the dentrys is likely to -+ * be NULL, so carefull: -+ */ -+ ASSERT((hidden_inode != NULL) || (hidden_sto_inode != NULL)); -+ -+ if(hidden_sto_inode) -+ master_inode = hidden_sto_inode; -+ else -+ master_inode = hidden_inode; -+ -+ /* -+ * We allocate our new inode below, by calling iget. -+ * iget will call our read_inode which will initialize some -+ * of the new inode's fields -+ */ -+ -+ /* -+ * original: inode = iget(sb, hidden_inode->i_ino); -+ */ -+ inode = iget(sb, iunique(sb, 25)); -+ if (!inode) { -+ err = -EACCES; /* should be impossible??? */ -+ goto out; -+ } -+ -+ /* -+ * interpose the inode if not already interposed -+ * this is possible if the inode is being reused -+ * XXX: what happens if we get_empty_inode() but there's another already? -+ * for now, ASSERT() that this can't happen; fix later. -+ */ -+ if (itohi(inode) != NULL) { -+ printk(KERN_CRIT "mini_fo_tri_interpose: itohi(inode) != NULL.\n"); -+ } -+ if (itohi2(inode) != NULL) { -+ printk(KERN_CRIT "mini_fo_tri_interpose: itohi2(inode) != NULL.\n"); -+ } -+ -+ /* mk: Carefull, igrab can't handle NULL inodes (ok, why should it?), so -+ * we need to check here: -+ */ -+ if(hidden_inode) -+ itohi(inode) = igrab(hidden_inode); -+ else -+ itohi(inode) = NULL; -+ -+ if(hidden_sto_inode) -+ itohi2(inode) = igrab(hidden_sto_inode); -+ else -+ itohi2(inode) = NULL; -+ -+ -+ /* Use different set of inode ops for symlinks & directories*/ -+ if (S_ISLNK(master_inode->i_mode)) -+ inode->i_op = &mini_fo_symlink_iops; -+ else if (S_ISDIR(master_inode->i_mode)) -+ inode->i_op = &mini_fo_dir_iops; -+ -+ /* Use different set of file ops for directories */ -+ if (S_ISDIR(master_inode->i_mode)) -+ inode->i_fop = &mini_fo_dir_fops; -+ -+ /* properly initialize special inodes */ -+ if (S_ISBLK(master_inode->i_mode) || S_ISCHR(master_inode->i_mode) || -+ S_ISFIFO(master_inode->i_mode) || S_ISSOCK(master_inode->i_mode)) { -+ init_special_inode(inode, master_inode->i_mode, master_inode->i_rdev); -+ } -+ -+ /* Fix our inode's address operations to that of the lower inode */ -+ if (inode->i_mapping->a_ops != master_inode->i_mapping->a_ops) { -+ inode->i_mapping->a_ops = master_inode->i_mapping->a_ops; -+ } -+ -+ /* only (our) lookup wants to do a d_add */ -+ if (flag) -+ d_add(dentry, inode); -+ else -+ d_instantiate(dentry, inode); -+ -+ ASSERT(dtopd(dentry) != NULL); -+ -+ /* all well, copy inode attributes */ -+ fist_copy_attr_all(inode, master_inode); -+ -+ out: -+ return err; -+} -+ -+/* parse mount options "base=" and "sto=" */ -+dentry_t * -+mini_fo_parse_options(super_block_t *sb, char *options) -+{ -+ dentry_t *hidden_root = ERR_PTR(-EINVAL); -+ dentry_t *hidden_root2 = ERR_PTR(-EINVAL); -+ struct nameidata nd, nd2; -+ char *name, *tmp, *end; -+ int err = 0; -+ -+ /* We don't want to go off the end of our arguments later on. */ -+ for (end = options; *end; end++); -+ -+ while (options < end) { -+ tmp = options; -+ while (*tmp && *tmp != ',') -+ tmp++; -+ *tmp = '\0'; -+ if (!strncmp("base=", options, 5)) { -+ name = options + 5; -+ printk(KERN_INFO "mini_fo: using base directory: %s\n", name); -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ if (path_init(name, LOOKUP_FOLLOW, &nd)) -+ err = path_walk(name, &nd); -+#else -+ err = path_lookup(name, LOOKUP_FOLLOW, &nd); -+#endif -+ if (err) { -+ printk(KERN_CRIT "mini_fo: error accessing hidden directory '%s'\n", name); -+ hidden_root = ERR_PTR(err); -+ goto out; -+ } -+ hidden_root = nd.dentry; -+ stopd(sb)->base_dir_dentry = nd.dentry; -+ stopd(sb)->hidden_mnt = nd.mnt; -+ -+ } else if(!strncmp("sto=", options, 4)) { -+ /* parse the storage dir */ -+ name = options + 4; -+ printk(KERN_INFO "mini_fo: using storage directory: %s\n", name); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ if(path_init(name, LOOKUP_FOLLOW, &nd2)) -+ err = path_walk(name, &nd2); -+#else -+ err = path_lookup(name, LOOKUP_FOLLOW, &nd2); -+#endif -+ if(err) { -+ printk(KERN_CRIT "mini_fo: error accessing hidden storage directory '%s'\n", name); -+ -+ hidden_root2 = ERR_PTR(err); -+ goto out; -+ } -+ hidden_root2 = nd2.dentry; -+ stopd(sb)->storage_dir_dentry = nd2.dentry; -+ stopd(sb)->hidden_mnt2 = nd2.mnt; -+ stohs2(sb) = hidden_root2->d_sb; -+ -+ /* validate storage dir, this is done in -+ * mini_fo_read_super for the base directory. -+ */ -+ if (IS_ERR(hidden_root2)) { -+ printk(KERN_WARNING "mini_fo_parse_options: storage dentry lookup failed (err = %ld)\n", PTR_ERR(hidden_root2)); -+ goto out; -+ } -+ if (!hidden_root2->d_inode) { -+ printk(KERN_WARNING "mini_fo_parse_options: no storage dir to interpose on.\n"); -+ goto out; -+ } -+ stohs2(sb) = hidden_root2->d_sb; -+ } else { -+ printk(KERN_WARNING "mini_fo: unrecognized option '%s'\n", options); -+ hidden_root = ERR_PTR(-EINVAL); -+ goto out; -+ } -+ options = tmp + 1; -+ } -+ -+ out: -+ if(IS_ERR(hidden_root2)) -+ return hidden_root2; -+ return hidden_root; -+} -+ -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+static int -+#else -+super_block_t * -+#endif -+mini_fo_read_super(super_block_t *sb, void *raw_data, int silent) -+{ -+ dentry_t *hidden_root; -+ int err = 0; -+ -+ if (!raw_data) { -+ printk(KERN_WARNING "mini_fo_read_super: missing argument\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ /* -+ * Allocate superblock private data -+ */ -+ __stopd(sb) = kmalloc(sizeof(struct mini_fo_sb_info), GFP_KERNEL); -+ if (!stopd(sb)) { -+ printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__); -+ err = -ENOMEM; -+ goto out; -+ } -+ stohs(sb) = NULL; -+ -+ hidden_root = mini_fo_parse_options(sb, raw_data); -+ if (IS_ERR(hidden_root)) { -+ printk(KERN_WARNING "mini_fo_read_super: lookup_dentry failed (err = %ld)\n", PTR_ERR(hidden_root)); -+ err = PTR_ERR(hidden_root); -+ goto out_free; -+ } -+ if (!hidden_root->d_inode) { -+ printk(KERN_WARNING "mini_fo_read_super: no directory to interpose on\n"); -+ goto out_free; -+ } -+ stohs(sb) = hidden_root->d_sb; -+ -+ /* -+ * Linux 2.4.2-ac3 and beyond has code in -+ * mm/filemap.c:generic_file_write() that requires sb->s_maxbytes -+ * to be populated. If not set, all write()s under that sb will -+ * return 0. -+ * -+ * Linux 2.4.4+ automatically sets s_maxbytes to MAX_NON_LFS; -+ * the filesystem should override it only if it supports LFS. -+ */ -+ /* non-SCA code is good to go with LFS */ -+ sb->s_maxbytes = hidden_root->d_sb->s_maxbytes; -+ -+ sb->s_op = &mini_fo_sops; -+ /* -+ * we can't use d_alloc_root if we want to use -+ * our own interpose function unchanged, -+ * so we simply replicate *most* of the code in d_alloc_root here -+ */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ sb->s_root = d_alloc(NULL, &(const struct qstr) { "/", 1, 0 }); -+#else -+ sb->s_root = d_alloc(NULL, &(const struct qstr){hash: 0, name: "/", len : 1}); -+#endif -+ if (IS_ERR(sb->s_root)) { -+ printk(KERN_WARNING "mini_fo_read_super: d_alloc failed\n"); -+ err = -ENOMEM; -+ goto out_dput; -+ } -+ -+ sb->s_root->d_op = &mini_fo_dops; -+ sb->s_root->d_sb = sb; -+ sb->s_root->d_parent = sb->s_root; -+ -+ /* link the upper and lower dentries */ -+ __dtopd(sb->s_root) = (struct mini_fo_dentry_info *) -+ kmalloc(sizeof(struct mini_fo_dentry_info), GFP_KERNEL); -+ if (!dtopd(sb->s_root)) { -+ err = -ENOMEM; -+ goto out_dput2; -+ } -+ dtopd(sb->s_root)->state = MODIFIED; -+ dtohd(sb->s_root) = hidden_root; -+ -+ /* fanout relevant, interpose on storage root dentry too */ -+ dtohd2(sb->s_root) = stopd(sb)->storage_dir_dentry; -+ -+ /* ...and call tri-interpose to interpose root dir inodes -+ * if (mini_fo_interpose(hidden_root, sb->s_root, sb, 0)) -+ */ -+ if(mini_fo_tri_interpose(hidden_root, dtohd2(sb->s_root), sb->s_root, sb, 0)) -+ goto out_dput2; -+ -+ /* initalize the wol list */ -+ itopd(sb->s_root->d_inode)->deleted_list_size = -1; -+ itopd(sb->s_root->d_inode)->renamed_list_size = -1; -+ meta_build_lists(sb->s_root); -+ -+ goto out; -+ -+ out_dput2: -+ dput(sb->s_root); -+ out_dput: -+ dput(hidden_root); -+ dput(dtohd2(sb->s_root)); /* release the hidden_sto_dentry too */ -+ out_free: -+ kfree(stopd(sb)); -+ __stopd(sb) = NULL; -+ out: -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ return err; -+#else -+ if (err) { -+ return ERR_PTR(err); -+ } else { -+ return sb; -+ } -+#endif -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+static int mini_fo_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, -+ void *raw_data, struct vfsmount *mnt) -+{ -+ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super, mnt); -+} -+#else -+static struct super_block *mini_fo_get_sb(struct file_system_type *fs_type, -+ int flags, const char *dev_name, -+ void *raw_data) -+{ -+ return get_sb_nodev(fs_type, flags, raw_data, mini_fo_read_super); -+} -+#endif -+ -+void mini_fo_kill_block_super(struct super_block *sb) -+{ -+ generic_shutdown_super(sb); -+ /* -+ * XXX: BUG: Halcrow: Things get unstable sometime after this point: -+ * lib/rwsem-spinlock.c:127: spin_is_locked on uninitialized -+ * fs/fs-writeback.c:402: spin_lock(fs/super.c:a0381828) already -+ * locked by fs/fs-writeback.c/402 -+ * -+ * Apparently, someone's not releasing a lock on sb_lock... -+ */ -+} -+ -+static struct file_system_type mini_fo_fs_type = { -+ .owner = THIS_MODULE, -+ .name = "mini_fo", -+ .get_sb = mini_fo_get_sb, -+ .kill_sb = mini_fo_kill_block_super, -+ .fs_flags = 0, -+}; -+ -+ -+#else -+static DECLARE_FSTYPE(mini_fo_fs_type, "mini_fo", mini_fo_read_super, 0); -+#endif -+ -+static int __init init_mini_fo_fs(void) -+{ -+ printk("Registering mini_fo version $Id$\n"); -+ return register_filesystem(&mini_fo_fs_type); -+} -+static void __exit exit_mini_fo_fs(void) -+{ -+ printk("Unregistering mini_fo version $Id$\n"); -+ unregister_filesystem(&mini_fo_fs_type); -+} -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+EXPORT_NO_SYMBOLS; -+#endif -+ -+MODULE_AUTHOR("Erez Zadok <ezk@cs.sunysb.edu>"); -+MODULE_DESCRIPTION("FiST-generated mini_fo filesystem"); -+MODULE_LICENSE("GPL"); -+ -+/* MODULE_PARM(fist_debug_var, "i"); */ -+/* MODULE_PARM_DESC(fist_debug_var, "Debug level"); */ -+ -+module_init(init_mini_fo_fs) -+module_exit(exit_mini_fo_fs) ---- /dev/null -+++ b/fs/mini_fo/Makefile -@@ -0,0 +1,17 @@ -+# -+# Makefile for mini_fo 2.4 and 2.6 Linux kernels -+# -+# Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+# -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+obj-$(CONFIG_MINI_FO) := mini_fo.o -+mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o -+ -+# dependencies -+${mini_fo-objs}: mini_fo.h fist.h -+ ---- /dev/null -+++ b/fs/mini_fo/meta.c -@@ -0,0 +1,1000 @@ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif /* HAVE_CONFIG_H */ -+#include "fist.h" -+#include "mini_fo.h" -+ -+int meta_build_lists(dentry_t *dentry) -+{ -+ struct mini_fo_inode_info *inode_info; -+ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ void *buf; -+ -+ int bytes, len; -+ struct vfsmount *meta_mnt; -+ char *entry; -+ -+ inode_info = itopd(dentry->d_inode); -+ if(!(inode_info->deleted_list_size == -1 && -+ inode_info->renamed_list_size == -1)) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ Error, list(s) not virgin.\n"); -+ return -1; -+ } -+ -+ /* init our meta lists */ -+ INIT_LIST_HEAD(&inode_info->deleted_list); -+ inode_info->deleted_list_size = 0; -+ -+ INIT_LIST_HEAD(&inode_info->renamed_list); -+ inode_info->renamed_list_size = 0; -+ -+ /* might there be a META-file? */ -+ if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ dput(meta_dentry); -+ goto out_ok; -+ } -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ -+ -+ /* open META-file for reading */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x0); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR opening META file.\n"); -+ goto out_err; -+ } -+ -+ /* check if fs supports reading */ -+ if(!meta_file->f_op->read) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR, fs does not support reading.\n"); -+ goto out_err_close; -+ } -+ -+ /* allocate a page for transfering the data */ -+ buf = (void *) __get_free_page(GFP_KERNEL); -+ if(!buf) { -+ printk(KERN_CRIT "mini_fo: meta_build_lists: \ -+ ERROR, out of mem.\n"); -+ goto out_err_close; -+ } -+ meta_file->f_pos = 0; -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ do { -+ char *c; -+ bytes = meta_file->f_op->read(meta_file, buf, PAGE_SIZE, &meta_file->f_pos); -+ if(bytes == PAGE_SIZE) { -+ /* trim a cut off filename and adjust f_pos to get it next time */ -+ for(c = (char*) buf+PAGE_SIZE; -+ *c != '\n'; -+ c--, bytes--, meta_file->f_pos--); -+ } -+ entry = (char *) buf; -+ while(entry < (char *) buf+bytes) { -+ -+ char *old_path; -+ char *dir_name; -+ int old_len, new_len; -+ -+ /* len without '\n'*/ -+ len = (int) (strchr(entry, '\n') - entry); -+ switch (*entry) { -+ case 'D': -+ /* format: "D filename" */ -+ meta_list_add_d_entry(dentry, -+ entry+2, -+ len-2); -+ break; -+ case 'R': -+ /* format: "R path/xy/dir newDir" */ -+ old_path = entry+2; -+ dir_name = strchr(old_path, ' ') + 1; -+ old_len = dir_name - old_path - 1; -+ new_len = ((int) entry) + len - ((int ) dir_name); -+ meta_list_add_r_entry(dentry, -+ old_path, -+ old_len, -+ dir_name, -+ new_len); -+ break; -+ default: -+ /* unknown entry type detected */ -+ break; -+ } -+ entry += len+1; -+ } -+ -+ } while(meta_file->f_pos < meta_dentry->d_inode->i_size); -+ -+ free_page((unsigned long) buf); -+ set_fs(old_fs); -+ fput(meta_file); -+ } -+ goto out_ok; -+ -+ out_err_close: -+ fput(meta_file); -+ out_err: -+ mntput(meta_mnt); -+ dput(meta_dentry); -+ return -1; -+ out_ok: -+ return 1; /* check this!!! inode_info->wol_size; */ -+} -+ -+/* cleanups up all lists and free's the mem by dentry */ -+int meta_put_lists(dentry_t *dentry) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk("mini_fo: meta_put_lists: invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_put_lists(dentry->d_inode); -+} -+ -+/* cleanups up all lists and free's the mem by inode */ -+int __meta_put_lists(inode_t *inode) -+{ -+ int err = 0; -+ if(!inode || !itopd(inode)) { -+ printk("mini_fo: __meta_put_lists: invalid inode passed.\n"); -+ return -1; -+ } -+ err = __meta_put_d_list(inode); -+ err |= __meta_put_r_list(inode); -+ return err; -+} -+ -+int meta_sync_lists(dentry_t *dentry) -+{ -+ int err = 0; -+ if(!dentry || !dentry->d_inode) { -+ printk("mini_fo: meta_sync_lists: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ err = meta_sync_d_list(dentry, 0); -+ err |= meta_sync_r_list(dentry, 1); -+ return err; -+} -+ -+ -+/* remove all D entries from the renamed list and free the mem */ -+int __meta_put_d_list(inode_t *inode) -+{ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: __meta_put_d_list: \ -+ invalid inode passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ /* nuke the DELETED-list */ -+ if(inode_info->deleted_list_size <= 0) -+ return 0; -+ -+ while(!list_empty(&inode_info->deleted_list)) { -+ tmp = inode_info->deleted_list.next; -+ list_del(tmp); -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ kfree(del_entry->name); -+ kfree(del_entry); -+ } -+ inode_info->deleted_list_size = 0; -+ -+ return 0; -+} -+ -+/* remove all R entries from the renamed list and free the mem */ -+int __meta_put_r_list(inode_t *inode) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_put_r_list: invalid inode.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ /* nuke the RENAMED-list */ -+ if(inode_info->renamed_list_size <= 0) -+ return 0; -+ -+ while(!list_empty(&inode_info->renamed_list)) { -+ tmp = inode_info->renamed_list.next; -+ list_del(tmp); -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ } -+ inode_info->renamed_list_size = 0; -+ -+ return 0; -+} -+ -+int meta_add_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ int err = 0; -+ err = meta_list_add_d_entry(dentry, name, len); -+ err |= meta_write_d_entry(dentry,name,len); -+ return err; -+} -+ -+/* add a D entry to the deleted list */ -+int meta_list_add_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) -+ return -1; -+ -+ del_entry = (struct deleted_entry *) -+ kmalloc(sizeof(struct deleted_entry), GFP_KERNEL); -+ del_entry->name = (char*) kmalloc(len, GFP_KERNEL); -+ if(!del_entry || !del_entry->name) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_d_entry: \ -+ out of mem.\n"); -+ kfree(del_entry->name); -+ kfree(del_entry); -+ return -ENOMEM; -+ } -+ -+ strncpy(del_entry->name, name, len); -+ del_entry->len = len; -+ -+ list_add(&del_entry->list, &inode_info->deleted_list); -+ inode_info->deleted_list_size++; -+ return 0; -+} -+ -+int meta_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ int err = 0; -+ err = meta_list_add_r_entry(dentry, -+ old_name, old_len, -+ new_name, new_len); -+ err |= meta_write_r_entry(dentry, -+ old_name, old_len, -+ new_name, new_len); -+ return err; -+} -+ -+/* add a R entry to the renamed list */ -+int meta_list_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->renamed_list_size < 0) -+ return -1; -+ -+ ren_entry = (struct renamed_entry *) -+ kmalloc(sizeof(struct renamed_entry), GFP_KERNEL); -+ ren_entry->old_name = (char*) kmalloc(old_len, GFP_KERNEL); -+ ren_entry->new_name = (char*) kmalloc(new_len, GFP_KERNEL); -+ -+ if(!ren_entry || !ren_entry->old_name || !ren_entry->new_name) { -+ printk(KERN_CRIT "mini_fo: meta_list_add_r_entry: \ -+ out of mem.\n"); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ return -ENOMEM; -+ } -+ -+ strncpy(ren_entry->old_name, old_name, old_len); -+ ren_entry->old_len = old_len; -+ strncpy(ren_entry->new_name, new_name, new_len); -+ ren_entry->new_len = new_len; -+ -+ list_add(&ren_entry->list, &inode_info->renamed_list); -+ inode_info->renamed_list_size++; -+ return 0; -+} -+ -+ -+int meta_remove_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ int err = 0; -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT -+ "mini_fo: meta_remove_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ -+ err = meta_list_remove_r_entry(dentry, name, len); -+ err |= meta_sync_lists(dentry); -+ return err; -+} -+ -+int meta_list_remove_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT -+ "mini_fo: meta_list_remove_r_entry: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_list_remove_r_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_list_remove_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) -+ printk(KERN_CRIT -+ "mini_fo: __meta_list_remove_r_entry: \ -+ invalid inode passed.\n"); -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size < 0) -+ return -1; -+ if(inode_info->renamed_list_size == 0) -+ return 1; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) { -+ list_del(tmp); -+ kfree(ren_entry->new_name); -+ kfree(ren_entry->old_name); -+ kfree(ren_entry); -+ inode_info->renamed_list_size--; -+ return 0; -+ } -+ } -+ return 1; -+} -+ -+ -+/* append a single D entry to the meta file */ -+int meta_write_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ -+ int bytes, err; -+ struct vfsmount *meta_mnt = 0; -+ char *buf; -+ -+ err = 0; -+ -+ if(itopd(dentry->d_inode)->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), strlen (META_FILENAME)); -+ -+ /* We need to create a META-file */ -+ if(!meta_dentry->d_inode) { -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, -+ S_IRUSR | S_IWUSR, -+ NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, -+ S_IRUSR | S_IWUSR); -+#endif -+ } -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR opening meta file.\n"); -+ mntput(meta_mnt); /* $%& is this necessary? */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* size: len for name, 1 for \n and 2 for "D " */ -+ buf = (char *) kmalloc(len+3, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'D'; -+ buf[1] = ' '; -+ strncpy(buf+2, name, len); -+ buf[len+2] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, len+3, -+ &meta_file->f_pos); -+ if(bytes != len+3) { -+ printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ -+ ERROR writing.\n"); -+ err = -1; -+ } -+ kfree(buf); -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+/* append a single R entry to the meta file */ -+int meta_write_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len) -+{ -+ dentry_t *meta_dentry = 0; -+ file_t *meta_file = 0; -+ mm_segment_t old_fs; -+ -+ int bytes, err, buf_len; -+ struct vfsmount *meta_mnt = 0; -+ char *buf; -+ -+ -+ err = 0; -+ -+ if(itopd(dentry->d_inode)->renamed_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* build the storage structure? */ -+ if(dtopd(dentry)->state == UNMODIFIED) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen (META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ } -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ ERROR opening meta file.\n"); -+ mntput(meta_mnt); -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* size: 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ -+ buf_len = old_len + new_len + 4; -+ buf = (char *) kmalloc(buf_len, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'R'; -+ buf[1] = ' '; -+ strncpy(buf + 2, old_name, old_len); -+ buf[old_len + 2] = ' '; -+ strncpy(buf + old_len + 3, new_name, new_len); -+ buf[buf_len -1] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, buf_len, &meta_file->f_pos); -+ if(bytes != buf_len) { -+ printk(KERN_CRIT "mini_fo: meta_write_r_entry: ERROR writing.\n"); -+ err = -1; -+ } -+ -+ kfree(buf); -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+/* sync D list to disk, append data if app_flag is 1 */ -+/* check the meta_mnt, which seems not to be used (properly) */ -+ -+int meta_sync_d_list(dentry_t *dentry, int app_flag) -+{ -+ dentry_t *meta_dentry; -+ file_t *meta_file; -+ mm_segment_t old_fs; -+ -+ int bytes, err; -+ struct vfsmount *meta_mnt; -+ char *buf; -+ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ err = 0; -+ meta_file=0; -+ meta_mnt=0; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ invalid inode passed.\n"); -+ err = -1; -+ goto out; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* ok, there is something to sync */ -+ -+ /* build the storage structure? */ -+ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ app_flag = 0; -+ } -+ /* need we truncate the meta file? */ -+ if(!app_flag) { -+ struct iattr newattrs; -+ newattrs.ia_size = 0; -+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&meta_dentry->d_inode->i_mutex); -+#else -+ down(&meta_dentry->d_inode->i_sem); -+#endif -+ err = notify_change(meta_dentry, &newattrs); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&meta_dentry->d_inode->i_mutex); -+#else -+ up(&meta_dentry->d_inode->i_sem); -+#endif -+ -+ if(err || meta_dentry->d_inode->i_size != 0) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR truncating meta file.\n"); -+ goto out_err_close; -+ } -+ } -+ -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR opening meta file.\n"); -+ /* we don't mntget so we dont't mntput (for now) -+ * mntput(meta_mnt); -+ */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* here we go... */ -+ list_for_each(tmp, &inode_info->deleted_list) { -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ -+ /* size: len for name, 1 for \n and 2 for "D " */ -+ buf = (char *) kmalloc(del_entry->len+3, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ -+ buf[0] = 'D'; -+ buf[1] = ' '; -+ strncpy(buf+2, del_entry->name, del_entry->len); -+ buf[del_entry->len+2] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, -+ del_entry->len+3, -+ &meta_file->f_pos); -+ if(bytes != del_entry->len+3) { -+ printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ -+ ERROR writing.\n"); -+ err |= -1; -+ } -+ kfree(buf); -+ } -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+ -+} -+ -+int meta_sync_r_list(dentry_t *dentry, int app_flag) -+{ -+ dentry_t *meta_dentry; -+ file_t *meta_file; -+ mm_segment_t old_fs; -+ -+ int bytes, err, buf_len; -+ struct vfsmount *meta_mnt; -+ char *buf; -+ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ err = 0; -+ meta_file=0; -+ meta_mnt=0; -+ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ invalid dentry passed.\n"); -+ err = -1; -+ goto out; -+ } -+ inode_info = itopd(dentry->d_inode); -+ -+ if(inode_info->deleted_list_size < 0) { -+ err = -1; -+ goto out; -+ } -+ -+ /* ok, there is something to sync */ -+ -+ /* build the storage structure? */ -+ if(!dtohd2(dentry) && !itohi2(dentry->d_inode)) { -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ } -+ meta_dentry = lookup_one_len(META_FILENAME, -+ dtohd2(dentry), -+ strlen(META_FILENAME)); -+ if(!meta_dentry->d_inode) { -+ /* We need to create a META-file */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR, NULL); -+#else -+ vfs_create(dtohd2(dentry)->d_inode, -+ meta_dentry, S_IRUSR | S_IWUSR); -+#endif -+ app_flag = 0; -+ } -+ /* need we truncate the meta file? */ -+ if(!app_flag) { -+ struct iattr newattrs; -+ newattrs.ia_size = 0; -+ newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&meta_dentry->d_inode->i_mutex); -+#else -+ down(&meta_dentry->d_inode->i_sem); -+#endif -+ err = notify_change(meta_dentry, &newattrs); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&meta_dentry->d_inode->i_mutex); -+#else -+ up(&meta_dentry->d_inode->i_sem); -+#endif -+ if(err || meta_dentry->d_inode->i_size != 0) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR truncating meta file.\n"); -+ goto out_err_close; -+ } -+ } -+ -+ /* open META-file for writing */ -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ if(!meta_file || IS_ERR(meta_file)) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR opening meta file.\n"); -+ /* we don't mntget so we dont't mntput (for now) -+ * mntput(meta_mnt); -+ */ -+ dput(meta_dentry); -+ err = -1; -+ goto out; -+ } -+ -+ /* check if fs supports writing */ -+ if(!meta_file->f_op->write) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR, fs does not support writing.\n"); -+ goto out_err_close; -+ } -+ -+ meta_file->f_pos = meta_dentry->d_inode->i_size; /* append */ -+ old_fs = get_fs(); -+ set_fs(KERNEL_DS); -+ -+ /* here we go... */ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ /* size: -+ * 2 for "R ", old_len+new_len for names, 1 blank+1 \n */ -+ buf_len = ren_entry->old_len + ren_entry->new_len + 4; -+ buf = (char *) kmalloc(buf_len, GFP_KERNEL); -+ if (!buf) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ out of mem.\n"); -+ return -ENOMEM; -+ } -+ buf[0] = 'R'; -+ buf[1] = ' '; -+ strncpy(buf + 2, ren_entry->old_name, ren_entry->old_len); -+ buf[ren_entry->old_len + 2] = ' '; -+ strncpy(buf + ren_entry->old_len + 3, -+ ren_entry->new_name, ren_entry->new_len); -+ buf[buf_len - 1] = '\n'; -+ bytes = meta_file->f_op->write(meta_file, buf, -+ buf_len, &meta_file->f_pos); -+ if(bytes != buf_len) { -+ printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ -+ ERROR writing.\n"); -+ err |= -1; -+ } -+ kfree(buf); -+ } -+ set_fs(old_fs); -+ -+ out_err_close: -+ fput(meta_file); -+ out: -+ return err; -+} -+ -+int meta_check_d_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) -+ printk(KERN_CRIT "mini_fo: meta_check_d_dentry: \ -+ invalid dentry passed.\n"); -+ return __meta_check_d_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_check_d_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct deleted_entry *del_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) -+ printk(KERN_CRIT "mini_fo: __meta_check_d_dentry: \ -+ invalid inode passed.\n"); -+ -+ inode_info = itopd(inode); -+ -+ if(inode_info->deleted_list_size <= 0) -+ return 0; -+ -+ list_for_each(tmp, &inode_info->deleted_list) { -+ del_entry = list_entry(tmp, struct deleted_entry, list); -+ if(del_entry->len != len) -+ continue; -+ -+ if(!strncmp(del_entry->name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ -+/* -+ * check if file has been renamed and return path to orig. base dir. -+ * Implements no error return values so far, what of course sucks. -+ * String is null terminated.' -+ */ -+char* meta_check_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ -+ invalid dentry passed.\n"); -+ return NULL; -+ } -+ return __meta_check_r_entry(dentry->d_inode, name, len); -+} -+ -+char* __meta_check_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ char *old_path; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry: \ -+ invalid inode passed.\n"); -+ return NULL; -+ } -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size <= 0) -+ return NULL; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) { -+ old_path = (char *) -+ kmalloc(ren_entry->old_len+1, GFP_KERNEL); -+ strncpy(old_path, -+ ren_entry->old_name, -+ ren_entry->old_len); -+ old_path[ren_entry->old_len]='\0'; -+ return old_path; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * This version only checks if entry exists and return: -+ * 1 if exists, -+ * 0 if not, -+ * -1 if error. -+ */ -+int meta_is_r_entry(dentry_t *dentry, const char *name, int len) -+{ -+ if(!dentry || !dentry->d_inode) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ -+ invalid dentry passed.\n"); -+ return -1; -+ } -+ return __meta_is_r_entry(dentry->d_inode, name, len); -+} -+ -+int __meta_is_r_entry(inode_t *inode, const char *name, int len) -+{ -+ struct list_head *tmp; -+ struct renamed_entry *ren_entry; -+ struct mini_fo_inode_info *inode_info; -+ -+ if(!inode || !itopd(inode)) { -+ printk(KERN_CRIT "mini_fo: meta_check_r_dentry [2]: \ -+ invalid inode passed.\n"); -+ return -1; -+ } -+ inode_info = itopd(inode); -+ -+ if(inode_info->renamed_list_size <= 0) -+ return -1; -+ -+ list_for_each(tmp, &inode_info->renamed_list) { -+ ren_entry = list_entry(tmp, struct renamed_entry, list); -+ if(ren_entry->new_len != len) -+ continue; -+ -+ if(!strncmp(ren_entry->new_name, name, len)) -+ return 1; -+ } -+ return 0; -+} -+ ---- /dev/null -+++ b/fs/mini_fo/mini_fo.h -@@ -0,0 +1,510 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifndef __MINI_FO_H_ -+#define __MINI_FO_H_ -+ -+#ifdef __KERNEL__ -+ -+/* META stuff */ -+#define META_FILENAME "META_dAfFgHE39ktF3HD2sr" -+ -+/* use xattrs? */ -+#define XATTR -+ -+/* File attributes that when changed, result in a file beeing copied to storage */ -+#define COPY_FLAGS ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_SIZE -+ -+/* -+ * mini_fo filestates -+ */ -+#define MODIFIED 1 -+#define UNMODIFIED 2 -+#define CREATED 3 -+#define DEL_REWRITTEN 4 -+#define DELETED 5 -+#define NON_EXISTANT 6 -+ -+/* fist file systems superblock magic */ -+# define MINI_FO_SUPER_MAGIC 0xf15f -+ -+/* -+ * STRUCTURES: -+ */ -+ -+/* mini_fo inode data in memory */ -+struct mini_fo_inode_info { -+ inode_t *wii_inode; -+ inode_t *wii_inode2; /* pointer to storage inode */ -+ -+ /* META-data lists */ -+ /* deleted list, ex wol */ -+ struct list_head deleted_list; -+ int deleted_list_size; -+ -+ /* renamed list */ -+ struct list_head renamed_list; -+ int renamed_list_size; -+ -+ /* add other lists here ... */ -+}; -+ -+/* mini_fo dentry data in memory */ -+struct mini_fo_dentry_info { -+ dentry_t *wdi_dentry; -+ dentry_t *wdi_dentry2; /* pointer to storage dentry */ -+ unsigned int state; /* state of the mini_fo dentry */ -+}; -+ -+ -+/* mini_fo super-block data in memory */ -+struct mini_fo_sb_info { -+ super_block_t *wsi_sb, *wsi_sb2; /* mk: might point to the same sb */ -+ struct vfsmount *hidden_mnt, *hidden_mnt2; -+ dentry_t *base_dir_dentry; -+ dentry_t *storage_dir_dentry; -+ ; -+}; -+ -+/* readdir_data, readdir helper struct */ -+struct readdir_data { -+ struct list_head ndl_list; /* linked list head ptr */ -+ int ndl_size; /* list size */ -+ int sto_done; /* flag to show that the storage dir entries have -+ * all been read an now follow base entries */ -+}; -+ -+/* file private data. */ -+struct mini_fo_file_info { -+ struct file *wfi_file; -+ struct file *wfi_file2; /* pointer to storage file */ -+ struct readdir_data rd; -+}; -+ -+/* struct ndl_entry */ -+struct ndl_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/******************************** -+ * META-data structures -+ ********************************/ -+ -+/* deleted entry */ -+struct deleted_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/* renamed entry */ -+struct renamed_entry { -+ struct list_head list; -+ char *old_name; /* old directory with full path */ -+ int old_len; /* length of above string */ -+ char *new_name; /* new directory name */ -+ int new_len; /* length of above string */ -+}; -+ -+/* attr_change entry */ -+struct attr_change_entry { -+ struct list_head list; -+ char *name; -+ int len; -+}; -+ -+/* link entry */ -+struct link_entry { -+ struct list_head list; -+ int links_moved; -+ int inum_base; -+ int inum_sto; -+ char *weird_name; -+ int weird_name_len; -+}; -+ -+ -+/* Some other stuff required for mini_fo_filldir64, copied from -+ * fs/readdir.c -+ */ -+ -+#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) -+#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) -+ -+ -+struct linux_dirent64 { -+ u64 d_ino; -+ s64 d_off; -+ unsigned short d_reclen; -+ unsigned char d_type; -+ char d_name[0]; -+}; -+ -+ -+struct getdents_callback64 { -+ struct linux_dirent64 * current_dir; -+ struct linux_dirent64 * previous; -+ int count; -+ int error; -+}; -+ -+struct linux_dirent { -+ unsigned long d_ino; -+ unsigned long d_off; -+ unsigned short d_reclen; -+ char d_name[1]; -+}; -+ -+struct getdents_callback { -+ struct linux_dirent * current_dir; -+ struct linux_dirent * previous; -+ int count; -+ int error; -+}; -+ -+ -+/* -+ * MACROS: -+ */ -+ -+/* file TO private_data */ -+# define ftopd(file) ((struct mini_fo_file_info *)((file)->private_data)) -+# define __ftopd(file) ((file)->private_data) -+/* file TO hidden_file */ -+# define ftohf(file) ((ftopd(file))->wfi_file) -+# define ftohf2(file) ((ftopd(file))->wfi_file2) -+ -+/* inode TO private_data */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->i_private) -+# define __itopd(ino) ((ino)->i_private) -+#else -+# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->u.generic_ip) -+# define __itopd(ino) ((ino)->u.generic_ip) -+#endif -+/* inode TO hidden_inode */ -+# define itohi(ino) (itopd(ino)->wii_inode) -+# define itohi2(ino) (itopd(ino)->wii_inode2) -+ -+/* superblock TO private_data */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+# define stopd(super) ((struct mini_fo_sb_info *)(super)->s_fs_info) -+# define __stopd(super) ((super)->s_fs_info) -+#else -+# define stopd(super) ((struct mini_fo_sb_info *)(super)->u.generic_sbp) -+# define __stopd(super) ((super)->u.generic_sbp) -+#endif -+ -+/* unused? # define vfs2priv stopd */ -+/* superblock TO hidden_superblock */ -+ -+# define stohs(super) (stopd(super)->wsi_sb) -+# define stohs2(super) (stopd(super)->wsi_sb2) -+ -+/* dentry TO private_data */ -+# define dtopd(dentry) ((struct mini_fo_dentry_info *)(dentry)->d_fsdata) -+# define __dtopd(dentry) ((dentry)->d_fsdata) -+/* dentry TO hidden_dentry */ -+# define dtohd(dent) (dtopd(dent)->wdi_dentry) -+# define dtohd2(dent) (dtopd(dent)->wdi_dentry2) -+ -+/* dentry to state */ -+# define dtost(dent) (dtopd(dent)->state) -+# define sbt(sb) ((sb)->s_type->name) -+ -+#define IS_WRITE_FLAG(flag) (flag & (O_RDWR | O_WRONLY | O_APPEND)) -+#define IS_COPY_FLAG(flag) (flag & (COPY_FLAGS)) -+ -+/* macros to simplify non-SCA code */ -+# define MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages) -+# define MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages) -+# define FREE_PAGE_POINTERS(hidden_pages, num) -+# define FREE_PAGEDATA_POINTERS(hidden_pages_data, num) -+# define FOR_EACH_PAGE -+# define CURRENT_HIDDEN_PAGE hidden_page -+# define CURRENT_HIDDEN_PAGEDATA hidden_page_data -+# define CURRENT_HIDDEN_PAGEINDEX page->index -+ -+/* -+ * EXTERNALS: -+ */ -+extern struct file_operations mini_fo_main_fops; -+extern struct file_operations mini_fo_dir_fops; -+extern struct inode_operations mini_fo_main_iops; -+extern struct inode_operations mini_fo_dir_iops; -+extern struct inode_operations mini_fo_symlink_iops; -+extern struct super_operations mini_fo_sops; -+extern struct dentry_operations mini_fo_dops; -+extern struct vm_operations_struct mini_fo_shared_vmops; -+extern struct vm_operations_struct mini_fo_private_vmops; -+extern struct address_space_operations mini_fo_aops; -+ -+#if 0 /* unused by mini_fo */ -+extern int mini_fo_interpose(dentry_t *hidden_dentry, dentry_t *this_dentry, super_block_t *sb, int flag); -+#if defined(FIST_FILTER_DATA) || defined(FIST_FILTER_SCA) -+extern page_t *mini_fo_get1page(file_t *file, int index); -+extern int mini_fo_fill_zeros(file_t *file, page_t *page, unsigned from); -+# endif /* FIST_FILTER_DATA || FIST_FILTER_SCA */ -+ -+ -+# define mini_fo_hidden_dentry(d) __mini_fo_hidden_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) -+# define mini_fo_hidden_sto_dentry(d) __mini_fo_hidden_sto_dentry(__FILE__,__FUNCTION__,__LINE__,(d)) -+ -+extern dentry_t *__mini_fo_hidden_dentry(char *file, char *func, int line, dentry_t *this_dentry); -+extern dentry_t *__mini_fo_hidden_sto_dentry(char *file, char *func, int line, dentry_t *this_dentry); -+ -+extern int mini_fo_read_file(const char *filename, void *buf, int len); -+extern int mini_fo_write_file(const char *filename, void *buf, int len); -+extern dentry_t *fist_lookup(dentry_t *dir, const char *name, vnode_t **out, uid_t uid, gid_t gid); -+#endif /* unused by mini_fo */ -+ -+/* state transition functions */ -+extern int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag); -+extern int nondir_del_rew_to_del(dentry_t *dentry); -+extern int nondir_creat_to_del(dentry_t *dentry); -+extern int nondir_mod_to_del(dentry_t *dentry); -+extern int nondir_unmod_to_del(dentry_t *dentry); -+ -+extern int dir_unmod_to_mod(dentry_t *dentry); -+ -+/* rename specials */ -+extern int rename_directory(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); -+extern int rename_nondir(inode_t *old_dir, dentry_t *old_dentry, inode_t *new_dir, dentry_t *new_dentry); -+ -+/* misc stuff */ -+extern int mini_fo_tri_interpose(dentry_t *hidden_dentry, -+ dentry_t *hidden_sto_dentry, -+ dentry_t *dentry, -+ super_block_t *sb, int flag); -+ -+extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, -+ dentry_t *src_dentry, struct vfsmount *src_mnt); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd); -+ -+extern int create_sto_nod(dentry_t *dentry, int mode, dev_t dev); -+extern int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd); -+#else -+extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode); -+ -+extern int create_sto_nod(dentry_t *dentry, int mode, int dev); -+extern int create_sto_reg_file(dentry_t *dentry, int mode); -+#endif -+ -+extern int create_sto_dir(dentry_t *dentry, int mode); -+ -+extern int exists_in_storage(dentry_t *dentry); -+extern int is_mini_fo_existant(dentry_t *dentry); -+extern int get_neg_sto_dentry(dentry_t *dentry); -+extern int build_sto_structure(dentry_t *dir, dentry_t *dentry); -+extern int get_mini_fo_bpath(dentry_t *dentry, char **bpath, int *bpath_len); -+extern dentry_t *bpath_walk(super_block_t *sb, char *bpath); -+extern int bpath_put(dentry_t *dentry); -+ -+/* check_mini_fo types functions */ -+extern int check_mini_fo_dentry(dentry_t *dentry); -+extern int check_mini_fo_file(file_t *file); -+extern int check_mini_fo_inode(inode_t *inode); -+ -+/* General meta functions, can be called from outside of meta.c */ -+extern int meta_build_lists(dentry_t *dentry); -+extern int meta_put_lists(dentry_t *dentry); -+extern int __meta_put_lists(inode_t *inode); -+ -+extern int meta_add_d_entry(dentry_t *dentry, const char *name, int len); -+extern int meta_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_remove_r_entry(dentry_t *dentry, const char *name, int len); -+ -+extern int meta_check_d_entry(dentry_t *dentry, const char *name, int len); -+extern int __meta_check_d_entry(inode_t *inode, const char *name, int len); -+ -+extern char* meta_check_r_entry(dentry_t *dentry, const char *name, int len); -+extern char* __meta_check_r_entry(inode_t *inode, const char *name, int len); -+extern int meta_is_r_entry(dentry_t *dentry, const char *name, int len); -+extern int __meta_is_r_entry(inode_t *inode, const char *name, int len); -+ -+/* Specific meta functions, should be called only inside meta.c */ -+extern int __meta_put_d_list(inode_t *inode); -+extern int __meta_put_r_list(inode_t *inode); -+ -+extern int meta_list_add_d_entry(dentry_t *dentry, -+ const char *name, int len); -+extern int meta_list_add_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_list_remove_r_entry(dentry_t *dentry, -+ const char *name, int len); -+ -+extern int __meta_list_remove_r_entry(inode_t *inode, -+ const char *name, int len); -+ -+extern int meta_write_d_entry(dentry_t *dentry, const char *name, int len); -+extern int meta_write_r_entry(dentry_t *dentry, -+ const char *old_name, int old_len, -+ const char *new_name, int new_len); -+ -+extern int meta_sync_lists(dentry_t *dentry); -+extern int meta_sync_d_list(dentry_t *dentry, int app_flag); -+extern int meta_sync_r_list(dentry_t *dentry, int app_flag); -+ -+/* ndl stuff */ -+extern int ndl_add_entry(struct readdir_data *rd, const char *name, int len); -+extern void ndl_put_list(struct readdir_data *rd); -+extern int ndl_check_entry(struct readdir_data *rd, -+ const char *name, int len); -+ -+ -+# define copy_inode_size(dst, src) \ -+ dst->i_size = src->i_size; \ -+ dst->i_blocks = src->i_blocks; -+ -+static inline void -+fist_copy_attr_atime(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+} -+static inline void -+fist_copy_attr_times(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+} -+static inline void -+fist_copy_attr_timesizes(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+ copy_inode_size(dest, src); -+} -+static inline void -+fist_copy_attr_all(inode_t *dest, const inode_t *src) -+{ -+ ASSERT(dest != NULL); -+ ASSERT(src != NULL); -+ dest->i_mode = src->i_mode; -+ dest->i_nlink = src->i_nlink; -+ dest->i_uid = src->i_uid; -+ dest->i_gid = src->i_gid; -+ dest->i_rdev = src->i_rdev; -+ dest->i_atime = src->i_atime; -+ dest->i_mtime = src->i_mtime; -+ dest->i_ctime = src->i_ctime; -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -+ dest->i_blksize = src->i_blksize; -+#endif -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12) -+ dest->i_blkbits = src->i_blkbits; -+# endif /* linux 2.4.12 and newer */ -+ copy_inode_size(dest, src); -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) -+ dest->i_attr_flags = src->i_attr_flags; -+#else -+ dest->i_flags = src->i_flags; -+#endif -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+/* copied from linux/fs.h */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+static inline void double_lock(struct dentry *d1, struct dentry *d2) -+{ -+ struct mutex *m1 = &d1->d_inode->i_mutex; -+ struct mutex *m2 = &d2->d_inode->i_mutex; -+ if (m1 != m2) { -+ if ((unsigned long) m1 < (unsigned long) m2) { -+ struct mutex *tmp = m2; -+ m2 = m1; m1 = tmp; -+ } -+ mutex_lock(m1); -+ } -+ mutex_lock(m2); -+} -+ -+static inline void double_unlock(struct dentry *d1, struct dentry *d2) -+{ -+ struct mutex *m1 = &d1->d_inode->i_mutex; -+ struct mutex *m2 = &d2->d_inode->i_mutex; -+ mutex_unlock(m1); -+ if (m1 != m2) -+ mutex_unlock(m2); -+ dput(d1); -+ dput(d2); -+} -+ -+#else -+static inline void double_down(struct semaphore *s1, struct semaphore *s2) -+{ -+ if (s1 != s2) { -+ if ((unsigned long) s1 < (unsigned long) s2) { -+ struct semaphore *tmp = s2; -+ s2 = s1; s1 = tmp; -+ } -+ down(s1); -+ } -+ down(s2); -+} -+ -+static inline void double_up(struct semaphore *s1, struct semaphore *s2) -+{ -+ up(s1); -+ if (s1 != s2) -+ up(s2); -+} -+ -+static inline void double_lock(struct dentry *d1, struct dentry *d2) -+{ -+ double_down(&d1->d_inode->i_sem, &d2->d_inode->i_sem); -+} -+ -+static inline void double_unlock(struct dentry *d1, struct dentry *d2) -+{ -+ double_up(&d1->d_inode->i_sem,&d2->d_inode->i_sem); -+ dput(d1); -+ dput(d2); -+} -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) */ -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ -+#endif /* __KERNEL__ */ -+ -+/* -+ * Definitions for user and kernel code -+ */ -+ -+/* ioctls */ -+ -+#endif /* not __MINI_FO_H_ */ ---- /dev/null -+++ b/fs/mini_fo/mini_fo-merge -@@ -0,0 +1,180 @@ -+#!/bin/bash -+# -+# Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+BASE= -+STO= -+HELP= -+DRYRUN= -+VERBOSE= -+TMP="/tmp/" -+META_NAME="META_dAfFgHE39ktF3HD2sr" -+SKIP_DEL_LIST="skip-delete-list.mini_fo-merge" -+ -+COMMAND= -+exec_command() -+{ -+ if [ x$DRYRUN == "xset" ]; then -+ echo " would run: $COMMAND" -+ elif ! [ x$DRYRUN == "xset" ]; then -+ if [ x$VERBOSE == "xset" ]; then -+ echo " running: $COMMAND" -+ fi -+ eval $COMMAND -+ fi -+} -+ -+usage() -+{ -+cat <<EOF -+ -+USAGE: $0 -b <base dir> -s <storage dir> -+Version 0.1 -+ -+This script merges the contents of a mini_fo storage file system back -+to the base file system. -+ -+!!! Warning: This will modify the base filesystem and can destroy data -+ if used wrongly. -+ -+Options: -+ -b <base dir> -+ the directory of the base file system. -+ -+ -s <storage dir> -+ the directory of the storage file system. -+ -+ -d dry run, will not change anything and print the commands that -+ would be executed. -+ -+ -t tmp dir for storing temporary file. default: $TMP -+ -+ -v show what operations are performed. -+ -+ -h displays this message. -+ -+EOF -+} -+ -+# parse parameters -+while getopts hdvt:b:s: OPTS -+ do -+ case $OPTS in -+ h) HELP="set";; -+ d) DRYRUN="set";; -+ v) VERBOSE="set";; -+ b) BASE="$OPTARG";; -+ s) STO="$OPTARG";; -+ t) TMP="$OPTARG";; -+ ?) usage -+ exit 1;; -+ esac -+done -+ -+if [ "x$HELP" == "xset" ]; then -+ usage -+ exit -1 -+fi -+ -+if ! [ -d "$BASE" ] || ! [ -d "$STO" ]; then -+ echo -e "$0:\n Error, -s and/or -b argument missing. type $0 -h for help." -+ exit -1; -+fi -+ -+# get full paths -+pushd $STO; STO=`pwd`; popd -+pushd $BASE; BASE=`pwd`; popd -+TMP=${TMP%/} -+ -+ -+cat<<EOF -+############################################################################### -+# mini_fo-merge -+# -+# base dir: $BASE -+# storage dir: $STO -+# meta filename: $META_NAME -+# dry run: $DRYRUN -+# verbose: $VERBOSE -+# tmp files: $TMP -+############################################################################### -+ -+EOF -+ -+rm $TMP/$SKIP_DEL_LIST -+ -+# first process all renamed dirs -+echo "Merging renamed directories..." -+pushd $STO &> /dev/null -+find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^R ' | tr -s ':R' ' ' | while read ENTRY; do -+ echo "entry: $ENTRY" -+ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` -+ OLD_B_DIR=`echo $ENTRY | cut -d ' ' -f 2 | sed -e 's/\///'` -+ NEW_NAME=`echo $ENTRY | cut -d ' ' -f 3` -+ NEW_B_DIR=`echo $META_FILE | sed -e "s/$META_NAME/$NEW_NAME/" | sed -e 's/^\.\///'` -+ echo "META_FILE: $META_FILE" -+ echo "OLD_B_DIR: $OLD_B_DIR" -+ echo "NEW_NAME: $NEW_NAME" -+ echo "NEW_B_DIR: $NEW_B_DIR" -+ -+ pushd $BASE &> /dev/null -+ # remove an existing dir in storage -+ COMMAND="rm -rf $NEW_B_DIR"; exec_command -+ COMMAND="cp -R $OLD_B_DIR $NEW_B_DIR"; exec_command -+ echo "" -+ popd &> /dev/null -+ -+ # remember this dir to exclude it from deleting later -+ echo $NEW_B_DIR >> $TMP/$SKIP_DEL_LIST -+done -+ -+# delete all whiteouted files from base -+echo -e "\nDeleting whiteout'ed files from base file system..." -+find . -name $META_NAME -type f -print0 | xargs -0 -e grep -e '^D ' | sed -e 's/:D//' | while read ENTRY; do -+ META_FILE=`echo $ENTRY | cut -d ' ' -f 1` -+ DEL_NAME=`echo $ENTRY | cut -d ' ' -f 2` -+ DEL_FILE=`echo $META_FILE | sed -e "s/$META_NAME/$DEL_NAME/" | sed -e 's/^\.\///'` -+ grep -x $DEL_FILE $TMP/$SKIP_DEL_LIST &> /dev/null -+ if [ $? -ne 0 ]; then -+ pushd $BASE &> /dev/null -+ COMMAND="rm -rf $DEL_FILE"; exec_command -+ popd &> /dev/null -+ else -+ echo " excluding: $DEL_FILE as in skip-del-list." -+ fi -+done -+ -+# create all dirs and update permissions -+echo -e "\nSetting up directory structures in base file system..." -+find . -type d | sed -e 's/^\.\///' | while read DIR; do -+ PERMS=`stat -c %a $DIR` -+ DIR_UID=`stat -c %u $DIR` -+ DIR_GID=`stat -c %g $DIR` -+ pushd $BASE &> /dev/null -+ if ! [ -d $DIR ]; then -+ COMMAND="mkdir -p $DIR"; exec_command -+ fi -+ COMMAND="chmod $PERMS $DIR"; exec_command -+ COMMAND="chown $DIR_UID:$DIR_GID $DIR"; exec_command -+ popd &> /dev/null -+done -+ -+# merge all non-directory files -+echo -e "\nMerging all non-directory files...." -+for i in b c p f l s; do -+ find . -type $i | sed -e 's/^\.\///' | grep -v "$META_NAME" | while read FILE; do -+ pushd $BASE #&> /dev/null -+ COMMAND="cp -df $STO/$FILE $BASE/$FILE"; exec_command -+ popd &> /dev/null -+ done -+done -+popd &> /dev/null -+ -+#rm $TMP/$SKIP_DEL_LIST -+ -+echo "Done!" ---- /dev/null -+++ b/fs/mini_fo/mini_fo-overlay -@@ -0,0 +1,130 @@ -+#!/bin/bash -+# -+# Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> -+# This program is free software; you can redistribute it and/or -+# modify it under the terms of the GNU General Public License -+# as published by the Free Software Foundation; either version -+# 2 of the License, or (at your option) any later version. -+# -+ -+HELP= -+SUFF= -+MNTP= -+MNT_DIR="/mnt" -+STO= -+STO_DIR="/tmp" -+BASE= -+ -+usage() -+{ -+cat <<EOF -+ -+Usage: $0 [-s suffix] [-d sto_dir_dir] [-m mount point] base_dir -+Version 0.1 -+ -+This script overlays the given base directory using the mini_fo file -+system. If only the base directory base_dir is given, $0 -+will use a storage directory called "sto-<base_dir_name>" in $STO_DIR, -+and mount point "mini_fo-<base_dir_dir>" in $MNT_DIR. -+ -+Options: -+ -s <suffix> -+ add given suffix to storage directory and the mount -+ point. This is usefull for overlaying one base directory -+ several times and avoiding conflicts with storage directory -+ names and mount points. -+ -+ -d <sto_dir_dir> -+ change the directory in which the storage directory will be -+ created (default is currently "$STO_DIR". -+ -+ -m <mount point> -+ use an alternative directory to create the mini_fo -+ mountpoint (default is currently "$MNT_DIR". -+ -+ -h displays this message. -+ -+EOF -+exit 1; -+} -+ -+while getopts hm:s:d: OPTS -+ do -+ case $OPTS in -+ s) SUFF="$OPTARG";; -+ d) STO_DIR="$OPTARG";; -+ m) MNT_DIR="$OPTARG";; -+ h) HELP="set";; -+ ?) usage -+ exit 1;; -+ esac -+done -+shift $(($OPTIND - 1)) -+ -+BASE="$1" -+ -+if [ "x$HELP" == "xset" ]; then -+ usage -+ exit -1 -+fi -+ -+# fix suffix -+if [ "x$SUFF" != "x" ]; then -+ SUFF="-$SUFF" -+fi -+ -+# kill trailing slashes -+MNT_DIR=${MNT_DIR%/} -+STO_DIR=${STO_DIR%/} -+BASE=${BASE%/} -+ -+ -+if ! [ -d "$BASE" ]; then -+ echo "invalid base dir $BASE, run $0 -h for help." -+ exit -1 -+fi -+ -+# check opts -+if ! [ -d "$MNT_DIR" ]; then -+ echo "invalid mount dir $MNT_DIR, run $0 -h for help." -+ exit -1 -+fi -+ -+if ! [ -d "$STO_DIR" ]; then -+ echo "invalid sto_dir_dir $STO_DIR, run $0 -h for help." -+ exit -1 -+fi -+ -+MNTP="$MNT_DIR/mini_fo-`basename $BASE`$SUFF" -+STO="$STO_DIR/sto-`basename $BASE`$SUFF" -+ -+# create the mount point if it doesn't exist -+mkdir -p $MNTP -+if [ $? -ne 0 ]; then -+ echo "Error, failed to create mount point $MNTP" -+fi -+ -+mkdir -p $STO -+if [ $? -ne 0 ]; then -+ echo "Error, failed to create storage dir $STO" -+fi -+ -+# check if fs is already mounted -+mount | grep mini_fo | grep $MNTP &> /dev/null -+if [ $? -eq 0 ]; then -+ echo "Error, existing mini_fo mount at $MNTP." -+ exit -1 -+fi -+ -+mount | grep mini_fo | grep $STO &> /dev/null -+if [ $? -eq 0 ]; then -+ echo "Error, $STO seems to be used already." -+ exit -1 -+fi -+ -+# mount -+mount -t mini_fo -o base=$BASE,sto=$STO $BASE $MNTP -+ -+if [ $? -ne 0 ]; then -+ echo "Error, mounting failed, maybe no permisson to mount?" -+fi ---- /dev/null -+++ b/fs/mini_fo/mmap.c -@@ -0,0 +1,637 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif /* HAVE_CONFIG_H */ -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+#ifdef FIST_COUNT_WRITES -+/* for counting writes in the middle vs. regular writes */ -+unsigned long count_writes = 0, count_writes_middle = 0; -+#endif /* FIST_COUNT_WRITES */ -+ -+/* forward declaration of commit write and prepare write */ -+STATIC int mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to); -+STATIC int mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to); -+ -+ -+/* -+ * Function for handling creation of holes when lseek-ing past the -+ * end of the file and then writing some data. -+ */ -+int -+mini_fo_fill_zeros(file_t* file, page_t *page, unsigned from) -+{ -+ int err = 0; -+ dentry_t *dentry = file->f_dentry; -+ inode_t *inode = dentry->d_inode; -+ page_t *tmp_page; -+ int index; -+ -+ print_entry_location(); -+ -+ for (index = inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { -+ tmp_page = mini_fo_get1page(file, index); -+ if (IS_ERR(tmp_page)) { -+ err = PTR_ERR(tmp_page); -+ goto out; -+ } -+ -+ /* -+ * zero out rest of the contents of the page between the appropriate -+ * offsets. -+ */ -+ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); -+ -+ if (! (err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); -+ -+ page_cache_release(tmp_page); -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ -+ /* zero out appropriate parts of last page */ -+ -+ /* -+ * if the encoding type is block, then adjust the 'from' (where the -+ * zeroing will start) offset appropriately -+ */ -+ from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); -+ -+ if ((from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) { -+ -+ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, from - (inode->i_size & ~PAGE_CACHE_MASK)); -+ if (! (err = mini_fo_prepare_write(file, page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, page, 0, PAGE_CACHE_SIZE); -+ -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ -+ out: -+ print_exit_status(err); -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_writepage(page_t *page) -+{ -+ int err = -EIO; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ char *kaddr, *hidden_kaddr; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; -+ hidden_inode = itohi(inode); -+ -+ /* -+ * writepage is called when shared mmap'ed files need to write -+ * their pages, while prepare/commit_write are called from the -+ * non-paged write() interface. (However, in 2.3 the two interfaces -+ * share the same cache, while in 2.2 they didn't.) -+ * -+ * So we pretty much have to duplicate much of what commit_write does. -+ */ -+ -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+ /* get page address, and encode it */ -+ kaddr = (char *) kmap(page); -+ hidden_kaddr = (char*) kmap(hidden_page); -+ mini_fo_encode_block(kaddr, hidden_kaddr, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); -+ /* if encode_block could fail, then return error */ -+ kunmap(page); -+ kunmap(hidden_page); -+ -+ /* call lower writepage (expects locked page) */ -+ err = hidden_inode->i_mapping->a_ops->writepage(hidden_page); -+ -+ /* -+ * update mtime and ctime of lower level file system -+ * mini_fo' mtime and ctime are updated by generic_file_write -+ */ -+ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; -+ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,1) -+ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ -+# endif /* kernel older than 2.4.1 */ -+ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ -+ -+ if (err) -+ ClearPageUptodate(page); -+ else -+ SetPageUptodate(page); -+ out: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,1) -+ UnlockPage(page); -+# endif /* kernel 2.4.1 and newer */ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+/* -+ * get one page from cache or lower f/s, return error otherwise. -+ * returns unlocked, up-to-date page (if ok), with increased refcnt. -+ */ -+page_t * -+mini_fo_get1page(file_t *file, int index) -+{ -+ page_t *page; -+ dentry_t *dentry; -+ inode_t *inode; -+ struct address_space *mapping; -+ int err; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ inode = dentry->d_inode; -+ mapping = inode->i_mapping; -+ -+ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); -+ if (index < 0) { -+ printk("%s BUG: index=%d\n", __FUNCTION__, index); -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ page = read_cache_page(mapping, -+ index, -+ (filler_t *) mapping->a_ops->readpage, -+ (void *) file); -+ if (IS_ERR(page)) -+ goto out; -+ wait_on_page(page); -+ if (!Page_Uptodate(page)) { -+ lock_page(page); -+ err = mapping->a_ops->readpage(file, page); -+ if (err) { -+ page = ERR_PTR(err); -+ goto out; -+ } -+ wait_on_page(page); -+ if (!Page_Uptodate(page)) { -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ } -+ -+ out: -+ print_exit_pointer(page); -+ return page; -+} -+ -+ -+/* -+ * get one page from cache or lower f/s, return error otherwise. -+ * similar to get1page, but doesn't guarantee that it will return -+ * an unlocked page. -+ */ -+page_t * -+mini_fo_get1page_cached(file_t *file, int index) -+{ -+ page_t *page; -+ dentry_t *dentry; -+ inode_t *inode; -+ struct address_space *mapping; -+ int err; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ inode = dentry->d_inode; -+ mapping = inode->i_mapping; -+ -+ fist_dprint(8, "%s: read page index %d pid %d\n", __FUNCTION__, index, current->pid); -+ if (index < 0) { -+ printk("%s BUG: index=%d\n", __FUNCTION__, index); -+ page = ERR_PTR(-EIO); -+ goto out; -+ } -+ page = read_cache_page(mapping, -+ index, -+ (filler_t *) mapping->a_ops->readpage, -+ (void *) file); -+ if (IS_ERR(page)) -+ goto out; -+ -+ out: -+ print_exit_pointer(page); -+ return page; -+} -+ -+ -+/* -+ * readpage is called from generic_page_read and the fault handler. -+ * If your file system uses generic_page_read for the read op, it -+ * must implement readpage. -+ * -+ * Readpage expects a locked page, and must unlock it. -+ */ -+STATIC int -+mini_fo_do_readpage(file_t *file, page_t *page) -+{ -+ int err = -EIO; -+ dentry_t *dentry; -+ file_t *hidden_file = NULL; -+ dentry_t *hidden_dentry; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ char *page_data; -+ page_t *hidden_page; -+ char *hidden_page_data; -+ int real_size; -+ -+ print_entry_location(); -+ -+ dentry = file->f_dentry; /* CPW: Moved below print_entry_location */ -+ if (ftopd(file) != NULL) -+ hidden_file = ftohf(file); -+ hidden_dentry = dtohd(dentry); -+ inode = dentry->d_inode; -+ hidden_inode = itohi(inode); -+ -+ fist_dprint(7, "%s: requesting page %d from file %s\n", __FUNCTION__, page->index, dentry->d_name.name); -+ -+ MALLOC_PAGE_POINTERS(hidden_pages, num_hidden_pages); -+ MALLOC_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); -+ FOR_EACH_PAGE -+ CURRENT_HIDDEN_PAGE = NULL; -+ -+ /* find lower page (returns a locked page) */ -+ FOR_EACH_PAGE { -+ fist_dprint(8, "%s: Current page index = %d\n", __FUNCTION__, CURRENT_HIDDEN_PAGEINDEX); -+ CURRENT_HIDDEN_PAGE = read_cache_page(hidden_inode->i_mapping, -+ CURRENT_HIDDEN_PAGEINDEX, -+ (filler_t *) hidden_inode->i_mapping->a_ops->readpage, -+ (void *) hidden_file); -+ if (IS_ERR(CURRENT_HIDDEN_PAGE)) { -+ err = PTR_ERR(CURRENT_HIDDEN_PAGE); -+ CURRENT_HIDDEN_PAGE = NULL; -+ goto out_release; -+ } -+ } -+ -+ /* -+ * wait for the page data to show up -+ * (signaled by readpage as unlocking the page) -+ */ -+ FOR_EACH_PAGE { -+ wait_on_page(CURRENT_HIDDEN_PAGE); -+ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { -+ /* -+ * call readpage() again if we returned from wait_on_page with a -+ * page that's not up-to-date; that can happen when a partial -+ * page has a few buffers which are ok, but not the whole -+ * page. -+ */ -+ lock_page(CURRENT_HIDDEN_PAGE); -+ err = hidden_inode->i_mapping->a_ops->readpage(hidden_file, -+ CURRENT_HIDDEN_PAGE); -+ if (err) { -+ CURRENT_HIDDEN_PAGE = NULL; -+ goto out_release; -+ } -+ wait_on_page(CURRENT_HIDDEN_PAGE); -+ if (!Page_Uptodate(CURRENT_HIDDEN_PAGE)) { -+ err = -EIO; -+ goto out_release; -+ } -+ } -+ } -+ -+ /* map pages, get their addresses */ -+ page_data = (char *) kmap(page); -+ FOR_EACH_PAGE -+ CURRENT_HIDDEN_PAGEDATA = (char *) kmap(CURRENT_HIDDEN_PAGE); -+ -+ /* if decode_block could fail, then return error */ -+ err = 0; -+ real_size = hidden_inode->i_size - (page->index << PAGE_CACHE_SHIFT); -+ if (real_size <= 0) -+ memset(page_data, 0, PAGE_CACHE_SIZE); -+ else if (real_size < PAGE_CACHE_SIZE) { -+ mini_fo_decode_block(hidden_page_data, page_data, real_size, inode, inode->i_sb, page->index); -+ memset(page_data + real_size, 0, PAGE_CACHE_SIZE - real_size); -+ } else -+ mini_fo_decode_block(hidden_page_data, page_data, PAGE_CACHE_SIZE, inode, inode->i_sb, page->index); -+ -+ FOR_EACH_PAGE -+ kunmap(CURRENT_HIDDEN_PAGE); -+ kunmap(page); -+ -+ out_release: -+ FOR_EACH_PAGE -+ if (CURRENT_HIDDEN_PAGE) -+ page_cache_release(CURRENT_HIDDEN_PAGE); /* undo read_cache_page */ -+ -+ FREE_PAGE_POINTERS(hidden_pages, num_hidden_pages); -+ FREE_PAGEDATA_POINTERS(hidden_pages_data, num_hidden_pages); -+ -+ out: -+ if (err == 0) -+ SetPageUptodate(page); -+ else -+ ClearPageUptodate(page); -+ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_readpage(file_t *file, page_t *page) -+{ -+ int err; -+ print_entry_location(); -+ -+ err = mini_fo_do_readpage(file, page); -+ -+ /* -+ * we have to unlock our page, b/c we _might_ have gotten a locked page. -+ * but we no longer have to wakeup on our page here, b/c UnlockPage does -+ * it -+ */ -+ UnlockPage(page); -+ -+ print_exit_status(err); -+ return err; -+} -+ -+ -+STATIC int -+mini_fo_prepare_write(file_t *file, page_t *page, unsigned from, unsigned to) -+{ -+ int err = 0; -+ -+ print_entry_location(); -+ -+ /* -+ * we call kmap(page) only here, and do the kunmap -+ * and the actual downcalls, including unlockpage and uncache -+ * in commit_write. -+ */ -+ kmap(page); -+ -+ /* fast path for whole page writes */ -+ if (from == 0 && to == PAGE_CACHE_SIZE) -+ goto out; -+ /* read the page to "revalidate" our data */ -+ /* call the helper function which doesn't unlock the page */ -+ if (!Page_Uptodate(page)) -+ err = mini_fo_do_readpage(file, page); -+ -+ out: -+ print_exit_status(err); -+ return err; -+} -+ -+ -+ -+STATIC int -+mini_fo_commit_write(file_t *file, page_t *page, unsigned from, unsigned to) -+{ -+ int err = -ENOMEM; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ file_t *hidden_file = NULL; -+ loff_t pos; -+ unsigned bytes = to - from; -+ unsigned hidden_from, hidden_to, hidden_bytes; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ -+ hidden_inode = itohi(inode); -+ -+ ASSERT(file != NULL); -+ /* -+ * here we have a kmapped page, with data from the user copied -+ * into it. we need to encode_block it, and then call the lower -+ * commit_write. We also need to simulate same behavior of -+ * generic_file_write, and call prepare_write on the lower f/s first. -+ */ -+#ifdef FIST_COUNT_WRITES -+ count_writes++; -+# endif /* FIST_COUNT_WRITES */ -+ -+ /* this is append and/or extend -- we can't have holes so fill them in */ -+ if (page->index > (hidden_inode->i_size >> PAGE_CACHE_SHIFT)) { -+ page_t *tmp_page; -+ int index; -+ for (index = hidden_inode->i_size >> PAGE_CACHE_SHIFT; index < page->index; index++) { -+ tmp_page = mini_fo_get1page(file, index); -+ if (IS_ERR(tmp_page)) { -+ err = PTR_ERR(tmp_page); -+ goto out; -+ } -+ /* zero out the contents of the page at the appropriate offsets */ -+ memset((char*)page_address(tmp_page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, PAGE_CACHE_SIZE - (inode->i_size & ~PAGE_CACHE_MASK)); -+ if (!(err = mini_fo_prepare_write(file, tmp_page, 0, PAGE_CACHE_SIZE))) -+ err = mini_fo_commit_write(file, tmp_page, 0, PAGE_CACHE_SIZE); -+ page_cache_release(tmp_page); -+ if (err < 0) -+ goto out; -+ if (current->need_resched) -+ schedule(); -+ } -+ } -+ -+ if (ftopd(file) != NULL) -+ hidden_file = ftohf(file); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_inode->i_mutex); -+#else -+ down(&hidden_inode->i_sem); -+#endif -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+#if FIST_ENCODING_BLOCKSIZE > 1 -+# error encoding_blocksize greater than 1 is not yet supported -+# endif /* FIST_ENCODING_BLOCKSIZE > 1 */ -+ -+ hidden_from = from & (~(FIST_ENCODING_BLOCKSIZE - 1)); -+ hidden_to = ((to + FIST_ENCODING_BLOCKSIZE - 1) & (~(FIST_ENCODING_BLOCKSIZE - 1))); -+ if ((page->index << PAGE_CACHE_SHIFT) + to > hidden_inode->i_size) { -+ -+ /* -+ * if this call to commit_write had introduced holes and the code -+ * for handling holes was invoked, then the beginning of this page -+ * must be zeroed out -+ * zero out bytes from 'size_of_file%pagesize' to 'from'. -+ */ -+ if ((hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)) > 0) -+ memset((char*)page_address(page) + (inode->i_size & ~PAGE_CACHE_MASK), 0, hidden_from - (inode->i_size & ~PAGE_CACHE_MASK)); -+ -+ } -+ hidden_bytes = hidden_to - hidden_from; -+ -+ /* call lower prepare_write */ -+ err = -EINVAL; -+ if (hidden_inode->i_mapping && -+ hidden_inode->i_mapping->a_ops && -+ hidden_inode->i_mapping->a_ops->prepare_write) -+ err = hidden_inode->i_mapping->a_ops->prepare_write(hidden_file, -+ hidden_page, -+ hidden_from, -+ hidden_to); -+ if (err) -+ /* don't leave locked pages behind, esp. on an ENOSPC */ -+ goto out_unlock; -+ -+ fist_dprint(8, "%s: encoding %d bytes\n", __FUNCTION__, hidden_bytes); -+ mini_fo_encode_block((char *) page_address(page) + hidden_from, (char*) page_address(hidden_page) + hidden_from, hidden_bytes, inode, inode->i_sb, page->index); -+ /* if encode_block could fail, then goto unlock and return error */ -+ -+ /* call lower commit_write */ -+ err = hidden_inode->i_mapping->a_ops->commit_write(hidden_file, -+ hidden_page, -+ hidden_from, -+ hidden_to); -+ -+ if (err < 0) -+ goto out_unlock; -+ -+ err = bytes; /* convert error to no. of bytes */ -+ -+ inode->i_blocks = hidden_inode->i_blocks; -+ /* we may have to update i_size */ -+ pos = (page->index << PAGE_CACHE_SHIFT) + to; -+ if (pos > inode->i_size) -+ inode->i_size = pos; -+ -+ /* -+ * update mtime and ctime of lower level file system -+ * mini_fo' mtime and ctime are updated by generic_file_write -+ */ -+ hidden_inode->i_mtime = hidden_inode->i_ctime = CURRENT_TIME; -+ -+ mark_inode_dirty_sync(inode); -+ -+ out_unlock: -+ UnlockPage(hidden_page); -+ page_cache_release(hidden_page); -+ kunmap(page); /* kmap was done in prepare_write */ -+ out: -+ /* we must set our page as up-to-date */ -+ if (err < 0) -+ ClearPageUptodate(page); -+ else -+ SetPageUptodate(page); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_inode->i_mutex); -+#else -+ up(&hidden_inode->i_sem); -+#endif -+ print_exit_status(err); -+ return err; /* assume all is ok */ -+} -+ -+ -+STATIC int -+mini_fo_bmap(struct address_space *mapping, long block) -+{ -+ int err = 0; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ -+ print_entry_location(); -+ -+ inode = (inode_t *) mapping->host; -+ hidden_inode = itohi(inode); -+ -+ if (hidden_inode->i_mapping->a_ops->bmap) -+ err = hidden_inode->i_mapping->a_ops->bmap(hidden_inode->i_mapping, block); -+ print_exit_location(); -+ return err; -+} -+ -+ -+/* -+ * This function is copied verbatim from mm/filemap.c. -+ * XXX: It should be simply moved to some header file instead -- bug Al about it! -+ */ -+static inline int sync_page(struct page *page) -+{ -+ struct address_space *mapping = page->mapping; -+ -+ if (mapping && mapping->a_ops && mapping->a_ops->sync_page) -+ return mapping->a_ops->sync_page(page); -+ return 0; -+} -+ -+ -+/* -+ * XXX: we may not need this function if not FIST_FILTER_DATA. -+ * FIXME: for FIST_FILTER_SCA, get all lower pages and sync them each. -+ */ -+STATIC int -+mini_fo_sync_page(page_t *page) -+{ -+ int err = 0; -+ inode_t *inode; -+ inode_t *hidden_inode; -+ page_t *hidden_page; -+ -+ print_entry_location(); -+ -+ inode = page->mapping->host; /* CPW: Moved below print_entry_location */ -+ hidden_inode = itohi(inode); -+ -+ /* find lower page (returns a locked page) */ -+ hidden_page = grab_cache_page(hidden_inode->i_mapping, page->index); -+ if (!hidden_page) -+ goto out; -+ -+ err = sync_page(hidden_page); -+ -+ UnlockPage(hidden_page); /* b/c grab_cache_page locked it */ -+ page_cache_release(hidden_page); /* b/c grab_cache_page increased refcnt */ -+ -+ out: -+ print_exit_status(err); -+ return err; -+} ---- /dev/null -+++ b/fs/mini_fo/README -@@ -0,0 +1,163 @@ -+README for the mini_fo overlay file system -+========================================= -+ -+ -+WHAT IS MINI_FO? -+---------------- -+ -+mini_fo is a virtual kernel file system that can make read-only -+file systems writable. This is done by redirecting modifying operations -+to a writeable location called "storage directory", and leaving the -+original data in the "base directory" untouched. When reading, the -+file system merges the modifed and original data so that only the -+newest versions will appear. This occurs transparently to the user, -+who can access the data like on any other read-write file system. -+ -+Base and storage directories may be located on the same or on -+different partitions and may be of different file system types. While -+the storage directory obviously needs to be writable, the base may or -+may not be writable, what doesn't matter as it will no be modified -+anyway. -+ -+ -+WHAT IS GOOD FOR? -+----------------- -+ -+The primary purpose of the mini_fo file system is to allow easy -+software updates to embedded systems, that often store their root -+file system in a read-only flash file system, but there are many -+more as for example sandboxing, or for allowing live-cds to -+permanently store information. -+ -+ -+BUILDING -+-------- -+This should be simple. Adjust the Makefile to point to the correct -+kernel headers you want to build the module for. Then: -+ -+ # make -+ -+should build "mini_fo.o" for a 2.4 kernel or "mini_fo.ko" for a 2.6 -+kernel. -+ -+If you are building the module for you current kernel, you can install -+the module (as root): -+ -+ # make install -+ -+or uninstall with -+ -+ # make uninstall -+ -+ -+USING THE FILE SYSTEM -+-------------------- -+ -+the general mount syntax is: -+ -+ mount -t mini_fo -o base=<base directory>,sto=<storage directory>\ -+ <base directory> <mount point> -+ -+Example: -+ -+You have mounted a cdrom to /mnt/cdrom and want to modifiy some files -+on it: -+ -+load the module (as root) -+ -+ # insmod mini_fo.o for a 2.4 kernel or -+ -+ # insmod mini_fo.ko for a 2.6 kernel -+ -+ -+create a storage dir in tmp and a mountpoint for mini_fo: -+ -+ # mkdir /tmp/sto -+ # mkdir /mnt/mini_fo -+ -+and mount the mini_fo file system: -+ -+ # mount -t mini_fo -o base=/mnt/cdrom,sto=/tmp/sto /mnt/cdrom /mnt/mini_fo -+ -+ -+Now the data stored on the cd can be accessed via the mini_fo -+mountpoint just like any read-write file system, files can be modified -+and deleted, new ones can be created and so on. When done unmount the -+file system: -+ -+ # unmount /mnt/mini_fo -+ -+Note that if the file system is mounted again using the same storage -+file system, of course it will appear in the modified state again. If -+you remount it using an new empty storage directory, it will be -+unmodified. Therefore by executing: -+ -+ # cd /tmp/sto -+ # rm -rf * -+ -+you can nuke all the changes you made to the original file system. But -+ remember NEVER do this while the mini_fo file system is mounted! -+ -+ -+Alternatively you can use the mini_fo-overlay bash script, that -+simplifies managing mini_fo mounts. See TOOLS Section. -+ -+ -+TOOLS -+----- -+ -+mini_fo-merge (experimental): -+ -+This is a bash script that will merge changes contained in the storage -+directory back to the base directory. This allows mini_fo to function -+as a cache file system by overlaying a slow (network, ...) file system -+and using a fast (ramdisk, ...) as storage. When done, changes can be -+merged back to the (slow) base with mini_fo-merge. See "mini_fo-merge -+-h" for details. -+ -+It can be usefull for merging changes back after a successfull test -+(patches, software updates...) -+ -+ -+mini_fo-overlay: -+ -+This bash script simplifies managing one or more mini_fo mounts. For -+overlaying a directory called "basedir1", you can just call: -+ -+ # mini_fo-overlay basedir1 -+ -+This will mount mini_fo with "basedir1" as base, "/tmp/sto-basedir1/" -+as storage to "/mnt/mini_fo-basedir1/". It has more options though, -+type "mini_fo-overlay -h" for details. -+ -+ -+DOCUMENTATION, REPORTING BUGS, GETTING HELP -+------------------------------------------- -+ -+Please visit the mini_fo project page at: -+ -+http://www.denx.de/twiki/bin/view/Know/MiniFOHome -+ -+ -+WARNINGS -+-------- -+ -+Never modify the base or the storage directorys while the mini_fo -+file system is mounted, or you might crash you system. Simply accessing -+and reading should not cause any trouble. -+ -+Exporting a mini_fo mount point via NFS has not been tested, and may -+or may not work. -+ -+Check the RELEASE_NOTES for details on bugs and features. -+ -+ -+ -+Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ -+This program is free software; you can redistribute it and/or -+modify it under the terms of the GNU General Public License -+as published by the Free Software Foundation; either version -+2 of the License, or (at your option) any later version. -+ -+ ---- /dev/null -+++ b/fs/mini_fo/RELEASE_NOTES -@@ -0,0 +1,111 @@ -+Release: mini_fo-0.6.1 (v0-6-1) -+Date: 21.09.2005 -+ -+ -+Changes: -+-------- -+v0-6-1: -+ -+- bugfixes (see ChangeLog) -+ -+- two helper scripts "mini_fo_merge" and "mini_fo_overlay" (see -+ README for details). -+ -+v0-6-0: -+ -+- Support for 2.4 and 2.6 (see Makefile) -+ -+- Partial hard link support (creating works as expected, but already -+ existing links in the base file system will be treated as if they -+ were individual files). -+ -+- Various bugfixes and cleanups. -+ -+ -+v0-6-0-pre1: -+ -+- This is mini_fo-0-6-0-pre1! This release is a complete rewrite of -+ many vital mini_fo parts such as the old whiteout list code which -+ has been replaced by the new META subsystem. -+ -+- Light weight directory renaming implemented. This means if a -+ directory is renamed via the mini_fo filesystem this will no longer -+ result in a complete copy in storage, instead only one empty -+ directory will be created. All base filed contained in the original -+ directory stay there until modified. -+ -+- Special files (creating, renaming, deleting etc.) now working. -+ -+- Many bugfixes and cleanup, mini_fo is now a lot more stable. -+ -+ -+v0-5-10: -+ -+- Final release of the 0-5-* versions. Next will be a complete rewrite -+ of many features. This release contains several bugfixes related to -+ directory renaming. -+ -+ -+v0-5-10-pre6: -+ -+- Lots of cleanup and several bugfixes related to directory deleting -+ -+- Directory renaming suddenly works, what is most likely due to the -+ fact tha that "mv" is smart: if the classic rename doesn't work it -+ will assume that source and target file are on different fs and will -+ copy the directory and try to remove the source directory. Until -+ directory removing wasn't implemented, it would fail to do this and -+ rollback. -+ So, directory renaming works for now, but it doesn't yet do what you -+ would expect from a overlay fs, so use with care. -+ -+ -+v0-5-10-pre5: -+ -+- implemented directory deleting -+- made parsing of mount options more stable -+- New format of mount options! (See README) -+- I can't reproduce the unknown panic with 2.4.25 anymore, so I'll -+ happily assume it never existed! -+ -+ -+Implemented features: -+--------------------- -+ -+- creating hard links (see BUGS on already existing hard links) -+- lightweight directory renaming -+- renaming device files, pipes, sockets, etc. -+- creating, renaming, deleting of special files -+- deleting directorys -+- general directory reading (simple "ls" ) -+- creating files in existing directorys -+- creating directorys -+- renaming files. -+- reading and writing files (involves opening) -+- appending to files (creates copy in storage) -+- deleting files -+- llseek works too, what allows editors to work -+- persistency (a deleted file stay deleted over remounts) -+- use of symbolic links -+- creating of device files -+ -+ -+Not (yet) implemented features: -+------------------------------- -+ -+- full hard link support. -+ -+ -+ -+BUGS: -+----- -+ -+Hard links in the base file system will be treated as individual -+files, not as links to one inode. -+ -+The main problem with hard links isn't allowing to create them, but -+their pure existence. If you modify a base hard link, the changes made -+will only show up on this link, the other link will remain in the -+original state. I hope to fix this someday. Please note that this does -+not effect the special hard links '.' and '..', that are handled -+seperately by the lower fs. ---- /dev/null -+++ b/fs/mini_fo/state.c -@@ -0,0 +1,620 @@ -+/* -+ * Copyright (C) 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif /* HAVE_CONFIG_H */ -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+/* create the storage file, setup new states */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+int create_sto_reg_file(dentry_t *dentry, int mode, struct nameidata *nd) -+#else -+int create_sto_reg_file(dentry_t *dentry, int mode) -+#endif -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: wrong type or state.\n"); -+ err = -EINVAL; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode, nd); -+#else -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode); -+#endif -+ if(err) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file.\n"); -+ goto out_lock; -+ } -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: ERROR creating sto file [2].\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == UNMODIFIED) { -+ dtost(dentry) = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_file: invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ fist_copy_attr_timesizes(dentry->d_parent->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+/* create the sto dir, setup states */ -+int create_sto_dir(dentry_t *dentry, int mode) -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ /* had to take the "!S_ISDIR(mode))" check out, because it failed */ -+ if(exists_in_storage(dentry)) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: wrong type or state.\\ -+n"); -+ err = -EINVAL; -+ goto out; -+ } -+ -+ err = get_neg_sto_dentry(dentry); -+ if(err) { -+ err = -EINVAL; -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ err = vfs_mkdir(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ mode); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir.\n"); -+ goto out_lock; -+ } -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR creating sto dir [2].\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtopd(dentry)->state == NON_EXISTANT) { -+ dtopd(dentry)->state = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtopd(dentry)->state == UNMODIFIED) { -+ dtopd(dentry)->state = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_dir: ERROR, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ /* initalize the wol list */ -+ itopd(dentry->d_inode)->deleted_list_size = -1; -+ itopd(dentry->d_inode)->renamed_list_size = -1; -+ meta_build_lists(dentry); -+ -+ -+ out_lock: -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+int create_sto_nod(dentry_t *dentry, int mode, dev_t dev) -+#else -+int create_sto_nod(dentry_t *dentry, int mode, int dev) -+#endif -+{ -+ int err = 0; -+ inode_t *dir; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ if(exists_in_storage(dentry)) { -+ err = -EEXIST; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ dir = dentry->d_parent->d_inode; -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, hidden_sto_dentry, mode, dev); -+ if(err) -+ goto out_lock; -+ -+ if(!dtohd2(dentry)->d_inode) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: creating storage inode failed [1].\n"); -+ err = -EINVAL; /* return something indicating failure */ -+ goto out_lock; -+ } -+ -+ /* interpose the new inode */ -+ if(dtost(dentry) == DELETED) { -+ dtost(dentry) = DEL_REWRITTEN; -+ err = mini_fo_tri_interpose(NULL, hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == NON_EXISTANT) { -+ dtost(dentry) = CREATED; -+ err = mini_fo_tri_interpose(dtohd(dentry), hidden_sto_dentry, dentry, dir->i_sb, 0); -+ if(err) -+ goto out_lock; -+ } -+ else if(dtost(dentry) == UNMODIFIED) { -+ dtost(dentry) = MODIFIED; -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: create_sto_nod: error, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ } -+ -+ fist_copy_attr_timesizes(dir, hidden_sto_dir_dentry->d_inode); -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+ -+/* unimplemented (and possibly not usefull): -+ -+ nondir-del_to_del_rew -+ nondir-non_exist_to_creat -+ -+ dir-unmod_to_del -+ dir-mod_to_del -+ dir-creat_to_del -+ dir-del_rew_to_del -+ dir-del_to_del_rew -+ dir-non_exist_to_creat -+*/ -+ -+ -+/* bring a file of any type from state UNMODIFIED to MODIFIED */ -+int nondir_unmod_to_mod(dentry_t *dentry, int cp_flag) -+{ -+ int err = 0; -+ struct vfsmount *tgt_mnt; -+ struct vfsmount *src_mnt; -+ dentry_t *tgt_dentry; -+ dentry_t *src_dentry; -+ dentry_t *hidden_sto_dentry; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if((dtost(dentry) != UNMODIFIED) || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ err = get_neg_sto_dentry(dentry); -+ -+ if (err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR getting neg. sto dentry.\n"); -+ goto out; -+ } -+ -+ /* create sto file */ -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* lock parent */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ err = PTR_ERR(hidden_sto_dir_dentry); -+ if (IS_ERR(hidden_sto_dir_dentry)) -+ goto out; -+ -+ /* handle different types of nondirs */ -+ if(S_ISCHR(dentry->d_inode->i_mode) || -+ S_ISBLK(dentry->d_inode->i_mode)) { -+ err = vfs_mknod(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode, -+ dtohd(dentry)->d_inode->i_rdev); -+ } -+ -+ else if(S_ISREG(dentry->d_inode->i_mode)) { -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode, NULL); -+#else -+ err = vfs_create(hidden_sto_dir_dentry->d_inode, -+ hidden_sto_dentry, -+ dtohd(dentry)->d_inode->i_mode); -+#endif -+ } -+ if(err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR creating sto file.\n"); -+ goto out_lock; -+ } -+ -+ /* interpose on new inode */ -+ if(itohi2(dentry->d_inode) != NULL) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR, invalid inode detected.\n"); -+ err = -EINVAL; -+ goto out_lock; -+ } -+ -+ itohi2(dentry->d_inode) = igrab(dtohd2(dentry)->d_inode); -+ -+ fist_copy_attr_timesizes(dentry->d_parent->d_inode, -+ hidden_sto_dir_dentry->d_inode); -+ dtost(dentry) = MODIFIED; -+ -+ /* copy contents if regular file and cp_flag = 1 */ -+ if((cp_flag == 1) && S_ISREG(dentry->d_inode->i_mode)) { -+ -+ /* unlock first */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ dput(hidden_sto_dir_dentry); -+ -+ tgt_dentry = dtohd2(dentry); -+ tgt_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ src_dentry = dtohd(dentry); -+ src_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt; -+ -+ err = mini_fo_cp_cont(tgt_dentry, tgt_mnt, -+ src_dentry, src_mnt); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_mod: \ -+ ERROR copying contents.\n"); -+ } -+ goto out; -+ } -+ -+ out_lock: -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ out: -+ return err; -+} -+ -+/* this function is currently identical to nondir_creat_to_del */ -+int nondir_del_rew_to_del(dentry_t *dentry) -+{ -+ return nondir_creat_to_del(dentry); -+} -+ -+int nondir_creat_to_del(dentry_t *dentry) -+{ -+ int err = 0; -+ -+ inode_t *hidden_sto_dir_inode; -+ dentry_t *hidden_sto_dir_dentry; -+ dentry_t *hidden_sto_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ /* for now this function serves for both state DEL_REWRITTEN and -+ * CREATED */ -+ if(!(dtost(dentry) == CREATED || (dtost(dentry) == DEL_REWRITTEN)) || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_mod_to_del/del_rew_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was: hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry);*/ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* avoid destroying the hidden inode if the file is in use */ -+ dget(hidden_sto_dentry); -+ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ dtost(dentry) = NON_EXISTANT; -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ out: -+ return err; -+} -+ -+int nondir_mod_to_del(dentry_t *dentry) -+{ -+ int err; -+ dentry_t *hidden_sto_dentry; -+ inode_t *hidden_sto_dir_inode; -+ dentry_t *hidden_sto_dir_dentry; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != MODIFIED || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_mod_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ hidden_sto_dir_inode = itohi2(dentry->d_parent->d_inode); -+ hidden_sto_dentry = dtohd2(dentry); -+ -+ /* was hidden_sto_dir_dentry = lock_parent(hidden_sto_dentry); */ -+ hidden_sto_dir_dentry = dget(hidden_sto_dentry->d_parent); -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ down(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ -+ /* avoid destroying the hidden inode if the file is in use */ -+ dget(hidden_sto_dentry); -+ err = vfs_unlink(hidden_sto_dir_inode, hidden_sto_dentry); -+ dput(hidden_sto_dentry); -+ if(!err) -+ d_delete(hidden_sto_dentry); -+ -+ /* propagate number of hard-links */ -+ dentry->d_inode->i_nlink = itohi2(dentry->d_inode)->i_nlink; -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtost(dentry) = DELETED; -+ -+ /* add deleted file to META-file */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ -+ /* was: unlock_dir(hidden_sto_dir_dentry); */ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+#else -+ up(&hidden_sto_dir_dentry->d_inode->i_sem); -+#endif -+ dput(hidden_sto_dir_dentry); -+ -+ out: -+ return err; -+} -+ -+int nondir_unmod_to_del(dentry_t *dentry) -+{ -+ int err = 0; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != UNMODIFIED || -+ S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: nondir_unmod_to_del: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ /* next we have to get a negative dentry for the storage file */ -+ err = get_neg_sto_dentry(dentry); -+ -+ if(err) -+ goto out; -+ -+ /* add deleted file to META lists */ -+ err = meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ -+ if(err) -+ goto out; -+ -+ /* dput base dentry, this will relase the inode and free the -+ * dentry, as we will never need it again. */ -+ dput(dtohd(dentry)); -+ dtohd(dentry) = NULL; -+ dtost(dentry) = DELETED; -+ -+ out: -+ return err; -+} -+ -+/* bring a dir from state UNMODIFIED to MODIFIED */ -+int dir_unmod_to_mod(dentry_t *dentry) -+{ -+ int err; -+ -+ check_mini_fo_dentry(dentry); -+ -+ if(dtost(dentry) != UNMODIFIED || -+ !S_ISDIR(dentry->d_inode->i_mode)) { -+ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ -+ wrong type or state.\n"); -+ err = -1; -+ goto out; -+ } -+ -+ /* this creates our dir incl. sto. structure */ -+ err = build_sto_structure(dentry->d_parent, dentry); -+ if(err) { -+ printk(KERN_CRIT "mini_fo: dir_unmod_to_mod: \ -+ build_sto_structure failed.\n"); -+ goto out; -+ } -+ out: -+ return err; -+} -+ ---- /dev/null -+++ b/fs/mini_fo/super.c -@@ -0,0 +1,285 @@ -+/* -+ * Copyright (c) 1997-2003 Erez Zadok -+ * Copyright (c) 2001-2003 Stony Brook University -+ * -+ * For specific licensing information, see the COPYING file distributed with -+ * this package, or get one from ftp://ftp.filesystems.org/pub/fist/COPYING. -+ * -+ * This Copyright notice must be kept intact and distributed with all -+ * fistgen sources INCLUDING sources generated by fistgen. -+ */ -+/* -+ * Copyright (C) 2004, 2005 Markus Klotzbuecher <mk@creamnet.de> -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * $Id$ -+ */ -+ -+#ifdef HAVE_CONFIG_H -+# include <config.h> -+#endif -+ -+#include "fist.h" -+#include "mini_fo.h" -+ -+ -+STATIC void -+mini_fo_read_inode(inode_t *inode) -+{ -+ static struct address_space_operations mini_fo_empty_aops; -+ -+ __itopd(inode) = kmalloc(sizeof(struct mini_fo_inode_info), GFP_KERNEL); -+ if (!itopd(inode)) { -+ printk("<0>%s:%s:%d: No kernel memory!\n", __FILE__, __FUNCTION__, __LINE__); -+ ASSERT(NULL); -+ } -+ itohi(inode) = NULL; -+ itohi2(inode) = NULL; -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+ inode->i_version++; -+#else -+ inode->i_version = ++event; /* increment inode version */ -+#endif -+ inode->i_op = &mini_fo_main_iops; -+ inode->i_fop = &mini_fo_main_fops; -+#if 0 -+ /* -+ * XXX: To export a file system via NFS, it has to have the -+ * FS_REQUIRES_DEV flag, so turn it on. But should we inherit it from -+ * the lower file system, or can we allow our file system to be exported -+ * even if the lower one cannot be natively exported. -+ */ -+ inode->i_sb->s_type->fs_flags |= FS_REQUIRES_DEV; -+ /* -+ * OK, the above was a hack, which is now turned off because it may -+ * cause a panic/oops on some systems. The correct way to export a -+ * "nodev" filesystem is via using nfs-utils > 1.0 and the "fsid=" export -+ * parameter, which requires 2.4.20 or later. -+ */ -+#endif -+ /* I don't think ->a_ops is ever allowed to be NULL */ -+ inode->i_mapping->a_ops = &mini_fo_empty_aops; -+} -+ -+ -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+/* -+ * No need to call write_inode() on the lower inode, as it -+ * will have been marked 'dirty' anyway. But we might need -+ * to write some of our own stuff to disk. -+ */ -+STATIC void -+mini_fo_write_inode(inode_t *inode, int sync) -+{ -+ print_entry_location(); -+ print_exit_location(); -+} -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ -+ -+STATIC void -+mini_fo_put_inode(inode_t *inode) -+{ -+ /* -+ * This is really funky stuff: -+ * Basically, if i_count == 1, iput will then decrement it and this inode will be destroyed. -+ * It is currently holding a reference to the hidden inode. -+ * Therefore, it needs to release that reference by calling iput on the hidden inode. -+ * iput() _will_ do it for us (by calling our clear_inode), but _only_ if i_nlink == 0. -+ * The problem is, NFS keeps i_nlink == 1 for silly_rename'd files. -+ * So we must for our i_nlink to 0 here to trick iput() into calling our clear_inode. -+ */ -+ if (atomic_read(&inode->i_count) == 1) -+ inode->i_nlink = 0; -+} -+ -+ -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+/* -+ * we now define delete_inode, because there are two VFS paths that may -+ * destroy an inode: one of them calls clear inode before doing everything -+ * else that's needed, and the other is fine. This way we truncate the inode -+ * size (and its pages) and then clear our own inode, which will do an iput -+ * on our and the lower inode. -+ */ -+STATIC void -+mini_fo_delete_inode(inode_t *inode) -+{ -+ print_entry_location(); -+ -+ fist_checkinode(inode, "mini_fo_delete_inode IN"); -+ inode->i_size = 0; /* every f/s seems to do that */ -+ clear_inode(inode); -+ -+ print_exit_location(); -+} -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ -+ -+/* final actions when unmounting a file system */ -+STATIC void -+mini_fo_put_super(super_block_t *sb) -+{ -+ if (stopd(sb)) { -+ mntput(stopd(sb)->hidden_mnt); -+ mntput(stopd(sb)->hidden_mnt2); -+ -+ /* mk: no! dput(stopd(sb)->base_dir_dentry); -+ dput(stopd(sb)->storage_dir_dentry); */ -+ -+ kfree(stopd(sb)); -+ __stopd(sb) = NULL; -+ } -+} -+ -+ -+#ifdef NOT_NEEDED -+/* -+ * This is called in do_umount before put_super. -+ * The superblock lock is not held yet. -+ * We probably do not need to define this or call write_super -+ * on the hidden_sb, because sync_supers() will get to hidden_sb -+ * sooner or later. But it is also called from file_fsync()... -+ */ -+STATIC void -+mini_fo_write_super(super_block_t *sb) -+{ -+ return; -+} -+#endif /* NOT_NEEDED */ -+ -+ -+STATIC int -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_statfs(struct dentry *d, struct kstatfs *buf) -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+mini_fo_statfs(super_block_t *sb, struct kstatfs *buf) -+#else -+mini_fo_statfs(super_block_t *sb, struct statfs *buf) -+#endif -+{ -+ int err = 0; -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+ struct dentry *hidden_d; -+ -+ hidden_d = dtohd(d); -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) -+ err = statfs_by_dentry(hidden_d, buf); -+#else -+ err = vfs_statfs(hidden_d, buf); -+#endif -+#else -+ super_block_t *hidden_sb; -+ -+ hidden_sb = stohs(sb); -+ err = vfs_statfs(hidden_sb, buf); -+#endif -+ -+ return err; -+} -+ -+ -+/* -+ * XXX: not implemented. This is not allowed yet. -+ * Should we call this on the hidden_sb? Probably not. -+ */ -+STATIC int -+mini_fo_remount_fs(super_block_t *sb, int *flags, char *data) -+{ -+ //printk(KERN_CRIT "mini_fo_remount_fs: WARNING, this function is umimplemented.\n"); -+ return -ENOSYS; -+} -+ -+ -+/* -+ * 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 -+mini_fo_clear_inode(inode_t *inode) -+{ -+ /* -+ * Decrement a reference to a hidden_inode, which was incremented -+ * by our read_inode when it was created initially. -+ */ -+ -+ /* release the wol_list */ -+ if(S_ISDIR(inode->i_mode)) { -+ __meta_put_lists(inode); -+ } -+ -+ /* mk: fan out fun */ -+ if(itohi(inode)) -+ iput(itohi(inode)); -+ if(itohi2(inode)) -+ iput(itohi2(inode)); -+ -+ // XXX: why this assertion fails? -+ // because it doesn't like us -+ // ASSERT((inode->i_state & I_DIRTY) == 0); -+ kfree(itopd(inode)); -+ __itopd(inode) = NULL; -+} -+ -+ -+/* -+ * Called in do_umount() if the MNT_FORCE flag was used and this -+ * function is defined. See comment in linux/fs/super.c:do_umount(). -+ * 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. -+ * -+ * PS. I wonder if this is somehow useful to undo damage that was -+ * left in the kernel after a user level file server (such as amd) -+ * dies. -+ */ -+STATIC void -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+mini_fo_umount_begin(struct vfsmount *mnt, int flags) -+{ -+ struct vfsmount *hidden_mnt; -+ -+ hidden_mnt = stopd(mnt->mnt_sb)->hidden_mnt; -+ -+ if (hidden_mnt->mnt_sb->s_op->umount_begin) -+ hidden_mnt->mnt_sb->s_op->umount_begin(hidden_mnt, flags); -+ -+} -+#else -+mini_fo_umount_begin(super_block_t *sb) -+{ -+ super_block_t *hidden_sb; -+ -+ hidden_sb = stohs(sb); -+ -+ if (hidden_sb->s_op->umount_begin) -+ hidden_sb->s_op->umount_begin(hidden_sb); -+ -+} -+#endif -+ -+ -+struct super_operations mini_fo_sops = -+{ -+ read_inode: mini_fo_read_inode, -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+ write_inode: mini_fo_write_inode, -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ put_inode: mini_fo_put_inode, -+#if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -+ delete_inode: mini_fo_delete_inode, -+#endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+ put_super: mini_fo_put_super, -+ statfs: mini_fo_statfs, -+ remount_fs: mini_fo_remount_fs, -+ clear_inode: mini_fo_clear_inode, -+ umount_begin: mini_fo_umount_begin, -+}; ---- /dev/null -+++ b/fs/mini_fo/Kconfig -@@ -0,0 +1,3 @@ -+config MINI_FO -+ tristate "Mini fanout overlay filesystem" -+ diff --git a/target/linux/generic/patches-2.6.37/209-overlayfs.patch b/target/linux/generic/patches-2.6.37/209-overlayfs.patch new file mode 100644 index 000000000..386f99ef4 --- /dev/null +++ b/target/linux/generic/patches-2.6.37/209-overlayfs.patch @@ -0,0 +1,2787 @@ +--- a/fs/open.c ++++ b/fs/open.c +@@ -651,19 +651,19 @@ static inline int __get_file_write_acces + return error; + } + +-static struct file *__dentry_open(struct dentry *dentry, struct vfsmount *mnt, +- struct file *f, ++static struct file *__dentry_open(struct path *path, struct file *f, + int (*open)(struct inode *, struct file *), + const struct cred *cred) + { + struct inode *inode; + int error; + ++ path_get(path); + f->f_mode = OPEN_FMODE(f->f_flags) | FMODE_LSEEK | + FMODE_PREAD | FMODE_PWRITE; +- inode = dentry->d_inode; ++ inode = path->dentry->d_inode; + if (f->f_mode & FMODE_WRITE) { +- error = __get_file_write_access(inode, mnt); ++ error = __get_file_write_access(inode, path->mnt); + if (error) + goto cleanup_file; + if (!special_file(inode->i_mode)) +@@ -671,8 +671,7 @@ static struct file *__dentry_open(struct + } + + f->f_mapping = inode->i_mapping; +- f->f_path.dentry = dentry; +- f->f_path.mnt = mnt; ++ f->f_path = *path; + f->f_pos = 0; + f->f_op = fops_get(inode->i_fop); + file_sb_list_add(f, inode->i_sb); +@@ -718,7 +717,7 @@ cleanup_all: + * here, so just reset the state. + */ + file_reset_write(f); +- mnt_drop_write(mnt); ++ mnt_drop_write(path->mnt); + } + } + file_sb_list_del(f); +@@ -726,8 +725,7 @@ cleanup_all: + f->f_path.mnt = NULL; + cleanup_file: + put_filp(f); +- dput(dentry); +- mntput(mnt); ++ path_put(path); + return ERR_PTR(error); + } + +@@ -753,14 +751,14 @@ cleanup_file: + struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)) + { ++ struct path path = { .dentry = dentry, .mnt = nd->path.mnt }; + const struct cred *cred = current_cred(); + + if (IS_ERR(nd->intent.open.file)) + goto out; + if (IS_ERR(dentry)) + goto out_err; +- nd->intent.open.file = __dentry_open(dget(dentry), mntget(nd->path.mnt), +- nd->intent.open.file, ++ nd->intent.open.file = __dentry_open(&path, nd->intent.open.file, + open, cred); + out: + return nd->intent.open.file; +@@ -787,10 +785,17 @@ struct file *nameidata_to_filp(struct na + filp = nd->intent.open.file; + /* Has the filesystem initialised the file for us? */ + if (filp->f_path.dentry == NULL) { +- path_get(&nd->path); +- filp = __dentry_open(nd->path.dentry, nd->path.mnt, filp, +- NULL, cred); ++ struct inode *inode = nd->path.dentry->d_inode; ++ ++ if (inode->i_op->open) { ++ int flags = filp->f_flags; ++ put_filp(filp); ++ filp = inode->i_op->open(nd->path.dentry, flags, cred); ++ } else { ++ filp = __dentry_open(&nd->path, filp, NULL, cred); ++ } + } ++ + return filp; + } + +@@ -801,35 +806,45 @@ struct file *nameidata_to_filp(struct na + struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags, + const struct cred *cred) + { +- int error; ++ struct path path = { .dentry = dentry, .mnt = mnt }; ++ struct file *ret; ++ ++ BUG_ON(!mnt); ++ ++ ret = vfs_open(&path, flags, cred); ++ path_put(&path); ++ ++ return ret; ++} ++EXPORT_SYMBOL(dentry_open); ++ ++/** ++ * vfs_open - open the file at the given path ++ * @path: path to open ++ * @flags: open flags ++ * @cred: credentials to use ++ * ++ * Open the file. If successful, the returned file will have acquired ++ * an additional reference for path. ++ */ ++struct file *vfs_open(struct path *path, int flags, const struct cred *cred) ++{ + struct file *f; ++ struct inode *inode = path->dentry->d_inode; + + validate_creds(cred); + +- /* +- * We must always pass in a valid mount pointer. Historically +- * callers got away with not passing it, but we must enforce this at +- * the earliest possible point now to avoid strange problems deep in the +- * filesystem stack. +- */ +- if (!mnt) { +- printk(KERN_WARNING "%s called with NULL vfsmount\n", __func__); +- dump_stack(); +- return ERR_PTR(-EINVAL); +- } ++ if (inode->i_op->open) ++ return inode->i_op->open(path->dentry, flags, cred); + +- error = -ENFILE; + f = get_empty_filp(); +- if (f == NULL) { +- dput(dentry); +- mntput(mnt); +- return ERR_PTR(error); +- } ++ if (f == NULL) ++ return ERR_PTR(-ENFILE); + + f->f_flags = flags; +- return __dentry_open(dentry, mnt, f, NULL, cred); ++ return __dentry_open(path, f, NULL, cred); + } +-EXPORT_SYMBOL(dentry_open); ++EXPORT_SYMBOL(vfs_open); + + static void __put_unused_fd(struct files_struct *files, unsigned int fd) + { +--- a/include/linux/fs.h ++++ b/include/linux/fs.h +@@ -1574,6 +1574,7 @@ struct inode_operations { + loff_t len); + int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); ++ struct file *(*open)(struct dentry *, int flags, const struct cred *); + }; + + struct seq_file; +@@ -1975,6 +1976,7 @@ extern int do_fallocate(struct file *fil + extern long do_sys_open(int dfd, const char __user *filename, int flags, + int mode); + extern struct file *filp_open(const char *, int, int); ++extern struct file *vfs_open(struct path *, int flags, const struct cred *); + extern struct file * dentry_open(struct dentry *, struct vfsmount *, int, + const struct cred *); + extern int filp_close(struct file *, fl_owner_t id); +--- a/fs/splice.c ++++ b/fs/splice.c +@@ -1307,6 +1307,7 @@ long do_splice_direct(struct file *in, l + + return ret; + } ++EXPORT_SYMBOL(do_splice_direct); + + static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, + struct pipe_inode_info *opipe, +--- a/fs/namespace.c ++++ b/fs/namespace.c +@@ -1307,6 +1307,23 @@ void drop_collected_mounts(struct vfsmou + release_mounts(&umount_list); + } + ++struct vfsmount *clone_private_mount(struct path *path) ++{ ++ struct vfsmount *mnt; ++ ++ if (IS_MNT_UNBINDABLE(path->mnt)) ++ return ERR_PTR(-EINVAL); ++ ++ down_read(&namespace_sem); ++ mnt = clone_mnt(path->mnt, path->dentry, CL_PRIVATE); ++ up_read(&namespace_sem); ++ if (!mnt) ++ return ERR_PTR(-ENOMEM); ++ ++ return mnt; ++} ++EXPORT_SYMBOL_GPL(clone_private_mount); ++ + int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, + struct vfsmount *root) + { +--- a/include/linux/mount.h ++++ b/include/linux/mount.h +@@ -121,6 +121,9 @@ static inline void mntput(struct vfsmoun + } + } + ++struct path; ++extern struct vfsmount *clone_private_mount(struct path *path); ++ + extern struct vfsmount *do_kern_mount(const char *fstype, int flags, + const char *name, void *data); + +--- a/fs/Kconfig ++++ b/fs/Kconfig +@@ -64,6 +64,7 @@ source "fs/quota/Kconfig" + + source "fs/autofs4/Kconfig" + source "fs/fuse/Kconfig" ++source "fs/overlayfs/Kconfig" + + config CUSE + tristate "Character device in Userspace support" +--- a/fs/Makefile ++++ b/fs/Makefile +@@ -103,6 +103,7 @@ obj-$(CONFIG_QNX4FS_FS) += qnx4/ + obj-$(CONFIG_AUTOFS4_FS) += autofs4/ + obj-$(CONFIG_ADFS_FS) += adfs/ + obj-$(CONFIG_FUSE_FS) += fuse/ ++obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/ + obj-$(CONFIG_UDF_FS) += udf/ + obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ + obj-$(CONFIG_OMFS_FS) += omfs/ +--- /dev/null ++++ b/fs/overlayfs/Kconfig +@@ -0,0 +1,4 @@ ++config OVERLAYFS_FS ++ tristate "Overlay filesystem support" ++ help ++ Add support for overlay filesystem. +--- /dev/null ++++ b/fs/overlayfs/Makefile +@@ -0,0 +1,5 @@ ++# ++# Makefile for the overlay filesystem. ++# ++ ++obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o +--- /dev/null ++++ b/fs/overlayfs/overlayfs.c +@@ -0,0 +1,2358 @@ ++#include <linux/fs.h> ++#include <linux/namei.h> ++#include <linux/sched.h> ++#include <linux/fs_struct.h> ++#include <linux/file.h> ++#include <linux/xattr.h> ++#include <linux/security.h> ++#include <linux/device_cgroup.h> ++#include <linux/mount.h> ++#include <linux/splice.h> ++#include <linux/slab.h> ++#include <linux/parser.h> ++#include <linux/module.h> ++#include <linux/uaccess.h> ++#include <linux/rbtree.h> ++ ++MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>"); ++MODULE_DESCRIPTION("Overlay filesystem"); ++MODULE_LICENSE("GPL"); ++ ++#define OVL_COPY_UP_CHUNK_SIZE (1 << 20) ++ ++struct ovl_fs { ++ struct vfsmount *upper_mnt; ++ struct vfsmount *lower_mnt; ++}; ++ ++struct ovl_entry { ++ struct dentry *__upperdentry; ++ struct dentry *lowerdentry; ++ union { ++ struct { ++ u64 version; ++ bool opaque; ++ }; ++ struct rcu_head rcu; ++ }; ++}; ++ ++static const char *ovl_whiteout_xattr = "trusted.overlay.whiteout"; ++static const char *ovl_opaque_xattr = "trusted.overlay.opaque"; ++static const char *ovl_whiteout_symlink = "(overlay-whiteout)"; ++ ++enum ovl_path_type { ++ OVL_PATH_UPPER, ++ OVL_PATH_MERGE, ++ OVL_PATH_LOWER, ++}; ++ ++static enum ovl_path_type ovl_path_type(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ if (oe->__upperdentry) { ++ if (oe->lowerdentry && S_ISDIR(dentry->d_inode->i_mode)) ++ return OVL_PATH_MERGE; ++ else ++ return OVL_PATH_UPPER; ++ } else { ++ return OVL_PATH_LOWER; ++ } ++} ++ ++static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe) ++{ ++ struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry); ++ smp_read_barrier_depends(); ++ return upperdentry; ++} ++ ++static void ovl_path_upper(struct dentry *dentry, struct path *path) ++{ ++ struct ovl_fs *ofs = dentry->d_sb->s_fs_info; ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ path->mnt = ofs->upper_mnt; ++ path->dentry = ovl_upperdentry_dereference(oe); ++} ++ ++static void ovl_path_lower(struct dentry *dentry, struct path *path) ++{ ++ struct ovl_fs *ofs = dentry->d_sb->s_fs_info; ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ path->mnt = ofs->lower_mnt; ++ path->dentry = oe->lowerdentry; ++} ++ ++static enum ovl_path_type ovl_path_real(struct dentry *dentry, ++ struct path *path) ++{ ++ ++ enum ovl_path_type type = ovl_path_type(dentry); ++ ++ if (type == OVL_PATH_LOWER) ++ ovl_path_lower(dentry, path); ++ else ++ ovl_path_upper(dentry, path); ++ ++ return type; ++} ++ ++static struct dentry *ovl_dentry_upper(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ return ovl_upperdentry_dereference(oe); ++} ++ ++static struct dentry *ovl_dentry_lower(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ return oe->lowerdentry; ++} ++ ++static struct dentry *ovl_dentry_real(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ struct dentry *realdentry; ++ ++ realdentry = ovl_upperdentry_dereference(oe); ++ if (!realdentry) ++ realdentry = oe->lowerdentry; ++ ++ return realdentry; ++} ++ ++static bool ovl_dentry_is_opaque(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ return oe->opaque; ++} ++ ++static void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ oe->opaque = opaque; ++} ++ ++static void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&upperdentry->d_parent->d_inode->i_mutex)); ++ WARN_ON(oe->__upperdentry); ++ smp_wmb(); ++ oe->__upperdentry = upperdentry; ++} ++ ++static void ovl_dentry_version_inc(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); ++ oe->version++; ++} ++ ++static u64 ovl_dentry_version_get(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ WARN_ON(!mutex_is_locked(&dentry->d_inode->i_mutex)); ++ return oe->version; ++} ++ ++static bool ovl_is_whiteout(struct dentry *dentry) ++{ ++ int res; ++ char val; ++ ++ if (!dentry) ++ return false; ++ if (!dentry->d_inode) ++ return false; ++ if (!S_ISLNK(dentry->d_inode->i_mode)) ++ return false; ++ ++ res = vfs_getxattr(dentry, ovl_whiteout_xattr, &val, 1); ++ if (res == 1 && val == 'y') ++ return true; ++ ++ return false; ++} ++ ++static bool ovl_is_opaquedir(struct dentry *dentry) ++{ ++ int res; ++ char val; ++ ++ if (!S_ISDIR(dentry->d_inode->i_mode)) ++ return false; ++ ++ res = vfs_getxattr(dentry, ovl_opaque_xattr, &val, 1); ++ if (res == 1 && val == 'y') ++ return true; ++ ++ return false; ++} ++ ++struct ovl_cache_entry { ++ const char *name; ++ unsigned int len; ++ unsigned int type; ++ u64 ino; ++ bool is_whiteout; ++ struct list_head l_node; ++ struct rb_node node; ++}; ++ ++struct ovl_readdir_data { ++ struct rb_root *root; ++ struct list_head *list; ++ struct list_head *middle; ++ struct dentry *dir; ++ int count; ++ int err; ++}; ++ ++struct ovl_dir_file { ++ bool is_real; ++ bool is_cached; ++ struct list_head cursor; ++ u64 cache_version; ++ struct list_head cache; ++ struct file *realfile; ++}; ++ ++static struct ovl_cache_entry *ovl_cache_entry_from_node(struct rb_node *n) ++{ ++ return container_of(n, struct ovl_cache_entry, node); ++} ++ ++static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, ++ const char *name, int len) ++{ ++ struct rb_node *node = root->rb_node; ++ int cmp; ++ ++ while (node) { ++ struct ovl_cache_entry *p = ovl_cache_entry_from_node(node); ++ ++ cmp = strncmp(name, p->name, len); ++ if (cmp > 0) ++ node = p->node.rb_right; ++ else if (cmp < 0 || len < p->len) ++ node = p->node.rb_left; ++ else ++ return p; ++ } ++ ++ return NULL; ++} ++ ++static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, ++ u64 ino, unsigned int d_type, ++ bool is_whiteout) ++{ ++ struct ovl_cache_entry *p; ++ ++ p = kmalloc(sizeof(*p) + len + 1, GFP_KERNEL); ++ if (p) { ++ char *name_copy = (char *) (p + 1); ++ memcpy(name_copy, name, len); ++ name_copy[len] = '\0'; ++ p->name = name_copy; ++ p->len = len; ++ p->type = d_type; ++ p->ino = ino; ++ p->is_whiteout = is_whiteout; ++ } ++ ++ return p; ++} ++ ++static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, ++ const char *name, int len, u64 ino, ++ unsigned int d_type, bool is_whiteout) ++{ ++ struct rb_node **newp = &rdd->root->rb_node; ++ struct rb_node *parent = NULL; ++ struct ovl_cache_entry *p; ++ ++ while (*newp) { ++ int cmp; ++ struct ovl_cache_entry *tmp; ++ ++ parent = *newp; ++ tmp = ovl_cache_entry_from_node(*newp); ++ cmp = strncmp(name, tmp->name, len); ++ if (cmp > 0) ++ newp = &tmp->node.rb_right; ++ else if (cmp < 0 || len < tmp->len) ++ newp = &tmp->node.rb_left; ++ else ++ return 0; ++ } ++ ++ p = ovl_cache_entry_new(name, len, ino, d_type, is_whiteout); ++ if (p == NULL) ++ return -ENOMEM; ++ ++ list_add_tail(&p->l_node, rdd->list); ++ rb_link_node(&p->node, parent, newp); ++ rb_insert_color(&p->node, rdd->root); ++ ++ return 0; ++} ++ ++static int ovl_fill_lower(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct ovl_readdir_data *rdd = buf; ++ struct ovl_cache_entry *p; ++ ++ rdd->count++; ++ p = ovl_cache_entry_find(rdd->root, name, namelen); ++ if (p) { ++ list_move_tail(&p->l_node, rdd->middle); ++ } else { ++ p = ovl_cache_entry_new(name, namelen, ino, d_type, false); ++ if (p == NULL) ++ rdd->err = -ENOMEM; ++ else ++ list_add_tail(&p->l_node, rdd->middle); ++ } ++ ++ return rdd->err; ++} ++ ++static void ovl_cache_free(struct list_head *list) ++{ ++ struct ovl_cache_entry *p; ++ struct ovl_cache_entry *n; ++ ++ list_for_each_entry_safe(p, n, list, l_node) ++ kfree(p); ++ ++ INIT_LIST_HEAD(list); ++} ++ ++static int ovl_fill_upper(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct ovl_readdir_data *rdd = buf; ++ bool is_whiteout = false; ++ ++ rdd->count++; ++ if (d_type == DT_LNK) { ++ struct dentry *dentry; ++ ++ dentry = lookup_one_len(name, rdd->dir, namelen); ++ if (IS_ERR(dentry)) { ++ rdd->err = PTR_ERR(dentry); ++ goto out; ++ } ++ is_whiteout = ovl_is_whiteout(dentry); ++ dput(dentry); ++ } ++ ++ rdd->err = ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type, ++ is_whiteout); ++ ++out: ++ return rdd->err; ++} ++ ++static int ovl_dir_read(struct path *realpath, struct ovl_readdir_data *rdd, ++ filldir_t filler) ++{ ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ struct file *realfile; ++ int err; ++ ++ realfile = vfs_open(realpath, O_RDONLY | O_DIRECTORY, current_cred()); ++ if (IS_ERR(realfile)) ++ return PTR_ERR(realfile); ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (override_cred) { ++ /* ++ * CAP_SYS_ADMIN for getxattr ++ * CAP_DAC_OVERRIDE for lookup and unlink ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ old_cred = override_creds(override_cred); ++ ++ do { ++ rdd->count = 0; ++ rdd->err = 0; ++ err = vfs_readdir(realfile, filler, rdd); ++ if (err >= 0) ++ err = rdd->err; ++ } while (!err && rdd->count); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ } ++ fput(realfile); ++ ++ if (err) { ++ if (rdd->list) ++ ovl_cache_free(rdd->list); ++ return err; ++ } ++ ++ return 0; ++} ++ ++static void ovl_dir_reset(struct file *file) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ enum ovl_path_type type = ovl_path_type(file->f_path.dentry); ++ ++ if (ovl_dentry_version_get(file->f_path.dentry) != od->cache_version) { ++ list_del_init(&od->cursor); ++ ovl_cache_free(&od->cache); ++ od->is_cached = false; ++ } ++ WARN_ON(!od->is_real && type != OVL_PATH_MERGE); ++ if (od->is_real && type == OVL_PATH_MERGE) { ++ fput(od->realfile); ++ od->realfile = NULL; ++ od->is_real = false; ++ } ++} ++ ++static int ovl_dir_read_merged(struct path *upperpath, struct path *lowerpath, ++ struct ovl_readdir_data *rdd) ++{ ++ int err; ++ struct rb_root root = RB_ROOT; ++ struct list_head middle; ++ ++ rdd->root = &root; ++ if (upperpath->dentry) { ++ rdd->dir = upperpath->dentry; ++ err = ovl_dir_read(upperpath, rdd, ovl_fill_upper); ++ if (err) ++ goto out; ++ } ++ /* ++ * Insert lowerpath entries before upperpath ones, this allows ++ * offsets to be reasonably constant ++ */ ++ list_add(&middle, rdd->list); ++ rdd->middle = &middle; ++ err = ovl_dir_read(lowerpath, rdd, ovl_fill_lower); ++ list_del(&middle); ++out: ++ rdd->root = NULL; ++ ++ return err; ++} ++ ++static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) ++{ ++ struct list_head *l; ++ loff_t off; ++ ++ l = od->cache.next; ++ for (off = 0; off < pos; off++) { ++ if (l == &od->cache) ++ break; ++ l = l->next; ++ } ++ list_move_tail(&od->cursor, l); ++} ++ ++static int ovl_readdir(struct file *file, void *buf, filldir_t filler) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ int res; ++ ++ if (!file->f_pos) ++ ovl_dir_reset(file); ++ ++ if (od->is_real) { ++ res = vfs_readdir(od->realfile, filler, buf); ++ file->f_pos = od->realfile->f_pos; ++ ++ return res; ++ } ++ ++ if (!od->is_cached) { ++ struct path lowerpath; ++ struct path upperpath; ++ struct ovl_readdir_data rdd = { .list = &od->cache }; ++ ++ ovl_path_lower(file->f_path.dentry, &lowerpath); ++ ovl_path_upper(file->f_path.dentry, &upperpath); ++ ++ res = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd); ++ if (res) ++ return res; ++ ++ od->cache_version = ovl_dentry_version_get(file->f_path.dentry); ++ od->is_cached = true; ++ ++ ovl_seek_cursor(od, file->f_pos); ++ } ++ ++ while (od->cursor.next != &od->cache) { ++ int over; ++ loff_t off; ++ struct ovl_cache_entry *p; ++ ++ p = list_entry(od->cursor.next, struct ovl_cache_entry, l_node); ++ off = file->f_pos; ++ file->f_pos++; ++ list_move(&od->cursor, &p->l_node); ++ ++ if (p->is_whiteout) ++ continue; ++ ++ over = filler(buf, p->name, p->len, off, p->ino, p->type); ++ if (over) ++ break; ++ } ++ ++ return 0; ++} ++ ++static loff_t ovl_dir_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t res; ++ struct ovl_dir_file *od = file->private_data; ++ ++ mutex_lock(&file->f_dentry->d_inode->i_mutex); ++ if (!file->f_pos) ++ ovl_dir_reset(file); ++ ++ if (od->is_real) { ++ res = vfs_llseek(od->realfile, offset, origin); ++ file->f_pos = od->realfile->f_pos; ++ } else { ++ res = -EINVAL; ++ ++ switch (origin) { ++ case SEEK_CUR: ++ offset += file->f_pos; ++ break; ++ case SEEK_SET: ++ break; ++ default: ++ goto out_unlock; ++ } ++ if (offset < 0) ++ goto out_unlock; ++ ++ if (offset != file->f_pos) { ++ file->f_pos = offset; ++ if (od->is_cached) ++ ovl_seek_cursor(od, offset); ++ } ++ res = offset; ++ } ++out_unlock: ++ mutex_unlock(&file->f_dentry->d_inode->i_mutex); ++ ++ return res; ++} ++ ++static int ovl_dir_fsync(struct file *file, int datasync) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ ++ /* May need to reopen directory if it got copied up */ ++ if (!od->realfile) { ++ struct path upperpath; ++ ++ ovl_path_upper(file->f_path.dentry, &upperpath); ++ od->realfile = vfs_open(&upperpath, O_RDONLY, current_cred()); ++ if (IS_ERR(od->realfile)) ++ return PTR_ERR(od->realfile); ++ } ++ ++ return vfs_fsync(od->realfile, datasync); ++} ++ ++static int ovl_dir_release(struct inode *inode, struct file *file) ++{ ++ struct ovl_dir_file *od = file->private_data; ++ ++ list_del(&od->cursor); ++ ovl_cache_free(&od->cache); ++ if (od->realfile) ++ fput(od->realfile); ++ kfree(od); ++ ++ return 0; ++} ++ ++static int ovl_dir_open(struct inode *inode, struct file *file) ++{ ++ struct path realpath; ++ struct file *realfile; ++ struct ovl_dir_file *od; ++ enum ovl_path_type type; ++ ++ od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL); ++ if (!od) ++ return -ENOMEM; ++ ++ type = ovl_path_real(file->f_path.dentry, &realpath); ++ realfile = vfs_open(&realpath, file->f_flags, current_cred()); ++ if (IS_ERR(realfile)) { ++ kfree(od); ++ return PTR_ERR(realfile); ++ } ++ INIT_LIST_HEAD(&od->cache); ++ INIT_LIST_HEAD(&od->cursor); ++ od->is_cached = false; ++ od->realfile = realfile; ++ od->is_real = (type != OVL_PATH_MERGE); ++ file->private_data = od; ++ ++ return 0; ++} ++ ++static const struct file_operations ovl_dir_operations = { ++ .read = generic_read_dir, ++ .open = ovl_dir_open, ++ .readdir = ovl_readdir, ++ .llseek = ovl_dir_llseek, ++ .fsync = ovl_dir_fsync, ++ .release = ovl_dir_release, ++}; ++ ++static const struct inode_operations ovl_dir_inode_operations; ++ ++static void ovl_entry_free(struct rcu_head *head) ++{ ++ struct ovl_entry *oe = container_of(head, struct ovl_entry, rcu); ++ kfree(oe); ++} ++ ++static void ovl_dentry_release(struct dentry *dentry) ++{ ++ struct ovl_entry *oe = dentry->d_fsdata; ++ ++ if (oe) { ++ dput(oe->__upperdentry); ++ dput(oe->lowerdentry); ++ call_rcu(&oe->rcu, ovl_entry_free); ++ } ++} ++ ++static const struct dentry_operations ovl_dentry_operations = { ++ .d_release = ovl_dentry_release, ++}; ++ ++static struct dentry *ovl_lookup_real(struct dentry *dir, struct qstr *name) ++{ ++ struct dentry *dentry; ++ ++ mutex_lock(&dir->d_inode->i_mutex); ++ dentry = lookup_one_len(name->name, dir, name->len); ++ mutex_unlock(&dir->d_inode->i_mutex); ++ ++ if (IS_ERR(dentry)) { ++ if (PTR_ERR(dentry) == -ENOENT) ++ dentry = NULL; ++ } else if (!dentry->d_inode) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ return dentry; ++} ++ ++static struct ovl_entry *ovl_alloc_entry(void) ++{ ++ return kzalloc(sizeof(struct ovl_entry), GFP_KERNEL); ++} ++ ++static struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ++ struct ovl_entry *oe); ++ ++static struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ struct ovl_entry *oe; ++ struct dentry *upperdir; ++ struct dentry *lowerdir; ++ struct dentry *upperdentry = NULL; ++ struct dentry *lowerdentry = NULL; ++ struct inode *inode = NULL; ++ int err; ++ ++ err = -ENOMEM; ++ oe = ovl_alloc_entry(); ++ if (!oe) ++ goto out; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ lowerdir = ovl_dentry_lower(dentry->d_parent); ++ ++ if (upperdir) { ++ upperdentry = ovl_lookup_real(upperdir, &dentry->d_name); ++ err = PTR_ERR(upperdentry); ++ if (IS_ERR(upperdentry)) ++ goto out_put_dir; ++ ++ if (lowerdir && upperdentry && ++ (S_ISLNK(upperdentry->d_inode->i_mode) || ++ S_ISDIR(upperdentry->d_inode->i_mode))) { ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_dput_upper; ++ ++ /* CAP_SYS_ADMIN needed for getxattr */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ ++ if (ovl_is_opaquedir(upperdentry)) { ++ oe->opaque = true; ++ } else if (ovl_is_whiteout(upperdentry)) { ++ dput(upperdentry); ++ upperdentry = NULL; ++ oe->opaque = true; ++ } ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ } ++ } ++ if (lowerdir && !oe->opaque) { ++ lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name); ++ err = PTR_ERR(lowerdentry); ++ if (IS_ERR(lowerdentry)) ++ goto out_dput_upper; ++ } ++ ++ if (lowerdentry && upperdentry && ++ (!S_ISDIR(upperdentry->d_inode->i_mode) || ++ !S_ISDIR(lowerdentry->d_inode->i_mode))) { ++ dput(lowerdentry); ++ lowerdentry = NULL; ++ oe->opaque = true; ++ } ++ ++ if (lowerdentry || upperdentry) { ++ struct dentry *realdentry; ++ ++ realdentry = upperdentry ? upperdentry : lowerdentry; ++ err = -ENOMEM; ++ inode = ovl_new_inode(dir->i_sb, realdentry->d_inode->i_mode, oe); ++ if (!inode) ++ goto out_dput; ++ } ++ ++ if (upperdentry) ++ oe->__upperdentry = upperdentry; ++ ++ if (lowerdentry) ++ oe->lowerdentry = lowerdentry; ++ ++ dentry->d_fsdata = oe; ++ dentry->d_op = &ovl_dentry_operations; ++ d_add(dentry, inode); ++ ++ return NULL; ++ ++out_dput: ++ dput(lowerdentry); ++out_dput_upper: ++ dput(upperdentry); ++out_put_dir: ++ kfree(oe); ++out: ++ return ERR_PTR(err); ++} ++ ++static int ovl_copy_up_xattr(struct dentry *old, struct dentry *new) ++{ ++ ssize_t list_size, size; ++ char *buf, *name, *value; ++ int error; ++ ++ if (!old->d_inode->i_op->getxattr || ++ !new->d_inode->i_op->getxattr) ++ return 0; ++ ++ list_size = vfs_listxattr(old, NULL, 0); ++ if (list_size <= 0) { ++ if (list_size == -EOPNOTSUPP) ++ return 0; ++ return list_size; ++ } ++ ++ buf = kzalloc(list_size, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ error = -ENOMEM; ++ value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL); ++ if (!value) ++ goto out; ++ ++ list_size = vfs_listxattr(old, buf, list_size); ++ if (list_size <= 0) { ++ error = list_size; ++ goto out_free_value; ++ } ++ ++ for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { ++ size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX); ++ if (size <= 0) { ++ error = size; ++ goto out_free_value; ++ } ++ error = vfs_setxattr(new, name, value, size, 0); ++ if (error) ++ goto out_free_value; ++ } ++ ++out_free_value: ++ kfree(value); ++out: ++ kfree(buf); ++ return error; ++} ++ ++static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) ++{ ++ struct file *old_file; ++ struct file *new_file; ++ int error = 0; ++ ++ if (len == 0) ++ return 0; ++ ++ old_file = vfs_open(old, O_RDONLY, current_cred()); ++ if (IS_ERR(old_file)) ++ return PTR_ERR(old_file); ++ ++ new_file = vfs_open(new, O_WRONLY, current_cred()); ++ if (IS_ERR(new_file)) { ++ error = PTR_ERR(new_file); ++ goto out_fput; ++ } ++ ++ /* FIXME: copy up sparse files efficiently */ ++ while (len) { ++ loff_t offset = new_file->f_pos; ++ size_t this_len = OVL_COPY_UP_CHUNK_SIZE; ++ long bytes; ++ ++ if (len < this_len) ++ this_len = len; ++ ++ if (signal_pending_state(TASK_KILLABLE, current)) ++ return -EINTR; ++ ++ bytes = do_splice_direct(old_file, &offset, new_file, this_len, ++ SPLICE_F_MOVE); ++ if (bytes <= 0) { ++ error = bytes; ++ break; ++ } ++ ++ len -= bytes; ++ } ++ ++ fput(new_file); ++out_fput: ++ fput(old_file); ++ return error; ++} ++ ++static struct dentry *ovl_lookup_create(struct dentry *upperdir, ++ struct dentry *template) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct qstr *name = &template->d_name; ++ ++ newdentry = lookup_one_len(name->name, upperdir, name->len); ++ if (IS_ERR(newdentry)) ++ return newdentry; ++ ++ if (newdentry->d_inode) { ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ /* No need to check whiteout if lower parent is non-existent */ ++ err = -EEXIST; ++ if (!ovl_dentry_lower(template->d_parent)) ++ goto out_dput; ++ ++ if (!S_ISLNK(newdentry->d_inode->i_mode)) ++ goto out_dput; ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_dput; ++ ++ /* ++ * CAP_SYS_ADMIN for getxattr ++ * CAP_FOWNER for unlink in sticky directory ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ old_cred = override_creds(override_cred); ++ ++ err = -EEXIST; ++ if (ovl_is_whiteout(newdentry)) ++ err = vfs_unlink(upperdir->d_inode, newdentry); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ if (err) ++ goto out_dput; ++ ++ dput(newdentry); ++ newdentry = lookup_one_len(name->name, upperdir, name->len); ++ if (IS_ERR(newdentry)) ++ return newdentry; ++ ++ /* ++ * Whiteout just been successfully removed, parent ++ * i_mutex is still held, there's no way the lookup ++ * could return positive. ++ */ ++ WARN_ON(newdentry->d_inode); ++ } ++ ++ return newdentry; ++ ++out_dput: ++ dput(newdentry); ++ return ERR_PTR(err); ++} ++ ++static struct dentry *ovl_upper_create(struct dentry *upperdir, ++ struct dentry *dentry, ++ struct kstat *stat, const char *link) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct inode *dir = upperdir->d_inode; ++ ++ newdentry = ovl_lookup_create(upperdir, dentry); ++ if (IS_ERR(newdentry)) ++ goto out; ++ ++ switch (stat->mode & S_IFMT) { ++ case S_IFREG: ++ err = vfs_create(dir, newdentry, stat->mode, NULL); ++ break; ++ ++ case S_IFDIR: ++ err = vfs_mkdir(dir, newdentry, stat->mode); ++ break; ++ ++ case S_IFCHR: ++ case S_IFBLK: ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfs_mknod(dir, newdentry, stat->mode, stat->rdev); ++ break; ++ ++ case S_IFLNK: ++ err = vfs_symlink(dir, newdentry, link); ++ break; ++ ++ default: ++ err = -EPERM; ++ } ++ if (err) { ++ dput(newdentry); ++ newdentry = ERR_PTR(err); ++ } ++ ++out: ++ return newdentry; ++ ++} ++ ++static char *ovl_read_symlink(struct dentry *realdentry) ++{ ++ int res; ++ char *buf; ++ struct inode *inode = realdentry->d_inode; ++ mm_segment_t old_fs; ++ ++ res = -EINVAL; ++ if (!inode->i_op->readlink) ++ goto err; ++ ++ res = -ENOMEM; ++ buf = (char *) __get_free_page(GFP_KERNEL); ++ if (!buf) ++ goto err; ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ /* The cast to a user pointer is valid due to the set_fs() */ ++ res = inode->i_op->readlink(realdentry, ++ (char __user *)buf, PAGE_SIZE - 1); ++ set_fs(old_fs); ++ if (res < 0) { ++ free_page((unsigned long) buf); ++ goto err; ++ } ++ buf[res] = '\0'; ++ ++ return buf; ++ ++err: ++ return ERR_PTR(res); ++} ++ ++static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) ++{ ++ struct iattr attr = { ++ .ia_valid = ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET, ++ .ia_atime = stat->atime, ++ .ia_mtime = stat->mtime, ++ }; ++ ++ return notify_change(upperdentry, &attr); ++} ++ ++static int ovl_set_mode(struct dentry *upperdentry, umode_t mode) ++{ ++ struct iattr attr = { ++ .ia_valid = ATTR_MODE, ++ .ia_mode = mode, ++ }; ++ ++ return notify_change(upperdentry, &attr); ++} ++ ++static int ovl_set_opaque(struct dentry *upperdentry) ++{ ++ int err; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ return -ENOMEM; ++ ++ /* CAP_SYS_ADMIN for setxattr of "trusted" namespace */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ err = vfs_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0); ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return err; ++} ++ ++static int ovl_remove_opaque(struct dentry *upperdentry) ++{ ++ int err; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ return -ENOMEM; ++ ++ /* CAP_SYS_ADMIN for removexattr of "trusted" namespace */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ old_cred = override_creds(override_cred); ++ err = vfs_removexattr(upperdentry, ovl_opaque_xattr); ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++ return err; ++} ++ ++static int ovl_copy_up_locked(struct dentry *upperdir, struct dentry *dentry, ++ struct path *lowerpath, struct kstat *stat, ++ const char *link) ++{ ++ int err; ++ struct path newpath; ++ umode_t mode = stat->mode; ++ struct ovl_fs *ofs = dentry->d_sb->s_fs_info; ++ ++ /* Can't properly set mode on creation because of the umask */ ++ stat->mode &= S_IFMT; ++ ++ newpath.mnt = ofs->upper_mnt; ++ newpath.dentry = ovl_upper_create(upperdir, dentry, stat, link); ++ if (IS_ERR(newpath.dentry)) { ++ err = PTR_ERR(newpath.dentry); ++ ++ /* Already copied up? */ ++ if (err == -EEXIST && ovl_path_type(dentry) != OVL_PATH_LOWER) ++ return 0; ++ ++ return err; ++ } ++ ++ /* FIXME: recovery from failure to copy up */ ++ ++ if (S_ISREG(stat->mode)) { ++ err = ovl_copy_up_data(lowerpath, &newpath, stat->size); ++ if (err) ++ return err; ++ } ++ ++ err = ovl_copy_up_xattr(lowerpath->dentry, newpath.dentry); ++ if (err) ++ return err; ++ ++ mutex_lock(&newpath.dentry->d_inode->i_mutex); ++ if (!S_ISLNK(stat->mode)) ++ err = ovl_set_mode(newpath.dentry, mode); ++ if (!err) ++ err = ovl_set_timestamps(newpath.dentry, stat); ++ mutex_unlock(&newpath.dentry->d_inode->i_mutex); ++ if (err) ++ return err; ++ ++ ovl_dentry_update(dentry, newpath.dentry); ++ ++ /* ++ * Easiest way to get rid of the lower dentry reference is to ++ * drop this dentry. This is neither needed nor possible for ++ * directories. ++ */ ++ if (!S_ISDIR(stat->mode)) ++ d_drop(dentry); ++ ++ return 0; ++} ++ ++static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ++ struct path *lowerpath, struct kstat *stat) ++{ ++ int err; ++ struct kstat pstat; ++ struct path parentpath; ++ struct dentry *upperdir; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ char *link = NULL; ++ ++ ovl_path_upper(parent, &parentpath); ++ upperdir = parentpath.dentry; ++ ++ err = vfs_getattr(parentpath.mnt, parentpath.dentry, &pstat); ++ if (err) ++ return err; ++ ++ if (S_ISLNK(stat->mode)) { ++ link = ovl_read_symlink(lowerpath->dentry); ++ if (IS_ERR(link)) ++ return PTR_ERR(link); ++ } ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out_free_link; ++ ++ override_cred->fsuid = stat->uid; ++ override_cred->fsgid = stat->gid; ++ /* ++ * CAP_SYS_ADMIN for copying up extended attributes ++ * CAP_DAC_OVERRIDE for create ++ * CAP_FOWNER for chmod, timestamp update ++ * CAP_FSETID for chmod ++ * CAP_MKNOD for mknod ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ cap_raise(override_cred->cap_effective, CAP_FOWNER); ++ cap_raise(override_cred->cap_effective, CAP_FSETID); ++ cap_raise(override_cred->cap_effective, CAP_MKNOD); ++ old_cred = override_creds(override_cred); ++ ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ /* ++ * Using upper filesystem locking to protect against copy up ++ * racing with rename (rename means the copy up was already ++ * successful). ++ */ ++ if (dentry->d_parent != parent) { ++ WARN_ON((ovl_path_type(dentry) == OVL_PATH_LOWER)); ++ err = 0; ++ } else { ++ err = ovl_copy_up_locked(upperdir, dentry, lowerpath, ++ stat, link); ++ if (!err) { ++ /* Restore timestamps on parent (best effort) */ ++ ovl_set_timestamps(upperdir, &pstat); ++ } ++ } ++ ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++ ++ revert_creds(old_cred); ++ put_cred(override_cred); ++ ++out_free_link: ++ if (link) ++ free_page((unsigned long) link); ++ ++ return err; ++} ++ ++static int ovl_copy_up(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ while (!err) { ++ struct dentry *next; ++ struct dentry *parent; ++ struct path lowerpath; ++ struct kstat stat; ++ enum ovl_path_type type = ovl_path_type(dentry); ++ ++ if (type != OVL_PATH_LOWER) ++ break; ++ ++ next = dget(dentry); ++ /* find the topmost dentry not yet copied up */ ++ for (;;) { ++ parent = dget_parent(next); ++ ++ type = ovl_path_type(parent); ++ if (type != OVL_PATH_LOWER) ++ break; ++ ++ dput(next); ++ next = parent; ++ } ++ ++ ovl_path_lower(next, &lowerpath); ++ err = vfs_getattr(lowerpath.mnt, lowerpath.dentry, &stat); ++ if (!err) ++ err = ovl_copy_up_one(parent, next, &lowerpath, &stat); ++ ++ dput(parent); ++ dput(next); ++ } ++ ++ return err; ++} ++ ++/* Optimize by not copying up the file first and truncating later */ ++static int ovl_copy_up_truncate(struct dentry *dentry, loff_t size) ++{ ++ int err; ++ struct kstat stat; ++ struct path lowerpath; ++ struct dentry *parent = dget_parent(dentry); ++ ++ err = ovl_copy_up(parent); ++ if (err) ++ goto out_dput_parent; ++ ++ ovl_path_lower(dentry, &lowerpath); ++ err = vfs_getattr(lowerpath.mnt, lowerpath.dentry, &stat); ++ if (err) ++ goto out_dput_parent; ++ ++ if (size < stat.size) ++ stat.size = size; ++ ++ err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat); ++ ++out_dput_parent: ++ dput(parent); ++ return err; ++} ++ ++static int ovl_setattr(struct dentry *dentry, struct iattr *attr) ++{ ++ struct dentry *upperdentry; ++ int err; ++ ++ if ((attr->ia_valid & ATTR_SIZE) && !ovl_dentry_upper(dentry)) ++ err = ovl_copy_up_truncate(dentry, attr->ia_size); ++ else ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ upperdentry = ovl_dentry_upper(dentry); ++ ++ if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) ++ attr->ia_valid &= ~ATTR_MODE; ++ ++ mutex_lock(&upperdentry->d_inode->i_mutex); ++ err = notify_change(upperdentry, attr); ++ mutex_unlock(&upperdentry->d_inode->i_mutex); ++ ++ return err; ++} ++ ++static int ovl_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat) ++{ ++ struct path realpath; ++ ++ ovl_path_real(dentry, &realpath); ++ return vfs_getattr(realpath.mnt, realpath.dentry, stat); ++} ++ ++static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, ++ struct kstat *stat) ++{ ++ int err; ++ enum ovl_path_type type; ++ struct path realpath; ++ ++ type = ovl_path_real(dentry, &realpath); ++ err = vfs_getattr(realpath.mnt, realpath.dentry, stat); ++ if (err) ++ return err; ++ ++ stat->dev = dentry->d_sb->s_dev; ++ stat->ino = dentry->d_inode->i_ino; ++ ++ /* ++ * It's probably not worth it to count subdirs to get the ++ * correct link count. nlink=1 seems to pacify 'find' and ++ * other utilities. ++ */ ++ if (type == OVL_PATH_MERGE) ++ stat->nlink = 1; ++ ++ return 0; ++} ++ ++static int ovl_permission(struct inode *inode, int mask, unsigned int flags) ++{ ++ struct ovl_entry *oe; ++ struct dentry *alias = NULL; ++ struct inode *realinode; ++ struct dentry *realdentry; ++ bool is_upper; ++ int err; ++ ++ if (S_ISDIR(inode->i_mode)) { ++ oe = inode->i_private; ++ } else if (flags & IPERM_FLAG_RCU) { ++ return -ECHILD; ++ } else { ++ /* ++ * For non-directories find an alias and get the info ++ * from there. ++ */ ++ spin_lock(&inode->i_lock); ++ if (WARN_ON(list_empty(&inode->i_dentry))) { ++ spin_unlock(&inode->i_lock); ++ return -ENOENT; ++ } ++ alias = list_entry(inode->i_dentry.next, struct dentry, d_alias); ++ dget(alias); ++ spin_unlock(&inode->i_lock); ++ oe = alias->d_fsdata; ++ } ++ ++ realdentry = ovl_upperdentry_dereference(oe); ++ is_upper = true; ++ if (!realdentry) { ++ realdentry = oe->lowerdentry; ++ is_upper = false; ++ } ++ ++ /* Careful in RCU walk mode */ ++ realinode = ACCESS_ONCE(realdentry->d_inode); ++ if (!realinode) { ++ WARN_ON(!(flags & IPERM_FLAG_RCU)); ++ return -ENOENT; ++ } ++ ++ if (mask & MAY_WRITE) { ++ umode_t mode = realinode->i_mode; ++ ++ /* ++ * Writes will always be redirected to upper layer, so ++ * ignore lower layer being read-only. ++ */ ++ err = -EROFS; ++ if (is_upper && IS_RDONLY(realinode) && ++ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) ++ goto out_dput; ++ ++ /* ++ * Nobody gets write access to an immutable file. ++ */ ++ err = -EACCES; ++ if (IS_IMMUTABLE(realinode)) ++ goto out_dput; ++ } ++ ++ if (realinode->i_op->permission) ++ err = realinode->i_op->permission(realinode, mask, flags); ++ else ++ err = generic_permission(realinode, mask, flags, ++ realinode->i_op->check_acl); ++out_dput: ++ dput(alias); ++ return err; ++} ++ ++static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, ++ const char *link) ++{ ++ int err; ++ struct dentry *newdentry; ++ struct dentry *upperdir; ++ struct inode *inode; ++ struct kstat stat = { ++ .mode = mode, ++ .rdev = rdev, ++ }; ++ ++ err = -ENOMEM; ++ inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata); ++ if (!inode) ++ goto out; ++ ++ err = ovl_copy_up(dentry->d_parent); ++ if (err) ++ goto out_iput; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ ++ newdentry = ovl_upper_create(upperdir, dentry, &stat, link); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ if (ovl_dentry_is_opaque(dentry) && S_ISDIR(mode)) { ++ err = ovl_set_opaque(newdentry); ++ if (err) ++ goto out_dput; ++ } ++ ovl_dentry_update(dentry, newdentry); ++ d_instantiate(dentry, inode); ++ inode = NULL; ++ newdentry = NULL; ++ err = 0; ++ ++out_dput: ++ dput(newdentry); ++out_unlock: ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++out_iput: ++ iput(inode); ++out: ++ return err; ++} ++ ++static int ovl_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL); ++} ++ ++static int ovl_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ return ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL); ++} ++ ++static int ovl_mknod(struct inode *dir, struct dentry *dentry, int mode, ++ dev_t rdev) ++{ ++ return ovl_create_object(dentry, mode, rdev, NULL); ++} ++ ++static int ovl_symlink(struct inode *dir, struct dentry *dentry, ++ const char *link) ++{ ++ return ovl_create_object(dentry, S_IFLNK, 0, link); ++} ++ ++struct ovl_link_data { ++ struct dentry *realdentry; ++ void *cookie; ++}; ++ ++static void *ovl_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ void *ret; ++ struct dentry *realdentry; ++ struct inode *realinode; ++ ++ realdentry = ovl_dentry_real(dentry); ++ realinode = realdentry->d_inode; ++ ++ if (WARN_ON(!realinode->i_op->follow_link)) ++ return ERR_PTR(-EPERM); ++ ++ ret = realinode->i_op->follow_link(realdentry, nd); ++ if (IS_ERR(ret)) ++ return ret; ++ ++ if (realinode->i_op->put_link) { ++ struct ovl_link_data *data; ++ ++ data = kmalloc(sizeof(struct ovl_link_data), GFP_KERNEL); ++ if (!data) { ++ realinode->i_op->put_link(realdentry, nd, ret); ++ return ERR_PTR(-ENOMEM); ++ } ++ data->realdentry = realdentry; ++ data->cookie = ret; ++ ++ return data; ++ } else { ++ return NULL; ++ } ++} ++ ++static void ovl_put_link(struct dentry *dentry, struct nameidata *nd, void *c) ++{ ++ struct inode *realinode; ++ struct ovl_link_data *data = c; ++ ++ if (!data) ++ return; ++ ++ realinode = data->realdentry->d_inode; ++ realinode->i_op->put_link(data->realdentry, nd, data->cookie); ++ kfree(data); ++} ++ ++static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ struct path realpath; ++ struct inode *realinode; ++ ++ ovl_path_real(dentry, &realpath); ++ realinode = realpath.dentry->d_inode; ++ ++ if (!realinode->i_op->readlink) ++ return -EINVAL; ++ ++ touch_atime(realpath.mnt, realpath.dentry); ++ ++ return realinode->i_op->readlink(realpath.dentry, buf, bufsiz); ++} ++ ++static int ovl_whiteout(struct dentry *upperdir, struct dentry *dentry) ++{ ++ int err; ++ struct dentry *newdentry; ++ const struct cred *old_cred; ++ struct cred *override_cred; ++ ++ /* FIXME: recheck lower dentry to see if whiteout is really needed */ ++ ++ err = -ENOMEM; ++ override_cred = prepare_creds(); ++ if (!override_cred) ++ goto out; ++ ++ /* ++ * CAP_SYS_ADMIN for setxattr ++ * CAP_DAC_OVERRIDE for symlink creation ++ */ ++ cap_raise(override_cred->cap_effective, CAP_SYS_ADMIN); ++ cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE); ++ override_cred->fsuid = 0; ++ override_cred->fsgid = 0; ++ old_cred = override_creds(override_cred); ++ ++ newdentry = lookup_one_len(dentry->d_name.name, upperdir, ++ dentry->d_name.len); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_put_cred; ++ ++ /* Just been removed within the same locked region */ ++ WARN_ON(newdentry->d_inode); ++ ++ err = vfs_symlink(upperdir->d_inode, newdentry, ovl_whiteout_symlink); ++ if (err) ++ goto out_dput; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ ++ err = vfs_setxattr(newdentry, ovl_whiteout_xattr, "y", 1, 0); ++ ++out_dput: ++ dput(newdentry); ++out_put_cred: ++ revert_creds(old_cred); ++ put_cred(override_cred); ++out: ++ return err; ++} ++ ++static int ovl_do_remove(struct dentry *dentry, bool is_dir) ++{ ++ int err; ++ enum ovl_path_type type; ++ struct path realpath; ++ struct dentry *upperdir; ++ ++ err = ovl_copy_up(dentry->d_parent); ++ if (err) ++ return err; ++ ++ upperdir = ovl_dentry_upper(dentry->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ type = ovl_path_real(dentry, &realpath); ++ if (type != OVL_PATH_LOWER) { ++ err = -ESTALE; ++ if (realpath.dentry->d_parent != upperdir) ++ goto out_d_drop; ++ ++ if (is_dir) ++ err = vfs_rmdir(upperdir->d_inode, realpath.dentry); ++ else ++ err = vfs_unlink(upperdir->d_inode, realpath.dentry); ++ if (err) ++ goto out_d_drop; ++ ++ ovl_dentry_version_inc(dentry->d_parent); ++ } ++ ++ if (type != OVL_PATH_UPPER || ovl_dentry_is_opaque(dentry)) ++ err = ovl_whiteout(upperdir, dentry); ++ ++ /* ++ * Keeping this dentry hashed would mean having to release ++ * upperpath/lowerpath, which could only be done if we are the ++ * sole user of this dentry. Too tricky... Just unhash for ++ * now. ++ */ ++out_d_drop: ++ d_drop(dentry); ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++ ++ return err; ++} ++ ++static int ovl_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ return ovl_do_remove(dentry, false); ++} ++ ++static int ovl_check_empty_dir(struct dentry *dentry) ++{ ++ int err; ++ struct path lowerpath; ++ struct path upperpath; ++ struct ovl_cache_entry *p; ++ LIST_HEAD(list); ++ struct ovl_readdir_data rdd = { .list = &list }; ++ ++ ovl_path_upper(dentry, &upperpath); ++ ovl_path_lower(dentry, &lowerpath); ++ ++ err = ovl_dir_read_merged(&upperpath, &lowerpath, &rdd); ++ if (err) ++ return err; ++ ++ err = 0; ++ ++ list_for_each_entry(p, &list, l_node) { ++ if (p->is_whiteout) ++ continue; ++ ++ if (p->name[0] == '.') { ++ if (p->len == 1) ++ continue; ++ if (p->len == 2 && p->name[1] == '.') ++ continue; ++ } ++ err = -ENOTEMPTY; ++ break; ++ } ++ ++ ovl_cache_free(&list); ++ ++ return err; ++} ++ ++static int ovl_unlink_whiteout(void *buf, const char *name, int namelen, ++ loff_t offset, u64 ino, unsigned int d_type) ++{ ++ struct ovl_readdir_data *rdd = buf; ++ ++ rdd->count++; ++ /* check d_type to filter out "." and ".." */ ++ if (d_type == DT_LNK) { ++ struct dentry *dentry; ++ ++ dentry = lookup_one_len(name, rdd->dir, namelen); ++ if (IS_ERR(dentry)) { ++ rdd->err = PTR_ERR(dentry); ++ } else { ++ rdd->err = vfs_unlink(rdd->dir->d_inode, dentry); ++ dput(dentry); ++ } ++ } ++ ++ return rdd->err; ++} ++ ++static int ovl_remove_whiteouts(struct dentry *dentry) ++{ ++ struct path upperpath; ++ struct ovl_readdir_data rdd = { .list = NULL }; ++ ++ ovl_path_upper(dentry, &upperpath); ++ rdd.dir = upperpath.dentry; ++ ++ return ovl_dir_read(&upperpath, &rdd, ovl_unlink_whiteout); ++} ++ ++static int ovl_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ enum ovl_path_type type; ++ ++ type = ovl_path_type(dentry); ++ if (type != OVL_PATH_UPPER) { ++ err = ovl_check_empty_dir(dentry); ++ if (err) ++ return err; ++ ++ if (type == OVL_PATH_MERGE) { ++ err = ovl_remove_whiteouts(dentry); ++ if (err) ++ return err; ++ } ++ } ++ ++ return ovl_do_remove(dentry, true); ++} ++ ++static int ovl_link(struct dentry *old, struct inode *newdir, ++ struct dentry *new) ++{ ++ int err; ++ struct dentry *olddentry; ++ struct dentry *newdentry; ++ struct dentry *upperdir; ++ ++ err = ovl_copy_up(old); ++ if (err) ++ goto out; ++ ++ err = ovl_copy_up(new->d_parent); ++ if (err) ++ goto out; ++ ++ upperdir = ovl_dentry_upper(new->d_parent); ++ mutex_lock_nested(&upperdir->d_inode->i_mutex, I_MUTEX_PARENT); ++ newdentry = ovl_lookup_create(upperdir, new); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ ++ olddentry = ovl_dentry_upper(old); ++ err = vfs_link(olddentry, upperdir->d_inode, newdentry); ++ if (!err) { ++ ovl_dentry_version_inc(new->d_parent); ++ ovl_dentry_update(new, newdentry); ++ ++ ihold(old->d_inode); ++ d_instantiate(new, old->d_inode); ++ } else { ++ dput(newdentry); ++ } ++out_unlock: ++ mutex_unlock(&upperdir->d_inode->i_mutex); ++out: ++ return err; ++ ++} ++ ++static int ovl_rename(struct inode *olddir, struct dentry *old, ++ struct inode *newdir, struct dentry *new) ++{ ++ int err; ++ enum ovl_path_type old_type; ++ struct dentry *old_upperdir; ++ struct dentry *new_upperdir; ++ struct dentry *olddentry; ++ struct dentry *newdentry; ++ struct dentry *trap; ++ bool is_dir = S_ISDIR(old->d_inode->i_mode); ++ ++ /* Don't copy up directory trees */ ++ old_type = ovl_path_type(old); ++ if (old_type != OVL_PATH_UPPER && is_dir) ++ return -EXDEV; ++ ++ if (new->d_inode) { ++ enum ovl_path_type new_type; ++ ++ new_type = ovl_path_type(new); ++ ++ if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { ++ if (ovl_dentry_lower(old)->d_inode == ++ ovl_dentry_lower(new)->d_inode) ++ return 0; ++ } ++ if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { ++ if (ovl_dentry_upper(old)->d_inode == ++ ovl_dentry_upper(new)->d_inode) ++ return 0; ++ } ++ ++ if (new_type != OVL_PATH_UPPER && ++ S_ISDIR(new->d_inode->i_mode)) { ++ err = ovl_check_empty_dir(new); ++ if (err) ++ return err; ++ ++ if (new_type == OVL_PATH_MERGE) { ++ err = ovl_remove_whiteouts(new); ++ if (err) ++ return err; ++ } ++ } ++ } ++ ++ err = ovl_copy_up(old); ++ if (err) ++ return err; ++ ++ err = ovl_copy_up(new->d_parent); ++ if (err) ++ return err; ++ ++ old_upperdir = ovl_dentry_upper(old->d_parent); ++ new_upperdir = ovl_dentry_upper(new->d_parent); ++ ++ trap = lock_rename(new_upperdir, old_upperdir); ++ ++ olddentry = ovl_dentry_upper(old); ++ newdentry = ovl_dentry_upper(new); ++ if (newdentry) { ++ dget(newdentry); ++ } else { ++ newdentry = ovl_lookup_create(new_upperdir, new); ++ err = PTR_ERR(newdentry); ++ if (IS_ERR(newdentry)) ++ goto out_unlock; ++ } ++ ++ err = -ESTALE; ++ if (olddentry->d_parent != old_upperdir) ++ goto out_dput; ++ if (newdentry->d_parent != new_upperdir) ++ goto out_dput; ++ if (olddentry == trap) ++ goto out_dput; ++ if (newdentry == trap) ++ goto out_dput; ++ ++ err = vfs_rename(old_upperdir->d_inode, olddentry, ++ new_upperdir->d_inode, newdentry); ++ ++ if (!err) { ++ bool old_opaque = ovl_dentry_is_opaque(old); ++ bool new_opaque = ovl_dentry_is_opaque(new); ++ ++ if (ovl_path_type(new) != OVL_PATH_UPPER) ++ new_opaque = true; ++ ++ if (old_type != OVL_PATH_UPPER || old_opaque) ++ err = ovl_whiteout(old_upperdir, old); ++ if (!err && is_dir) { ++ if (old_opaque && !new_opaque) { ++ ovl_remove_opaque(olddentry); ++ ovl_dentry_set_opaque(old, false); ++ } ++ if (!old_opaque && new_opaque) { ++ err = ovl_set_opaque(olddentry); ++ ovl_dentry_set_opaque(old, true); ++ } ++ } ++ ovl_dentry_version_inc(old->d_parent); ++ ovl_dentry_version_inc(new->d_parent); ++ } ++ ++out_dput: ++ dput(newdentry); ++out_unlock: ++ unlock_rename(new_upperdir, old_upperdir); ++ return err; ++} ++ ++static bool ovl_is_private_xattr(const char *name) ++{ ++ return strncmp(name, "trusted.overlay.", 14) == 0; ++} ++ ++static int ovl_setxattr(struct dentry *dentry, const char *name, ++ const void *value, size_t size, int flags) ++{ ++ int err; ++ struct dentry *upperdentry; ++ ++ if (ovl_is_private_xattr(name)) ++ return -EPERM; ++ ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ upperdentry = ovl_dentry_upper(dentry); ++ return vfs_setxattr(upperdentry, name, value, size, flags); ++} ++ ++static ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ++ void *value, size_t size) ++{ ++ if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ++ ovl_is_private_xattr(name)) ++ return -ENODATA; ++ ++ return vfs_getxattr(ovl_dentry_real(dentry), name, value, size); ++} ++ ++static ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size) ++{ ++ ssize_t res; ++ int off; ++ ++ res = vfs_listxattr(ovl_dentry_real(dentry), list, size); ++ if (res <= 0 || size == 0) ++ return res; ++ ++ if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE) ++ return res; ++ ++ /* filter out private xattrs */ ++ for (off = 0; off < res;) { ++ char *s = list + off; ++ size_t slen = strlen(s) + 1; ++ ++ BUG_ON(off + slen > res); ++ ++ if (ovl_is_private_xattr(s)) { ++ res -= slen; ++ memmove(s, s + slen, res - off); ++ } else { ++ off += slen; ++ } ++ } ++ ++ return res; ++} ++ ++static int ovl_removexattr(struct dentry *dentry, const char *name) ++{ ++ int err; ++ struct path realpath; ++ enum ovl_path_type type; ++ ++ if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE && ++ ovl_is_private_xattr(name)) ++ return -ENODATA; ++ ++ type = ovl_path_real(dentry, &realpath); ++ if (type == OVL_PATH_LOWER) { ++ err = vfs_getxattr(realpath.dentry, name, NULL, 0); ++ if (err < 0) ++ return err; ++ ++ err = ovl_copy_up(dentry); ++ if (err) ++ return err; ++ ++ ovl_path_upper(dentry, &realpath); ++ } ++ ++ return vfs_removexattr(realpath.dentry, name); ++} ++ ++static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, ++ struct dentry *realdentry) ++{ ++ if (type != OVL_PATH_LOWER) ++ return false; ++ ++ if (special_file(realdentry->d_inode->i_mode)) ++ return false; ++ ++ if (!(OPEN_FMODE(flags) & FMODE_WRITE) && !(flags & O_TRUNC)) ++ return false; ++ ++ return true; ++} ++ ++static struct file *ovl_open(struct dentry *dentry, int flags, ++ const struct cred *cred) ++{ ++ int err; ++ struct path realpath; ++ enum ovl_path_type type; ++ ++ type = ovl_path_real(dentry, &realpath); ++ if (ovl_open_need_copy_up(flags, type, realpath.dentry)) { ++ if (flags & O_TRUNC) ++ err = ovl_copy_up_truncate(dentry, 0); ++ else ++ err = ovl_copy_up(dentry); ++ if (err) ++ return ERR_PTR(err); ++ ++ ovl_path_upper(dentry, &realpath); ++ } ++ ++ return vfs_open(&realpath, flags, cred); ++} ++ ++static const struct inode_operations ovl_dir_inode_operations = { ++ .lookup = ovl_lookup, ++ .mkdir = ovl_mkdir, ++ .symlink = ovl_symlink, ++ .unlink = ovl_unlink, ++ .rmdir = ovl_rmdir, ++ .rename = ovl_rename, ++ .link = ovl_link, ++ .setattr = ovl_setattr, ++ .create = ovl_create, ++ .mknod = ovl_mknod, ++ .permission = ovl_permission, ++ .getattr = ovl_dir_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++}; ++ ++static const struct inode_operations ovl_file_inode_operations = { ++ .setattr = ovl_setattr, ++ .permission = ovl_permission, ++ .getattr = ovl_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++ .open = ovl_open, ++}; ++ ++static const struct inode_operations ovl_symlink_inode_operations = { ++ .setattr = ovl_setattr, ++ .follow_link = ovl_follow_link, ++ .put_link = ovl_put_link, ++ .readlink = ovl_readlink, ++ .getattr = ovl_getattr, ++ .setxattr = ovl_setxattr, ++ .getxattr = ovl_getxattr, ++ .listxattr = ovl_listxattr, ++ .removexattr = ovl_removexattr, ++}; ++ ++static struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ++ struct ovl_entry *oe) ++{ ++ struct inode *inode; ++ ++ inode = new_inode(sb); ++ if (!inode) ++ return NULL; ++ ++ mode &= S_IFMT; ++ ++ inode->i_ino = get_next_ino(); ++ inode->i_mode = mode; ++ inode->i_flags |= S_NOATIME | S_NOCMTIME; ++ ++ switch (mode) { ++ case S_IFDIR: ++ inode->i_private = oe; ++ inode->i_op = &ovl_dir_inode_operations; ++ inode->i_fop = &ovl_dir_operations; ++ break; ++ ++ case S_IFLNK: ++ inode->i_op = &ovl_symlink_inode_operations; ++ break; ++ ++ case S_IFREG: ++ case S_IFSOCK: ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ inode->i_op = &ovl_file_inode_operations; ++ break; ++ ++ default: ++ WARN(1, "illegal file type: %i\n", mode); ++ inode = NULL; ++ } ++ ++ return inode; ++ ++} ++ ++static void ovl_put_super(struct super_block *sb) ++{ ++ struct ovl_fs *ufs = sb->s_fs_info; ++ ++ if (!(sb->s_flags & MS_RDONLY)) ++ mnt_drop_write(ufs->upper_mnt); ++ ++ mntput(ufs->upper_mnt); ++ mntput(ufs->lower_mnt); ++ ++ kfree(ufs); ++} ++ ++static int ovl_remount_fs(struct super_block *sb, int *flagsp, char *data) ++{ ++ int flags = *flagsp; ++ struct ovl_fs *ufs = sb->s_fs_info; ++ ++ /* When remounting rw or ro, we need to adjust the write access to the ++ * upper fs. ++ */ ++ if (((flags ^ sb->s_flags) & MS_RDONLY) == 0) ++ /* No change to readonly status */ ++ return 0; ++ ++ if (flags & MS_RDONLY) { ++ mnt_drop_write(ufs->upper_mnt); ++ return 0; ++ } else ++ return mnt_want_write(ufs->upper_mnt); ++} ++ ++/** ++ * ovl_statfs ++ * @sb: The overlayfs super block ++ * @buf: The struct kstatfs to fill in with stats ++ * ++ * Get the filesystem statistics. As writes always target the upper layer ++ * filesystem pass the statfs to the same filesystem. ++ */ ++static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct dentry *root_dentry = dentry->d_sb->s_root; ++ struct path path; ++ ovl_path_upper(root_dentry, &path); ++ ++ if (!path.dentry->d_sb->s_op->statfs) ++ return -ENOSYS; ++ return path.dentry->d_sb->s_op->statfs(path.dentry, buf); ++} ++ ++static const struct super_operations ovl_super_operations = { ++ .put_super = ovl_put_super, ++ .remount_fs = ovl_remount_fs, ++ .statfs = ovl_statfs, ++}; ++ ++struct ovl_config { ++ char *lowerdir; ++ char *upperdir; ++}; ++ ++enum { ++ Opt_lowerdir, ++ Opt_upperdir, ++ Opt_err, ++}; ++ ++static const match_table_t ovl_tokens = { ++ {Opt_lowerdir, "lowerdir=%s"}, ++ {Opt_upperdir, "upperdir=%s"}, ++ {Opt_err, NULL} ++}; ++ ++static int ovl_parse_opt(char *opt, struct ovl_config *config) ++{ ++ char *p; ++ ++ config->upperdir = NULL; ++ config->lowerdir = NULL; ++ ++ while ((p = strsep(&opt, ",")) != NULL) { ++ int token; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ if (!*p) ++ continue; ++ ++ token = match_token(p, ovl_tokens, args); ++ switch (token) { ++ case Opt_upperdir: ++ kfree(config->upperdir); ++ config->upperdir = match_strdup(&args[0]); ++ if (!config->upperdir) ++ return -ENOMEM; ++ break; ++ ++ case Opt_lowerdir: ++ kfree(config->lowerdir); ++ config->lowerdir = match_strdup(&args[0]); ++ if (!config->lowerdir) ++ return -ENOMEM; ++ break; ++ ++ default: ++ return -EINVAL; ++ } ++ } ++ return 0; ++} ++ ++static int ovl_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct path lowerpath; ++ struct path upperpath; ++ struct inode *root_inode; ++ struct dentry *root_dentry; ++ struct ovl_entry *oe; ++ struct ovl_fs *ufs; ++ struct ovl_config config; ++ int err; ++ ++ err = ovl_parse_opt((char *) data, &config); ++ if (err) ++ goto out; ++ ++ err = -EINVAL; ++ if (!config.upperdir || !config.lowerdir) { ++ printk(KERN_ERR "overlayfs: missing upperdir or lowerdir\n"); ++ goto out_free_config; ++ } ++ ++ err = -ENOMEM; ++ ufs = kmalloc(sizeof(struct ovl_fs), GFP_KERNEL); ++ if (!ufs) ++ goto out_free_config; ++ ++ oe = ovl_alloc_entry(); ++ if (oe == NULL) ++ goto out_free_ufs; ++ ++ root_inode = ovl_new_inode(sb, S_IFDIR, oe); ++ if (!root_inode) ++ goto out_free_oe; ++ ++ err = kern_path(config.upperdir, LOOKUP_FOLLOW, &upperpath); ++ if (err) ++ goto out_put_root; ++ ++ err = kern_path(config.lowerdir, LOOKUP_FOLLOW, &lowerpath); ++ if (err) ++ goto out_put_upperpath; ++ ++ err = -ENOTDIR; ++ if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) || ++ !S_ISDIR(lowerpath.dentry->d_inode->i_mode)) ++ goto out_put_lowerpath; ++ ++ ufs->upper_mnt = clone_private_mount(&upperpath); ++ err = PTR_ERR(ufs->upper_mnt); ++ if (IS_ERR(ufs->upper_mnt)) { ++ printk(KERN_ERR "overlayfs: failed to clone upperpath\n"); ++ goto out_put_lowerpath; ++ } ++ ++ ufs->lower_mnt = clone_private_mount(&lowerpath); ++ err = PTR_ERR(ufs->lower_mnt); ++ if (IS_ERR(ufs->lower_mnt)) { ++ printk(KERN_ERR "overlayfs: failed to clone lowerpath\n"); ++ goto out_put_upper_mnt; ++ } ++ ++ if (!(sb->s_flags & MS_RDONLY)) { ++ err = mnt_want_write(ufs->upper_mnt); ++ if (err) ++ goto out_put_lower_mnt; ++ } ++ ++ err = -ENOMEM; ++ root_dentry = d_alloc_root(root_inode); ++ if (!root_dentry) ++ goto out_drop_write; ++ ++ mntput(upperpath.mnt); ++ mntput(lowerpath.mnt); ++ ++ oe->__upperdentry = upperpath.dentry; ++ oe->lowerdentry = lowerpath.dentry; ++ ++ root_dentry->d_fsdata = oe; ++ root_dentry->d_op = &ovl_dentry_operations; ++ ++ sb->s_op = &ovl_super_operations; ++ sb->s_root = root_dentry; ++ sb->s_fs_info = ufs; ++ ++ return 0; ++ ++out_drop_write: ++ if (!(sb->s_flags & MS_RDONLY)) ++ mnt_drop_write(ufs->upper_mnt); ++out_put_lower_mnt: ++ mntput(ufs->lower_mnt); ++out_put_upper_mnt: ++ mntput(ufs->upper_mnt); ++out_put_lowerpath: ++ path_put(&lowerpath); ++out_put_upperpath: ++ path_put(&upperpath); ++out_put_root: ++ iput(root_inode); ++out_free_oe: ++ kfree(oe); ++out_free_ufs: ++ kfree(ufs); ++out_free_config: ++ kfree(config.lowerdir); ++ kfree(config.upperdir); ++out: ++ return err; ++} ++ ++static int ovl_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *raw_data, struct vfsmount *mnt) ++{ ++ return get_sb_nodev(fs_type, flags, raw_data, ovl_fill_super, mnt); ++} ++ ++static struct file_system_type ovl_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "overlayfs", ++ .get_sb = ovl_get_sb, ++ .kill_sb = kill_anon_super, ++}; ++ ++static int __init ovl_init(void) ++{ ++ return register_filesystem(&ovl_fs_type); ++} ++ ++static void __exit ovl_exit(void) ++{ ++ unregister_filesystem(&ovl_fs_type); ++} ++ ++module_init(ovl_init); ++module_exit(ovl_exit); +--- /dev/null ++++ b/Documentation/filesystems/overlayfs.txt +@@ -0,0 +1,163 @@ ++Written by: Neil Brown <neilb@suse.de> ++ ++Overlay Filesystem ++================== ++ ++This document describes a prototype for a new approach to providing ++overlay-filesystem functionality in Linux (sometimes referred to as ++union-filesystems). An overlay-filesystem tries to present a ++filesystem which is the result over overlaying one filesystem on top ++of the other. ++ ++The result will inevitably fail to look exactly like a normal ++filesystem for various technical reasons. The expectation is that ++many use cases will be able to ignore these differences. ++ ++This approach is 'hybrid' because the objects that appear in the ++filesystem do not all appear to belong to that filesystem. In many ++case an object accessed in the union will be indistinguishable ++from accessing the corresponding object from the original filesystem. ++This is most obvious from the 'st_dev' field returned by stat(2). ++ ++While directories will report an st_dev for the overlay-filesystem, ++all non-directory objects will report an st_dev whichever of the ++'lower' or 'upper' filesystem that is providing the object. Similarly ++st_ino will only be unique when combined with st_dev, and both of ++these can change over the lifetime of a non-directory object. Many ++applications and tools ignore these values and will not be affected. ++ ++Upper and Lower ++--------------- ++ ++An overlay filesystem combines two filesystems - an 'upper' filesystem ++and a 'lower' filesystem. When a name exists in both filesystems, the ++object in the 'upper' filesystem is visible while the object in the ++'lower' filesystem is either hidden or, in the case of directories, ++merged with the 'upper' object. ++ ++It would be more correct to refer to an upper and lower 'directory ++tree' rather than 'filesystem' as it is quite possible for both ++directory trees to be in the same filesystem and there is no ++requirement that the root of a filesystem be given for either upper or ++lower. ++ ++The lower filesystem can be any filesystem supported by Linux and does ++not need to be writable. The lower filesystem can even be another ++overlayfs. The upper filesystem will normally be writable and if it ++is it must support the creation of trusted.* extended attributes, and ++must provide valid d_type in readdir responses, at least for symbolic ++links - so NFS is not suitable. ++ ++A read-only overlay of two read-only filesystems may use any ++filesystem type. ++ ++Directories ++----------- ++ ++Overlaying mainly involved directories. If a given name appears in both ++upper and lower filesystems and refers to a non-directory in either, ++then the lower object is hidden - the name refers only to the upper ++object. ++ ++Where both upper and lower objects are directories, a merged directory ++is formed. ++ ++At mount time, the two directories given as mount options are combined ++into a merged directory. Then whenever a lookup is requested in such ++a merged directory, the lookup is performed in each actual directory ++and the combined result is cached in the dentry belonging to the overlay ++filesystem. If both actual lookups find directories, both are stored ++and a merged directory is created, otherwise only one is stored: the ++upper if it exists, else the lower. ++ ++Only the lists of names from directories are merged. Other content ++such as metadata and extended attributes are reported for the upper ++directory only. These attributes of the lower directory are hidden. ++ ++whiteouts and opaque directories ++-------------------------------- ++ ++In order to support rm and rmdir without changing the lower ++filesystem, an overlay filesystem needs to record in the upper filesystem ++that files have been removed. This is done using whiteouts and opaque ++directories (non-directories are always opaque). ++ ++The overlay filesystem uses extended attributes with a ++"trusted.overlay." prefix to record these details. ++ ++A whiteout is created as a symbolic link with target ++"(overlay-whiteout)" and with xattr "trusted.overlay.whiteout" set to "y". ++When a whiteout is found in the upper level of a merged directory, any ++matching name in the lower level is ignored, and the whiteout itself ++is also hidden. ++ ++A directory is made opaque by setting the xattr "trusted.overlay.opaque" ++to "y". Where the upper filesystem contains an opaque directory, any ++directory in the lower filesystem with the same name is ignored. ++ ++readdir ++------- ++ ++When a 'readdir' request is made on a merged directory, the upper and ++lower directories are each read and the name lists merged in the ++obvious way (upper is read first, then lower - entries that already ++exist are not re-added). This merged name list is cached in the ++'struct file' and so remains as long as the file is kept open. If the ++directory is opened and read by two processes at the same time, they ++will each have separate caches. A seekdir to the start of the ++directory (offset 0) followed by a readdir will cause the cache to be ++discarded and rebuilt. ++ ++This means that changes to the merged directory do not appear while a ++directory is being read. This is unlikely to be noticed by many ++programs. ++ ++seek offsets are assigned sequentially when the directories are read. ++Thus if ++ - read part of a directory ++ - remember an offset, and close the directory ++ - re-open the directory some time later ++ - seek to the remembered offset ++ ++there may be little correlation between the old and new locations in ++the list of filenames, particularly if anything has changed in the ++directory. ++ ++Readdir on directories that are not merged is simply handled by the ++underlying directory (upper or lower). ++ ++ ++Non-directories ++--------------- ++ ++Objects that are not directories (files, symlinks, device-special ++files etc) are presented either from the upper or lower filesystem as ++appropriate. When a file in the lower filesystem is accessed in a way ++the requires write-access; such as opening for write access, changing ++some metadata etc, the file is first copied from the lower filesystem ++to the upper filesystem (copy_up). Note that creating a hard-link ++also requires copy-up, though of course creation of a symlink does ++not. ++ ++The copy_up process first makes sure that the containing directory ++exists in the upper filesystem - creating it and any parents as ++necessary. It then creates the object with the same metadata (owner, ++mode, mtime, symlink-target etc) and then if the object is a file, the ++data is copied from the lower to the upper filesystem. Finally any ++extended attributes are copied up. ++ ++Once the copy_up is complete, the overlay filesystem simply ++provides direct access to the newly created file in the upper ++filesystem - future operations on the file are barely noticed by the ++overlay filesystem (though an operation on the name of the file such as ++rename or unlink will of course be noticed and handled). ++ ++Changes to underlying filesystems ++--------------------------------- ++ ++Offline changes, when the overlay is not mounted, are allowed to either ++the upper or the lower trees. ++ ++Changes to the underlying filesystems while part of a mounted overlay ++filesystem are not allowed. This is not yet enforced, but will be in ++the future. diff --git a/target/linux/generic/patches-2.6.37/210-mini_fo_2.6.25_fixes.patch b/target/linux/generic/patches-2.6.37/210-mini_fo_2.6.25_fixes.patch deleted file mode 100644 index d021ec1b3..000000000 --- a/target/linux/generic/patches-2.6.37/210-mini_fo_2.6.25_fixes.patch +++ /dev/null @@ -1,143 +0,0 @@ ---- a/fs/mini_fo/main.c -+++ b/fs/mini_fo/main.c -@@ -79,6 +79,7 @@ mini_fo_tri_interpose(dentry_t *hidden_d - * of the new inode's fields - */ - -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - /* - * original: inode = iget(sb, hidden_inode->i_ino); - */ -@@ -87,6 +88,13 @@ mini_fo_tri_interpose(dentry_t *hidden_d - err = -EACCES; /* should be impossible??? */ - goto out; - } -+#else -+ inode = mini_fo_iget(sb, iunique(sb, 25)); -+ if (IS_ERR(inode)) { -+ err = PTR_ERR(inode); -+ goto out; -+ } -+#endif - - /* - * interpose the inode if not already interposed -@@ -184,9 +192,9 @@ mini_fo_parse_options(super_block_t *sb, - hidden_root = ERR_PTR(err); - goto out; - } -- hidden_root = nd.dentry; -- stopd(sb)->base_dir_dentry = nd.dentry; -- stopd(sb)->hidden_mnt = nd.mnt; -+ hidden_root = nd_get_dentry(&nd); -+ stopd(sb)->base_dir_dentry = nd_get_dentry(&nd); -+ stopd(sb)->hidden_mnt = nd_get_mnt(&nd); - - } else if(!strncmp("sto=", options, 4)) { - /* parse the storage dir */ -@@ -204,9 +212,9 @@ mini_fo_parse_options(super_block_t *sb, - hidden_root2 = ERR_PTR(err); - goto out; - } -- hidden_root2 = nd2.dentry; -- stopd(sb)->storage_dir_dentry = nd2.dentry; -- stopd(sb)->hidden_mnt2 = nd2.mnt; -+ hidden_root2 = nd_get_dentry(&nd2); -+ stopd(sb)->storage_dir_dentry = nd_get_dentry(&nd2); -+ stopd(sb)->hidden_mnt2 = nd_get_mnt(&nd2); - stohs2(sb) = hidden_root2->d_sb; - - /* validate storage dir, this is done in ---- a/fs/mini_fo/mini_fo.h -+++ b/fs/mini_fo/mini_fo.h -@@ -302,6 +302,10 @@ extern int mini_fo_tri_interpose(dentry_ - extern int mini_fo_cp_cont(dentry_t *tgt_dentry, struct vfsmount *tgt_mnt, - dentry_t *src_dentry, struct vfsmount *src_mnt); - -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) -+extern struct inode *mini_fo_iget(struct super_block *sb, unsigned long ino); -+#endif -+ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - extern int mini_fo_create(inode_t *dir, dentry_t *dentry, int mode, struct nameidata *nd); - -@@ -501,6 +505,29 @@ static inline void double_unlock(struct - #endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ - #endif /* __KERNEL__ */ - -+ -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) -+static inline dentry_t *nd_get_dentry(struct nameidata *nd) -+{ -+ return (nd->path.dentry); -+} -+ -+static inline struct vfsmount *nd_get_mnt(struct nameidata *nd) -+{ -+ return (nd->path.mnt); -+} -+#else -+static inline dentry_t *nd_get_dentry(struct nameidata *nd) -+{ -+ return (nd->dentry); -+} -+ -+static inline struct vfsmount *nd_get_mnt(struct nameidata *nd) -+{ -+ return (nd->mnt); -+} -+#endif -+ - /* - * Definitions for user and kernel code - */ ---- a/fs/mini_fo/super.c -+++ b/fs/mini_fo/super.c -@@ -266,10 +266,31 @@ mini_fo_umount_begin(super_block_t *sb) - } - #endif - -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) -+struct inode * -+mini_fo_iget(struct super_block *sb, unsigned long ino) -+{ -+ struct inode *inode; -+ -+ inode = iget_locked(sb, ino); -+ if (!inode) -+ return ERR_PTR(-ENOMEM); -+ -+ if (!(inode->i_state & I_NEW)) -+ return inode; -+ -+ mini_fo_read_inode(inode); -+ -+ unlock_new_inode(inode); -+ return inode; -+} -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25) */ - - struct super_operations mini_fo_sops = - { -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,25) - read_inode: mini_fo_read_inode, -+#endif - #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) - write_inode: mini_fo_write_inode, - #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ ---- a/fs/mini_fo/aux.c -+++ b/fs/mini_fo/aux.c -@@ -164,11 +164,11 @@ dentry_t *bpath_walk(super_block_t *sb, - err = vfs_path_lookup(mnt->mnt_root, mnt, bpath+1, 0, &nd); - - /* validate */ -- if (err || !nd.dentry || !nd.dentry->d_inode) { -+ if (err || !nd_get_dentry(&nd) || !nd_get_dentry(&nd)->d_inode) { - printk(KERN_CRIT "mini_fo: bpath_walk: path_walk failed.\n"); - return NULL; - } -- return nd.dentry; -+ return nd_get_dentry(&nd); - } - - diff --git a/target/linux/generic/patches-2.6.37/210-overlayfs_2.6.37_backport.patch b/target/linux/generic/patches-2.6.37/210-overlayfs_2.6.37_backport.patch new file mode 100644 index 000000000..b7638c05c --- /dev/null +++ b/target/linux/generic/patches-2.6.37/210-overlayfs_2.6.37_backport.patch @@ -0,0 +1,78 @@ +--- a/fs/overlayfs/overlayfs.c ++++ b/fs/overlayfs/overlayfs.c +@@ -28,13 +28,8 @@ struct ovl_fs { + struct ovl_entry { + struct dentry *__upperdentry; + struct dentry *lowerdentry; +- union { +- struct { +- u64 version; +- bool opaque; +- }; +- struct rcu_head rcu; +- }; ++ u64 version; ++ bool opaque; + }; + + static const char *ovl_whiteout_xattr = "trusted.overlay.whiteout"; +@@ -632,12 +627,6 @@ static const struct file_operations ovl_ + + static const struct inode_operations ovl_dir_inode_operations; + +-static void ovl_entry_free(struct rcu_head *head) +-{ +- struct ovl_entry *oe = container_of(head, struct ovl_entry, rcu); +- kfree(oe); +-} +- + static void ovl_dentry_release(struct dentry *dentry) + { + struct ovl_entry *oe = dentry->d_fsdata; +@@ -645,7 +634,7 @@ static void ovl_dentry_release(struct de + if (oe) { + dput(oe->__upperdentry); + dput(oe->lowerdentry); +- call_rcu(&oe->rcu, ovl_entry_free); ++ kfree(oe); + } + } + +@@ -1338,7 +1327,7 @@ static int ovl_dir_getattr(struct vfsmou + return 0; + } + +-static int ovl_permission(struct inode *inode, int mask, unsigned int flags) ++static int ovl_permission(struct inode *inode, int mask) + { + struct ovl_entry *oe; + struct dentry *alias = NULL; +@@ -1349,8 +1338,6 @@ static int ovl_permission(struct inode * + + if (S_ISDIR(inode->i_mode)) { + oe = inode->i_private; +- } else if (flags & IPERM_FLAG_RCU) { +- return -ECHILD; + } else { + /* + * For non-directories find an alias and get the info +@@ -1377,7 +1364,6 @@ static int ovl_permission(struct inode * + /* Careful in RCU walk mode */ + realinode = ACCESS_ONCE(realdentry->d_inode); + if (!realinode) { +- WARN_ON(!(flags & IPERM_FLAG_RCU)); + return -ENOENT; + } + +@@ -1402,9 +1388,9 @@ static int ovl_permission(struct inode * + } + + if (realinode->i_op->permission) +- err = realinode->i_op->permission(realinode, mask, flags); ++ err = realinode->i_op->permission(realinode, mask); + else +- err = generic_permission(realinode, mask, flags, ++ err = generic_permission(realinode, mask, + realinode->i_op->check_acl); + out_dput: + dput(alias); diff --git a/target/linux/generic/patches-2.6.37/211-mini_fo_2.6.25_dentry_open_war.patch b/target/linux/generic/patches-2.6.37/211-mini_fo_2.6.25_dentry_open_war.patch deleted file mode 100644 index 48a19429b..000000000 --- a/target/linux/generic/patches-2.6.37/211-mini_fo_2.6.25_dentry_open_war.patch +++ /dev/null @@ -1,66 +0,0 @@ ---- a/fs/mini_fo/meta.c -+++ b/fs/mini_fo/meta.c -@@ -442,6 +442,11 @@ int meta_write_d_entry(dentry_t *dentry, - S_IRUSR | S_IWUSR); - #endif - } -+ -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ - /* open META-file for writing */ - meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); - if(!meta_file || IS_ERR(meta_file)) { -@@ -535,6 +540,11 @@ int meta_write_r_entry(dentry_t *dentry, - meta_dentry, S_IRUSR | S_IWUSR); - #endif - } -+ -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ - /* open META-file for writing */ - meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); - if(!meta_file || IS_ERR(meta_file)) { -@@ -671,14 +681,16 @@ int meta_sync_d_list(dentry_t *dentry, i - } - } - -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ - /* open META-file for writing */ - meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ - ERROR opening meta file.\n"); -- /* we don't mntget so we dont't mntput (for now) -- * mntput(meta_mnt); -- */ -+ mntput(meta_mnt); - dput(meta_dentry); - err = -1; - goto out; -@@ -811,14 +823,16 @@ int meta_sync_r_list(dentry_t *dentry, i - } - } - -+ /* $%& err, is this correct? */ -+ meta_mnt = stopd(dentry->d_inode->i_sb)->hidden_mnt2; -+ mntget(meta_mnt); -+ - /* open META-file for writing */ - meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ - ERROR opening meta file.\n"); -- /* we don't mntget so we dont't mntput (for now) -- * mntput(meta_mnt); -- */ -+ mntput(meta_mnt); - dput(meta_dentry); - err = -1; - goto out; diff --git a/target/linux/generic/patches-2.6.37/211-overlayfs_fix_readdir_deadlock.patch b/target/linux/generic/patches-2.6.37/211-overlayfs_fix_readdir_deadlock.patch new file mode 100644 index 000000000..188be3d88 --- /dev/null +++ b/target/linux/generic/patches-2.6.37/211-overlayfs_fix_readdir_deadlock.patch @@ -0,0 +1,124 @@ +--- a/fs/overlayfs/overlayfs.c ++++ b/fs/overlayfs/overlayfs.c +@@ -248,8 +248,7 @@ static struct ovl_cache_entry *ovl_cache + } + + static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, +- u64 ino, unsigned int d_type, +- bool is_whiteout) ++ u64 ino, unsigned int d_type) + { + struct ovl_cache_entry *p; + +@@ -262,7 +261,7 @@ static struct ovl_cache_entry *ovl_cache + p->len = len; + p->type = d_type; + p->ino = ino; +- p->is_whiteout = is_whiteout; ++ p->is_whiteout = false; + } + + return p; +@@ -270,7 +269,7 @@ static struct ovl_cache_entry *ovl_cache + + static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, + const char *name, int len, u64 ino, +- unsigned int d_type, bool is_whiteout) ++ unsigned int d_type) + { + struct rb_node **newp = &rdd->root->rb_node; + struct rb_node *parent = NULL; +@@ -291,11 +290,18 @@ static int ovl_cache_entry_add_rb(struct + return 0; + } + +- p = ovl_cache_entry_new(name, len, ino, d_type, is_whiteout); ++ p = ovl_cache_entry_new(name, len, ino, d_type); + if (p == NULL) + return -ENOMEM; + +- list_add_tail(&p->l_node, rdd->list); ++ /* ++ * Add links before other types to be able to quicky mark ++ * any whiteout entries ++ */ ++ if (d_type == DT_LNK) ++ list_add(&p->l_node, rdd->list); ++ else ++ list_add_tail(&p->l_node, rdd->list); + rb_link_node(&p->node, parent, newp); + rb_insert_color(&p->node, rdd->root); + +@@ -313,7 +319,7 @@ static int ovl_fill_lower(void *buf, con + if (p) { + list_move_tail(&p->l_node, rdd->middle); + } else { +- p = ovl_cache_entry_new(name, namelen, ino, d_type, false); ++ p = ovl_cache_entry_new(name, namelen, ino, d_type); + if (p == NULL) + rdd->err = -ENOMEM; + else +@@ -338,26 +344,9 @@ static int ovl_fill_upper(void *buf, con + loff_t offset, u64 ino, unsigned int d_type) + { + struct ovl_readdir_data *rdd = buf; +- bool is_whiteout = false; + + rdd->count++; +- if (d_type == DT_LNK) { +- struct dentry *dentry; +- +- dentry = lookup_one_len(name, rdd->dir, namelen); +- if (IS_ERR(dentry)) { +- rdd->err = PTR_ERR(dentry); +- goto out; +- } +- is_whiteout = ovl_is_whiteout(dentry); +- dput(dentry); +- } +- +- rdd->err = ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type, +- is_whiteout); +- +-out: +- return rdd->err; ++ return ovl_cache_entry_add_rb(rdd, name, namelen, ino, d_type); + } + + static int ovl_dir_read(struct path *realpath, struct ovl_readdir_data *rdd, +@@ -423,6 +412,26 @@ static void ovl_dir_reset(struct file *f + } + } + ++static void ovl_dir_mark_whiteouts(struct ovl_readdir_data *rdd) ++{ ++ struct ovl_cache_entry *p; ++ struct dentry *dentry; ++ ++ mutex_lock(&rdd->dir->d_inode->i_mutex); ++ list_for_each_entry(p, rdd->list, l_node) { ++ if (p->type != DT_LNK) ++ break; ++ ++ dentry = lookup_one_len(p->name, rdd->dir, p->len); ++ if (IS_ERR(dentry)) ++ continue; ++ ++ p->is_whiteout = ovl_is_whiteout(dentry); ++ dput(dentry); ++ } ++ mutex_unlock(&rdd->dir->d_inode->i_mutex); ++} ++ + static int ovl_dir_read_merged(struct path *upperpath, struct path *lowerpath, + struct ovl_readdir_data *rdd) + { +@@ -436,6 +445,8 @@ static int ovl_dir_read_merged(struct pa + err = ovl_dir_read(upperpath, rdd, ovl_fill_upper); + if (err) + goto out; ++ ++ ovl_dir_mark_whiteouts(rdd); + } + /* + * Insert lowerpath entries before upperpath ones, this allows diff --git a/target/linux/generic/patches-2.6.37/212-mini_fo_2.6.26_fixes.patch b/target/linux/generic/patches-2.6.37/212-mini_fo_2.6.26_fixes.patch deleted file mode 100644 index d2a843c82..000000000 --- a/target/linux/generic/patches-2.6.37/212-mini_fo_2.6.26_fixes.patch +++ /dev/null @@ -1,37 +0,0 @@ ---- a/fs/mini_fo/super.c -+++ b/fs/mini_fo/super.c -@@ -84,6 +84,7 @@ mini_fo_write_inode(inode_t *inode, int - #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ - - -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) - STATIC void - mini_fo_put_inode(inode_t *inode) - { -@@ -99,6 +100,7 @@ mini_fo_put_inode(inode_t *inode) - if (atomic_read(&inode->i_count) == 1) - inode->i_nlink = 0; - } -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */ - - - #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -@@ -242,7 +244,7 @@ mini_fo_clear_inode(inode_t *inode) - * dies. - */ - STATIC void --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18) -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,18)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26)) - mini_fo_umount_begin(struct vfsmount *mnt, int flags) - { - struct vfsmount *hidden_mnt; -@@ -294,7 +296,9 @@ struct super_operations mini_fo_sops = - #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) - write_inode: mini_fo_write_inode, - #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ -+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) - put_inode: mini_fo_put_inode, -+#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */ - #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) - delete_inode: mini_fo_delete_inode, - #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ diff --git a/target/linux/generic/patches-2.6.37/213-mini_fo_2.6.27_fixes.patch b/target/linux/generic/patches-2.6.37/213-mini_fo_2.6.27_fixes.patch deleted file mode 100644 index f92c18b25..000000000 --- a/target/linux/generic/patches-2.6.37/213-mini_fo_2.6.27_fixes.patch +++ /dev/null @@ -1,41 +0,0 @@ ---- a/fs/mini_fo/inode.c -+++ b/fs/mini_fo/inode.c -@@ -439,7 +439,7 @@ mini_fo_symlink(inode_t *dir, dentry_t * - int err=0; - dentry_t *hidden_sto_dentry; - dentry_t *hidden_sto_dir_dentry; --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) - umode_t mode; - #endif - -@@ -466,7 +466,7 @@ mini_fo_symlink(inode_t *dir, dentry_t * - down(&hidden_sto_dir_dentry->d_inode->i_sem); - #endif - --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) - mode = S_IALLUGO; - err = vfs_symlink(hidden_sto_dir_dentry->d_inode, - hidden_sto_dentry, symname, mode); -@@ -1128,7 +1128,7 @@ void mini_fo_put_link(struct dentry *den - #endif - - STATIC int --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)) - mini_fo_permission(inode_t *inode, int mask, struct nameidata *nd) - #else - mini_fo_permission(inode_t *inode, int mask) -@@ -1150,8 +1150,9 @@ mini_fo_permission(inode_t *inode, int m - * if (err) - * goto out; - */ -- --#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27) -+ err = inode_permission(hidden_inode, mask); -+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) - err = permission(hidden_inode, mask, nd); - #else - err = permission(hidden_inode, mask); diff --git a/target/linux/generic/patches-2.6.37/214-mini_fo_2.6.29.patch b/target/linux/generic/patches-2.6.37/214-mini_fo_2.6.29.patch deleted file mode 100644 index 63d704bf5..000000000 --- a/target/linux/generic/patches-2.6.37/214-mini_fo_2.6.29.patch +++ /dev/null @@ -1,96 +0,0 @@ ---- a/fs/mini_fo/aux.c -+++ b/fs/mini_fo/aux.c -@@ -236,7 +236,7 @@ int mini_fo_cp_cont(dentry_t *tgt_dentry - mntget(src_mnt); - - /* open file write only */ -- tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1); -+ tgt_file = dentry_open(tgt_dentry, tgt_mnt, 0x1, current_cred()); - if(!tgt_file || IS_ERR(tgt_file)) { - printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening target file.\n"); - err = PTR_ERR(tgt_file); -@@ -244,7 +244,7 @@ int mini_fo_cp_cont(dentry_t *tgt_dentry - } - - /* open file read only */ -- src_file = dentry_open(src_dentry, src_mnt, 0x0); -+ src_file = dentry_open(src_dentry, src_mnt, 0x0, current_cred()); - if(!src_file || IS_ERR(src_file)) { - printk(KERN_CRIT "mini_fo_cp_cont: ERROR opening source file.\n"); - err = PTR_ERR(src_file); ---- a/fs/mini_fo/file.c -+++ b/fs/mini_fo/file.c -@@ -437,7 +437,7 @@ mini_fo_open(inode_t *inode, file_t *fil - mntget(stopd(inode->i_sb)->hidden_mnt); - hidden_file = dentry_open(hidden_dentry, - stopd(inode->i_sb)->hidden_mnt, -- hidden_flags); -+ hidden_flags, file->f_cred); - if (IS_ERR(hidden_file)) { - err = PTR_ERR(hidden_file); - dput(hidden_dentry); -@@ -479,7 +479,7 @@ mini_fo_open(inode_t *inode, file_t *fil - mntget(stopd(inode->i_sb)->hidden_mnt); - hidden_file = dentry_open(hidden_dentry, - stopd(inode->i_sb)->hidden_mnt, -- hidden_flags); -+ hidden_flags, file->f_cred); - if (IS_ERR(hidden_file)) { - err = PTR_ERR(hidden_file); - dput(hidden_dentry); -@@ -512,7 +512,7 @@ mini_fo_open(inode_t *inode, file_t *fil - mntget(stopd(inode->i_sb)->hidden_mnt2); - hidden_sto_file = dentry_open(hidden_sto_dentry, - stopd(inode->i_sb)->hidden_mnt2, -- hidden_flags); -+ hidden_flags, file->f_cred); - - /* dentry_open dputs the dentry if it fails */ - if (IS_ERR(hidden_sto_file)) { ---- a/fs/mini_fo/meta.c -+++ b/fs/mini_fo/meta.c -@@ -56,7 +56,7 @@ int meta_build_lists(dentry_t *dentry) - - - /* open META-file for reading */ -- meta_file = dentry_open(meta_dentry, meta_mnt, 0x0); -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x0, current_cred()); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_build_lists: \ - ERROR opening META file.\n"); -@@ -448,7 +448,7 @@ int meta_write_d_entry(dentry_t *dentry, - mntget(meta_mnt); - - /* open META-file for writing */ -- meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1, current_cred()); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_write_d_entry: \ - ERROR opening meta file.\n"); -@@ -546,7 +546,7 @@ int meta_write_r_entry(dentry_t *dentry, - mntget(meta_mnt); - - /* open META-file for writing */ -- meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1, current_cred()); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_write_r_entry: \ - ERROR opening meta file.\n"); -@@ -686,7 +686,7 @@ int meta_sync_d_list(dentry_t *dentry, i - mntget(meta_mnt); - - /* open META-file for writing */ -- meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1, current_cred()); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_sync_d_list: \ - ERROR opening meta file.\n"); -@@ -828,7 +828,7 @@ int meta_sync_r_list(dentry_t *dentry, i - mntget(meta_mnt); - - /* open META-file for writing */ -- meta_file = dentry_open(meta_dentry, meta_mnt, 0x1); -+ meta_file = dentry_open(meta_dentry, meta_mnt, 0x1, current_cred()); - if(!meta_file || IS_ERR(meta_file)) { - printk(KERN_CRIT "mini_fo: meta_sync_r_list: \ - ERROR opening meta file.\n"); diff --git a/target/linux/generic/patches-2.6.37/215-mini_fo_2.6.30.patch b/target/linux/generic/patches-2.6.37/215-mini_fo_2.6.30.patch deleted file mode 100644 index 2d2092061..000000000 --- a/target/linux/generic/patches-2.6.37/215-mini_fo_2.6.30.patch +++ /dev/null @@ -1,157 +0,0 @@ ---- a/fs/mini_fo/aux.c -+++ b/fs/mini_fo/aux.c -@@ -86,8 +86,10 @@ int get_neg_sto_dentry(dentry_t *dentry) - len = dentry->d_name.len; - name = dentry->d_name.name; - -+ mutex_lock(&dtohd2(dentry->d_parent)->d_inode->i_mutex); - dtohd2(dentry) = - lookup_one_len(name, dtohd2(dentry->d_parent), len); -+ mutex_unlock(&dtohd2(dentry->d_parent)->d_inode->i_mutex); - - out: - return err; -@@ -426,7 +428,9 @@ int build_sto_structure(dentry_t *dir, d - const unsigned char *name; - len = dtohd(dentry)->d_name.len; - name = dtohd(dentry)->d_name.name; -+ mutex_lock(&dtohd2(dir)->d_inode->i_mutex); - hidden_sto_dentry = lookup_one_len(name, dtohd2(dir), len); -+ mutex_unlock(&dtohd2(dir)->d_inode->i_mutex); - dtohd2(dentry) = hidden_sto_dentry; - } - ---- a/fs/mini_fo/inode.c -+++ b/fs/mini_fo/inode.c -@@ -113,17 +113,23 @@ mini_fo_lookup(inode_t *dir, dentry_t *d - hidden_dir_dentry = hidden_dentry->d_parent; - kfree(bpath); - } -- else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) -+ else if(hidden_dir_dentry && hidden_dir_dentry->d_inode) { -+ mutex_lock(&hidden_dir_dentry->d_inode->i_mutex); - hidden_dentry = - lookup_one_len(name, hidden_dir_dentry, namelen); -- else -+ mutex_unlock(&hidden_dir_dentry->d_inode->i_mutex); -+ } else { - hidden_dentry = NULL; -+ } - -- if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) -+ if(hidden_sto_dir_dentry && hidden_sto_dir_dentry->d_inode) { -+ mutex_lock(&hidden_sto_dir_dentry->d_inode->i_mutex); - hidden_sto_dentry = - lookup_one_len(name, hidden_sto_dir_dentry, namelen); -- else -+ mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); -+ } else { - hidden_sto_dentry = NULL; -+ } - - /* catch error in lookup */ - if (IS_ERR(hidden_dentry) || IS_ERR(hidden_sto_dentry)) { -@@ -553,9 +559,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - #endif - - /* Delete an old WOL file contained in the storage dir */ -+ mutex_lock(&hidden_sto_dentry->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - hidden_sto_dentry, - strlen(META_FILENAME)); -+ mutex_unlock(&hidden_sto_dentry->d_inode->i_mutex); - if(meta_dentry->d_inode) { - err = vfs_unlink(hidden_sto_dentry->d_inode, meta_dentry); - dput(meta_dentry); -@@ -643,9 +651,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - #endif - - /* Delete an old WOL file contained in the storage dir */ -+ mutex_lock(&hidden_sto_dentry->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - hidden_sto_dentry, - strlen(META_FILENAME)); -+ mutex_unlock(&hidden_sto_dentry->d_inode->i_mutex); - if(meta_dentry->d_inode) { - /* is this necessary? dget(meta_dentry); */ - err = vfs_unlink(hidden_sto_dentry->d_inode, -@@ -688,9 +698,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - #endif - - /* Delete an old WOL file contained in the storage dir */ -+ mutex_lock(&hidden_sto_dentry->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - hidden_sto_dentry, - strlen(META_FILENAME)); -+ mutex_unlock(&hidden_sto_dentry->d_inode->i_mutex); - if(meta_dentry->d_inode) { - /* is this necessary? dget(meta_dentry); */ - err = vfs_unlink(hidden_sto_dentry->d_inode, ---- a/fs/mini_fo/meta.c -+++ b/fs/mini_fo/meta.c -@@ -43,9 +43,11 @@ int meta_build_lists(dentry_t *dentry) - - /* might there be a META-file? */ - if(dtohd2(dentry) && dtohd2(dentry)->d_inode) { -+ mutex_lock(&dtohd2(dentry)->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), - strlen(META_FILENAME)); -+ mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); - if(!meta_dentry->d_inode) { - dput(meta_dentry); - goto out_ok; -@@ -426,8 +428,11 @@ int meta_write_d_entry(dentry_t *dentry, - goto out; - } - } -+ -+ mutex_lock(&dtohd2(dentry)->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), strlen (META_FILENAME)); -+ mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); - - /* We need to create a META-file */ - if(!meta_dentry->d_inode) { -@@ -527,9 +532,13 @@ int meta_write_r_entry(dentry_t *dentry, - goto out; - } - } -+ -+ mutex_lock(&dtohd2(dentry)->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), - strlen (META_FILENAME)); -+ mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -@@ -641,9 +650,13 @@ int meta_sync_d_list(dentry_t *dentry, i - goto out; - } - } -+ -+ mutex_lock(&dtohd2(dentry)->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), - strlen(META_FILENAME)); -+ mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) -@@ -784,9 +797,13 @@ int meta_sync_r_list(dentry_t *dentry, i - goto out; - } - } -+ -+ mutex_lock(&dtohd2(dentry)->d_inode->i_mutex); - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), - strlen(META_FILENAME)); -+ mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) diff --git a/target/linux/generic/patches-2.6.37/216-mini_fo_locking.patch b/target/linux/generic/patches-2.6.37/216-mini_fo_locking.patch deleted file mode 100644 index 4f075920f..000000000 --- a/target/linux/generic/patches-2.6.37/216-mini_fo_locking.patch +++ /dev/null @@ -1,25 +0,0 @@ ---- a/fs/mini_fo/state.c -+++ b/fs/mini_fo/state.c -@@ -537,17 +537,17 @@ int nondir_mod_to_del(dentry_t *dentry) - dtohd(dentry) = NULL; - dtost(dentry) = DELETED; - -- /* add deleted file to META-file */ -- meta_add_d_entry(dentry->d_parent, -- dentry->d_name.name, -- dentry->d_name.len); -- - /* was: unlock_dir(hidden_sto_dir_dentry); */ - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - mutex_unlock(&hidden_sto_dir_dentry->d_inode->i_mutex); - #else - up(&hidden_sto_dir_dentry->d_inode->i_sem); - #endif -+ /* add deleted file to META-file */ -+ meta_add_d_entry(dentry->d_parent, -+ dentry->d_name.name, -+ dentry->d_name.len); -+ - dput(hidden_sto_dir_dentry); - - out: diff --git a/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.35.patch b/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.35.patch deleted file mode 100644 index 61f54f035..000000000 --- a/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.35.patch +++ /dev/null @@ -1,45 +0,0 @@ ---- a/fs/mini_fo/file.c -+++ b/fs/mini_fo/file.c -@@ -599,7 +599,7 @@ mini_fo_release(inode_t *inode, file_t * - } - - STATIC int --mini_fo_fsync(file_t *file, dentry_t *dentry, int datasync) -+mini_fo_fsync(file_t *file, int datasync) - { - int err1 = 0; - int err2 = 0; -@@ -609,14 +609,14 @@ mini_fo_fsync(file_t *file, dentry_t *de - check_mini_fo_file(file); - - if ((hidden_file = ftohf(file)) != NULL) { -- hidden_dentry = dtohd(dentry); -+ hidden_dentry = dtohd(file->f_path.dentry); - if (hidden_file->f_op && hidden_file->f_op->fsync) { - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - mutex_lock(&hidden_dentry->d_inode->i_mutex); - #else - down(&hidden_dentry->d_inode->i_sem); - #endif -- err1 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+ err1 = hidden_file->f_op->fsync(hidden_file, datasync); - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - mutex_unlock(&hidden_dentry->d_inode->i_mutex); - #else -@@ -626,14 +626,14 @@ mini_fo_fsync(file_t *file, dentry_t *de - } - - if ((hidden_file = ftohf2(file)) != NULL) { -- hidden_dentry = dtohd2(dentry); -+ hidden_dentry = dtohd2(file->f_path.dentry); - if (hidden_file->f_op && hidden_file->f_op->fsync) { - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - mutex_lock(&hidden_dentry->d_inode->i_mutex); - #else - down(&hidden_dentry->d_inode->i_sem); - #endif -- err2 = hidden_file->f_op->fsync(hidden_file, hidden_dentry, datasync); -+ err2 = hidden_file->f_op->fsync(hidden_file, datasync); - #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) - mutex_unlock(&hidden_dentry->d_inode->i_mutex); - #else diff --git a/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.36_fixes.patch b/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.36_fixes.patch deleted file mode 100644 index 79024adbb..000000000 --- a/target/linux/generic/patches-2.6.37/217-mini_fo_2.6.36_fixes.patch +++ /dev/null @@ -1,43 +0,0 @@ ---- a/fs/mini_fo/super.c -+++ b/fs/mini_fo/super.c -@@ -76,7 +76,7 @@ mini_fo_read_inode(inode_t *inode) - * to write some of our own stuff to disk. - */ - STATIC void --mini_fo_write_inode(inode_t *inode, int sync) -+mini_fo_write_inode(inode_t *inode, struct writeback_control *wbc) - { - print_entry_location(); - print_exit_location(); -@@ -112,13 +112,14 @@ mini_fo_put_inode(inode_t *inode) - * on our and the lower inode. - */ - STATIC void --mini_fo_delete_inode(inode_t *inode) -+mini_fo_evict_inode(inode_t *inode) - { - print_entry_location(); - -- fist_checkinode(inode, "mini_fo_delete_inode IN"); -- inode->i_size = 0; /* every f/s seems to do that */ -- clear_inode(inode); -+ fist_checkinode(inode, "mini_fo_evict_inode IN"); -+ truncate_inode_pages(&inode->i_data, 0); /* FIXME: do we need this? */ -+ invalidate_inode_buffers(inode); -+ end_writeback(inode); - - print_exit_location(); - } -@@ -300,11 +301,10 @@ struct super_operations mini_fo_sops = - put_inode: mini_fo_put_inode, - #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,26) */ - #if defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) -- delete_inode: mini_fo_delete_inode, -+ evict_inode: mini_fo_evict_inode, - #endif /* defined(FIST_DEBUG) || defined(FIST_FILTER_SCA) */ - put_super: mini_fo_put_super, - statfs: mini_fo_statfs, - remount_fs: mini_fo_remount_fs, -- clear_inode: mini_fo_clear_inode, - umount_begin: mini_fo_umount_begin, - }; diff --git a/target/linux/generic/patches-2.6.37/218-mini_fo_dentry_check.patch b/target/linux/generic/patches-2.6.37/218-mini_fo_dentry_check.patch deleted file mode 100644 index cf53e4826..000000000 --- a/target/linux/generic/patches-2.6.37/218-mini_fo_dentry_check.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- a/fs/mini_fo/meta.c -+++ b/fs/mini_fo/meta.c -@@ -48,6 +48,9 @@ int meta_build_lists(dentry_t *dentry) - dtohd2(dentry), - strlen(META_FILENAME)); - mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ if (IS_ERR(meta_dentry)) -+ goto out_ok; -+ - if(!meta_dentry->d_inode) { - dput(meta_dentry); - goto out_ok; -@@ -433,6 +436,8 @@ int meta_write_d_entry(dentry_t *dentry, - meta_dentry = lookup_one_len(META_FILENAME, - dtohd2(dentry), strlen (META_FILENAME)); - mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ if (IS_ERR(meta_dentry)) -+ return PTR_ERR(meta_dentry); - - /* We need to create a META-file */ - if(!meta_dentry->d_inode) { -@@ -538,6 +543,8 @@ int meta_write_r_entry(dentry_t *dentry, - dtohd2(dentry), - strlen (META_FILENAME)); - mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ if (IS_ERR(meta_dentry)) -+ return PTR_ERR(meta_dentry); - - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ -@@ -656,6 +663,8 @@ int meta_sync_d_list(dentry_t *dentry, i - dtohd2(dentry), - strlen(META_FILENAME)); - mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ if (IS_ERR(meta_dentry)) -+ return PTR_ERR(meta_dentry); - - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ -@@ -803,6 +812,8 @@ int meta_sync_r_list(dentry_t *dentry, i - dtohd2(dentry), - strlen(META_FILENAME)); - mutex_unlock(&dtohd2(dentry)->d_inode->i_mutex); -+ if (IS_ERR(meta_dentry)) -+ return PTR_ERR(meta_dentry); - - if(!meta_dentry->d_inode) { - /* We need to create a META-file */ |