From ccd5ec0709e670a4fc4e530121f3132bf58e2915 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Sun, 29 Oct 2006 03:42:58 +0000 Subject: [PATCH] move mini_fo into the kernel, fix the nasty readdir bug, add hack to disallow rmdir on directories that are also present in the base dir, not easy to fix and too dangerous in its present state SVN-Revision: 5330 --- .../package/base-files/default/etc/preinit | 1 - openwrt/target/linux/linux-2.4/config/brcm | 1 + .../patches/generic/223-mini_fo.patch | 7773 +++++++++++++++++ openwrt/target/linux/package/Config.in | 1 - openwrt/target/linux/package/Makefile | 1 - .../target/linux/package/mini_fo/Config.in | 6 - openwrt/target/linux/package/mini_fo/Makefile | 39 - .../package/mini_fo/ipkg/kmod-mini-fo.control | 4 - .../mini_fo/patches/101-kmod_build.patch | 52 - .../mini_fo/patches/102-mutex_change.patch | 602 -- .../patches/150-glob_use_getdents64.patch | 12 - 11 files changed, 7774 insertions(+), 718 deletions(-) create mode 100644 openwrt/target/linux/linux-2.4/patches/generic/223-mini_fo.patch delete mode 100644 openwrt/target/linux/package/mini_fo/Config.in delete mode 100644 openwrt/target/linux/package/mini_fo/Makefile delete mode 100644 openwrt/target/linux/package/mini_fo/ipkg/kmod-mini-fo.control delete mode 100644 openwrt/target/linux/package/mini_fo/patches/101-kmod_build.patch delete mode 100644 openwrt/target/linux/package/mini_fo/patches/102-mutex_change.patch delete mode 100644 openwrt/toolchain/uClibc/patches/150-glob_use_getdents64.patch diff --git a/openwrt/package/base-files/default/etc/preinit b/openwrt/package/base-files/default/etc/preinit index 499bf74f90..029f7c2b71 100755 --- a/openwrt/package/base-files/default/etc/preinit +++ b/openwrt/package/base-files/default/etc/preinit @@ -57,7 +57,6 @@ else fi echo /sbin/hotplug > /proc/sys/kernel/hotplug -insmod mini_fo 2>&- mount_root ${FAILSAFE:+failsafe} exec /sbin/init diff --git a/openwrt/target/linux/linux-2.4/config/brcm b/openwrt/target/linux/linux-2.4/config/brcm index ed5bba7bac..2dc1393971 100644 --- a/openwrt/target/linux/linux-2.4/config/brcm +++ b/openwrt/target/linux/linux-2.4/config/brcm @@ -1050,6 +1050,7 @@ CONFIG_JOLIET=y # CONFIG_JFS_DEBUG is not set # CONFIG_JFS_STATISTICS is not set CONFIG_MINIX_FS=m +CONFIG_MINI_FO=y # CONFIG_VXFS_FS is not set # CONFIG_NTFS_FS is not set # CONFIG_NTFS_RW is not set diff --git a/openwrt/target/linux/linux-2.4/patches/generic/223-mini_fo.patch b/openwrt/target/linux/linux-2.4/patches/generic/223-mini_fo.patch new file mode 100644 index 0000000000..15863f9fdd --- /dev/null +++ b/openwrt/target/linux/linux-2.4/patches/generic/223-mini_fo.patch @@ -0,0 +1,7773 @@ +diff -urN linux.old/fs/Config.in linux.dev/fs/Config.in +--- linux.old/fs/Config.in 2006-10-28 22:29:42.000000000 +0200 ++++ linux.dev/fs/Config.in 2006-10-29 01:45:26.000000000 +0200 +@@ -70,6 +70,7 @@ + dep_mbool ' JFS statistics' CONFIG_JFS_STATISTICS $CONFIG_JFS_FS + + tristate 'Minix fs support' CONFIG_MINIX_FS ++tristate 'mini_fo filesystem overlay' CONFIG_MINI_FO + + tristate 'FreeVxFS file system support (VERITAS VxFS(TM) compatible)' CONFIG_VXFS_FS + tristate 'NTFS file system support (read only)' CONFIG_NTFS_FS +diff -urN linux.old/fs/Makefile linux.dev/fs/Makefile +--- linux.old/fs/Makefile 2006-10-28 22:29:42.000000000 +0200 ++++ linux.dev/fs/Makefile 2006-10-29 01:45:50.000000000 +0200 +@@ -31,6 +31,7 @@ + subdir-$(CONFIG_CODA_FS) += coda + subdir-$(CONFIG_INTERMEZZO_FS) += intermezzo + subdir-$(CONFIG_MINIX_FS) += minix ++subdir-$(CONFIG_MINI_FO) += mini_fo + subdir-$(CONFIG_FAT_FS) += fat + subdir-$(CONFIG_UMSDOS_FS) += umsdos + subdir-$(CONFIG_MSDOS_FS) += msdos +diff -urN linux.old/fs/mini_fo/aux.c linux.dev/fs/mini_fo/aux.c +--- linux.old/fs/mini_fo/aux.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/aux.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,580 @@ ++/* ++ * 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 ++ * ++ * 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 ++#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 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; ++ } ++ ++ /* setup nd as path_init does */ ++ nd.last_type = LAST_ROOT; /* if there are only slashes... */ ++ nd.flags = LOOKUP_FOLLOW; ++ /* fix this: how do I reach this lock? ++ * read_lock(¤t->fs->lock); */ ++ nd.mnt = mntget(stopd(sb)->hidden_mnt); ++ nd.dentry = dget(stopd(sb)->base_dir_dentry); ++ /* read_unlock(¤t->fs->lock); */ ++ ++ err = path_walk(bpath+1, &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 */ ++ +diff -urN linux.old/fs/mini_fo/ChangeLog linux.dev/fs/mini_fo/ChangeLog +--- linux.old/fs/mini_fo/ChangeLog 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/ChangeLog 2006-10-29 01:18:22.000000000 +0200 +@@ -0,0 +1,281 @@ ++2006-01-24 Markus Klotzbuecher ++ ++ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to ++ retain backwards compatibility. ++ ++2006-01-24 Ed L. Cashin ++ ++ * Support for the new mutex infrastructure ++ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) ++ ++2005-10-15 Markus Klotzbuecher ++ ++ * Bugfix for a serious memory leak in mini_fo_follow_link. ++ ++2005-09-21 Markus Klotzbuecher ++ ++ * new release 0.6.1 ++ ++ * fix of a compiler warning due to changes in 2.6.13 ++ ++2005-09-21 Klaus Wenninger ++ ++ * 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 ++ ++ * fix for an oops when overwriting a binary thats beeing ++ executed. ++ ++2005-06-09 ++ ++ * 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 ++ ++ * Added overlay script that allows to easily mount mini_fo ontop ++ of a given base directory ++ ++2005-05-10 ++ ++ * inode.c: xattr functions return -EOPNOSUPP instead of ++ -ENOSUPP, what confuses "ls -l" ++ ++ * Changed license from LGPL to GPL. ++ ++2005-05-08 root ++ ++ * Makefile: clean it up and added make install and make ++ uninstall. ++ ++2005-05-06 ++ ++ * 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 ++ ++ * 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 ++ ++ * 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 ++ ++ * 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 ++ ++ * 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 ++ ++ * aux.c: get_neg_sto_dentry(): allow to be called for dentries ++ in state UNMODIFIED, NON_EXISTANT _and_ DELETED. ++ ++2005-04-19 ++ ++ * 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 ++ ++ * Bugfixes ++ ++2005-04-13 root ++ ++ * Added file state.c for the state transition ++ functions. Doesn't work very well yet, though... ++ ++2005-04-12 ++ ++ * Porting to 2.6 started, which is easier than expected, also ++ due to Olivier previous work. ++ ++2005-04-08 ++ ++ * 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 ++ ++ * Merged devel branch back to main trunk and updated the ++ RELEASE notes. This will be 0-6-0-pre1. ++ ++2005-03-31 Markus Klotzbuecher ++ ++ * 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 ++ ++ * Many, many directory renaming bugfixes and a lot of other ++ cleanup. Dir renaming seems to work relatively stable. ++ ++2005-03-22 Markus Klotzbuecher ++ ++ * 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 ++ ++ * Finished implementing meta.c stuff required for directory ++ renaming. ++ ++2005-03-17 Markus Klotzbuecher ++ ++ * 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 ++ ++ * Implemented new META subsystem, removed old WOL stuff in favor ++ if it. ++ ++ * After some basic testing everything seems ok... ++ ++2005-03-11 Markus Klotzbuecher ++ ++ * 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 ++ ++ * 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 ++ ++ * 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 ++ ++ * Implemented directory deleting (inode.c) ++ ++ * main.c: made mini_fo_parse_options a little more robust. ++ ++2004-12-22 ++ ++ * 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 ++ ++ * Fix: owner and group where not correctly copied from base to ++ storage. ++ ++ ++2004-10-05 Gleb Natapov ++ ++ * Implementation of fsync, fasync and lock mini_fo functions. ++ ++ ++2004-09-29 Bob Lee ++ ++ * Fix of a serious pointer bug ++ ++ ++2004-09-28 Gleb Natapov ++ ++ * Implementation of mini_fo_mknod and mini_fo_rename, support ++ for device files. ++ +diff -urN linux.old/fs/mini_fo/dentry.c linux.dev/fs/mini_fo/dentry.c +--- linux.old/fs/mini_fo/dentry.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/dentry.c 2006-10-29 04:32:06.000000000 +0100 +@@ -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 ++ * ++ * 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 ++#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, ++}; +diff -urN linux.old/fs/mini_fo/file.c linux.dev/fs/mini_fo/file.c +--- linux.old/fs/mini_fo/file.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/file.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,717 @@ ++/* ++ * 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 ++ * ++ * 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 ++#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 ++mini_fo_filldir(void * __buf, const char * name, int namlen, loff_t offset, ++ ino_t ino, unsigned int d_type) ++{ ++ struct linux_dirent *dirent, d; ++ struct getdents_callback * buf = (struct getdents_callback *) __buf; ++ int reclen; ++ 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) ++ hidden_file->f_op->flush(hidden_file); ++ 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) ++ hidden_file->f_op->flush(hidden_file); ++ 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 ++mini_fo_flush(file_t *file) ++{ ++ 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) ++ err1 = hidden_file->f_op->flush(hidden_file); ++ } ++ if(ftohf2(file) != NULL) { ++ hidden_file = ftohf2(file); ++ if (hidden_file->f_op && hidden_file->f_op->flush) ++ err2 = hidden_file->f_op->flush(hidden_file); ++ } ++ } ++ 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); ++} ++ ++ ++STATIC int ++mini_fo_lock(file_t *file, int cmd, struct file_lock *fl) ++{ ++ int err = -EINVAL; ++ file_t *hidden_file = NULL; ++ ++ if(!check_mini_fo_file(file)) ++ goto out; ++ ++ /* which file shall we lock? */ ++ if(ftohf2(file)) ++ hidden_file = ftohf2(file); ++ else ++ hidden_file = ftohf(file); ++ ++ if (hidden_file->f_op->lock) { ++ fl->fl_file = hidden_file; ++ err = hidden_file->f_op->lock(hidden_file, F_GETLK, fl); ++ fl->fl_file = file; ++ } else { ++ if(posix_test_lock(hidden_file, fl)) ++ err = 0; ++ } ++ out: ++ return err; ++} ++ ++ ++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 */ ++ }; +diff -urN linux.old/fs/mini_fo/fist.h linux.dev/fs/mini_fo/fist.h +--- linux.old/fs/mini_fo/fist.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/fist.h 2005-10-15 12:49:43.000000000 +0200 +@@ -0,0 +1,248 @@ ++/* ++ * 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 ++ * ++ * 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 ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#ifdef CONFIG_MODVERSIONS ++# define MODVERSIONS ++# include ++#endif /* CONFIG_MODVERSIONS */ ++#endif /* KERNEL_VERSION < 2.6.0 */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0) ++#include ++#else ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#endif ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) ++#include ++#endif ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include ++#endif ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* ++ * 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_ */ +diff -urN linux.old/fs/mini_fo/inode.c linux.dev/fs/mini_fo/inode.c +--- linux.old/fs/mini_fo/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/inode.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,1574 @@ ++/* ++ * 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 ++ * ++ * 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 ++#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) { ++ 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 ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ ++ /* 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 { ++ /* 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; ++ ++ /* 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) { ++ ++ err = get_neg_sto_dentry(dentry); ++ if(err) { ++ err = -EINVAL; ++ goto out; ++ } ++ ++ /* 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; ++ ++ /* 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 ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ ++ /* 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 ++ ++ /* avoid destroying the hidden inode if the file is in use */ ++ dget(hidden_sto_dentry); ++ ++ /* 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_lock(&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) */ ++ }; +diff -urN linux.old/fs/mini_fo/main.c linux.dev/fs/mini_fo/main.c +--- linux.old/fs/mini_fo/main.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/main.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,414 @@ ++/* ++ * 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 ++ * ++ * 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 ++#endif ++ ++#include "fist.h" ++#include "mini_fo.h" ++#include ++ ++/* This definition must only appear after we include */ ++#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) ++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); ++} ++ ++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 "); ++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) +diff -urN linux.old/fs/mini_fo/Makefile linux.dev/fs/mini_fo/Makefile +--- linux.old/fs/mini_fo/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/Makefile 2006-10-29 03:08:04.000000000 +0100 +@@ -0,0 +1,22 @@ ++# ++# Makefile for mini_fo 2.4 and 2.6 Linux kernels ++# ++# Copyright (C) 2004, 2005 Markus Klotzbuecher ++# ++# 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 ++ ++O_TARGET := $(obj-$(CONFIG_MINI_FO)) ++obj-y := $(mini_fo-objs) ++ ++-include $(TOPDIR)/Rules.make ++ ++# dependencies ++${mini_fo-objs}: mini_fo.h fist.h ++ +diff -urN linux.old/fs/mini_fo/meta.c linux.dev/fs/mini_fo/meta.c +--- linux.old/fs/mini_fo/meta.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/meta.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,1000 @@ ++/* ++ * Copyright (C) 2004, 2005 Markus Klotzbuecher ++ * ++ * 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 ++#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; ++} ++ +diff -urN linux.old/fs/mini_fo/mini_fo.h linux.dev/fs/mini_fo/mini_fo.h +--- linux.old/fs/mini_fo/mini_fo.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/mini_fo.h 2006-10-29 01:18:22.000000000 +0200 +@@ -0,0 +1,503 @@ ++/* ++ * 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 ++ * ++ * 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 */ ++# define itopd(ino) ((struct mini_fo_inode_info *)(ino)->u.generic_ip) ++# define __itopd(ino) ((ino)->u.generic_ip) ++/* 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; ++ dest->i_blksize = src->i_blksize; ++#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_ */ +diff -urN linux.old/fs/mini_fo/mini_fo-merge linux.dev/fs/mini_fo/mini_fo-merge +--- linux.old/fs/mini_fo/mini_fo-merge 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/mini_fo-merge 2005-10-15 12:49:43.000000000 +0200 +@@ -0,0 +1,180 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# 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 < -s ++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 ++ the directory of the base file system. ++ ++ -s ++ 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< /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!" +diff -urN linux.old/fs/mini_fo/mini_fo-overlay linux.dev/fs/mini_fo/mini_fo-overlay +--- linux.old/fs/mini_fo/mini_fo-overlay 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/mini_fo-overlay 2005-10-15 12:49:43.000000000 +0200 +@@ -0,0 +1,130 @@ ++#!/bin/bash ++# ++# Copyright (C) 2005 Markus Klotzbuecher ++# 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 <" in $STO_DIR, ++and mount point "mini_fo-" in $MNT_DIR. ++ ++Options: ++ -s ++ 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 ++ change the directory in which the storage directory will be ++ created (default is currently "$STO_DIR". ++ ++ -m ++ 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 +diff -urN linux.old/fs/mini_fo/mmap.c linux.dev/fs/mini_fo/mmap.c +--- linux.old/fs/mini_fo/mmap.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/mmap.c 2006-10-29 04:32:06.000000000 +0100 +@@ -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 ++ * ++ * 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 ++#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; ++} +diff -urN linux.old/fs/mini_fo/README linux.dev/fs/mini_fo/README +--- linux.old/fs/mini_fo/README 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/README 2005-10-15 12:49:43.000000000 +0200 +@@ -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=,sto=\ ++ ++ ++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 ++ ++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. ++ ++ +diff -urN linux.old/fs/mini_fo/RELEASE_NOTES linux.dev/fs/mini_fo/RELEASE_NOTES +--- linux.old/fs/mini_fo/RELEASE_NOTES 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/RELEASE_NOTES 2005-10-15 12:49:43.000000000 +0200 +@@ -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. +diff -urN linux.old/fs/mini_fo/state.c linux.dev/fs/mini_fo/state.c +--- linux.old/fs/mini_fo/state.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/state.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,620 @@ ++/* ++ * Copyright (C) 2005 Markus Klotzbuecher ++ * ++ * 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 ++#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; ++} ++ +diff -urN linux.old/fs/mini_fo/super.c linux.dev/fs/mini_fo/super.c +--- linux.old/fs/mini_fo/super.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux.dev/fs/mini_fo/super.c 2006-10-29 04:32:06.000000000 +0100 +@@ -0,0 +1,259 @@ ++/* ++ * 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 ++ * ++ * 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 ++#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,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; ++ super_block_t *hidden_sb; ++ ++ hidden_sb = stohs(sb); ++ err = vfs_statfs(hidden_sb, buf); ++ ++ 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 ++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); ++ ++} ++ ++ ++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, ++}; diff --git a/openwrt/target/linux/package/Config.in b/openwrt/target/linux/package/Config.in index a89d0effc5..35d005ea8d 100644 --- a/openwrt/target/linux/package/Config.in +++ b/openwrt/target/linux/package/Config.in @@ -1,7 +1,6 @@ source "target/linux/package/diag/Config.in" source "target/linux/package/wlcompat/Config.in" source "target/linux/package/madwifi/Config.in" -source "target/linux/package/mini_fo/Config.in" source "target/linux/package/fuse/Config.in" source "target/linux/package/shfs/Config.in" source "target/linux/package/nozomi/Config.in" diff --git a/openwrt/target/linux/package/Makefile b/openwrt/target/linux/package/Makefile index 79b6b22cd6..ce97c4c197 100644 --- a/openwrt/target/linux/package/Makefile +++ b/openwrt/target/linux/package/Makefile @@ -8,7 +8,6 @@ package-$(BR2_PACKAGE_KMOD_SWITCH) += switch package-$(BR2_PACKAGE_KMOD_NOZOMI) += nozomi package-$(BR2_PACKAGE_KMOD_OPENSWAN) += openswan package-$(BR2_PACKAGE_KMOD_MADWIFI) += madwifi -package-$(BR2_PACKAGE_KMOD_MINI_FO) += mini_fo package-y += openwrt package-$(BR2_PACKAGE_KMOD_WLCOMPAT) += wlcompat diff --git a/openwrt/target/linux/package/mini_fo/Config.in b/openwrt/target/linux/package/mini_fo/Config.in deleted file mode 100644 index 922e9608ac..0000000000 --- a/openwrt/target/linux/package/mini_fo/Config.in +++ /dev/null @@ -1,6 +0,0 @@ -config BR2_PACKAGE_KMOD_MINI_FO - prompt "kmod-mini_fo - mini fanout overlay file system" - tristate - default y - help - The mini fanout overlay file system. diff --git a/openwrt/target/linux/package/mini_fo/Makefile b/openwrt/target/linux/package/mini_fo/Makefile deleted file mode 100644 index 5e1a1abe7e..0000000000 --- a/openwrt/target/linux/package/mini_fo/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# $Id: Makefile 3112 2006-02-01 23:53:19Z mbm $ - -include $(TOPDIR)/rules.mk - -PKG_NAME := mini_fo -PKG_VERSION := 0.6.2pre1 -PKG_RELEASE := 1 -PKG_MD5SUM := acd12157be25d7b3c8df90914049403e - -PKG_SOURCE_URL := http://www.denx.de/twiki/pub/Know/MiniFOHome/ -PKG_SOURCE := mini_fo-0-6-2-pre1.tar.bz2 -PKG_CAT := bzcat - -PKG_BUILD_DIR := $(BUILD_DIR)/mini_fo-0-6-2-pre1 - -include $(TOPDIR)/package/rules.mk - -$(eval $(call PKG_template,KMOD_MINI_FO,kmod-mini-fo,$(LINUX_VERSION)+$(PKG_VERSION)-$(BOARD)-$(PKG_RELEASE),$(ARCH),kernel ($(LINUX_VERSION)-$(BOARD)-$(LINUX_RELEASE)))) - -$(PKG_BUILD_DIR)/.configured: - touch $@ - -$(PKG_BUILD_DIR)/.built: - $(MAKE) -C $(PKG_BUILD_DIR) \ - PATH="$(TARGET_PATH)" \ - ARCH="mips" \ - CROSS_COMPILE="$(TARGET_CROSS)" \ - KERNELVERSION="2.4" \ - KERNEL_SRC="$(LINUX_DIR)-2.4.30" - touch $@ - -$(IPKG_KMOD_MINI_FO): - install -m0755 -d $(IDIR_KMOD_MINI_FO)/lib/modules/$(LINUX_VERSION) - install -m0755 -d $(IDIR_KMOD_MINI_FO)/etc/modules.d - $(CP) $(PKG_BUILD_DIR)/mini_fo.o \ - $(IDIR_KMOD_MINI_FO)/lib/modules/$(LINUX_VERSION)/ - echo "mini_fo" > $(IDIR_KMOD_MINI_FO)/etc/modules.d/15-mini-fo - $(IPKG_BUILD) $(IDIR_KMOD_MINI_FO) $(PACKAGE_DIR) - diff --git a/openwrt/target/linux/package/mini_fo/ipkg/kmod-mini-fo.control b/openwrt/target/linux/package/mini_fo/ipkg/kmod-mini-fo.control deleted file mode 100644 index 9ccdc5fa79..0000000000 --- a/openwrt/target/linux/package/mini_fo/ipkg/kmod-mini-fo.control +++ /dev/null @@ -1,4 +0,0 @@ -Package: kmod-mini-fo -Priority: optional -Section: sys -Description: mini fanout overlay file system diff --git a/openwrt/target/linux/package/mini_fo/patches/101-kmod_build.patch b/openwrt/target/linux/package/mini_fo/patches/101-kmod_build.patch deleted file mode 100644 index 9b47a69ab7..0000000000 --- a/openwrt/target/linux/package/mini_fo/patches/101-kmod_build.patch +++ /dev/null @@ -1,52 +0,0 @@ -diff -Nur mini_fo-0-6-2-pre1/Makefile mini_fo-0-6-2-pre1.patched/Makefile ---- mini_fo-0-6-2-pre1/Makefile 2005-10-15 12:49:43.000000000 +0200 -+++ mini_fo-0-6-2-pre1.patched/Makefile 2005-12-25 14:03:36.864205250 +0100 -@@ -26,36 +26,29 @@ - # allow custom override of TOPINC for fistgen developers - -include fistdev.mk - -+obj-m := mini_fo.o -+mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o -+ - ifeq ($(KERNELVERSION),2.4) --CFLAGS = -D__KERNEL__ -DMODULE -DFISTGEN -I. ${TOPINC} -O2 -Wall -Wno-unused -g -fno-common -fno-schedule-insns -fno-schedule-insns2 -fno-strict-aliasing -msoft-float -Werror --ifeq ($(ARCH),um) --CFLAGS += $(shell cd $(KERNEL_SRC) ; make script 'SCRIPT=@echo $$(CFLAGS)' ARCH=um) --endif --UCFLAGS = -DFISTGEN -I. ${TOPINC} -g -O2 -Wall -Wno-unused -Werror --endif - --CC = $(CROSS_COMPILE)gcc --LD = $(CROSS_COMPILE)ld -+O_TARGET := $(obj-m) -+obj-y := $(mini_fo-objs) - --obj-m := mini_fo.o --mini_fo-objs := meta.o dentry.o file.o inode.o main.o super.o state.o aux.o -+-include $(TOPDIR)/Rules.make -+ -+endif - - all: mini_fo$(KERNELVERSION) - --mini_fo2.4: ${obj-m} -+mini_fo2.4: -+ $(MAKE) -C ${KERNEL_SRC} TOPDIR="${KERNEL_SRC}" SUBDIRS=$(shell pwd) modules - - mini_fo2.6: -- make -C ${KERNEL_SRC} SUBDIRS=$(PWD) modules -- --${obj-m}: ${mini_fo-objs} -- $(LD) -o ${obj-m} -r ${mini_fo-objs} -- --tags: -- ctags -R -e -+ $(MAKE) -C ${KERNEL_SRC} SUBDIRS=$(shell pwd) modules - - clean: - ifeq ($(KERNELVERSION),2.6) -- -make -C ${KERNEL_SRC} SUBDIRS=$(PWD) clean -+ -$(MAKE) -C ${KERNEL_SRC} SUBDIRS=$(shell pwd) clean - endif - rm -f ${obj-m} ${mini_fo-objs} ${obj-m:.o=.ko} *.d .*.flags *~ - rm -f TAGS diff --git a/openwrt/target/linux/package/mini_fo/patches/102-mutex_change.patch b/openwrt/target/linux/package/mini_fo/patches/102-mutex_change.patch deleted file mode 100644 index 4dec47bf5e..0000000000 --- a/openwrt/target/linux/package/mini_fo/patches/102-mutex_change.patch +++ /dev/null @@ -1,602 +0,0 @@ -From: -Date: Tue, 24 Jan 2006 14:09:21 +0000 (+0100) -Subject: Support for new mutex infrastructure -X-Git-Url: http://www.denx.de/cgi-bin/gitweb.cgi?p=mini_fo.git;a=commitdiff;h=1dcc028729060ea83ea662155634b33ae8e2c493 - - Support for new mutex infrastructure - (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) ---- - ---- a/ChangeLog -+++ b/ChangeLog -@@ -1,3 +1,13 @@ -+2006-01-24 Markus Klotzbuecher -+ -+ * Add tons of ugly ifdefs to Ed L. Cashin's mutex patch to -+ retain backwards compatibility. -+ -+2006-01-24 Ed L. Cashin -+ -+ * Support for the new mutex infrastructure -+ (7892f2f48d165a34b0b8130c8a195dfd807b8cb6) -+ - 2005-10-15 Markus Klotzbuecher - - * Bugfix for a serious memory leak in mini_fo_follow_link. ---- a/aux.c -+++ b/aux.c -@@ -435,8 +435,11 @@ int build_sto_structure(dentry_t *dir, d - - /* 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"); -@@ -457,7 +460,11 @@ int build_sto_structure(dentry_t *dir, d - 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; - } -@@ -466,7 +473,11 @@ int build_sto_structure(dentry_t *dir, d - 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; - } -@@ -485,7 +496,11 @@ int build_sto_structure(dentry_t *dir, d - 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; - } ---- a/file.c -+++ b/file.c -@@ -613,18 +613,34 @@ mini_fo_fsync(file_t *file, dentry_t *de - 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 ---- a/inode.c -+++ b/inode.c -@@ -355,7 +355,11 @@ mini_fo_link(dentry_t *old_dentry, inode - - /* 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, -@@ -374,7 +378,11 @@ mini_fo_link(dentry_t *old_dentry, inode - - 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); -@@ -452,7 +460,11 @@ mini_fo_symlink(inode_t *dir, dentry_t * - 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; -@@ -481,7 +493,11 @@ mini_fo_symlink(inode_t *dir, dentry_t * - - 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); -@@ -524,7 +540,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - - /* 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); -@@ -572,7 +592,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - 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; - } -@@ -602,7 +626,12 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - - /* 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); -@@ -630,7 +659,11 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - 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; -@@ -641,7 +674,12 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - - /* 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); -@@ -668,7 +706,12 @@ mini_fo_rmdir(inode_t *dir, dentry_t *de - 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; - } -@@ -1294,11 +1337,19 @@ mini_fo_getxattr(struct dentry *dentry, - 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_lock(&hidden_dentry->d_inode->i_mutex); -+#else - up(&hidden_dentry->d_inode->i_sem); -+#endif - } - return err; - } -@@ -1340,11 +1391,19 @@ mini_fo_setxattr(struct dentry *dentry, - 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; - } -@@ -1372,11 +1431,19 @@ mini_fo_removexattr(struct dentry *dentr - 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; - } -@@ -1403,11 +1470,20 @@ mini_fo_listxattr(struct dentry *dentry, - - 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; - } ---- a/meta.c -+++ b/meta.c -@@ -650,9 +650,20 @@ int meta_sync_d_list(dentry_t *dentry, i - 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"); -@@ -780,9 +791,19 @@ int meta_sync_r_list(dentry_t *dentry, i - 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"); ---- a/mini_fo.h -+++ b/mini_fo.h -@@ -433,6 +433,33 @@ fist_copy_attr_all(inode_t *dest, const - - #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) { -@@ -463,8 +490,8 @@ static inline void double_unlock(struct - dput(d1); - dput(d2); - } -- --#endif -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) */ -+#endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) */ - #endif /* __KERNEL__ */ - - /* ---- a/mmap.c -+++ b/mmap.c -@@ -478,7 +478,11 @@ mini_fo_commit_write(file_t *file, page_ - 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) -@@ -556,7 +560,12 @@ mini_fo_commit_write(file_t *file, page_ - 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 */ - } ---- a/state.c -+++ b/state.c -@@ -44,7 +44,12 @@ int create_sto_reg_file(dentry_t *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)) -@@ -97,7 +102,11 @@ int create_sto_reg_file(dentry_t *dentry - 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; -@@ -130,7 +139,12 @@ n"); - - /* 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)) -@@ -184,7 +198,11 @@ n"); - - 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; -@@ -217,7 +235,12 @@ int create_sto_nod(dentry_t *dentry, int - - /* 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)) -@@ -260,7 +283,11 @@ int create_sto_nod(dentry_t *dentry, int - 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; -@@ -314,7 +341,12 @@ int nondir_unmod_to_mod(dentry_t *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)) -@@ -365,7 +397,12 @@ int nondir_unmod_to_mod(dentry_t *dentry - 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); -@@ -383,7 +420,11 @@ int nondir_unmod_to_mod(dentry_t *dentry - } - - 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; -@@ -420,7 +461,12 @@ int nondir_creat_to_del(dentry_t *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); -@@ -435,7 +481,11 @@ int nondir_creat_to_del(dentry_t *dentry - 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: -@@ -464,7 +514,12 @@ int nondir_mod_to_del(dentry_t *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); -@@ -488,7 +543,11 @@ int nondir_mod_to_del(dentry_t *dentry) - 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: - diff --git a/openwrt/toolchain/uClibc/patches/150-glob_use_getdents64.patch b/openwrt/toolchain/uClibc/patches/150-glob_use_getdents64.patch deleted file mode 100644 index f6e3e65920..0000000000 --- a/openwrt/toolchain/uClibc/patches/150-glob_use_getdents64.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff -ur uClibc.old/libc/misc/glob/glob.c uClibc.dev/libc/misc/glob/glob.c ---- uClibc.old/libc/misc/glob/glob.c 2005-01-12 08:59:21.000000000 +0100 -+++ uClibc.dev/libc/misc/glob/glob.c 2006-10-22 22:30:29.000000000 +0200 -@@ -420,7 +420,7 @@ - } - else - { -- struct dirent *d = readdir ((DIR *) stream); -+ struct dirent64 *d = readdir64 ((DIR *) stream); - if (d == NULL) - break; - if (! (d->d_ino != 0)) -- 2.30.2