diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/aufs.h linux-2.6.25.4-unionfs/fs/aufs/aufs.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/aufs.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/aufs.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * main header files + * + * $Id: aufs.h,v 1.2 2008/04/21 01:31:49 sfjro Exp $ + */ + +#ifndef __AUFS_H__ +#define __AUFS_H__ + +#ifdef __KERNEL__ + +#include + +#include "debug.h" + +#include "branch.h" +#include "cpup.h" +#include "dcsub.h" +#include "dentry.h" +#include "dir.h" +#include "file.h" +#include "hinode.h" +#include "inode.h" +#include "misc.h" +#include "module.h" +#include "opts.h" +#include "super.h" +#include "sysaufs.h" +#include "vfsub.h" +#include "whout.h" +#include "wkq.h" +//#include "xattr.h" + +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/branch.c linux-2.6.25.4-unionfs/fs/aufs/branch.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/branch.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/branch.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * branch management + * + * $Id: branch.c,v 1.5 2008/05/19 01:52:18 sfjro Exp $ + */ + +#include +#include +#include +#include +#include "aufs.h" + +static void free_branch(struct au_branch *br) +{ + AuTraceEnter(); + + if (br->br_xino) + fput(br->br_xino); + dput(br->br_wh); + dput(br->br_plink); + if (!au_test_nfs(br->br_mnt->mnt_sb)) + mntput(br->br_mnt); + else { + lockdep_off(); + mntput(br->br_mnt); + lockdep_on(); + } + AuDebugOn(au_br_count(br) || atomic_read(&br->br_wh_running)); + //AuDbg("free\n"); + kfree(br); +} + +/* + * frees all branches + */ +void au_br_free(struct au_sbinfo *sbinfo) +{ + aufs_bindex_t bmax; + struct au_branch **br; + + AuTraceEnter(); + bmax = sbinfo->si_bend + 1; + br = sbinfo->si_branch; + while (bmax--) + free_branch(*br++); +} + +/* + * find the index of a branch which is specified by @br_id. + */ +int au_br_index(struct super_block *sb, aufs_bindex_t br_id) +{ + aufs_bindex_t bindex, bend; + + AuTraceEnter(); + + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) + if (au_sbr_id(sb, bindex) == br_id) + return bindex; + return -1; +} + +/* + * test if the @h_sb is real-readonly. + */ +int au_test_def_rr(struct super_block *h_sb) +{ + switch (h_sb->s_magic) { +#ifdef CONFIG_AUFS_RR_SQUASHFS + case SQUASHFS_MAGIC_LZMA: + case SQUASHFS_MAGIC: + case SQUASHFS_MAGIC_LZMA_SWAP: + case SQUASHFS_MAGIC_SWAP: + return 1; /* real readonly */ +#endif + +#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) + case ISOFS_SUPER_MAGIC: + return 1; +#endif + +#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) + case CRAMFS_MAGIC: + return 1; +#endif + +#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) + case ROMFS_MAGIC: + return 1; +#endif + + default: + return 0; + } +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if two hidden_dentries have overlapping branches. + */ +static int do_test_overlap(struct super_block *sb, struct dentry *h_d1, + struct dentry *h_d2) +{ + int err; + + LKTRTrace("%.*s, %.*s\n", AuDLNPair(h_d1), AuDLNPair(h_d2)); + + err = au_test_subdir(h_d1, h_d2); + AuTraceErr(err); + return err; +} + +static int test_overlap_loopback(struct super_block *sb, struct dentry *h_d1, + struct dentry *h_d2) +{ +#if defined(CONFIG_BLK_DEV_LOOP) || defined(CONFIG_BLK_DEV_LOOP_MODULE) + struct inode *h_inode; + struct loop_device *l; + + h_inode = h_d1->d_inode; + if (MAJOR(h_inode->i_sb->s_dev) != LOOP_MAJOR) + return 0; + + l = h_inode->i_sb->s_bdev->bd_disk->private_data; + h_d1 = l->lo_backing_file->f_dentry; + if (unlikely(h_d1->d_sb == sb)) + return 1; + return do_test_overlap(sb, h_d1, h_d2); +#else + return 0; +#endif +} + +static int test_overlap(struct super_block *sb, struct dentry *h_d1, + struct dentry *h_d2) +{ + LKTRTrace("d1 %.*s, d2 %.*s\n", AuDLNPair(h_d1), AuDLNPair(h_d2)); + + if (unlikely(h_d1 == h_d2)) + return 1; + return do_test_overlap(sb, h_d1, h_d2) + || do_test_overlap(sb, h_d2, h_d1) + || test_overlap_loopback(sb, h_d1, h_d2) + || test_overlap_loopback(sb, h_d2, h_d1); +} + +/* ---------------------------------------------------------------------- */ + +static int init_br_wh(struct super_block *sb, aufs_bindex_t bindex, + struct au_branch *br, int new_perm, + struct dentry *h_root, struct vfsmount *h_mnt) +{ + int err, old_perm; + struct inode *dir, *h_dir; + const int new = (bindex < 0); + + LKTRTrace("b%d, new_perm %d\n", bindex, new_perm); + + dir = sb->s_root->d_inode; + h_dir = h_root->d_inode; + if (new) + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); + else + au_hdir_lock(h_dir, dir, bindex); + + br_wh_write_lock(br); + old_perm = br->br_perm; + br->br_perm = new_perm; + err = au_wh_init(h_root, br, au_do_nfsmnt(h_mnt), sb); + br->br_perm = old_perm; + br_wh_write_unlock(br); + + if (new) + mutex_unlock(&h_dir->i_mutex); + else + au_hdir_unlock(h_dir, dir, bindex); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * returns a newly allocated branch. @new_nbranch is a number of branches + * after adding a branch. + */ +static struct au_branch *alloc_addbr(struct super_block *sb, int new_nbranch) +{ + struct au_branch **branchp, *add_branch; + int sz; + void *p; + struct dentry *root; + struct inode *inode; + struct au_hinode *hinodep; + struct au_hdentry *hdentryp; + + LKTRTrace("new_nbranch %d\n", new_nbranch); + SiMustWriteLock(sb); + root = sb->s_root; + DiMustWriteLock(root); + inode = root->d_inode; + IiMustWriteLock(inode); + + add_branch = kmalloc(sizeof(*add_branch), GFP_KERNEL); + //if (LktrCond) {kfree(add_branch); add_branch = NULL;} + if (unlikely(!add_branch)) + goto out; + + sz = sizeof(*branchp) * (new_nbranch - 1); + if (unlikely(!sz)) + sz = sizeof(*branchp); + p = au_sbi(sb)->si_branch; + branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch, + GFP_KERNEL); + //if (LktrCond) branchp = NULL; + if (unlikely(!branchp)) + goto out_br; + au_sbi(sb)->si_branch = branchp; + + sz = sizeof(*hdentryp) * (new_nbranch - 1); + if (unlikely(!sz)) + sz = sizeof(*hdentryp); + p = au_di(root)->di_hdentry; + hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch, + GFP_KERNEL); + //if (LktrCond) hdentryp = NULL; + if (unlikely(!hdentryp)) + goto out_br; + au_di(root)->di_hdentry = hdentryp; + + sz = sizeof(*hinodep) * (new_nbranch - 1); + if (unlikely(!sz)) + sz = sizeof(*hinodep); + p = au_ii(inode)->ii_hinode; + hinodep = au_kzrealloc(p, sz, sizeof(*hinodep) * new_nbranch, + GFP_KERNEL); + //if (LktrCond) hinodep = NULL; // unavailable test + if (unlikely(!hinodep)) + goto out_br; + au_ii(inode)->ii_hinode = hinodep; + return add_branch; /* success */ + + out_br: + kfree(add_branch); + out: + AuTraceErr(-ENOMEM); + return ERR_PTR(-ENOMEM); +} + +/* + * test if the branch permission is legal or not. + */ +static int test_br(struct super_block *sb, struct inode *inode, int brperm, + char *path) +{ + int err; + + err = 0; + if (unlikely(au_br_writable(brperm) && IS_RDONLY(inode))) { + AuErr("write permission for readonly fs or inode, %s\n", path); + err = -EINVAL; + } + + AuTraceErr(err); + return err; +} + +/* + * returns: + * 0: success, the caller will add it + * plus: success, it is already unified, the caller should ignore it + * minus: error + */ +static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err; + struct dentry *root; + struct inode *inode, *h_inode; + aufs_bindex_t bend, bindex; + + LKTRTrace("%s, remo%d\n", add->path, remount); + + root = sb->s_root; + bend = au_sbend(sb); + if (unlikely(bend >= 0 + && au_find_dbindex(root, add->nd.path.dentry) >= 0)) { + err = 1; + if (!remount) { + err = -EINVAL; + AuErr("%s duplicated\n", add->path); + } + goto out; + } + + err = -ENOSPC; //-E2BIG; + //if (LktrCond) bend = AUFS_BRANCH_MAX; + if (unlikely(AUFS_BRANCH_MAX <= add->bindex + || AUFS_BRANCH_MAX - 1 <= bend)) { + AuErr("number of branches exceeded %s\n", add->path); + goto out; + } + + err = -EDOM; + if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { + AuErr("bad index %d\n", add->bindex); + goto out; + } + + inode = add->nd.path.dentry->d_inode; + AuDebugOn(!inode || !S_ISDIR(inode->i_mode)); + err = -ENOENT; + if (unlikely(!inode->i_nlink)) { + AuErr("no existence %s\n", add->path); + goto out; + } + + err = -EINVAL; + if (unlikely(inode->i_sb == sb)) { + AuErr("%s must be outside\n", add->path); + goto out; + } + + if (unlikely(au_test_nested(inode->i_sb))) { + AuErr("nested " AUFS_NAME " %s\n", add->path); + goto out; + } + + if (unlikely(!strcmp(au_sbtype(inode->i_sb), "unionfs"))) { + AuErr("unsupported filesystem, %s\n", add->path); + goto out; + } + + if (unlikely(au_test_unsupported_nfs(inode->i_sb))) { + AuErr(AuNoNfsBranchMsg " %s\n", add->path); + goto out; + } + + err = test_br(sb, add->nd.path.dentry->d_inode, add->perm, add->path); + if (unlikely(err)) + goto out; + + if (bend < 0) + return 0; /* success */ + + h_inode = au_h_dptr(root, 0)->d_inode; + if (unlikely(au_opt_test(au_mntflags(sb), WARN_PERM) + && ((h_inode->i_mode & S_IALLUGO) + != (inode->i_mode & S_IALLUGO) + || h_inode->i_uid != inode->i_uid + || h_inode->i_gid != inode->i_gid))) + AuWarn("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", + add->path, + inode->i_uid, inode->i_gid, (inode->i_mode & S_IALLUGO), + h_inode->i_uid, h_inode->i_gid, + (h_inode->i_mode & S_IALLUGO)); + + err = -EINVAL; + for (bindex = 0; bindex <= bend; bindex++) + if (unlikely(test_overlap(sb, add->nd.path.dentry, + au_h_dptr(root, bindex)))) { + AuErr("%s is overlapped\n", add->path); + goto out; + } + err = 0; + + out: + AuTraceErr(err); + return err; +} + +static int au_br_init(struct au_branch *br, struct super_block *sb, + struct au_opt_add *add) +{ + int err; + + AuTraceEnter(); + + err = 0; + au_rw_init_nolock(&br->br_wh_rwsem); + br->br_plink = NULL; + br->br_wh = NULL; + if (unlikely(au_br_writable(add->perm))) { + err = init_br_wh(sb, /*bindex*/-1, br, add->perm, + add->nd.path.dentry, add->nd.path.mnt); + if (unlikely(err)) + goto out; + } + + br->br_xino = NULL; + br->br_mnt = mntget(add->nd.path.mnt); + if (au_opt_test(au_mntflags(sb), XINO)) { + err = au_xino_br(sb, br, add->nd.path.dentry->d_inode->i_ino, + au_sbr(sb, 0)->br_xino, /*do_test*/1); + if (unlikely(err)) { + AuDebugOn(br->br_xino); + goto out; + } + } + + atomic_set(&br->br_wh_running, 0); + br->br_id = au_new_br_id(sb); + br->br_perm = add->perm; + atomic_set(&br->br_count, 0); + br->br_bytes = 0; + br->br_xino_upper = AUFS_XINO_TRUNC_INIT; + atomic_set(&br->br_xino_running, 0); + sysaufs_br_init(br); + br->br_generation = au_sigen(sb); + //smp_mb(); /* atomic_set */ + + out: + AuTraceErr(err); + return err; +} + +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) +{ + int err, amount; + aufs_bindex_t bend, add_bindex; + struct dentry *root, *dentry; + struct au_iinfo *iinfo; + struct au_sbinfo *sbinfo; + struct au_dinfo *dinfo; + struct inode *root_inode, *inode; + unsigned long long maxb; + struct au_branch **branchp, *add_branch; + struct au_hdentry *hdentryp; + struct au_hinode *hinodep; + + dentry = add->nd.path.dentry; + LKTRTrace("b%d, %s, 0x%x, %.*s\n", + add->bindex, add->path, add->perm, AuDLNPair(dentry)); + SiMustWriteLock(sb); + root = sb->s_root; + DiMustWriteLock(root); + root_inode = root->d_inode; + IMustLock(root_inode); + IiMustWriteLock(root_inode); + + err = test_add(sb, add, remount); + if (unlikely(err < 0)) + goto out; + if (err) + return 0; /* success */ + + bend = au_sbend(sb); + add_branch = alloc_addbr(sb, bend + 2); + err = PTR_ERR(add_branch); + if (IS_ERR(add_branch)) + goto out; + err = au_br_init(add_branch, sb, add); + if (unlikely(err)) { + kfree(add_branch); + goto out; + } + + add_bindex = add->bindex; + if (remount) + sysaufs_brs_del(sb, add_bindex); + + sbinfo = au_sbi(sb); + dinfo = au_di(root); + iinfo = au_ii(root_inode); + + amount = bend + 1 - add_bindex; + branchp = sbinfo->si_branch + add_bindex; + memmove(branchp + 1, branchp, sizeof(*branchp) * amount); + *branchp = add_branch; + hdentryp = dinfo->di_hdentry + add_bindex; + memmove(hdentryp + 1, hdentryp, sizeof(*hdentryp) * amount); + au_h_dentry_init(hdentryp); + hinodep = iinfo->ii_hinode + add_bindex; + memmove(hinodep + 1, hinodep, sizeof(*hinodep) * amount); + hinodep->hi_inode = NULL; + au_hin_init(hinodep, NULL); + + sbinfo->si_bend++; + dinfo->di_bend++; + iinfo->ii_bend++; + if (unlikely(bend < 0)) { + sbinfo->si_bend = 0; + dinfo->di_bstart = 0; + iinfo->ii_bstart = 0; + } + inode = dentry->d_inode; + au_set_h_dptr(root, add_bindex, dget(dentry)); + au_set_h_iptr(root_inode, add_bindex, igrab(inode), 0); + if (remount) + sysaufs_brs_add(sb, add_bindex); + + if (!add_bindex) + au_cpup_attr_all(root_inode); + else + au_add_nlink(root_inode, inode); + maxb = dentry->d_sb->s_maxbytes; + if (sb->s_maxbytes < maxb) + sb->s_maxbytes = maxb; + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuVerbose(do_info, fmt, args...) do { \ + if (!do_info) \ + LKTRTrace(fmt, ##args); \ + else \ + AuInfo(fmt, ##args); \ +} while (0) + +/* + * test if the branch is deletable or not. + */ +static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, + au_gen_t sigen) +{ + int err, i, j, ndentry, verbose; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry *d; + aufs_bindex_t bstart, bend; + struct inode *inode; + + LKTRTrace("b%d, gen%d\n", bindex, sigen); + SiMustWriteLock(root->d_sb); + + err = au_dpages_init(&dpages, GFP_TEMPORARY); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, root, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + verbose = !!au_opt_test(au_mntflags(root->d_sb), VERBOSE); + for (i = 0; !err && i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + ndentry = dpage->ndentry; + for (j = 0; !err && j < ndentry; j++) { + d = dpage->dentries[j]; + AuDebugOn(!atomic_read(&d->d_count)); + inode = d->d_inode; + AuDebugOn(!inode); + if (au_digen(d) == sigen + && au_iigen(inode) == sigen) + di_read_lock_child(d, AuLock_IR); + else { + di_write_lock_child(d); + err = au_reval_dpath(d, sigen); + if (!err) + di_downgrade_lock(d, AuLock_IR); + else { + di_write_unlock(d); + break; + } + } + + bstart = au_dbstart(d); + bend = au_dbend(d); + if (bstart <= bindex + && bindex <= bend + && au_h_dptr(d, bindex) + && (!S_ISDIR(d->d_inode->i_mode) + || bstart == bend)) { + err = -EBUSY; + AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); + } + di_read_unlock(d, AuLock_IR); + } + } + + out_dpages: + au_dpages_free(&dpages); + out: + AuTraceErr(err); + return err; +} + +static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, + au_gen_t sigen) +{ + int err, verbose; + struct inode *i; + aufs_bindex_t bstart, bend; + + LKTRTrace("b%d, gen%d\n", bindex, sigen); + SiMustWriteLock(sb); + + err = 0; + verbose = !!au_opt_test(au_mntflags(sb), VERBOSE); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) { + AuDebugOn(!atomic_read(&i->i_count)); + if (!list_empty(&i->i_dentry)) + continue; + + if (au_iigen(i) == sigen) + ii_read_lock_child(i); + else { + ii_write_lock_child(i); + err = au_refresh_hinode_self(i); + if (!err) + ii_downgrade_lock(i); + else { + ii_write_unlock(i); + break; + } + } + + bstart = au_ibstart(i); + bend = au_ibend(i); + if (bstart <= bindex + && bindex <= bend + && au_h_iptr(i, bindex) + && (!S_ISDIR(i->i_mode) || bstart == bend)) { + err = -EBUSY; + AuVerbose(verbose, "busy i%lu\n", i->i_ino); + //au_debug_on(); + //DbgInode(i); + //au_debug_off(); + ii_read_unlock(i); + break; + } + ii_read_unlock(i); + } + + AuTraceErr(err); + return err; +} + +static int test_children_busy(struct dentry *root, aufs_bindex_t bindex) +{ + int err; + au_gen_t sigen; + + LKTRTrace("b%d\n", bindex); + SiMustWriteLock(root->d_sb); + DiMustWriteLock(root); + //todo: dont trust BKL. + AuDebugOn(!kernel_locked()); + + sigen = au_sigen(root->d_sb); + DiMustNoWaiters(root); + IiMustNoWaiters(root->d_inode); + di_write_unlock(root); + err = test_dentry_busy(root, bindex, sigen); + if (!err) + err = test_inode_busy(root->d_sb, bindex, sigen); + di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ + + AuTraceErr(err); + return err; +} + +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) +{ + int err, do_wh, rerr, verbose; + struct dentry *root; + struct inode *inode, *hidden_dir; + aufs_bindex_t bindex, bend, br_id; + struct au_sbinfo *sbinfo; + struct au_dinfo *dinfo; + struct au_iinfo *iinfo; + struct au_branch *br; + unsigned int mnt_flags; + + //au_debug_on(); + LKTRTrace("%s, %.*s\n", del->path, AuDLNPair(del->h_root)); + SiMustWriteLock(sb); + root = sb->s_root; + DiMustWriteLock(root); + inode = root->d_inode; + IiMustWriteLock(inode); + + err = 0; + bindex = au_find_dbindex(root, del->h_root); + if (bindex < 0) { + if (remount) + goto out; /* success */ + err = -ENOENT; + AuErr("%s no such branch\n", del->path); + goto out; + } + LKTRTrace("bindex b%d\n", bindex); + + err = -EBUSY; + mnt_flags = au_mntflags(sb); + verbose = au_opt_test(mnt_flags, VERBOSE); + bend = au_sbend(sb); + if (unlikely(!bend)) { + AuVerbose(verbose, "no more branches left\n"); + goto out; + } + br = au_sbr(sb, bindex); + if (unlikely(au_br_count(br))) { + AuVerbose(verbose, "%d file(s) opened\n", au_br_count(br)); + goto out; + } + + do_wh = 0; + hidden_dir = del->h_root->d_inode; + if (br->br_wh || br->br_plink) { + dput(br->br_wh); + dput(br->br_plink); + br->br_plink = NULL; + br->br_wh = NULL; + do_wh = 1; + } + + err = test_children_busy(root, bindex); + if (unlikely(err)) { + if (unlikely(do_wh)) + goto out_wh; + goto out; + } + + err = 0; + if (remount) + sysaufs_brs_del(sb, bindex); + sbinfo = au_sbi(sb); + dinfo = au_di(root); + iinfo = au_ii(inode); + + dput(au_h_dptr(root, bindex)); + au_hiput(iinfo->ii_hinode + bindex); + br_id = br->br_id; + free_branch(br); + + //todo: realloc and shrink memory + if (bindex < bend) { + const aufs_bindex_t n = bend - bindex; + struct au_branch **brp; + struct au_hdentry *hdp; + struct au_hinode *hip; + + brp = sbinfo->si_branch + bindex; + memmove(brp, brp + 1, sizeof(*brp) * n); + hdp = dinfo->di_hdentry + bindex; + memmove(hdp, hdp + 1, sizeof(*hdp) * n); + hip = iinfo->ii_hinode + bindex; + memmove(hip, hip + 1, sizeof(*hip) * n); + } + sbinfo->si_branch[0 + bend] = NULL; + dinfo->di_hdentry[0 + bend].hd_dentry = NULL; + iinfo->ii_hinode[0 + bend].hi_inode = NULL; + au_hin_init(iinfo->ii_hinode + bend, NULL); + + sbinfo->si_bend--; + dinfo->di_bend--; + iinfo->ii_bend--; + if (remount) + sysaufs_brs_add(sb, bindex); + + if (!bindex) + au_cpup_attr_all(inode); + else + au_sub_nlink(inode, del->h_root->d_inode); + if (au_opt_test(mnt_flags, PLINK)) + au_plink_half_refresh(sb, br_id); + + if (sb->s_maxbytes == del->h_root->d_sb->s_maxbytes) { + bend--; + sb->s_maxbytes = 0; + for (bindex = 0; bindex <= bend; bindex++) { + unsigned long long maxb; + maxb = au_sbr_sb(sb, bindex)->s_maxbytes; + if (sb->s_maxbytes < maxb) + sb->s_maxbytes = maxb; + } + } + goto out; /* success */ + + out_wh: + /* revert */ + rerr = init_br_wh(sb, bindex, br, br->br_perm, del->h_root, br->br_mnt); + if (rerr) + AuWarn("failed re-creating base whiteout, %s. (%d)\n", + del->path, rerr); + out: + AuTraceErr(err); + //au_debug_off(); + return err; +} + +static int do_need_sigen_inc(int a, int b) +{ + return (au_br_whable(a) && !au_br_whable(b)); +} + +static int need_sigen_inc(int old, int new) +{ + return (do_need_sigen_inc(old, new) + || do_need_sigen_inc(new, old)); +} + +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_update) +{ + int err; + struct dentry *root; + aufs_bindex_t bindex; + struct au_branch *br; + struct inode *hidden_dir; + + LKTRTrace("%s, %.*s, 0x%x\n", + mod->path, AuDLNPair(mod->h_root), mod->perm); + SiMustWriteLock(sb); + root = sb->s_root; + DiMustWriteLock(root); + IiMustWriteLock(root->d_inode); + + bindex = au_find_dbindex(root, mod->h_root); + if (bindex < 0) { + if (remount) + return 0; /* success */ + err = -ENOENT; + AuErr("%s no such branch\n", mod->path); + goto out; + } + LKTRTrace("bindex b%d\n", bindex); + + hidden_dir = mod->h_root->d_inode; + err = test_br(sb, hidden_dir, mod->perm, mod->path); + if (unlikely(err)) + goto out; + + br = au_sbr(sb, bindex); + if (br->br_perm == mod->perm) + return 0; /* success */ + + if (au_br_writable(br->br_perm)) { + /* remove whiteout base */ + //todo: mod->perm? + err = init_br_wh(sb, bindex, br, AuBr_RO, mod->h_root, + br->br_mnt); + if (unlikely(err)) + goto out; + + if (!au_br_writable(mod->perm)) { + /* rw --> ro, file might be mmapped */ + struct file *file, *hf; + + DiMustNoWaiters(root); + IiMustNoWaiters(root->d_inode); + di_write_unlock(root); + + /* + * no need file_list_lock() + * since BKL (and sbinfo) is locked + */ + AuDebugOn(!kernel_locked()); + list_for_each_entry(file, &sb->s_files, f_u.fu_list) { + LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry)); + if (!au_test_aufs_file(file)) + continue; + + fi_read_lock(file); + if (!S_ISREG(file->f_dentry->d_inode->i_mode) + || !(file->f_mode & FMODE_WRITE) + || au_fbstart(file) != bindex) { + FiMustNoWaiters(file); + fi_read_unlock(file); + continue; + } + + if (unlikely(au_test_mmapped(file))) { + err = -EBUSY; + FiMustNoWaiters(file); + fi_read_unlock(file); + break; + } + + // todo: already flushed? + hf = au_h_fptr(file, au_fbstart(file)); + hf->f_flags = au_file_roflags(hf->f_flags); + hf->f_mode &= ~FMODE_WRITE; + put_write_access(hf->f_dentry->d_inode); + FiMustNoWaiters(file); + fi_read_unlock(file); + } + + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + } + } + + if (!err) { + *do_update |= need_sigen_inc(br->br_perm, mod->perm); + br->br_perm = mod->perm; + } + + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/branch.h linux-2.6.25.4-unionfs/fs/aufs/branch.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/branch.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/branch.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * branch filesystems and xino for them + * + * $Id: branch.h,v 1.3 2008/04/28 03:03:32 sfjro Exp $ + */ + +#ifndef __AUFS_BRANCH_H__ +#define __AUFS_BRANCH_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "misc.h" +#include "super.h" + +/* ---------------------------------------------------------------------- */ + +/* an entry in a xino file */ +struct au_xino_entry { + ino_t ino; + //__u32 h_gen; +} __packed; + +//#define AuXino_INVALID_HGEN (-1) + +/* a xino file */ +struct au_xino_file { + //struct file **xi_file; + struct file *xi_file; + + /* array management */ + //unsigned long long xi_limit; /* Max xino file size */ + //unsigned long long xi_size; /* s_maxbytes */ + + /* truncation */ + blkcnt_t xi_upper; /* watermark in blocks */ + //u64 xi_upper; /* watermark in bytes */ + //u64 xi_step; /* to next watermark in bytes */ + atomic_t xi_running; +}; + +/* protected by superblock rwsem */ +struct au_branch { + struct file *br_xino; + //struct au_xino_file *br_xino; + + aufs_bindex_t br_id; + + int br_perm; + struct vfsmount *br_mnt; + atomic_t br_count; + + /* whiteout base */ + struct au_rwsem br_wh_rwsem; + struct dentry *br_wh; + atomic_t br_wh_running; + + /* pseudo-link dir */ + struct dentry *br_plink; + + /* xino truncation */ + blkcnt_t br_xino_upper; /* watermark in blocks */ + atomic_t br_xino_running; + + /* mfs mode */ + u64 br_bytes; + +#ifdef CONFIG_SYSFS + /* an entry under sysfs per mount-point */ + char br_name[8]; + struct attribute br_attr; +#endif + + au_gen_t br_generation; +}; + +/* ---------------------------------------------------------------------- */ + +/* branch permission and attribute */ +enum { + AuBr_RW, /* writable, linkable wh */ + AuBr_RO, /* readonly, no wh */ + AuBr_RR, /* natively readonly, no wh */ + + AuBr_RWNoLinkWH, /* un-linkable whiteouts */ + + AuBr_ROWH, + AuBr_RRWH, /* whiteout-able */ + + AuBr_Last +}; + +static inline int au_br_writable(int brperm) +{ + return (brperm == AuBr_RW || brperm == AuBr_RWNoLinkWH); +} + +static inline int au_br_whable(int brperm) +{ + return (brperm == AuBr_RW + || brperm == AuBr_ROWH + || brperm == AuBr_RRWH); +} + +static inline int au_br_hinotifyable(int brperm) +{ +#ifdef CONFIG_AUFS_HINOTIFY + return (brperm != AuBr_RR && brperm != AuBr_RRWH); +#else + return 0; +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* branch.c */ +struct au_sbinfo; +void au_br_free(struct au_sbinfo *sinfo); +int au_test_def_rr(struct super_block *h_sb); +int au_br_index(struct super_block *sb, aufs_bindex_t br_id); +struct au_opt_add; +int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); +struct au_opt_del; +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); +struct au_opt_mod; +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, + int *do_update); + +/* xino.c */ +int au_xib_trunc(struct super_block *sb); + +struct file *au_xino_create(struct super_block *sb, char *fname, int silent, + struct dentry *parent); +ino_t au_xino_new_ino(struct super_block *sb); +int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino); +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + struct au_xino_entry *xinoe); +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + struct au_xino_entry *xinoe); +int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, + struct file *base_file, int do_test); +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); + +struct au_opt_xino; +int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); +void au_xino_clr(struct super_block *sb); +struct file *au_xino_def(struct super_block *sb); + +/* ---------------------------------------------------------------------- */ + +//todo: memory barrier? +static inline int au_br_count(struct au_branch *br) +{ + return atomic_read(&br->br_count); +} + +static inline int au_br_get(struct au_branch *br) +{ + return atomic_inc_return(&br->br_count); +} + +static inline int au_br_put(struct au_branch *br) +{ + return atomic_dec_return(&br->br_count); +} + +static inline au_gen_t au_br_gen(struct au_branch *br) +{ + return br->br_generation; +} + +/* + * test if the @br is readonly or not. + */ +static inline int au_br_rdonly(struct au_branch *br) +{ + return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} + +/* ---------------------------------------------------------------------- */ + +/* Superblock to branch */ +static inline +aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_id; +} + +static inline +struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_mnt; +} + +static inline +struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr_mnt(sb, bindex)->mnt_sb; +} + +static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) +{ + au_br_put(au_sbr(sb, bindex)); +} + +static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_sbr(sb, bindex)->br_perm; +} + +static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_br_whable(au_sbr_perm(sb, bindex)); +} + +static inline int au_test_trunc_xino(struct super_block *sb) +{ + return au_test_tmpfs(sb); +} + +/* temporary support for i#1 in cramfs */ +static inline int au_test_unique_ino(struct dentry *h_dentry, ino_t h_ino) +{ +#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) + if (unlikely(h_dentry->d_sb->s_magic == CRAMFS_MAGIC)) + return (h_ino != 1); +#endif + return 1; +} + +#ifdef CONFIG_AUFS_BR_NFS +static inline int au_test_unsupported_nfs(struct super_block *h_sb) +{ + return 0; +} + +static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt) +{ + if (!au_test_nfs(h_mnt->mnt_sb)) + return NULL; + return h_mnt; +} + +/* it doesn't mntget() */ +static inline +struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex) +{ + return au_do_nfsmnt(au_sbr_mnt(sb, bindex)); +} + +#define AuNoNfsBranchMsg "dummy" + +#else +static inline int au_test_unsupported_nfs(struct super_block *h_sb) +{ + return au_test_nfs(h_sb); +} + +static inline struct vfsmount *au_do_nfsmnt(struct vfsmount *h_mnt) +{ + return NULL; +} + +static inline +struct vfsmount *au_nfsmnt(struct super_block *sb, aufs_bindex_t bindex) +{ + return NULL; +} + +#define AuNoNfsBranchMsg "NFS branch is not supported" \ + ", try some configurations and patches included in aufs source CVS." + +#endif /* CONFIG_AUFS_BR_NFS */ + +/* ---------------------------------------------------------------------- */ + +/* + * br_wh_read_lock, br_wh_write_lock + * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock + */ +AuSimpleRwsemFuncs(br_wh, struct au_branch *br, br->br_wh_rwsem); + +/* to debug easier, do not make them inlined functions */ +#define BrWhMustReadLock(br) do { \ + /* SiMustAnyLock(sb); */ \ + AuRwMustReadLock(&(br)->br_wh_rwsem); \ +} while (0) + +#define BrWhMustWriteLock(br) do { \ + /* SiMustAnyLock(sb); */ \ + AuRwMustWriteLock(&(br)->br_wh_rwsem); \ +} while (0) + +#define BrWhMustAnyLock(br) do { \ + /* SiMustAnyLock(sb); */ \ + AuRwMustAnyLock(&(br)->br_wh_rwsem); \ +} while (0) + +#endif /* __KERNEL__ */ +#endif /* __AUFS_BRANCH_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/br_fuse.c linux-2.6.25.4-unionfs/fs/aufs/br_fuse.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/br_fuse.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/br_fuse.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * special handling for inode attributes on FUSE branch + * + * $Id: br_fuse.c,v 1.2 2008/04/21 01:32:05 sfjro Exp $ + */ + +#include "aufs.h" + +/* h_mnt can be NULL, is it safe? */ +int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry) +{ + int err; + struct kstat st; + + LKTRTrace("%.*s\n", AuDLNPair(h_dentry)); + + err = 0; + if (unlikely(h_dentry->d_inode + //&& atomic_read(&h_dentry->d_inode->i_count) + && au_test_fuse(h_dentry->d_sb))) { + err = vfsub_getattr(h_mnt, h_dentry, &st, /*dlgt*/0); + if (unlikely(err)) { + AuDbg("err %d\n", err); + au_debug_on(); + AuDbgDentry(h_dentry); + au_debug_off(); + WARN_ON(err); + } + } + return err; +} + +/* currently, for fuse only */ +int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st) +{ + int err; + struct inode *inode, *h_inode; + struct dentry *h_dentry; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + err = 0; + aufs_read_lock(dentry, AuLock_IR); + inode = dentry->d_inode; + h_inode = au_h_iptr(inode, au_ibstart(inode)); + if (unlikely(au_test_fuse(h_inode->i_sb))) { + h_dentry = d_find_alias(h_inode); + /* simply gave up updating fuse inode */ + if (h_dentry) { + /*ignore*/ + if (!au_update_fuse_h_inode(NULL, h_dentry)) + au_cpup_attr_all(inode); + dput(h_dentry); + } + } + generic_fillattr(inode, st); + + aufs_read_unlock(dentry, AuLock_IR); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/br_nfs.c linux-2.6.25.4-unionfs/fs/aufs/br_nfs.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/br_nfs.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/br_nfs.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,358 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * lookup functions for NFS branch in linux-2.6.19 and later + * + * $Id: br_nfs.c,v 1.3 2008/05/04 23:51:14 sfjro Exp $ + */ + +#include "aufs.h" + +static struct file *au_find_h_intent(struct au_hdentry *hd, struct file *file) +{ + struct file *h_file, *hf; + struct au_hdintent *hdi, *tmp, *do_free; + + LKTRTrace("%.*s\n", AuDLNPair(hd->hd_dentry)); + + h_file = NULL; + do_free = NULL; + spin_lock(&hd->hd_lock); + list_for_each_entry_safe(hdi, tmp, hd->hd_intent_list, hdi_list) { + hf = hdi->hdi_file[AuIntent_BRANCH]; + if (hdi->hdi_file[AuIntent_AUFS] == file + && hf->f_dentry == hd->hd_dentry) { + h_file = hf; + do_free = hdi; + list_del(&hdi->hdi_list); + break; + } + } + spin_unlock(&hd->hd_lock); + kfree(do_free); + + return h_file; +} + +struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex, + struct file *file) +{ + struct file *h_file; + struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; + + LKTRTrace("%.*s, b%d, f %p\n", AuDLNPair(dentry), bindex, file); + DiMustAnyLock(dentry); + AuDebugOn(bindex < au_di(dentry)->di_bstart + || bindex > au_di(dentry)->di_bend); + + h_file = NULL; + if (!hd->hd_intent_list || !file) + return h_file; /* success */ + + //AuDebugOn(au_test_wkq(current)); + h_file = au_find_h_intent(hd, file); + //AuDbgFile(h_file); + return h_file; +} + +static int au_set_h_intent(struct dentry *dentry, aufs_bindex_t bindex, + struct file *file, struct file *h_file) +{ + int err; + struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; + struct au_hdintent *hdi; + struct file *hf; + + LKTRTrace("%.*s, b%d, f %p\n", AuDLNPair(dentry), bindex, file); + /* d_revalidate() holds read_lock */ + //DiMustWriteLock(dentry); + AuDebugOn(bindex < au_di(dentry)->di_bstart + || bindex > au_di(dentry)->di_bend + || !file + || !h_file + /* || au_test_wkq(current) */); + + err = -ENOMEM; + if (hd->hd_intent_list) { + while (1) { + hf = au_find_h_intent(hd, file); + if (!hf) + break; + fput(hf); + AuWarn("freed hfile %.*s b%d left\n", + AuDLNPair(dentry), bindex); + } + } else { + spin_lock(&hd->hd_lock); + if (!hd->hd_intent_list) { + hd->hd_intent_list + = kmalloc(sizeof(*hd->hd_intent_list), + GFP_ATOMIC); + if (unlikely(!hd->hd_intent_list)) { + spin_unlock(&hd->hd_lock); + goto out; + } + INIT_LIST_HEAD(hd->hd_intent_list); + } + spin_unlock(&hd->hd_lock); + } + + hdi = kmalloc(sizeof(*hdi), GFP_TEMPORARY); + if (unlikely(!hdi)) + goto out; + + err = 0; + //hdi->hdi_pid = current->pid; + hdi->hdi_file[AuIntent_AUFS] = file; + hdi->hdi_file[AuIntent_BRANCH] = h_file; + spin_lock(&hd->hd_lock); + list_add(&hdi->hdi_list, hd->hd_intent_list); + spin_unlock(&hd->hd_lock); + //AuDbgDentry(dentry); + //AuDbgFile(h_file); + + out: + AuTraceErr(err); + return err; +} + +int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry, + aufs_bindex_t bindex, struct nameidata *nd) +{ + int err; + + AuTraceEnter(); + + err = 0; + if (!nd_file) + goto out; + + AuDebugOn(!nd); + err = au_set_h_intent(dentry, bindex, nd->intent.open.file, nd_file); + //err = -1; + if (unlikely(err)) { + fput(nd_file); + au_set_h_dptr(dentry, bindex, NULL); + //todo: update bstart and bend? + } + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_hintent_put(struct au_hdentry *hd, int do_free) +{ + struct au_hdintent *hdi, *tmp; + struct file *hf; + + if (unlikely(hd->hd_intent_list)) { + // no spin lock + list_for_each_entry_safe(hdi, tmp, hd->hd_intent_list, + hdi_list) { + LKTRTrace("hdi %p\n", hdi); + hf = hdi->hdi_file[AuIntent_BRANCH]; + if (unlikely(hf)) + fput(hf); + //list_del(&hdi->hdi_list); + kfree(hdi); + } + if (do_free) + kfree(hd->hd_intent_list); + } +} + +/* ---------------------------------------------------------------------- */ + +int au_fake_intent(/* struct au_ndsub *save, */struct nameidata *nd, + int perm) +{ + int err; + + LKTRTrace("perm %d\n", perm); + + err = 0; + + nd->intent.open.file = NULL; + if (nd->flags & LOOKUP_OPEN) { + err = -ENFILE; + nd->intent.open.file = get_empty_filp(); + if (unlikely(!nd->intent.open.file)) { + //nd->intent.open.file = save->intent.open.file; + goto out; + } + + err = 0; + if (!au_br_writable(perm)) { + nd->intent.open.flags = FMODE_READ + | au_file_roflags(nd->intent.open.flags); + nd->flags &= ~LOOKUP_CREATE; + } + } + + out: + AuTraceErr(err); + return err; +} + +int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry, + aufs_bindex_t bindex, struct file *file) +{ + int err; + + LKTRTrace("nd %p, %.*s, b%d, f %d\n", + nd, AuDLNPair(dentry), bindex, !!file); + + err = 0; + if ((nd->flags & LOOKUP_OPEN) + && nd->intent.open.file + && !IS_ERR(nd->intent.open.file)) { + if (nd->intent.open.file->f_dentry) { + //AuDbgFile(nd->intent.open.file); + err = au_set_h_intent(dentry, bindex, file, + nd->intent.open.file); + if (!err) + nd->intent.open.file = NULL; + } + if (unlikely(nd->intent.open.file)) + put_filp(nd->intent.open.file); + } + + return err; +} + +#ifdef CONFIG_AUFS_DLGT +struct au_lookup_hash_args { + struct dentry **errp; + struct qstr *name; + struct dentry *base; + struct nameidata *nd; +}; + +static void au_call_lookup_hash(void *args) +{ + struct au_lookup_hash_args *a = args; + *a->errp = vfsub__lookup_hash(a->name, a->base, a->nd); +} + +static struct dentry * +au_lkup_hash_dlgt(struct qstr *this, struct dentry *parent, + struct nameidata *nd, unsigned int flags) +{ + struct dentry *dentry; + int dirperm1; + + dirperm1 = au_ftest_ndx(flags, DIRPERM1); + if (!dirperm1 && !au_ftest_ndx(flags, DLGT)) + dentry = vfsub__lookup_hash(this, parent, nd); + else { + int wkq_err; + struct au_lookup_hash_args args = { + .errp = &dentry, + .name = this, + .base = parent, + .nd = nd + }; + wkq_err = au_wkq_wait(au_call_lookup_hash, &args, + /*dlgt*/!dirperm1); + if (unlikely(wkq_err)) + dentry = ERR_PTR(wkq_err); + } + + AuTraceErrPtr(dentry); + return dentry; +} +#else +static struct dentry * +au_lkup_hash_dlgt(struct qstr *this, struct dentry *parent, + struct nameidata *nd, unsigned int flags) +{ + return vfsub__lookup_hash(this, parent, nd); +} +#endif /* CONFIG_AUFS_DLGT */ + +struct dentry *au_lkup_hash(const char *name, struct dentry *parent, + int len, struct au_ndx *ndx) +{ + struct dentry *dentry; + char *p; + unsigned long hash; + struct qstr this; + unsigned int c; + struct nameidata tmp_nd, *ndo; + int err; + + LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name); + + //todo: export and call __lookup_one_len() in fs/namei.c + dentry = ERR_PTR(-EACCES); + this.name = name; + this.len = len; + if (unlikely(!len)) + goto out; + + p = (void *)name; + hash = init_name_hash(); + while (len--) { + c = *p++; + if (unlikely(c == '/' || c == '\0')) + goto out; + hash = partial_name_hash(c, hash); + } + this.hash = end_name_hash(hash); + + ndo = ndx->nd; + if (ndo) { + tmp_nd = *ndo; + err = au_fake_intent(&tmp_nd, ndx->br->br_perm); + dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_intent; + } else + memset(&tmp_nd, 0, sizeof(tmp_nd)); + + tmp_nd.path.dentry = parent; + tmp_nd.path.mnt = ndx->nfsmnt; + path_get(&tmp_nd.path); + dentry = au_lkup_hash_dlgt(&this, parent, &tmp_nd, ndx->flags); + if (0 && !IS_ERR(dentry)) + AuDbgDentry(dentry); + if (!IS_ERR(dentry)) { + /* why negative dentry for a new dir was unhashed? */ + if (unlikely(d_unhashed(dentry))) + d_rehash(dentry); + if (tmp_nd.intent.open.file + && tmp_nd.intent.open.file->f_dentry) { + //AuDbgFile(tmp_nd.intent.open.file); + ndx->nd_file = tmp_nd.intent.open.file; + tmp_nd.intent.open.file = NULL; + //au_br_get(ndx->br); + } + } + path_put(&tmp_nd.path); + + out_intent: + if (tmp_nd.intent.open.file) + put_filp(tmp_nd.intent.open.file); + out: + AuTraceErrPtr(dentry); + return dentry; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/br_xfs.c linux-2.6.25.4-unionfs/fs/aufs/br_xfs.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/br_xfs.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/br_xfs.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * special handling inode attributes on XFS branch in linux-2.6.24 and later + * + * $Id: br_xfs.c,v 1.2 2008/04/21 01:33:00 sfjro Exp $ + */ + +#include "aufs.h" + +/* h_mnt can be NULL, is it safe? */ +dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt, + struct dentry *h_dentry) +{ + dev_t rdev; + int err; + struct kstat st; + + LKTRTrace("hi%lu\n", h_inode->i_ino); + if (h_dentry) + LKTRTrace("%.*s\n", AuDLNPair(h_dentry)); + + rdev = h_inode->i_rdev; + if (!rdev || !au_test_xfs(h_inode->i_sb)) + goto out; + + rdev = 0; + if (!h_dentry) { + err = 0; + h_dentry = d_find_alias(h_inode); + if (unlikely(!h_dentry)) + goto failure; + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) { + h_dentry = NULL; + goto failure; + } + LKTRTrace("%.*s\n", AuDLNPair(h_dentry)); + } else + dget(h_dentry); + + err = vfsub_getattr(h_mnt, h_dentry, &st, /*dlgt*/0); + dput(h_dentry); + if (!err) { + rdev = st.rdev; + goto out; /* success */ + } + + failure: + AuIOErr("failed rdev for XFS inode, hi%lu, %d\n", h_inode->i_ino, err); + out: + return rdev; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/cpup.c linux-2.6.25.4-unionfs/fs/aufs/cpup.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/cpup.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/cpup.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,1039 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * copy-up functions, see wbr_policy.c for copy-down + * + * $Id: cpup.c,v 1.5 2008/05/12 00:29:11 sfjro Exp $ + */ + +#include +#include +#include "aufs.h" + +/* violent cpup_attr_*() functions don't care inode lock */ +void au_cpup_attr_timesizes(struct inode *inode) +{ + struct inode *h_inode; + + LKTRTrace("i%lu\n", inode->i_ino); + //IMustLock(inode); + h_inode = au_h_iptr(inode, au_ibstart(inode)); + AuDebugOn(!h_inode); + //IMustLock(!h_inode); + + fsstack_copy_attr_times(inode, h_inode); + //todo: this spin_lock conflicts the new unionfs patch in -mm tree + spin_lock(&inode->i_lock); + fsstack_copy_inode_size(inode, h_inode); + spin_unlock(&inode->i_lock); +} + +void au_cpup_attr_nlink(struct inode *inode) +{ + struct inode *h_inode; + + LKTRTrace("i%lu\n", inode->i_ino); + //IMustLock(inode); + AuDebugOn(!inode->i_mode); + + h_inode = au_h_iptr(inode, au_ibstart(inode)); + inode->i_nlink = h_inode->i_nlink; + + /* + * fewer nlink makes find(1) noisy, but larger nlink doesn't. + * it may includes whplink directory. + */ + if (unlikely(S_ISDIR(h_inode->i_mode))) { + aufs_bindex_t bindex, bend; + bend = au_ibend(inode); + for (bindex = au_ibstart(inode) + 1; bindex <= bend; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode) + au_add_nlink(inode, h_inode); + } + } +} + +void au_cpup_attr_changeable(struct inode *inode) +{ + struct inode *h_inode; + + LKTRTrace("i%lu\n", inode->i_ino); + //IMustLock(inode); + h_inode = au_h_iptr(inode, au_ibstart(inode)); + AuDebugOn(!h_inode); + + inode->i_mode = h_inode->i_mode; + inode->i_uid = h_inode->i_uid; + inode->i_gid = h_inode->i_gid; + au_cpup_attr_timesizes(inode); + + //?? + inode->i_flags = h_inode->i_flags; +} + +void au_cpup_igen(struct inode *inode, struct inode *h_inode) +{ + inode->i_generation = h_inode->i_generation; + au_ii(inode)->ii_hsb1 = h_inode->i_sb; +} + +void au_cpup_attr_all(struct inode *inode) +{ + struct inode *h_inode; + + LKTRTrace("i%lu\n", inode->i_ino); + //IMustLock(inode); + h_inode = au_h_iptr(inode, au_ibstart(inode)); + AuDebugOn(!h_inode); + + au_cpup_attr_changeable(inode); + if (inode->i_nlink > 0) + au_cpup_attr_nlink(inode); + + switch (inode->i_mode & S_IFMT) { + case S_IFBLK: + case S_IFCHR: + inode->i_rdev = au_h_rdev(h_inode, /*h_mnt*/NULL, + /*h_dentry*/NULL); + } + inode->i_blkbits = h_inode->i_blkbits; + au_cpup_igen(inode, h_inode); +} + +/* ---------------------------------------------------------------------- */ + +/* Note: dt_dentry and dt_hidden_dentry are not dget/dput-ed */ + +/* keep the timestamps of the parent dir when cpup */ +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct dentry *h_dentry, struct au_hinode *hdir) +{ + struct inode *inode; + + LKTRTrace("%.*s, hdir %d\n", AuDLNPair(dentry), !!hdir); + AuDebugOn(!dentry || !h_dentry || !h_dentry->d_inode); + + dt->dt_dentry = dentry; + dt->dt_h_dentry = h_dentry; + dt->dt_hdir = hdir; + inode = h_dentry->d_inode; + dt->dt_atime = inode->i_atime; + dt->dt_mtime = inode->i_mtime; + //smp_mb(); +} + +void au_dtime_revert(struct au_dtime *dt) +{ + struct iattr attr; + int err; + struct au_hin_ignore ign; + struct vfsub_args vargs; + + LKTRTrace("%.*s\n", AuDLNPair(dt->dt_dentry)); + + attr.ia_atime = dt->dt_atime; + attr.ia_mtime = dt->dt_mtime; + attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET + | ATTR_ATIME | ATTR_ATIME_SET; + + vfsub_args_init(&vargs, &ign, + au_opt_test_dlgt(au_mntflags(dt->dt_dentry->d_sb)), 0); + if (unlikely(dt->dt_hdir)) + vfsub_ign_hinode(&vargs, IN_ATTRIB, dt->dt_hdir); + err = vfsub_notify_change(dt->dt_h_dentry, &attr, &vargs); + if (unlikely(err)) + AuWarn("restoring timestamps failed(%d). ignored\n", err); +} + +/* ---------------------------------------------------------------------- */ + +static noinline_for_stack int +cpup_iattr(struct dentry *h_dst, struct dentry *h_src, int dlgt) +{ + int err, sbits; + struct iattr ia; + struct inode *h_isrc, *h_idst; + struct vfsub_args vargs; + + LKTRTrace("%.*s\n", AuDLNPair(h_dst)); + h_idst = h_dst->d_inode; + //IMustLock(h_idst); + h_isrc = h_src->d_inode; + //IMustLock(h_isrc); + + ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID + | ATTR_ATIME | ATTR_MTIME + | ATTR_ATIME_SET | ATTR_MTIME_SET; + ia.ia_mode = h_isrc->i_mode; + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + ia.ia_atime = h_isrc->i_atime; + ia.ia_mtime = h_isrc->i_mtime; + sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); + + vfsub_args_init(&vargs, NULL, dlgt, /*force_unlink*/0); + err = vfsub_notify_change(h_dst, &ia, &vargs); + //if (LktrCond) err = -1; + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_dst->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_notify_change(h_dst, &ia, &vargs); + } + + if (!err) + h_idst->i_flags = h_isrc->i_flags; //?? + + AuTraceErr(err); + return err; +} + +/* + * to support a sparse file which is opened with O_APPEND, + * we need to close the file. + */ +static noinline_for_stack int +cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc, + loff_t len) +{ + int err, i; + struct super_block *sb; + struct inode *h_inode; + enum { SRC, DST }; + struct { + aufs_bindex_t bindex; + unsigned int flags; + struct dentry *dentry; + struct file *file; + void *label, *label_file; + } *h, hidden[] = { + { + .bindex = bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out, + .label_file = &&out_src_file + }, + { + .bindex = bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out_src_file, + .label_file = &&out_dst_file + } + }; + + LKTRTrace("dentry %.*s, bdst %d, bsrc %d, len %lld\n", + AuDLNPair(dentry), bdst, bsrc, len); + AuDebugOn(bsrc <= bdst); + AuDebugOn(!len); + sb = dentry->d_sb; + AuDebugOn(au_test_ro(sb, bdst, dentry->d_inode)); + /* bsrc branch can be ro/rw. */ + + h = hidden; + for (i = 0; i < 2; i++, h++) { + h->dentry = au_h_dptr(dentry, h->bindex); + AuDebugOn(!h->dentry); + h_inode = h->dentry->d_inode; + AuDebugOn(!h_inode || !S_ISREG(h_inode->i_mode)); + h->file = au_h_open(dentry, h->bindex, h->flags, /*file*/NULL); + //if (LktrCond) + //{fput(h->file);au_sbr_put(sb, h->bindex);h->file=ERR_PTR(-1);} + err = PTR_ERR(h->file); + if (IS_ERR(h->file)) + goto *h->label; + err = -EINVAL; + if (unlikely(!h->file->f_op)) + goto *h->label_file; + } + + /* stop updating while we copyup */ + IMustLock(hidden[SRC].dentry->d_inode); + err = au_copy_file(hidden[DST].file, hidden[SRC].file, len, sb); + + out_dst_file: + fput(hidden[DST].file); + au_sbr_put(sb, hidden[DST].bindex); + out_src_file: + fput(hidden[SRC].file); + au_sbr_put(sb, hidden[SRC].bindex); + out: + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, struct inode *h_inode, + struct inode *h_dir, struct dentry *h_dst, int dlgt) +{ + int err, rerr; + loff_t l; + struct vfsub_args vargs; + + AuTraceEnter(); + + err = 0; + l = i_size_read(h_inode); + if (len == -1 || l < len) + len = l; + if (len) + err = cpup_regular(dentry, bdst, bsrc, len); + //if (LktrCond) err = -1; + if (!err) + goto out; /* success */ + + vfsub_args_init(&vargs, NULL, dlgt, 0); + rerr = vfsub_unlink(h_dir, h_dst, &vargs); + if (rerr) { + AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n", + AuDLNPair(h_dst), err, rerr); + err = -EIO; + } + + out: + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +au_do_cpup_symlink(struct dentry *h_dst, struct dentry *h_src, + struct inode *h_dir, umode_t mode, int dlgt) +{ + int err, symlen; + char *sym; + mm_segment_t old_fs; + + AuTraceEnter(); + + err = -ENOMEM; + sym = __getname(); + //if (LktrCond) {__putname(sym); sym = NULL;} + if (unlikely(!sym)) + goto out; + + old_fs = get_fs(); + set_fs(KERNEL_DS); + symlen = h_src->d_inode->i_op->readlink(h_src, (char __user *)sym, + PATH_MAX); + err = symlen; + //if (LktrCond) err = symlen = -1; + set_fs(old_fs); + + if (symlen > 0) { + sym[symlen] = 0; + err = vfsub_symlink(h_dir, h_dst, sym, mode, dlgt); + //if (LktrCond) + //{vfs_unlink(h_dir, h_dst); err = -1;} + } + __putname(sym); + + out: + AuTraceErr(err); + return err; +} + +/* return with hidden dst inode is locked */ +static noinline_for_stack int +cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, aufs_bindex_t bsrc, + loff_t len, unsigned int flags, int dlgt) +{ + int err, isdir, hinotify; + struct dentry *h_src, *h_dst, *h_parent, *parent; + struct inode *h_inode, *h_dir; + struct au_dtime dt; + umode_t mode; + struct super_block *sb; + struct au_hinode *hgdir; + const int do_dt = au_ftest_cpup(flags, DTIME); + unsigned int mnt_flags; + + LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, dtime %u\n", + AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len, + do_dt); + sb = dentry->d_sb; + AuDebugOn(bdst >= bsrc || au_test_ro(sb, bdst, NULL)); + /* bsrc branch can be ro/rw. */ + + h_src = au_h_dptr(dentry, bsrc); + AuDebugOn(!h_src); + h_inode = h_src->d_inode; + AuDebugOn(!h_inode); + + /* stop referencing while we are creating */ + parent = dget_parent(dentry); + h_dst = au_h_dptr(dentry, bdst); + AuDebugOn(h_dst && h_dst->d_inode); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + + mnt_flags = au_mntflags(sb); + hinotify = !!au_opt_test(mnt_flags, UDBA_INOTIFY); + if (do_dt) { + hgdir = NULL; + if (unlikely(hinotify && !IS_ROOT(parent))) { + struct dentry *gparent; + gparent = dget_parent(parent); + hgdir = au_hi(gparent->d_inode, bdst); + dput(gparent); + } + au_dtime_store(&dt, parent, h_parent, hgdir); + } + + isdir = 0; + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + /* stop updating while we are referencing */ + IMustLock(h_inode); + err = au_h_create(h_dir, h_dst, mode | S_IWUSR, dlgt, NULL, + au_nfsmnt(sb, bdst)); + //if (LktrCond) {vfs_unlink(h_dir, h_dst); err = -1;} + if (!err) + err = au_do_cpup_regular(dentry, bdst, bsrc, len, + h_inode, h_dir, h_dst, dlgt); + break; + case S_IFDIR: + isdir = 1; + err = vfsub_mkdir(h_dir, h_dst, mode, dlgt); + //if (LktrCond) {vfs_rmdir(h_dir, h_dst); err = -1;} + if (!err) { + /* setattr case: dir is not locked */ + if (0 && au_ibstart(parent->d_inode) == bdst) + au_cpup_attr_nlink(parent->d_inode); + au_cpup_attr_nlink(dentry->d_inode); + } + break; + case S_IFLNK: + err = au_do_cpup_symlink(h_dst, h_src, h_dir, mode, dlgt); + break; + case S_IFCHR: + case S_IFBLK: + AuDebugOn(!capable(CAP_MKNOD)); + /*FALLTHROUGH*/ + case S_IFIFO: + case S_IFSOCK: + err = vfsub_mknod(h_dir, h_dst, mode, + au_h_rdev(h_inode, /*h_mnt*/NULL, h_src), + dlgt); + //if (LktrCond) {vfs_unlink(h_dir, h_dst); err = -1;} + break; + default: + AuIOErr("Unknown inode type 0%o\n", mode); + err = -EIO; + } + + //todo: should it be always? + if (unlikely(hinotify + && !isdir + && au_opt_test(mnt_flags, XINO) + && h_inode->i_nlink == 1 + && bdst < bsrc)) + au_xino_write0(sb, bsrc, h_inode->i_ino, /*ino*/0); + /* ignore this error */ + + if (do_dt) + au_dtime_revert(&dt); + dput(parent); + AuTraceErr(err); + return err; +} + +/* + * copyup the @dentry from @bsrc to @bdst. + * the caller must set the both of hidden dentries. + * @len is for truncating when it is -1 copyup the entire file. + */ +int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags) +{ + int err, rerr, isdir, dlgt, plink; + struct dentry *h_src, *h_dst, *parent, *h_parent; + struct inode *dst_inode, *h_dir, *inode; + struct super_block *sb; + aufs_bindex_t old_ibstart; + struct au_dtime dt; + struct vfsub_args vargs; + struct au_hinode *hgdir; + unsigned int mnt_flags; + + LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n", + AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len, + flags); + sb = dentry->d_sb; + AuDebugOn(bsrc <= bdst); + h_dst = au_h_dptr(dentry, bdst); + AuDebugOn(!h_dst || h_dst->d_inode); + h_parent = h_dst->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + h_src = au_h_dptr(dentry, bsrc); + AuDebugOn(!h_src || !h_src->d_inode); + inode = dentry->d_inode; + IiMustWriteLock(inode); + parent = dget_parent(dentry); + + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + plink = !!au_opt_test(mnt_flags, PLINK); + dst_inode = au_h_iptr(inode, bdst); + if (unlikely(dst_inode)) { + if (unlikely(!plink)) { + err = -EIO; + AuIOErr("i%lu exists on a upper branch " + "but plink is disabled\n", inode->i_ino); + goto out; + } + + if (dst_inode->i_nlink) { + h_src = au_plink_lkup(sb, bdst, inode); + err = PTR_ERR(h_src); + if (IS_ERR(h_src)) + goto out; + AuDebugOn(!h_src->d_inode); + err = vfsub_link(h_src, h_dir, h_dst, dlgt); + dput(h_src); + goto out; + } else + //todo: cpup_wh_file + /* udba work */ + au_update_brange(inode, 1); + } + + old_ibstart = au_ibstart(inode); + err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt); + if (unlikely(err)) + goto out; + dst_inode = h_dst->d_inode; + mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); + + //todo: test dlgt + err = cpup_iattr(h_dst, h_src, dlgt); + //if (LktrCond) err = -1; + isdir = S_ISDIR(dst_inode->i_mode); + if (!err) { + if (bdst < old_ibstart) + au_set_ibstart(inode, bdst); + au_set_h_iptr(inode, bdst, igrab(dst_inode), + au_hi_flags(inode, isdir)); + mutex_unlock(&dst_inode->i_mutex); + if (!isdir + && h_src->d_inode->i_nlink > 1 + && plink) + au_plink_append(sb, inode, h_dst, bdst); + goto out; /* success */ + } + + /* revert */ + mutex_unlock(&dst_inode->i_mutex); + hgdir = NULL; + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) + && !IS_ROOT(parent))) { + struct dentry *gparent; + gparent = dget_parent(parent); + hgdir = au_hi(gparent->d_inode, bdst); + dput(gparent); + } + au_dtime_store(&dt, parent, h_parent, hgdir); + vfsub_args_init(&vargs, NULL, dlgt, 0); + if (!isdir) + rerr = vfsub_unlink(h_dir, h_dst, &vargs); + else + rerr = vfsub_rmdir(h_dir, h_dst, &vargs); + //rerr = -1; + au_dtime_revert(&dt); + if (rerr) { + AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); + err = -EIO; + } + + out: + dput(parent); + AuTraceErr(err); + return err; +} + +struct au_cpup_single_args { + int *errp; + struct dentry *dentry; + aufs_bindex_t bdst, bsrc; + loff_t len; + unsigned int flags; +}; + +static void au_call_cpup_single(void *args) +{ + struct au_cpup_single_args *a = args; + *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len, + a->flags); +} + +int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags) +{ + int err, wkq_err; + struct dentry *h_dentry; + umode_t mode; + + LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, flags 0x%x\n", + AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len, + flags); + + h_dentry = au_h_dptr(dentry, bsrc); + mode = h_dentry->d_inode->i_mode & S_IFMT; + if ((mode != S_IFCHR && mode != S_IFBLK) + || capable(CAP_MKNOD)) + err = au_cpup_single(dentry, bdst, bsrc, len, flags); + else { + struct au_cpup_single_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .bsrc = bsrc, + .len = len, + .flags = flags + }; + wkq_err = au_wkq_wait(au_call_cpup_single, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } + + AuTraceErr(err); + return err; +} + +/* + * copyup the @dentry from the first active hidden branch to @bdst, + * using au_cpup_single(). + */ +int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + unsigned int flags) +{ + int err; + struct inode *inode; + aufs_bindex_t bsrc, bend; + + LKTRTrace("%.*s, bdst %d, len %Ld, flags 0x%x\n", + AuDLNPair(dentry), bdst, len, flags); + inode = dentry->d_inode; + AuDebugOn(!S_ISDIR(inode->i_mode) && au_dbstart(dentry) < bdst); + + bend = au_dbend(dentry); + for (bsrc = bdst + 1; bsrc <= bend; bsrc++) + if (au_h_dptr(dentry, bsrc)) + break; + AuDebugOn(!au_h_dptr(dentry, bsrc)); + + err = au_lkup_neg(dentry, bdst); + //err = -1; + if (!err) { + err = au_cpup_single(dentry, bdst, bsrc, len, flags); + if (!err) + return 0; /* success */ + + /* revert */ + au_set_h_dptr(dentry, bdst, NULL); + au_set_dbstart(dentry, bsrc); + } + + AuTraceErr(err); + return err; +} + +struct au_cpup_simple_args { + int *errp; + struct dentry *dentry; + aufs_bindex_t bdst; + loff_t len; + unsigned int flags; +}; + +static void au_call_cpup_simple(void *args) +{ + struct au_cpup_simple_args *a = args; + *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags); +} + +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + unsigned int flags) +{ + int err, do_sio, dlgt, wkq_err; + struct dentry *parent; + struct inode *h_dir, *dir; + + LKTRTrace("%.*s, b%d, len %Ld, flags 0x%x\n", + AuDLNPair(dentry), bdst, len, flags); + + parent = dget_parent(dentry); + dir = parent->d_inode; + h_dir = au_h_iptr(dir, bdst); + dlgt = !!au_opt_test_dlgt(au_mntflags(dir->i_sb)); + do_sio = au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE, dlgt); + if (!do_sio) { + /* + * testing CAP_MKNOD is for generic fs, + * but CAP_FSETID is for xfs only, currently. + */ + umode_t mode = dentry->d_inode->i_mode; + do_sio = (((mode & (S_IFCHR | S_IFBLK)) + && !capable(CAP_MKNOD)) + || ((mode & (S_ISUID | S_ISGID)) + && !capable(CAP_FSETID))); + } + if (!do_sio) + err = au_cpup_simple(dentry, bdst, len, flags); + else { + struct au_cpup_simple_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, + .flags = flags + }; + wkq_err = au_wkq_wait(au_call_cpup_simple, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } + + dput(parent); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static noinline_for_stack int +au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *wh_dentry, struct file *file, loff_t len) +{ + int err; + struct au_dinfo *dinfo; + aufs_bindex_t bstart; + struct dentry *h_d_bdst, *h_d_bstart; + + AuTraceEnter(); + + dinfo = au_di(dentry); + bstart = dinfo->di_bstart; + h_d_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry; + dinfo->di_bstart = bdst; + dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry; + h_d_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry; + if (file) + dinfo->di_hdentry[0 + bstart].hd_dentry + = au_h_fptr(file, au_fbstart(file))->f_dentry; + err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME); + //if (LktrCond) err = -1; + if (!err && file) { + err = au_reopen_nondir(file); + //err = -1; + dinfo->di_hdentry[0 + bstart].hd_dentry = h_d_bstart; + } + dinfo->di_hdentry[0 + bdst].hd_dentry = h_d_bdst; + dinfo->di_bstart = bstart; + + AuTraceErr(err); + return err; +} + +/* + * copyup the deleted file for writing. + */ +int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + struct file *file) +{ + int err, dlgt; + struct dentry *parent, *h_parent, *wh_dentry; + struct super_block *sb; + unsigned int mnt_flags; + struct au_dtime dt; + struct vfsub_args vargs; + struct au_hinode *hgdir; + struct au_ndx ndx = { + .nd = NULL, + .flags = 0, + //.br = NULL + }; + + LKTRTrace("%.*s, bdst %d, len %Lu\n", AuDLNPair(dentry), bdst, len); + AuDebugOn(S_ISDIR(dentry->d_inode->i_mode) + || (file && !(file->f_mode & FMODE_WRITE))); + DiMustWriteLock(dentry); + + parent = dget_parent(dentry); + IiMustAnyLock(parent->d_inode); + h_parent = au_h_dptr(parent, bdst); + AuDebugOn(!h_parent); + + sb = parent->d_sb; + mnt_flags = au_mntflags(sb); + dlgt = 0; + ndx.nfsmnt = au_nfsmnt(sb, bdst); + if (unlikely(au_opt_test_dlgt(mnt_flags))) { + dlgt = 1; + au_fset_ndx(ndx.flags, DLGT); + } + wh_dentry = au_whtmp_lkup(h_parent, &dentry->d_name, &ndx); + //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);} + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + hgdir = NULL; + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) + && !IS_ROOT(parent))) { + struct dentry *gparent; + gparent = dget_parent(parent); + hgdir = au_hi(gparent->d_inode, bdst); + dput(gparent); + } + au_dtime_store(&dt, parent, h_parent, hgdir); + err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); + if (unlikely(err)) + goto out_wh; + + AuDebugOn(!d_unhashed(dentry)); + /* dget first to force sillyrename on nfs */ + dget(wh_dentry); + vfsub_args_init(&vargs, NULL, dlgt, 0); + err = vfsub_unlink(h_parent->d_inode, wh_dentry, &vargs); + //if (LktrCond) err = -1; + if (unlikely(err)) { + AuIOErr("failed remove copied-up tmp file %.*s(%d)\n", + AuDLNPair(wh_dentry), err); + err = -EIO; + } + au_dtime_revert(&dt); + au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); + + out_wh: + dput(wh_dentry); + out: + dput(parent); + AuTraceErr(err); + //au_debug_off(); + return err; +} + +struct au_cpup_wh_args { + int *errp; + struct dentry *dentry; + aufs_bindex_t bdst; + loff_t len; + struct file *file; +}; + +static void au_call_cpup_wh(void *args) +{ + struct au_cpup_wh_args *a = args; + *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file); +} + +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + struct file *file) +{ + int err, wkq_err; + struct dentry *parent; + struct inode *dir, *h_dir; + + AuTraceEnter(); + parent = dget_parent(dentry); + dir = parent->d_inode; + IiMustAnyLock(dir); + h_dir = au_h_iptr(dir, bdst); + + if (!au_test_h_perm_sio + (h_dir, MAY_EXEC | MAY_WRITE, + au_opt_test_dlgt(au_mntflags(dentry->d_sb)))) + err = au_cpup_wh(dentry, bdst, len, file); + else { + struct au_cpup_wh_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, + .file = file + }; + wkq_err = au_wkq_wait(au_call_cpup_wh, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } + dput(parent); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * generic routine for both of copy-up and copy-down. + * Although I've tried building a path by dcsub, I gave up this approach. + * Since the ancestor directory may be moved/renamed during copy. + */ +/* cf. revalidate function in file.c */ +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *h_parent, void *arg), + void *arg) +{ + int err, hinotify; + struct super_block *sb; + struct dentry *d, *parent, *h_parent, *gparent, *real_parent; + + LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n", + AuDLNPair(dentry), bdst, parent_ino(dentry), locked); + sb = dentry->d_sb; + AuDebugOn(au_test_ro(sb, bdst, NULL)); + err = 0; + parent = dget_parent(dentry); + IiMustWriteLock(parent->d_inode); + if (unlikely(IS_ROOT(parent))) + goto out; + if (locked) { + DiMustAnyLock(locked); + IiMustAnyLock(locked->d_inode); + } + + /* slow loop, keep it simple and stupid */ + real_parent = parent; + hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY); + while (1) { + dput(parent); + parent = dget_parent(dentry); + h_parent = au_h_dptr(parent, bdst); + if (h_parent) + goto out; /* success */ + + /* find top dir which is needed to cpup */ + do { + d = parent; + dput(parent); + parent = dget_parent(d); + if (parent != locked) { + di_read_lock_parent3(parent, !AuLock_IR); + h_parent = au_h_dptr(parent, bdst); + di_read_unlock(parent, !AuLock_IR); + } else + h_parent = au_h_dptr(parent, bdst); + } while (!h_parent); + + if (d != real_parent) + di_write_lock_child3(d); + + /* somebody else might create while we were sleeping */ + if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { + struct inode *h_dir = h_parent->d_inode, + *dir = parent->d_inode; + + if (au_h_dptr(d, bdst)) + au_update_dbstart(d); + //AuDebugOn(au_dbstart(d) <= bdst); + if (parent != locked) + di_read_lock_parent3(parent, AuLock_IR); + gparent = NULL; + if (unlikely(hinotify && !IS_ROOT(parent))) { + gparent = dget_parent(parent); + if (gparent != locked) + ii_read_lock_parent4(gparent->d_inode); + else { + dput(gparent); + gparent = NULL; + } + } + au_hdir_lock(h_dir, dir, bdst); + err = cp(d, bdst, h_parent, arg); + //if (LktrCond) err = -1; + au_hdir_unlock(h_dir, dir, bdst); + if (unlikely(gparent)) { + ii_read_unlock(gparent->d_inode); + dput(gparent); + } + if (parent != locked) + di_read_unlock(parent, AuLock_IR); + } + + if (d != real_parent) + di_write_unlock(d); + if (unlikely(err)) + break; + } + + out: + dput(parent); + AuTraceErr(err); + return err; +} + +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *h_parent, void *arg) +{ + int err; + + err = au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); + + AuTraceErr(err); + return err; +} + +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked) +{ + int err; + + err = au_cp_dirs(dentry, bdst, locked, au_cpup_dir, NULL); + + AuTraceErr(err); + return err; +} + +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked) +{ + int err; + struct dentry *parent; + struct inode *dir; + + parent = dget_parent(dentry); + dir = parent->d_inode; + LKTRTrace("%.*s, b%d, parent i%lu, locked %p\n", + AuDLNPair(dentry), bdst, dir->i_ino, locked); + DiMustReadLock(parent); + IiMustReadLock(dir); + + err = 0; + if (au_h_iptr(dir, bdst)) + goto out; + + di_read_unlock(parent, AuLock_IR); + di_write_lock_parent2(parent); + /* someone else might change our inode while we were sleeping */ + if (unlikely(!au_h_iptr(dir, bdst))) + err = au_cpup_dirs(dentry, bdst, locked); + di_downgrade_lock(parent, AuLock_IR); + + out: + dput(parent); + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/cpup.h linux-2.6.25.4-unionfs/fs/aufs/cpup.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/cpup.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/cpup.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * copy-up/down functions + * + * $Id: cpup.h,v 1.2 2008/04/21 01:33:43 sfjro Exp $ + */ + +#ifndef __AUFS_CPUP_H__ +#define __AUFS_CPUP_H__ + +#ifdef __KERNEL__ + +#include +#include + +void au_cpup_attr_timesizes(struct inode *inode); +void au_cpup_attr_nlink(struct inode *inode); +void au_cpup_attr_changeable(struct inode *inode); +void au_cpup_igen(struct inode *inode, struct inode *h_inode); +void au_cpup_attr_all(struct inode *inode); + +/* ---------------------------------------------------------------------- */ + +/* cpup flags */ +#define AuCpup_DTIME 1 /* do dtime_store/revert */ +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) { (flags) |= AuCpup_##name; } +#define au_fclr_cpup(flags, name) { (flags) &= ~AuCpup_##name; } + +int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags); +int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, + aufs_bindex_t bsrc, loff_t len, unsigned int flags); +int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + unsigned int flags); +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + unsigned int flags); +int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + struct file *file); +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, + struct file *file); + +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, struct dentry *locked, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *h_parent, void *arg), + void *arg); +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked); +int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked); + +/* ---------------------------------------------------------------------- */ + +/* keep timestamps when copyup */ +struct au_dtime { + struct dentry *dt_dentry, *dt_h_dentry; + struct au_hinode *dt_hdir; + struct timespec dt_atime, dt_mtime; +}; +void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, + struct dentry *h_dentry, struct au_hinode *hdir); +void au_dtime_revert(struct au_dtime *dt); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dcsub.c linux-2.6.25.4-unionfs/fs/aufs/dcsub.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/dcsub.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dcsub.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,233 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sub-routines for dentry cache + * + * $Id: dcsub.c,v 1.3 2008/05/19 01:47:37 sfjro Exp $ + */ + +#include "aufs.h" + +static void au_dpage_free(struct au_dpage *dpage) +{ + int i; + + AuTraceEnter(); + AuDebugOn(!dpage); + + for (i = 0; i < dpage->ndentry; i++) + dput(dpage->dentries[i]); + free_page((unsigned long)dpage->dentries); +} + +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) +{ + int err; + void *p; + + AuTraceEnter(); + + err = -ENOMEM; + dpages->dpages = kmalloc(sizeof(*dpages->dpages), gfp); + if (unlikely(!dpages->dpages)) + goto out; + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out_dpages; + dpages->dpages[0].ndentry = 0; + dpages->dpages[0].dentries = p; + dpages->ndpage = 1; + return 0; /* success */ + + out_dpages: + kfree(dpages->dpages); + out: + AuTraceErr(err); + return err; +} + +void au_dpages_free(struct au_dcsub_pages *dpages) +{ + int i; + + AuTraceEnter(); + + for (i = 0; i < dpages->ndpage; i++) + au_dpage_free(dpages->dpages + i); + kfree(dpages->dpages); +} + +static int au_dpages_append(struct au_dcsub_pages *dpages, + struct dentry *dentry, gfp_t gfp) +{ + int err, sz; + struct au_dpage *dpage; + void *p; + + //AuTraceEnter(); + + dpage = dpages->dpages + dpages->ndpage - 1; + AuDebugOn(!dpage); + sz = PAGE_SIZE / sizeof(dentry); + if (unlikely(dpage->ndentry >= sz)) { + LKTRLabel(new dpage); + err = -ENOMEM; + sz = dpages->ndpage * sizeof(*dpages->dpages); + p = au_kzrealloc(dpages->dpages, sz, + sz + sizeof(*dpages->dpages), gfp); + if (unlikely(!p)) + goto out; + dpages->dpages = p; + dpage = dpages->dpages + dpages->ndpage; + p = (void *)__get_free_page(gfp); + if (unlikely(!p)) + goto out; + dpage->ndentry = 0; + dpage->dentries = p; + dpages->ndpage++; + } + + dpage->dentries[dpage->ndentry++] = dget(dentry); + return 0; /* success */ + + out: + //AuTraceErr(err); + return err; +} + +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg) +{ + int err; + struct dentry *this_parent = root; + struct list_head *next; + struct super_block *sb = root->d_sb; + + AuTraceEnter(); + + err = 0; + spin_lock(&dcache_lock); + repeat: + next = this_parent->d_subdirs.next; + resume: + if (this_parent->d_sb == sb + && !IS_ROOT(this_parent) + && atomic_read(&this_parent->d_count) + && this_parent->d_inode + && (!test || test(this_parent, arg))) { + err = au_dpages_append(dpages, this_parent, GFP_ATOMIC); + if (unlikely(err)) + goto out; + } + + while (next != &this_parent->d_subdirs) { + struct list_head *tmp = next; + struct dentry *dentry = list_entry(tmp, struct dentry, + d_u.d_child); + next = tmp->next; + if (unlikely(/*d_unhashed(dentry) || */!dentry->d_inode)) + continue; + if (!list_empty(&dentry->d_subdirs)) { + this_parent = dentry; + goto repeat; + } + if (dentry->d_sb == sb + && atomic_read(&dentry->d_count) + && (!test || test(dentry, arg))) { + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + if (unlikely(err)) + goto out; + } + } + + if (this_parent != root) { + next = this_parent->d_u.d_child.next; + this_parent = this_parent->d_parent; /* dcache_lock is locked */ + goto resume; + } + out: + spin_unlock(&dcache_lock); + AuTraceErr(err); + return err; +} + +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg) +{ + int err; + + AuTraceEnter(); + + err = 0; + spin_lock(&dcache_lock); + if (do_include && (!test || test(dentry, arg))) { + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + if (unlikely(err)) + goto out; + } + while (!IS_ROOT(dentry)) { + dentry = dentry->d_parent; /* dcache_lock is locked */ + if (!test || test(dentry, arg)) { + err = au_dpages_append(dpages, dentry, GFP_ATOMIC); + if (unlikely(err)) + break; + } + } + + out: + spin_unlock(&dcache_lock); + + AuTraceErr(err); + return err; +} + +int au_test_subdir(struct dentry *d1, struct dentry *d2) +{ + int err; + int i, j; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + LKTRTrace("%.*s, %.*s\n", AuDLNPair(d1), AuDLNPair(d2)); + + err = au_dpages_init(&dpages, GFP_TEMPORARY); + if (unlikely(err)) + goto out; + err = au_dcsub_pages_rev(&dpages, d1, /*do_include*/1, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = dpages.ndpage - 1; !err && i >= 0; i--) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + for (j = dpage->ndentry - 1; !err && j >= 0; j--) { + struct dentry *d; + d = dentries[j]; + //AuDbg("d %.*s\n", AuDLNPair(d)); + err = (d == d2); + } + } + + out_dpages: + au_dpages_free(&dpages); + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dcsub.h linux-2.6.25.4-unionfs/fs/aufs/dcsub.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/dcsub.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dcsub.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sub-routines for dentry cache + * + * $Id: dcsub.h,v 1.1 2008/04/18 12:18:29 sfjro Exp $ + */ + +#ifndef __AUFS_DCSUB_H__ +#define __AUFS_DCSUB_H__ + +#ifdef __KERNEL__ + +#include + +struct au_dpage { + int ndentry; + struct dentry **dentries; +}; + +struct au_dcsub_pages { + int ndpage; + struct au_dpage *dpages; +}; + +/* ---------------------------------------------------------------------- */ + +int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp); +void au_dpages_free(struct au_dcsub_pages *dpages); +typedef int (*au_dpages_test)(struct dentry *dentry, void *arg); +int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root, + au_dpages_test test, void *arg); +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry, + int do_include, au_dpages_test test, void *arg); +int au_test_subdir(struct dentry *d1, struct dentry *d2); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/debug.c linux-2.6.25.4-unionfs/fs/aufs/debug.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/debug.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/debug.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * debug print functions + * + * $Id: debug.c,v 1.2 2008/04/21 01:33:00 sfjro Exp $ + */ + +//#include +#include "aufs.h" + +atomic_t au_cond = ATOMIC_INIT(0); + +char *au_plevel = KERN_DEBUG; +#define dpri(fmt, arg...) do { \ + if (LktrCond) \ + printk("%s" fmt, au_plevel, ##arg); \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +void au_dpri_whlist(struct au_nhash *whlist) +{ + int i; + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos; + + for (i = 0; i < AuSize_NHASH; i++) { + head = whlist->heads + i; + hlist_for_each_entry(tpos, pos, head, wh_hash) + dpri("b%d, %.*s, %d\n", + tpos->wh_bindex, + tpos->wh_str.len, tpos->wh_str.name, + tpos->wh_str.len); + } +} + +void au_dpri_vdir(struct au_vdir *vdir) +{ + int i; + union au_vdir_deblk_p p; + unsigned char *o; + + if (!vdir || IS_ERR(vdir)) { + dpri("err %ld\n", PTR_ERR(vdir)); + return; + } + + dpri("nblk %d, deblk %p, last{%d, %p}, ver %lu\n", + vdir->vd_nblk, vdir->vd_deblk, + vdir->vd_last.i, vdir->vd_last.p.p, vdir->vd_version); + for (i = 0; i < vdir->vd_nblk; i++) { + p.deblk = vdir->vd_deblk[i]; + o = p.p; + dpri("[%d]: %p\n", i, o); + } +} + +static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, + struct dentry *wh) +{ + char *n = NULL; + int l = 0, ntfy = 0; + + if (!inode || IS_ERR(inode)) { + dpri("i%d: err %ld\n", bindex, PTR_ERR(inode)); + return -1; + } + + /* the type of i_blocks depends upon CONFIG_LSF */ + BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) + && sizeof(inode->i_blocks) != sizeof(u64)); + if (wh) { + n = (void *)wh->d_name.name; + l = wh->d_name.len; + } + + ntfy = au_test_inotify(inode); + dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, ntfy %d, sz %Lu, blk %Lu," + " ct %Ld, np %lu, st 0x%lx, f 0x%x, g %x%s%.*s\n", + bindex, + inode->i_ino, inode->i_sb ? au_sbtype(inode->i_sb) : "??", + atomic_read(&inode->i_count), inode->i_nlink, inode->i_mode, + ntfy, + i_size_read(inode), (u64)inode->i_blocks, + timespec_to_ns(&inode->i_ctime) & 0x0ffff, + inode->i_mapping ? inode->i_mapping->nrpages : 0, + inode->i_state, inode->i_flags, inode->i_generation, + l ? ", wh " : "", l, n); + return 0; +} + +void au_dpri_inode(struct inode *inode) +{ + struct au_iinfo *iinfo; + aufs_bindex_t bindex; + int err; + + err = do_pri_inode(-1, inode, NULL); + if (err || !au_test_aufs(inode->i_sb)) + return; + + iinfo = au_ii(inode); + if (!iinfo) + return; + dpri("i-1: bstart %d, bend %d, gen %d\n", + iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode)); + if (iinfo->ii_bstart < 0) + return; + for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) + do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, + iinfo->ii_hinode[0 + bindex].hi_whdentry); +} + +static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry, + struct list_head *intent) +{ + struct dentry *wh = NULL; + + if (!dentry || IS_ERR(dentry)) { + dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry)); + return -1; + } + /* do not call dget_parent() here */ + dpri("d%d: %.*s/%.*s, %s, cnt %d, flags 0x%x, intent %d\n", + bindex, + AuDLNPair(dentry->d_parent), AuDLNPair(dentry), + dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", + atomic_read(&dentry->d_count), dentry->d_flags, !!intent); + if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { + struct au_iinfo *iinfo = au_ii(dentry->d_inode); + if (iinfo) + wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; + } + do_pri_inode(bindex, dentry->d_inode, wh); + return 0; +} + +static struct list_head *au_dbg_h_intent(struct au_dinfo *dinfo, + aufs_bindex_t bindex) +{ +#ifdef CONFIG_AUFS_BR_NFS + return dinfo->di_hdentry[0 + bindex].hd_intent_list; +#else + return NULL; +#endif +} + +void au_dpri_dentry(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + aufs_bindex_t bindex; + int err; + + err = do_pri_dentry(-1, dentry, NULL); + if (err || !au_test_aufs(dentry->d_sb)) + return; + + dinfo = au_di(dentry); + if (!dinfo) + return; + dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n", + dinfo->di_bstart, dinfo->di_bend, + dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry)); + if (dinfo->di_bstart < 0) + return; + for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) + do_pri_dentry(bindex, dinfo->di_hdentry[0 + bindex].hd_dentry, + au_dbg_h_intent(dinfo, bindex)); +} + +static int do_pri_file(aufs_bindex_t bindex, struct file *file) +{ + char a[32]; + + if (!file || IS_ERR(file)) { + dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); + return -1; + } + a[0] = 0; + if (bindex < 0 + && file->f_dentry + && au_test_aufs(file->f_dentry->d_sb) + && au_fi(file)) + snprintf(a, sizeof(a), ", mmapped %d", au_test_mmapped(file)); + dpri("f%d: mode 0x%x, flags 0%o, cnt %d, pos %Lu%s\n", + bindex, file->f_mode, file->f_flags, file_count(file), + file->f_pos, a); + if (file->f_dentry) + do_pri_dentry(bindex, file->f_dentry, NULL); + return 0; +} + +void au_dpri_file(struct file *file) +{ + struct au_finfo *finfo; + aufs_bindex_t bindex; + int err; + + err = do_pri_file(-1, file); + if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb)) + return; + + finfo = au_fi(file); + if (!finfo) + return; + if (finfo->fi_bstart < 0) + return; + for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) { + struct au_hfile *hf; + //dpri("bindex %d\n", bindex); + hf = finfo->fi_hfile + bindex; + do_pri_file(bindex, hf ? hf->hf_file : NULL); + } +} + +static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) +{ + struct vfsmount *mnt; + struct super_block *sb; + + if (!br || IS_ERR(br) + || !(mnt = br->br_mnt) || IS_ERR(mnt) + || !(sb = mnt->mnt_sb) || IS_ERR(sb)) { + dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); + return -1; + } + + dpri("s%d: {perm 0x%x, cnt %d}, " + "%s, flags 0x%lx, cnt(BIAS) %d, active %d, xino %d\n", + bindex, br->br_perm, au_br_count(br), + au_sbtype(sb), sb->s_flags, sb->s_count - S_BIAS, + atomic_read(&sb->s_active), !!br->br_xino); + return 0; +} + +void au_dpri_sb(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + aufs_bindex_t bindex; + int err; + struct vfsmount mnt = { .mnt_sb = sb }; + struct au_branch fake = { + .br_perm = 0, + .br_mnt = &mnt, + .br_count = ATOMIC_INIT(0), + .br_xino = NULL + }; + + atomic_set(&fake.br_count, 0); + smp_mb(); /* atomic_set */ + err = do_pri_br(-1, &fake); + dpri("dev 0x%x\n", sb->s_dev); + if (err || !au_test_aufs(sb)) + return; + + sbinfo = au_sbi(sb); + if (!sbinfo) + return; + for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) { + //dpri("bindex %d\n", bindex); + do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); + } +} + +/* ---------------------------------------------------------------------- */ + +void au_dbg_sleep(int sec) +{ + static DECLARE_WAIT_QUEUE_HEAD(wq); + wait_event_timeout(wq, 0, sec * HZ); +} + +/* ---------------------------------------------------------------------- */ + +void au_debug_sbinfo_init(struct au_sbinfo *sbinfo) +{ +#ifdef ForceInotify + au_opt_set_udba(sbinfo->si_mntflags, UDBA_INOTIFY); +#endif +#ifdef ForceDlgt + au_opt_set(sbinfo->si_mntflags, DLGT); +#endif +#ifdef ForceNoPlink + au_opt_clr(sbinfo->si_mntflags, PLINK); +#endif +#ifdef ForceNoXino + au_opt_clr(sbinfo->si_mntflags, XINO); +#endif +#ifdef ForceNoRefrof + au_opt_clr(sbinfo->si_mntflags, REFROF); +#endif +} + +int __init au_debug_init(void) +{ + aufs_bindex_t bindex; + struct au_vdir_destr destr; + + bindex = -1; + AuDebugOn(bindex >= 0); + + destr.len = -1; + AuDebugOn(destr.len < NAME_MAX); + +#ifdef CONFIG_4KSTACKS + AuWarn("CONFIG_4KSTACKS is defined.\n"); +#endif + + return 0; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/debug.h linux-2.6.25.4-unionfs/fs/aufs/debug.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/debug.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/debug.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * debug print functions + * + * $Id: debug.h,v 1.2 2008/04/21 02:00:37 sfjro Exp $ + */ + +#ifndef __AUFS_DEBUG_H__ +#define __AUFS_DEBUG_H__ + +#ifdef __KERNEL__ + +#include + +/* to debug easier, do not make it an inlined function */ +#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) + +#ifdef CONFIG_AUFS_DEBUG +/* sparse warns about pointer */ +#define AuDebugOn(a) BUG_ON(!!(a)) +extern atomic_t au_cond; +#define au_debug_on() atomic_inc_return(&au_cond) +#define au_debug_off() atomic_dec_return(&au_cond) +static inline int au_debug_test(void) +{ + return atomic_read(&au_cond); +} +#else +#define AuDebugOn(a) do {} while (0) +#define au_debug_on() do {} while (0) +#define au_debug_off() do {} while (0) +static inline int au_debug_test(void) +{ + return 0; +} +#endif /* CONFIG_AUFS_DEBUG */ + +/* ---------------------------------------------------------------------- */ + +/* debug print */ +#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE) +#include +#ifdef CONFIG_AUFS_DEBUG +#undef LktrCond +#define LktrCond unlikely(au_debug_test() || (lktr_cond && lktr_cond())) +#endif +#else +#define LktrCond au_debug_test() +#define LKTRDumpVma(pre, vma, suf) do {} while (0) +#define LKTRDumpStack() do {} while (0) +#define LKTRTrace(fmt, args...) do { \ + if (LktrCond) \ + AuDbg(fmt, ##args); \ +} while (0) +#define LKTRLabel(label) LKTRTrace("%s\n", #label) +#endif /* CONFIG_LKTR */ + +#define AuTraceErr(e) do { \ + if (unlikely((e) < 0)) \ + LKTRTrace("err %d\n", (int)(e)); \ +} while (0) + +#define AuTraceErrPtr(p) do { \ + if (IS_ERR(p)) \ + LKTRTrace("err %ld\n", PTR_ERR(p)); \ +} while (0) + +#define AuTraceEnter() LKTRLabel(enter) + +/* dirty macros for debug print, use with "%.*s" and caution */ +#define AuLNPair(qstr) (qstr)->len, (qstr)->name +#define AuDLNPair(d) AuLNPair(&(d)->d_name) + +/* ---------------------------------------------------------------------- */ + +#define AuDpri(lvl, fmt, arg...) \ + printk(lvl AUFS_NAME " %s:%d:%s[%d]: " fmt, \ + __func__, __LINE__, current->comm, current->pid, ##arg) +#define AuDbg(fmt, arg...) AuDpri(KERN_DEBUG, fmt, ##arg) +#define AuInfo(fmt, arg...) AuDpri(KERN_INFO, fmt, ##arg) +#define AuWarn(fmt, arg...) AuDpri(KERN_WARNING, fmt, ##arg) +#define AuErr(fmt, arg...) AuDpri(KERN_ERR, fmt, ##arg) +#define AuIOErr(fmt, arg...) AuErr("I/O Error, " fmt, ##arg) +#define AuIOErrWhck(fmt, arg...) AuErr("I/O Error, try whck. " fmt, ##arg) +#define AuWarn1(fmt, arg...) do { \ + static unsigned char _c; \ + if (!_c++) AuWarn(fmt, ##arg); \ +} while (0) + +#define AuErr1(fmt, arg...) do { \ + static unsigned char _c; \ + if (!_c++) AuErr(fmt, ##arg); \ +} while (0) + +#define AuIOErr1(fmt, arg...) do { \ + static unsigned char _c; \ + if (!_c++) AuIOErr(fmt, ##arg); \ +} while (0) + +#define AuUnsupportMsg "This operation is not supported." \ + " Please report this application to aufs-users ML." +#define AuUnsupport(fmt, args...) do { \ + AuErr(AuUnsupportMsg "\n" fmt, ##args); \ + dump_stack(); \ +} while (0) + +/* ---------------------------------------------------------------------- */ + +struct au_sbinfo; +#ifdef CONFIG_AUFS_DEBUG +extern char *au_plevel; +struct au_nhash; +void au_dpri_whlist(struct au_nhash *whlist); +struct au_vdir; +void au_dpri_vdir(struct au_vdir *vdir); +void au_dpri_inode(struct inode *inode); +void au_dpri_dentry(struct dentry *dentry); +void au_dpri_file(struct file *filp); +void au_dpri_sb(struct super_block *sb); +void au_dbg_sleep(int sec); +int __init au_debug_init(void); +void au_debug_sbinfo_init(struct au_sbinfo *sbinfo); +#define AuDbgWhlist(w) do { \ + LKTRTrace(#w "\n"); \ + au_dpri_whlist(w); \ +} while (0) + +#define AuDbgVdir(v) do { \ + LKTRTrace(#v "\n"); \ + au_dpri_vdir(v); \ +} while (0) + +#define AuDbgInode(i) do { \ + LKTRTrace(#i "\n"); \ + au_dpri_inode(i); \ +} while (0) + +#define AuDbgDentry(d) do { \ + LKTRTrace(#d "\n"); \ + au_dpri_dentry(d); \ +} while (0) + +#define AuDbgFile(f) do { \ + LKTRTrace(#f "\n"); \ + au_dpri_file(f); \ +} while (0) + +#define AuDbgSb(sb) do { \ + LKTRTrace(#sb "\n"); \ + au_dpri_sb(sb); \ +} while (0) + +#define AuDbgSleep(sec) do { \ + AuDbg("sleep %d sec\n", sec); \ + au_dbg_sleep(sec); \ +} while (0) +#else +static inline int au_debug_init(void) +{ + return 0; +} +static inline void au_debug_sbinfo_init(struct au_sbinfo *sbinfo) +{ + /* empty */ +} +#define AuDbgWhlist(w) do {} while (0) +#define AuDbgVdir(v) do {} while (0) +#define AuDbgInode(i) do {} while (0) +#define AuDbgDentry(d) do {} while (0) +#define AuDbgFile(f) do {} while (0) +#define AuDbgSb(sb) do {} while (0) +#define AuDbgSleep(sec) do {} while (0) +#endif /* CONFIG_AUFS_DEBUG */ + +#ifdef DbgUdbaRace +#define AuDbgSleep_UdbaRace() AuDbgSleep(DbgUdbaRace) +#else +#define AuDbgSleep_UdbaRace() do {} while (0) +#endif + +#ifdef CONFIG_AUFS_MAGIC_SYSRQ +int __init au_sysrq_init(void); +void au_sysrq_fin(void); +#else +static inline int au_sysrq_init(void) +{ + return 0; +} +#define au_sysrq_fin() do {} while (0) +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dentry.c linux-2.6.25.4-unionfs/fs/aufs/dentry.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/dentry.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dentry.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,976 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * lookup and dentry operations + * + * $Id: dentry.c,v 1.6 2008/05/19 01:46:53 sfjro Exp $ + */ + +//#include +//#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +/* + * au_lkup_one() is a generic abstract entry function which calls + * lookup_one_len() or __lookup_hash() finally. it is some condisions that makes + * lookup complicated, which are nfs branch, open-intent and dlgt mode. + */ + +#if defined(CONFIG_AUFS_BR_NFS) || defined(CONFIG_AUFS_DLGT) +/* cf. lookup_one_len() in linux/fs/namei.c */ +struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx) +{ + struct dentry *dentry; + + LKTRTrace("%.*s/%.*s, ndx{%d, 0x%x}\n", + AuDLNPair(parent), len, name, !!ndx->nfsmnt, ndx->flags); + + ndx->nd_file = NULL; + if (!ndx->nfsmnt) + dentry = au_lkup_one_dlgt(name, parent, len, ndx->flags); + else + dentry = au_lkup_hash(name, parent, len, ndx); + + AuTraceErrPtr(dentry); + return dentry; +} +#endif /* CONFIG_AUFS_BR_NFS || CONFIG_AUFS_DLGT */ + +struct au_lkup_one_args { + struct dentry **errp; + const char *name; + struct dentry *parent; + int len; + struct au_ndx *ndx; +}; + +static void au_call_lkup_one(void *args) +{ + struct au_lkup_one_args *a = args; + *a->errp = au_lkup_one(a->name, a->parent, a->len, a->ndx); +} + +#define AuLkup_ALLOW_NEG 1 +#define AuLkup_DLGT (1 << 1) +#define AuLkup_DIRPERM1 (1 << 2) +#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) +#define au_fset_lkup(flags, name) { (flags) |= AuLkup_##name; } +#define au_fclr_lkup(flags, name) { (flags) &= ~AuLkup_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuLkup_DLGT +#define AuLkup_DLGT 0 +#undef AuLkup_DIRPERM1 +#define AuLkup_DIRPERM1 0 +#endif + +struct au_do_lookup_args { + unsigned int flags; + mode_t type; + struct nameidata *nd; +}; + +/* + * returns positive/negative dentry, NULL or an error. + * NULL means whiteout-ed or not-found. + */ +static noinline_for_stack +struct dentry *au_do_lookup(struct dentry *h_parent, struct dentry *dentry, + aufs_bindex_t bindex, struct qstr *wh_name, + struct au_do_lookup_args *args) +{ + struct dentry *h_dentry; + int wh_found, wh_able, opq, err; + struct inode *h_dir, *h_inode, *inode; + struct qstr *name; + struct super_block *sb; + unsigned int nd_flags; + struct au_ndx ndx = { + .flags = 0, + .nd = args->nd + }; + const int allow_neg = au_ftest_lkup(args->flags, ALLOW_NEG); + + LKTRTrace("%.*s/%.*s, b%d, {flags 0x%x, type 0%o, nd %d}\n", + AuDLNPair(h_parent), AuDLNPair(dentry), bindex, + args->flags, args->type, !!args->nd); + if (args->nd) + LKTRTrace("nd{0x%x}\n", args->nd->flags); + AuDebugOn(IS_ROOT(dentry)); + h_dir = h_parent->d_inode; + + nd_flags = 0; + wh_found = 0; + sb = dentry->d_sb; + ndx.nfsmnt = au_nfsmnt(sb, bindex); + if (unlikely(au_ftest_lkup(args->flags, DLGT))) + au_fset_ndx(ndx.flags, DLGT); + if (unlikely(au_ftest_lkup(args->flags, DIRPERM1))) + au_fset_ndx(ndx.flags, DIRPERM1); + LKTRTrace("nfsmnt %p\n", ndx.nfsmnt); + ndx.br = au_sbr(sb, bindex); + wh_able = au_br_whable(ndx.br->br_perm); + name = &dentry->d_name; + if (unlikely(wh_able)) + wh_found = au_test_robr_wh(name, h_parent, wh_name, + /*try_sio*/0, &ndx); + //if (LktrCond) wh_found = -1; + h_dentry = ERR_PTR(wh_found); + if (!wh_found) + goto real_lookup; + if (unlikely(wh_found < 0)) + goto out; + + /* We found a whiteout */ + //au_set_dbend(dentry, bindex); + au_set_dbwh(dentry, bindex); + if (!allow_neg) + return NULL; /* success */ + if (unlikely(ndx.nd + && au_test_nfs(h_parent->d_sb) + && (ndx.nd->flags & LOOKUP_CREATE))) { + nd_flags = ndx.nd->flags; + ndx.nd->flags &= ~(LOOKUP_OPEN | LOOKUP_CREATE); + } + + real_lookup: + /* do not superio. */ + h_dentry = au_lkup_one(name->name, h_parent, name->len, &ndx); + //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);} + if (IS_ERR(h_dentry)) + goto out; + AuDebugOn(d_unhashed(h_dentry)); + h_inode = h_dentry->d_inode; + if (!h_inode) { + if (!allow_neg) + goto out_neg; + } else if (wh_found + || (args->type && args->type != (h_inode->i_mode & S_IFMT))) + goto out_neg; + + if (au_dbend(dentry) <= bindex) + au_set_dbend(dentry, bindex); + if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) + au_set_dbstart(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + + err = au_br_nfs_h_intent(ndx.nd_file, dentry, bindex, args->nd); + if (unlikely(err)) { + h_dentry = ERR_PTR(err); + goto out; + } + + inode = dentry->d_inode; + if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able + || (inode && !S_ISDIR(inode->i_mode))) + goto out; /* success */ + + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + opq = au_diropq_test(h_dentry, &ndx); + //if (LktrCond) opq = -1; + mutex_unlock(&h_inode->i_mutex); + if (opq > 0) + au_set_dbdiropq(dentry, bindex); + else if (unlikely(opq < 0)) { + au_set_h_dptr(dentry, bindex, NULL); + h_dentry = ERR_PTR(opq); + } + goto out; + + out_neg: + dput(h_dentry); + h_dentry = NULL; + out: + if (unlikely(nd_flags)) + ndx.nd->flags |= (nd_flags & (LOOKUP_OPEN | LOOKUP_CREATE)); + AuTraceErrPtr(h_dentry); + return h_dentry; +} + +/* + * returns the number of hidden positive dentries, + * otherwise an error. + * can be called at unlinking with @type is zero. + */ +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, + struct nameidata *nd) +{ + int npositive, err, isdir; + struct dentry *parent; + aufs_bindex_t bindex, btail, bdiropq; + const struct qstr *name = &dentry->d_name; + struct qstr whname; + struct super_block *sb; + unsigned int mnt_flags; + struct inode *inode; + struct au_do_lookup_args args = { + .type = type, + .nd = nd + }; + + LKTRTrace("%.*s, b%d, type 0%o\n", AuLNPair(name), bstart, type); + AuDebugOn(bstart < 0 || IS_ROOT(dentry)); + + /* dir may not be locked */ + parent = dget_parent(dentry); + + err = au_test_robr_shwh(dentry->d_sb, name); + if (unlikely(err)) + goto out; + + err = au_wh_name_alloc(name->name, name->len, &whname); + //if (LktrCond) {au_wh_name_free(&whname); err = -1;} + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + inode = dentry->d_inode; + isdir = (inode && S_ISDIR(inode->i_mode)); + args.flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_lkup(args.flags, DLGT); + if (unlikely(au_opt_test_dirperm1(mnt_flags))) + au_fset_lkup(args.flags, DIRPERM1); + if (!type) + au_fset_lkup(args.flags, ALLOW_NEG); + npositive = 0; + btail = au_dbtaildir(parent); + for (bindex = bstart; bindex <= btail; bindex++) { + struct dentry *h_parent, *h_dentry; + struct inode *h_inode, *h_dir; + + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry) { + if (h_dentry->d_inode) + npositive++; + if (type != S_IFDIR) + break; + continue; + } + h_parent = au_h_dptr(parent, bindex); + if (!h_parent) + continue; + h_dir = h_parent->d_inode; + if (!h_dir || !S_ISDIR(h_dir->i_mode)) + continue; + + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); + h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, + &args); + // do not dput for testing + //if (LktrCond) {h_dentry = ERR_PTR(-1);} + mutex_unlock(&h_dir->i_mutex); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out_wh; + au_fclr_lkup(args.flags, ALLOW_NEG); + + if (au_dbwh(dentry) >= 0) + break; + if (!h_dentry) + continue; + h_inode = h_dentry->d_inode; + if (!h_inode) + continue; + npositive++; + if (!args.type) + args.type = h_inode->i_mode & S_IFMT; + if (args.type != S_IFDIR) + break; + else if (isdir) { + /* the type of lowers may be different */ + bdiropq = au_dbdiropq(dentry); + if (bdiropq >= 0 && bdiropq <= bindex) + break; + } + } + + if (npositive) { + LKTRLabel(positive); + au_update_dbstart(dentry); + } + err = npositive; + + out_wh: + au_wh_name_free(&whname); + out: + dput(parent); + AuTraceErr(err); + return err; +} + +struct dentry *au_sio_lkup_one(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx) +{ + struct dentry *dentry; + int wkq_err; + + LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name); + + if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC, + au_ftest_ndx(ndx->flags, DLGT))) + dentry = au_lkup_one(name, parent, len, ndx); + else { + // ugly + unsigned int flags = ndx->flags; + struct au_lkup_one_args args = { + .errp = &dentry, + .name = name, + .parent = parent, + .len = len, + .ndx = ndx + }; + + au_fclr_ndx(ndx->flags, DLGT); + au_fclr_ndx(ndx->flags, DIRPERM1); + wkq_err = au_wkq_wait(au_call_lkup_one, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + dentry = ERR_PTR(wkq_err); + ndx->flags = flags; + } + + AuTraceErrPtr(dentry); + return dentry; +} + +/* + * lookup @dentry on @bindex which should be negative. + */ +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) +{ + int err; + struct dentry *parent, *h_parent, *h_dentry; + struct inode *h_dir; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + struct super_block *sb; + unsigned int mnt_flags; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex); + /* dir may not be locked */ + parent = dget_parent(dentry); + AuDebugOn(!parent || !parent->d_inode + || !S_ISDIR(parent->d_inode->i_mode)); + h_parent = au_h_dptr(parent, bindex); + AuDebugOn(!h_parent); + h_dir = h_parent->d_inode; + AuDebugOn(!h_dir || !S_ISDIR(h_dir->i_mode)); + + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + ndx.nfsmnt = au_nfsmnt(sb, bindex); + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_ndx(ndx.flags, DLGT); + if (unlikely(au_opt_test_dirperm1(mnt_flags))) + au_fset_ndx(ndx.flags, DIRPERM1); + h_dentry = au_sio_lkup_one(dentry->d_name.name, h_parent, + dentry->d_name.len, &ndx); + //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);} + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out; + if (unlikely(h_dentry->d_inode)) { + err = -EIO; + AuIOErr("b%d %.*s should be negative.\n", + bindex, AuDLNPair(h_dentry)); + dput(h_dentry); + goto out; + } + + if (bindex < au_dbstart(dentry)) + au_set_dbstart(dentry, bindex); + if (au_dbend(dentry) < bindex) + au_set_dbend(dentry, bindex); + au_set_h_dptr(dentry, bindex, h_dentry); + err = 0; + + out: + dput(parent); + AuTraceErr(err); + return err; +} + +/* + * returns the number of found hidden positive dentries, + * otherwise an error. + */ +int au_refresh_hdentry(struct dentry *dentry, mode_t type) +{ + int npositive, new_sz; + struct au_dinfo *dinfo; + struct super_block *sb; + struct dentry *parent; + aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend; + struct au_hdentry *p; + au_gen_t sgen; + + LKTRTrace("%.*s, type 0%o\n", AuDLNPair(dentry), type); + DiMustWriteLock(dentry); + sb = dentry->d_sb; + AuDebugOn(IS_ROOT(dentry)); + sgen = au_sigen(sb); + parent = dget_parent(dentry); + AuDebugOn(au_digen(parent) != sgen + || au_iigen(parent->d_inode) != sgen); + + npositive = -ENOMEM; + new_sz = sizeof(*dinfo->di_hdentry) * (au_sbend(sb) + 1); + dinfo = au_di(dentry); + p = au_kzrealloc(dinfo->di_hdentry, sizeof(*p) * (dinfo->di_bend + 1), + new_sz, GFP_KERNEL); + //p = NULL; + if (unlikely(!p)) + goto out; + dinfo->di_hdentry = p; + + bend = dinfo->di_bend; + bwh = dinfo->di_bwh; + bdiropq = dinfo->di_bdiropq; + p += dinfo->di_bstart; + for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { + struct dentry *hd, *hdp; + struct au_hdentry tmp, *q; + aufs_bindex_t new_bindex; + + hd = p->hd_dentry; + if (!hd) + continue; + hdp = dget_parent(hd); + if (hdp == au_h_dptr(parent, bindex)) { + dput(hdp); + continue; + } + + new_bindex = au_find_dbindex(parent, hdp); + dput(hdp); + AuDebugOn(new_bindex == bindex); + if (dinfo->di_bwh == bindex) + bwh = new_bindex; + if (dinfo->di_bdiropq == bindex) + bdiropq = new_bindex; + if (new_bindex < 0) { // test here + au_hdput(p, /*do_free*/0); + p->hd_dentry = NULL; + continue; + } + /* swap two hidden dentries, and loop again */ + q = dinfo->di_hdentry + new_bindex; + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hd_dentry) { + bindex--; + p--; + } + } + + // test here + dinfo->di_bwh = -1; + if (unlikely(bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh))) + dinfo->di_bwh = bwh; + dinfo->di_bdiropq = -1; + if (unlikely(bdiropq >= 0 && bdiropq <= au_sbend(sb) + && au_sbr_whable(sb, bdiropq))) + dinfo->di_bdiropq = bdiropq; + parent_bend = au_dbend(parent); + p = dinfo->di_hdentry; + for (bindex = 0; bindex <= parent_bend; bindex++, p++) + if (p->hd_dentry) { + dinfo->di_bstart = bindex; + break; + } + p = dinfo->di_hdentry + parent_bend; + //for (bindex = parent_bend; bindex > dinfo->di_bstart; bindex--, p--) + for (bindex = parent_bend; bindex >= 0; bindex--, p--) + if (p->hd_dentry) { + dinfo->di_bend = bindex; + break; + } + + npositive = 0; + parent_bstart = au_dbstart(parent); + if (type != S_IFDIR && dinfo->di_bstart == parent_bstart) + goto out_dgen; /* success */ + + npositive = au_lkup_dentry(dentry, parent_bstart, type, /*nd*/NULL); + //if (LktrCond) npositive = -1; + if (npositive < 0) + goto out; + if (unlikely(dinfo->di_bwh >= 0 && dinfo->di_bwh <= dinfo->di_bstart)) + d_drop(dentry); + + out_dgen: + au_update_digen(dentry); + out: + dput(parent); + AuTraceErr(npositive); + return npositive; +} + +static int au_lock_nd(struct dentry *dentry, struct nameidata *nd) +{ + int locked = 0; + if (nd && dentry != nd->path.dentry) { + di_read_lock_parent(nd->path.dentry, 0); + locked = 1; + } + return locked; +} + +static void au_unlock_nd(int locked, struct nameidata *nd) +{ + if (locked) + di_read_unlock(nd->path.dentry, 0); +} + +//#define TestingFuse +static noinline_for_stack int +au_do_h_d_reval(struct dentry *dentry, aufs_bindex_t bindex, + struct nameidata *nd, struct dentry *h_dentry) +{ + int err, valid, e; + int (*reval)(struct dentry *, struct nameidata *); + struct super_block *sb; + struct nameidata fake_nd, *p; + + LKTRTrace("%.*s, b%d, nd %d\n", AuDLNPair(dentry), bindex, !!nd); + + err = 0; + reval = NULL; + if (h_dentry->d_op) + reval = h_dentry->d_op->d_revalidate; + if (!reval) + goto out; + + sb = dentry->d_sb; + if (nd) { + memcpy(&fake_nd, nd, sizeof(*nd)); + err = au_fake_intent(&fake_nd, au_sbr_perm(sb, bindex)); + if (unlikely(err)) { + err = -EINVAL; + goto out; + } + } + p = au_fake_dm(&fake_nd, nd, sb, bindex); + AuDebugOn(IS_ERR(p)); + AuDebugOn(nd && p != &fake_nd); + LKTRTrace("b%d\n", bindex); + + /* it may return tri-state */ + valid = reval(h_dentry, p); + if (unlikely(valid < 0)) + err = valid; + else if (!valid) + err = -EINVAL; + else + AuDebugOn(err); + + if (p) { + AuDebugOn(!nd); + e = au_hin_after_reval(p, dentry, bindex, nd->intent.open.file); +#ifndef TestingFuse + au_update_fuse_h_inode(p->path.mnt, h_dentry); /*ignore*/ +#endif + if (unlikely(e && !err)) + err = e; + } +#ifndef TestingFuse + else + au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/ +#endif + au_fake_dm_release(p); + + out: + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +h_d_revalidate(struct dentry *dentry, struct inode *inode, + struct nameidata *nd, int do_udba) +{ + int err, plus, locked, unhashed, is_root, h_plus; + aufs_bindex_t bindex, btail, bstart, ibs, ibe; + struct super_block *sb; + struct inode *first, *h_inode, *h_cached_inode; + umode_t mode, h_mode; + struct dentry *h_dentry; + struct qstr *name; + + LKTRTrace("%.*s, nd %d\n", AuDLNPair(dentry), !!nd); + AuDebugOn(inode && au_digen(dentry) != au_iigen(inode)); + + err = 0; + sb = dentry->d_sb; + plus = 0; + mode = 0; + first = NULL; + ibs = -1; + ibe = -1; + unhashed = d_unhashed(dentry); + is_root = IS_ROOT(dentry); + name = &dentry->d_name; + + /* + * Theoretically, REVAL test should be unnecessary in case of INOTIFY. + * But inotify doesn't fire some necessary events, + * IN_ATTRIB for atime/nlink/pageio + * IN_DELETE for NFS dentry + * Let's do REVAL test too. + */ + if (do_udba && inode) { + mode = (inode->i_mode & S_IFMT); + plus = (inode->i_nlink > 0); + first = au_h_iptr(inode, au_ibstart(inode)); + ibs = au_ibstart(inode); + ibe = au_ibend(inode); + } + + bstart = au_dbstart(dentry); + btail = bstart; + if (inode && S_ISDIR(inode->i_mode)) + btail = au_dbtaildir(dentry); + locked = au_lock_nd(dentry, nd); + for (bindex = bstart; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + LKTRTrace("b%d, %.*s\n", bindex, AuDLNPair(h_dentry)); +#ifdef TestingFuse + /* force re-lookup for fuse, in order to update attributes */ + if (unlikely(au_test_fuse(h_dentry->d_sb))) + goto err; +#endif + + if (unlikely(do_udba + && !is_root + && (unhashed != d_unhashed(h_dentry) + || name->len != h_dentry->d_name.len + || memcmp(name->name, h_dentry->d_name.name, + name->len) + ))) { + LKTRTrace("unhash 0x%x 0x%x, %.*s %.*s\n", + unhashed, d_unhashed(h_dentry), + AuDLNPair(dentry), AuDLNPair(h_dentry)); + goto err; + } + + err = au_do_h_d_reval(dentry, bindex, nd, h_dentry); + if (unlikely(err)) + /* do not goto err, to keep the errno */ + break; + + if (unlikely(!do_udba)) + continue; + + /* UDBA tests */ + h_inode = h_dentry->d_inode; + if (unlikely(!!inode != !!h_inode)) { + //AuDbg("here\n"); + goto err; + } + + h_plus = plus; + h_mode = mode; + h_cached_inode = h_inode; + if (h_inode) { + h_mode = (h_inode->i_mode & S_IFMT); + h_plus = (h_inode->i_nlink > 0); + //AuDbgInode(inode); + //AuDbgInode(h_inode); + } + if (inode && ibs <= bindex && bindex <= ibe) + h_cached_inode = au_h_iptr(inode, bindex); + + LKTRTrace("{%d, 0%o, %d}, h{%d, 0%o, %d}\n", + plus, mode, !!h_cached_inode, + h_plus, h_mode, !!h_inode); + if (unlikely(plus != h_plus + || mode != h_mode + || h_cached_inode != h_inode)) + goto err; + continue; + + err: + err = -EINVAL; + break; + } + au_unlock_nd(locked, nd); + + /* + * judging by timestamps is meaningless since some filesystem uses + * CURRENT_TIME_SEC instead of CURRENT_TIME. + */ + /* + * NFS may stop IN_DELETE because of DCACHE_NFSFS_RENAMED. + */ + + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +simple_reval_dpath(struct dentry *dentry, au_gen_t sgen) +{ + int err; + mode_t type; + struct dentry *parent; + struct inode *inode; + + LKTRTrace("%.*s, sgen %d\n", AuDLNPair(dentry), sgen); + SiMustAnyLock(dentry->d_sb); + DiMustWriteLock(dentry); + inode = dentry->d_inode; + AuDebugOn(!inode); + + if (au_digen(dentry) == sgen && au_iigen(inode) == sgen) + return 0; + + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + AuDebugOn(au_digen(parent) != sgen + || au_iigen(parent->d_inode) != sgen); +#ifdef CONFIG_AUFS_DEBUG + { + int i, j; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + err = au_dpages_init(&dpages, GFP_TEMPORARY); + AuDebugOn(err); + err = au_dcsub_pages_rev(&dpages, parent, /*do_include*/1, NULL, + NULL); + AuDebugOn(err); + for (i = dpages.ndpage - 1; !err && i >= 0; i--) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + for (j = dpage->ndentry - 1; !err && j >= 0; j--) + AuDebugOn(au_digen(dentries[j]) != sgen); + } + au_dpages_free(&dpages); + } +#endif + type = (inode->i_mode & S_IFMT); + /* returns a number of positive dentries */ + err = au_refresh_hdentry(dentry, type); + if (err >= 0) + err = au_refresh_hinode(inode, dentry); + di_read_unlock(parent, AuLock_IR); + dput(parent); + AuTraceErr(err); + return err; +} + +int au_reval_dpath(struct dentry *dentry, au_gen_t sgen) +{ + int err; + struct dentry *d, *parent; + struct inode *inode; + + LKTRTrace("%.*s, sgen %d\n", AuDLNPair(dentry), sgen); + AuDebugOn(!dentry->d_inode); + DiMustWriteLock(dentry); + + if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS)) + return simple_reval_dpath(dentry, sgen); + + /* slow loop, keep it simple and stupid */ + /* cf: au_cpup_dirs() */ + err = 0; + parent = NULL; + while (au_digen(dentry) != sgen || au_iigen(dentry->d_inode) != sgen) { + d = dentry; + while (1) { + dput(parent); + parent = dget_parent(d); + if (au_digen(parent) == sgen + && au_iigen(parent->d_inode) == sgen) + break; + d = parent; + } + + inode = d->d_inode; + if (d != dentry) { + //mutex_lock(&inode->i_mutex); + di_write_lock_child(d); + } + + /* someone might update our dentry while we were sleeping */ + if (au_digen(d) != sgen || au_iigen(d->d_inode) != sgen) { + di_read_lock_parent(parent, AuLock_IR); + /* returns a number of positive dentries */ + err = au_refresh_hdentry(d, inode->i_mode & S_IFMT); + //err = -1; + if (err >= 0) + err = au_refresh_hinode(inode, d); + //err = -1; + di_read_unlock(parent, AuLock_IR); + } + + if (d != dentry) { + di_write_unlock(d); + //mutex_unlock(&inode->i_mutex); + } + dput(parent); + if (unlikely(err)) + break; + } + + AuTraceErr(err); + return err; +} + +/* + * THIS IS A BOOLEAN FUNCTION: returns 1 if valid, 0 otherwise. + * nfsd passes NULL as nameidata. + */ +static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + int valid, err, do_udba; + struct super_block *sb; + au_gen_t sgen; + struct inode *inode; + struct nameidata tmp_nd, *ndp; + + LKTRTrace("dentry %.*s\n", AuDLNPair(dentry)); + if (nd && nd->path.dentry) + LKTRTrace("nd{%.*s, 0x%x}\n", + AuDLNPair(nd->path.dentry), nd->flags); + //dir case: AuDebugOn(dentry->d_parent != nd->dentry); + //remove failure case:AuDebugOn(!IS_ROOT(dentry) && d_unhashed(dentry)); + AuDebugOn(!dentry->d_fsdata); + //AuDbgDentry(dentry); + + err = -EINVAL; + inode = dentry->d_inode; + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + + sgen = au_sigen(sb); + if (au_digen(dentry) == sgen) + di_read_lock_child(dentry, !AuLock_IR); + else { + AuDebugOn(IS_ROOT(dentry)); +#ifdef ForceInotify + AuDbg("UDBA or digen, %.*s\n", AuDLNPair(dentry)); +#endif + di_write_lock_child(dentry); + if (inode) + err = au_reval_dpath(dentry, sgen); + //err = -1; + di_downgrade_lock(dentry, AuLock_IR); + if (unlikely(err)) + goto out; + if (inode) + ii_read_unlock(inode); + AuDebugOn(au_digen(dentry) != sgen); + } + + if (inode) { + if (au_iigen(inode) == sgen) + ii_read_lock_child(inode); + else { + AuDebugOn(IS_ROOT(dentry)); +#ifdef ForceInotify + AuDbg("UDBA or survived, %.*s\n", AuDLNPair(dentry)); + //au_debug_on(); + //AuDbgInode(inode); + //au_debug_off(); +#endif + ii_write_lock_child(inode); + err = au_refresh_hinode(inode, dentry); + ii_downgrade_lock(inode); + if (unlikely(err)) + goto out; + AuDebugOn(au_iigen(inode) != sgen); + } + } + + AuDebugOn(au_digen(dentry) != sgen); + AuDebugOn(inode && au_iigen(inode) != sgen); + err = -EINVAL; + do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); + if (do_udba && inode) { + aufs_bindex_t bstart = au_ibstart(inode); + if (bstart >= 0 + && au_test_higen(inode, au_h_iptr(inode, bstart))) + goto out; + } + ndp = au_dup_nd(au_sbi(sb), &tmp_nd, nd); + err = h_d_revalidate(dentry, inode, ndp, do_udba); + //err = -1; + + out: + au_store_fmode_exec(nd, inode); + + if (inode) + ii_read_unlock(inode); + di_read_unlock(dentry, !AuLock_IR); + si_read_unlock(sb); + AuTraceErr(err); + valid = !err; + if (!valid) + LKTRTrace("%.*s invalid\n", AuDLNPair(dentry)); + return valid; +} + +static void aufs_d_release(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + aufs_bindex_t bend, bindex; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + AuDebugOn(!d_unhashed(dentry)); + + dinfo = dentry->d_fsdata; + if (unlikely(!dinfo)) + return; + + /* dentry may not be revalidated */ + bindex = dinfo->di_bstart; + if (bindex >= 0) { + struct au_hdentry *p; + bend = dinfo->di_bend; + AuDebugOn(bend < bindex); + p = dinfo->di_hdentry + bindex; + while (bindex++ <= bend) { + if (p->hd_dentry) + au_hdput(p, /*do_free*/1); + p++; + } + } + kfree(dinfo->di_hdentry); + au_cache_free_dinfo(dinfo); +} + +struct dentry_operations aufs_dop = { + .d_revalidate = aufs_d_revalidate, + .d_release = aufs_d_release, + /* never use d_delete, especially in case of nfs server */ + //.d_delete = aufs_d_delete + //.d_iput = aufs_d_iput +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dentry.h linux-2.6.25.4-unionfs/fs/aufs/dentry.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/dentry.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dentry.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * lookup and dentry operations + * + * $Id: dentry.h,v 1.4 2008/05/04 23:54:53 sfjro Exp $ + */ + +#ifndef __AUFS_DENTRY_H__ +#define __AUFS_DENTRY_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include "misc.h" +#include "super.h" +#include "vfsub.h" + +/* nameidata open_intent */ +enum { + AuIntent_AUFS, + AuIntent_BRANCH, + AuIntent_Last +}; + +struct au_hdintent { + struct list_head hdi_list; + struct file *hdi_file[AuIntent_Last]; +}; + +struct au_hdentry { + struct dentry *hd_dentry; + +#ifdef CONFIG_AUFS_BR_NFS + spinlock_t hd_lock; /* intest_list */ + struct list_head *hd_intent_list; +#endif +}; + +struct au_dinfo { + atomic_t di_generation; + + struct au_rwsem di_rwsem; + aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; + struct au_hdentry *di_hdentry; +}; + +/* nameidata extension flags */ +#define AuNdx_DLGT 1 +#define AuNdx_DIRPERM1 (1 << 1) +#define au_ftest_ndx(flags, name) ((flags) & AuNdx_##name) +#define au_fset_ndx(flags, name) { (flags) |= AuNdx_##name; } +#define au_fclr_ndx(flags, name) { (flags) &= ~AuNdx_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuNdx_DLGT +#define AuNdx_DLGT 0 +#undef AuNdx_DIRPERM1 +#define AuNdx_DIRPERM1 0 +#endif + +struct au_ndx { + struct vfsmount *nfsmnt; + unsigned int flags; + struct nameidata *nd; + struct au_branch *br; + struct file *nd_file; +}; + +/* ---------------------------------------------------------------------- */ + +static inline void au_do_h_dentry_init(struct au_hdentry *hdentry) +{ + hdentry->hd_dentry = NULL; +} + +#ifdef CONFIG_AUFS_BR_NFS +static inline void au_h_dentry_init(struct au_hdentry *hdentry) +{ + au_do_h_dentry_init(hdentry); + spin_lock_init(&hdentry->hd_lock); +} + +static inline void au_h_dentry_init_all(struct au_hdentry *hdentry, int n) +{ + while (n--) + spin_lock_init(&hdentry[n].hd_lock); +} + +/* br_nfs.c */ +struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex, + struct file *file); +int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry, + aufs_bindex_t bindex, struct nameidata *nd); +void au_hintent_put(struct au_hdentry *hd, int do_free); +int au_fake_intent(struct nameidata *nd, int perm); +int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry, + aufs_bindex_t bindex, struct file *file); +struct dentry *au_lkup_hash(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx); +#else + +static inline void au_h_dentry_init(struct au_hdentry *hdentry) +{ + au_do_h_dentry_init(hdentry); +} + +static inline void au_h_dentry_init_all(struct au_hdentry *hdentry, int n) +{ + /* nothing */ +} + +static inline +struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex, + struct file *file) +{ + //return ERR_PTR(-ENOSYS); + return NULL; +} + +static inline +int au_br_nfs_h_intent(struct file *nd_file, struct dentry *dentry, + aufs_bindex_t bindex, struct nameidata *nd) +{ + return 0; +} + +static inline void au_hintent_put(struct au_hdentry *hd, int do_free) +{ + /* empty */ +} + +static inline int au_fake_intent(struct nameidata *nd, int perm) +{ + return 0; +} + +static inline +int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry, + aufs_bindex_t bindex, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_AUFS_DLGT +static inline +struct dentry *au_lkup_hash(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx) +{ + //return ERR_PTR(-ENOSYS); + return vfsub_lookup_one_len(name, parent, len); +} +#endif +#endif /* CONFIG_AUFS_BR_NFS */ + +#ifdef CONFIG_AUFS_DLGT +/* dlgt.c */ +struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent, + int len, unsigned int flags); +#elif defined(CONFIG_AUFS_BR_NFS) +/* regardelss kernel version */ +static inline +struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent, + int len, unsigned int flags) +{ + return vfsub_lookup_one_len(name, parent, len); +} +#endif + +/* dentry.c */ +extern struct dentry_operations aufs_dop; +#if defined(CONFIG_AUFS_BR_NFS) || defined(CONFIG_AUFS_DLGT) +struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx); +#else +static inline +struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx) +{ + //todo: ndx->nd_file = NULL; + return vfsub_lookup_one_len(name, parent, len); +} +#endif +struct dentry *au_sio_lkup_one(const char *name, struct dentry *parent, int len, + struct au_ndx *ndx); +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, + struct nameidata *nd); +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); +int au_refresh_hdentry(struct dentry *dentry, mode_t type); +int au_reval_dpath(struct dentry *dentry, au_gen_t sgen); + +/* dinfo.c */ +int au_alloc_dinfo(struct dentry *dentry); +struct au_dinfo *au_di(struct dentry *dentry); + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc); +void di_read_unlock(struct dentry *d, int flags); +void di_downgrade_lock(struct dentry *d, int flags); +void di_write_lock(struct dentry *d, unsigned int lsc); +void di_write_unlock(struct dentry *d); +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); +void di_write_unlock2(struct dentry *d1, struct dentry *d2); + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); + +aufs_bindex_t au_dbtail(struct dentry *dentry); +aufs_bindex_t au_dbtaildir(struct dentry *dentry); + +void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex); +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry); + +void au_update_dbrange(struct dentry *dentry, int do_put_zero); +void au_update_dbstart(struct dentry *dentry); +void au_update_dbend(struct dentry *dentry); +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry); + +/* ---------------------------------------------------------------------- */ + +//todo: memory barrier? +static inline au_gen_t au_digen(struct dentry *d) +{ + return atomic_read(&au_di(d)->di_generation); +} + +#ifdef CONFIG_AUFS_HINOTIFY +static inline au_gen_t au_digen_dec(struct dentry *d) +{ + return atomic_dec_return(&au_di(d)->di_generation); +} +#endif /* CONFIG_AUFS_HINOTIFY */ + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for dinfo */ +enum { + AuLsc_DI_CHILD, /* child first */ + AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hinotify */ + AuLsc_DI_CHILD3, /* copyup dirs */ + AuLsc_DI_PARENT, + AuLsc_DI_PARENT2, + AuLsc_DI_PARENT3 +}; + +/* + * di_read_lock_child, di_write_lock_child, + * di_read_lock_child2, di_write_lock_child2, + * di_read_lock_child3, di_write_lock_child3, + * di_read_lock_parent, di_write_lock_parent, + * di_read_lock_parent2, di_write_lock_parent2, + * di_read_lock_parent3, di_write_lock_parent3, + */ +#define AuReadLockFunc(name, lsc) \ +static inline void di_read_lock_##name(struct dentry *d, int flags) \ +{ di_read_lock(d, flags, AuLsc_DI_##lsc); } + +#define AuWriteLockFunc(name, lsc) \ +static inline void di_write_lock_##name(struct dentry *d) \ +{ di_write_lock(d, AuLsc_DI_##lsc); } + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +/* to debug easier, do not make them inlined functions */ +#define DiMustReadLock(d) do { \ + SiMustAnyLock((d)->d_sb); \ + AuRwMustReadLock(&au_di(d)->di_rwsem); \ +} while (0) + +#define DiMustWriteLock(d) do { \ + SiMustAnyLock((d)->d_sb); \ + AuRwMustWriteLock(&au_di(d)->di_rwsem); \ +} while (0) + +#define DiMustAnyLock(d) do { \ + SiMustAnyLock((d)->d_sb); \ + AuRwMustAnyLock(&au_di(d)->di_rwsem); \ +} while (0) + +#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) + +/* ---------------------------------------------------------------------- */ + +static inline aufs_bindex_t au_dbstart(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bstart; +} + +static inline aufs_bindex_t au_dbend(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bend; +} + +static inline aufs_bindex_t au_dbwh(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + return au_di(dentry)->di_bwh; +} + +static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) +{ + DiMustAnyLock(dentry); + AuDebugOn(dentry->d_inode + && dentry->d_inode->i_mode + && !S_ISDIR(dentry->d_inode->i_mode)); + return au_di(dentry)->di_bdiropq; +} + +// hard/soft set +static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + AuDebugOn(au_sbend(dentry->d_sb) < bindex); + /* */ + au_di(dentry)->di_bstart = bindex; +} + +static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + AuDebugOn(au_sbend(dentry->d_sb) < bindex + || bindex < au_dbstart(dentry)); + au_di(dentry)->di_bend = bindex; +} + +static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + AuDebugOn(au_sbend(dentry->d_sb) < bindex); + /* dbwh can be outside of bstart - bend range */ + au_di(dentry)->di_bwh = bindex; +} + +static inline void au_hdput(struct au_hdentry *hd, int do_free) +{ + au_hintent_put(hd, do_free); + dput(hd->hd_dentry); +} + +static inline void au_update_digen(struct dentry *dentry) +{ + //DiMustWriteLock(dentry); + AuDebugOn(!dentry->d_sb); + atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); + //smp_mb(); /* atomic_set */ +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DENTRY_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dinfo.c linux-2.6.25.4-unionfs/fs/aufs/dinfo.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/dinfo.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dinfo.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * dentry private data + * + * $Id: dinfo.c,v 1.3 2008/04/28 03:04:23 sfjro Exp $ + */ + +#include "aufs.h" + +int au_alloc_dinfo(struct dentry *dentry) +{ + struct au_dinfo *dinfo; + struct super_block *sb; + int nbr; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + AuDebugOn(dentry->d_fsdata); + + dinfo = au_cache_alloc_dinfo(); + //if (LktrCond) {au_cache_free_dinfo(dinfo); dinfo = NULL;} + if (dinfo) { + sb = dentry->d_sb; + nbr = au_sbend(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; + dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), + GFP_KERNEL); + //if (LktrCond) + //{kfree(dinfo->di_hdentry); dinfo->di_hdentry = NULL;} + if (dinfo->di_hdentry) { + au_h_dentry_init_all(dinfo->di_hdentry, nbr); + atomic_set(&dinfo->di_generation, au_sigen(sb)); + //smp_mb(); /* atomic_set */ + au_rw_init_wlock_nested(&dinfo->di_rwsem, + AuLsc_DI_PARENT); + dinfo->di_bstart = -1; + dinfo->di_bend = -1; + dinfo->di_bwh = -1; + dinfo->di_bdiropq = -1; + + dentry->d_fsdata = dinfo; + dentry->d_op = &aufs_dop; + return 0; /* success */ + } + au_cache_free_dinfo(dinfo); + } + AuTraceErr(-ENOMEM); + return -ENOMEM; +} + +struct au_dinfo *au_di(struct dentry *dentry) +{ + struct au_dinfo *dinfo = dentry->d_fsdata; + AuDebugOn(!dinfo + || !dinfo->di_hdentry + /* || au_sbi(dentry->d_sb)->si_bend < dinfo->di_bend */ + || dinfo->di_bend < dinfo->di_bstart + /* dbwh can be outside of this range */ + || (0 <= dinfo->di_bdiropq + && (dinfo->di_bdiropq < dinfo->di_bstart + /* || dinfo->di_bend < dinfo->di_bdiropq */)) + ); + return dinfo; +} + +/* ---------------------------------------------------------------------- */ + +static void do_ii_write_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_write_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_write_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_write_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_write_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_write_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_write_lock_parent3(inode); + break; + default: + BUG(); + } +} + +static void do_ii_read_lock(struct inode *inode, unsigned int lsc) +{ + switch (lsc) { + case AuLsc_DI_CHILD: + ii_read_lock_child(inode); + break; + case AuLsc_DI_CHILD2: + ii_read_lock_child2(inode); + break; + case AuLsc_DI_CHILD3: + ii_read_lock_child3(inode); + break; + case AuLsc_DI_PARENT: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_read_lock_parent(inode); + break; + case AuLsc_DI_PARENT2: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_read_lock_parent2(inode); + break; + case AuLsc_DI_PARENT3: + //AuDebugOn(!S_ISDIR(inode->i_mode)); + ii_read_lock_parent3(inode); + break; + default: + BUG(); + } +} + +void di_read_lock(struct dentry *d, int flags, unsigned int lsc) +{ + SiMustAnyLock(d->d_sb); + // todo: always nested? + au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d->d_inode) { + if (au_ftest_lock(flags, IW)) + do_ii_write_lock(d->d_inode, lsc); + else if (au_ftest_lock(flags, IR)) + do_ii_read_lock(d->d_inode, lsc); + } +} + +void di_read_unlock(struct dentry *d, int flags) +{ + SiMustAnyLock(d->d_sb); + if (d->d_inode) { + if (au_ftest_lock(flags, IW)) + ii_write_unlock(d->d_inode); + else if (au_ftest_lock(flags, IR)) + ii_read_unlock(d->d_inode); + } + au_rw_read_unlock(&au_di(d)->di_rwsem); +} + +void di_downgrade_lock(struct dentry *d, int flags) +{ + SiMustAnyLock(d->d_sb); + au_rw_dgrade_lock(&au_di(d)->di_rwsem); + if (d->d_inode && au_ftest_lock(flags, IR)) + ii_downgrade_lock(d->d_inode); +} + +void di_write_lock(struct dentry *d, unsigned int lsc) +{ + SiMustAnyLock(d->d_sb); + // todo: always nested? + au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); + if (d->d_inode) + do_ii_write_lock(d->d_inode, lsc); +} + +void di_write_unlock(struct dentry *d) +{ + SiMustAnyLock(d->d_sb); + if (d->d_inode) + ii_write_unlock(d->d_inode); + au_rw_write_unlock(&au_di(d)->di_rwsem); +} + +void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuTraceEnter(); + AuDebugOn(d1 == d2 + || d1->d_inode == d2->d_inode + || d1->d_sb != d2->d_sb); + + if (isdir && au_test_subdir(d1, d2)) { + di_write_lock_child(d1); + di_write_lock_child2(d2); + } else { + /* there should be no races */ + di_write_lock_child(d2); + di_write_lock_child2(d1); + } +} + +void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir) +{ + AuTraceEnter(); + AuDebugOn(d1 == d2 + || d1->d_inode == d2->d_inode + || d1->d_sb != d2->d_sb); + + if (isdir && au_test_subdir(d1, d2)) { + di_write_lock_parent(d1); + di_write_lock_parent2(d2); + } else { + /* there should be no races */ + di_write_lock_parent(d2); + di_write_lock_parent2(d1); + } +} + +void di_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + di_write_unlock(d1); + if (d1->d_inode == d2->d_inode) + au_rw_write_unlock(&au_di(d2)->di_rwsem); + else + di_write_unlock(d2); +} + +/* ---------------------------------------------------------------------- */ + +struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) +{ + struct dentry *d; + + DiMustAnyLock(dentry); + if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) + return NULL; + AuDebugOn(bindex < 0 + /* || bindex > au_sbend(dentry->d_sb) */); + d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; + AuDebugOn(d && (atomic_read(&d->d_count) <= 0)); + return d; +} + +aufs_bindex_t au_dbtail(struct dentry *dentry) +{ + aufs_bindex_t bend, bwh; + + bend = au_dbend(dentry); + if (0 <= bend) { + bwh = au_dbwh(dentry); + //AuDebugOn(bend < bwh); + if (!bwh) + return bwh; + if (0 < bwh && bwh < bend) + return bwh - 1; + } + return bend; +} + +aufs_bindex_t au_dbtaildir(struct dentry *dentry) +{ + aufs_bindex_t bend, bopq; + + AuDebugOn(dentry->d_inode + && dentry->d_inode->i_mode + && !S_ISDIR(dentry->d_inode->i_mode)); + + bend = au_dbtail(dentry); + if (0 <= bend) { + bopq = au_dbdiropq(dentry); + AuDebugOn(bend < bopq); + if (0 <= bopq && bopq < bend) + bend = bopq; + } + return bend; +} + +/* ---------------------------------------------------------------------- */ + +void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) +{ + DiMustWriteLock(dentry); + AuDebugOn(au_sbend(dentry->d_sb) < bindex); + AuDebugOn((bindex >= 0 + && (bindex < au_dbstart(dentry) + || au_dbend(dentry) < bindex)) + || (dentry->d_inode + && dentry->d_inode->i_mode + && !S_ISDIR(dentry->d_inode->i_mode))); + au_di(dentry)->di_bdiropq = bindex; +} + +void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_dentry) +{ + struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; + DiMustWriteLock(dentry); + AuDebugOn(bindex < au_di(dentry)->di_bstart + || bindex > au_di(dentry)->di_bend + || (h_dentry && atomic_read(&h_dentry->d_count) <= 0) + || (h_dentry && hd->hd_dentry) + ); + if (hd->hd_dentry) + au_hdput(hd, /*do_free*/0); + hd->hd_dentry = h_dentry; +} + +/* ---------------------------------------------------------------------- */ + +void au_update_dbrange(struct dentry *dentry, int do_put_zero) +{ + struct au_dinfo *dinfo; + aufs_bindex_t bindex; + struct dentry *h_d; + + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), do_put_zero); + DiMustWriteLock(dentry); + + dinfo = au_di(dentry); + if (unlikely(!dinfo) || dinfo->di_bstart < 0) + return; + + if (do_put_zero) { + for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; + bindex++) { + h_d = dinfo->di_hdentry[0 + bindex].hd_dentry; + if (h_d && !h_d->d_inode) + au_set_h_dptr(dentry, bindex, NULL); + } + } + + dinfo->di_bstart = -1; + while (++dinfo->di_bstart <= dinfo->di_bend) + if (dinfo->di_hdentry[0 + dinfo->di_bstart].hd_dentry) + break; + if (dinfo->di_bstart > dinfo->di_bend) { + dinfo->di_bstart = -1; + dinfo->di_bend = -1; + return; + } + + dinfo->di_bend++; + while (0 <= --dinfo->di_bend) + if (dinfo->di_hdentry[0 + dinfo->di_bend].hd_dentry) + break; + AuDebugOn(dinfo->di_bstart > dinfo->di_bend || dinfo->di_bend < 0); +} + +void au_update_dbstart(struct dentry *dentry) +{ + aufs_bindex_t bindex, + bstart = au_dbstart(dentry), + bend = au_dbend(dentry); + struct dentry *h_dentry; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + DiMustWriteLock(dentry); + + for (bindex = bstart; bindex <= bend; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (h_dentry->d_inode) { + au_set_dbstart(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } + //au_set_dbstart(dentry, -1); + //au_set_dbend(dentry, -1); +} + +void au_update_dbend(struct dentry *dentry) +{ + aufs_bindex_t bindex, + bstart = au_dbstart(dentry), + bend = au_dbend(dentry); + struct dentry *h_dentry; + + DiMustWriteLock(dentry); + for (bindex = bend; bindex <= bstart; bindex--) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + if (h_dentry->d_inode) { + au_set_dbend(dentry, bindex); + return; + } + au_set_h_dptr(dentry, bindex, NULL); + } + //au_set_dbstart(dentry, -1); + //au_set_dbend(dentry, -1); +} + +int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) +{ + aufs_bindex_t bindex, bend; + + bend = au_dbend(dentry); + for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) + if (au_h_dptr(dentry, bindex) == h_dentry) + return bindex; + return -1; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dir.c linux-2.6.25.4-unionfs/fs/aufs/dir.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/dir.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dir.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,553 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * directory operations + * + * $Id: dir.c,v 1.4 2008/05/12 00:27:58 sfjro Exp $ + */ + +#include +#include "aufs.h" + +static int reopen_dir(struct file *file) +{ + int err; + struct dentry *dentry, *h_dentry; + aufs_bindex_t bindex, btail, bstart; + struct file *h_file; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); + + /* open all hidden dirs */ + bstart = au_dbstart(dentry); + for (bindex = au_fbstart(file); bindex < bstart; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbstart(file, bstart); + btail = au_dbtaildir(dentry); + for (bindex = au_fbend(file); btail < bindex; bindex--) + au_set_h_fptr(file, bindex, NULL); + au_set_fbend(file, btail); + for (bindex = bstart; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_file = au_h_fptr(file, bindex); + if (h_file) { + AuDebugOn(h_file->f_dentry != h_dentry); + continue; + } + + h_file = au_h_open(dentry, bindex, file->f_flags, file); + // unavailable + //if (LktrCond) {fput(h_file); + //au_br_put(au_sbr(dentry->d_sb, bindex));h_file=ERR_PTR(-1);} + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; // close all? + //cpup_file_flags(h_file, file); + au_set_h_fptr(file, bindex, h_file); + } + au_update_figen(file); + //file->f_ra = h_file->f_ra; //?? + err = 0; + + out: + AuTraceErr(err); + return err; +} + +static int do_open_dir(struct file *file, int flags) +{ + int err; + aufs_bindex_t bindex, btail; + struct dentry *dentry, *h_dentry; + struct file *h_file; + + dentry = file->f_dentry; + LKTRTrace("%.*s, 0x%x\n", AuDLNPair(dentry), flags); + AuDebugOn(!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)); + + err = 0; + au_set_fvdir_cache(file, NULL); + file->f_version = dentry->d_inode->i_version; + bindex = au_dbstart(dentry); + au_set_fbstart(file, bindex); + btail = au_dbtaildir(dentry); + au_set_fbend(file, btail); + for (; !err && bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + + h_file = au_h_open(dentry, bindex, flags, file); + //if (LktrCond) {fput(h_file); + //au_br_put(au_sbr(dentry->d_sb, bindex));h_file=ERR_PTR(-1);} + if (!IS_ERR(h_file)) { + au_set_h_fptr(file, bindex, h_file); + continue; + } + err = PTR_ERR(h_file); + } + au_update_figen(file); + //file->f_ra = h_file->f_ra; //?? + if (!err) + return 0; /* success */ + + /* close all */ + for (bindex = au_fbstart(file); !err && bindex <= btail; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbstart(file, -1); + au_set_fbend(file, -1); + return err; +} + +static int aufs_open_dir(struct inode *inode, struct file *file) +{ + return au_do_open(inode, file, do_open_dir); +} + +static int aufs_release_dir(struct inode *inode, struct file *file) +{ + struct au_vdir *vdir_cache; + struct super_block *sb; + + LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(file->f_dentry)); + + sb = file->f_dentry->d_sb; + si_noflush_read_lock(sb); + fi_write_lock(file); + vdir_cache = au_fvdir_cache(file); + if (vdir_cache) + au_vdir_free(vdir_cache); + fi_write_unlock(file); + au_finfo_fin(file); + si_read_unlock(sb); + return 0; +} + +static int fsync_dir(struct dentry *dentry, int datasync) +{ + int err; + struct inode *inode; + struct super_block *sb; + aufs_bindex_t bend, bindex; + + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync); + DiMustAnyLock(dentry); + sb = dentry->d_sb; + SiMustAnyLock(sb); + inode = dentry->d_inode; + IMustLock(inode); + IiMustAnyLock(inode); + + err = 0; + bend = au_dbend(dentry); + for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { + struct dentry *h_dentry; + struct inode *h_inode; + struct file_operations *fop; + + if (au_test_ro(sb, bindex, inode)) + continue; + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + h_inode = h_dentry->d_inode; + if (!h_inode) + continue; + + /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */ + //au_hdir_lock(h_inode, inode, bindex); + mutex_lock(&h_inode->i_mutex); + fop = (void *)h_inode->i_fop; + err = filemap_fdatawrite(h_inode->i_mapping); + if (!err && fop && fop->fsync) + err = fop->fsync(NULL, h_dentry, datasync); + if (!err) + err = filemap_fdatawrite(h_inode->i_mapping); + if (!err) + au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/ + //au_hdir_unlock(h_inode, inode, bindex); + mutex_unlock(&h_inode->i_mutex); + } + + AuTraceErr(err); + return err; +} + +/* + * @file may be NULL + */ +static int aufs_fsync_dir(struct file *file, struct dentry *dentry, + int datasync) +{ + int err; + struct inode *inode; + struct file *h_file; + struct super_block *sb; + aufs_bindex_t bend, bindex; + + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync); + inode = dentry->d_inode; + IMustLock(inode); + + err = 0; + sb = dentry->d_sb; + si_noflush_read_lock(sb); + if (file) { + err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1, + /*locked*/1); + //err = -1; + if (unlikely(err)) + goto out; + } else + di_read_lock_child(dentry, !AuLock_IW); + + ii_write_lock_child(inode); + if (file) { + bend = au_fbend(file); + for (bindex = au_fbstart(file); !err && bindex <= bend; + bindex++) { + h_file = au_h_fptr(file, bindex); + if (!h_file || au_test_ro(sb, bindex, inode)) + continue; + + err = -EINVAL; + if (h_file->f_op && h_file->f_op->fsync) { + // todo: try do_fsync() in fs/sync.c + mutex_lock(&h_file->f_mapping->host->i_mutex); + err = h_file->f_op->fsync + (h_file, h_file->f_dentry, datasync); + //err = -1; + if (!err) + au_update_fuse_h_inode + (h_file->f_vfsmnt, + h_file->f_dentry); + /*ignore*/ + mutex_unlock(&h_file->f_mapping->host->i_mutex); + } + } + } else + err = fsync_dir(dentry, datasync); + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + if (file) + fi_write_unlock(file); + else + di_read_unlock(dentry, !AuLock_IW); + + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_readdir(struct file *file, void *dirent, filldir_t filldir) +{ + int err; + struct dentry *dentry; + struct inode *inode; + struct super_block *sb; + + dentry = file->f_dentry; + LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos); + inode = dentry->d_inode; + IMustLock(inode); + + au_nfsd_lockdep_off(); + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, reopen_dir, /*wlock*/1, + /*locked*/1); + if (unlikely(err)) + goto out; + + ii_write_lock_child(inode); + err = au_vdir_init(file); + if (unlikely(err)) { + ii_write_unlock(inode); + goto out_unlock; + } + //DbgVdir(au_fvdir_cache(file));// goto out_unlock; + + /* nfsd filldir calls lookup_one_len(). */ + ii_downgrade_lock(inode); + err = au_vdir_fill_de(file, dirent, filldir); + //DbgVdir(au_fvdir_cache(file));// goto out_unlock; + + fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); + ii_read_unlock(inode); + + out_unlock: + fi_write_unlock(file); + out: + si_read_unlock(sb); + au_nfsd_lockdep_on(); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +#define AuTestEmpty_WHONLY 1 +#define AuTestEmpty_DLGT (1 << 1) +#define AuTestEmpty_DIRPERM1 (1 << 2) +#define AuTestEmpty_CALLED (1 << 3) +#define AuTestEmpty_SHWH (1 << 4) +#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) +#define au_fset_testempty(flags, name) { (flags) |= AuTestEmpty_##name; } +#define au_fclr_testempty(flags, name) { (flags) &= ~AuTestEmpty_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuTestEmpty_DLGT +#define AuTestEmpty_DLGT 0 +#undef AuTestEmpty_DIRPERM1 +#define AuTestEmpty_DIRPERM1 0 +#endif +#ifndef CONFIG_AUFS_SHWH +#undef AuTestEmpty_SHWH +#define AuTestEmpty_SHWH 0 +#endif + +struct test_empty_arg { + struct au_nhash *whlist; + unsigned int flags; + int err; + aufs_bindex_t bindex; +}; + +static int test_empty_cb(void *__arg, const char *__name, int namelen, + loff_t offset, u64 ino, unsigned int d_type) +{ + struct test_empty_arg *arg = __arg; + char *name = (void *)__name; + + LKTRTrace("%.*s\n", namelen, name); + + arg->err = 0; + au_fset_testempty(arg->flags, CALLED); + //smp_mb(); + if (name[0] == '.' + && (namelen == 1 || (name[1] == '.' && namelen == 2))) + return 0; /* success */ + + if (namelen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + if (au_ftest_testempty(arg->flags, WHONLY) + && !au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = -ENOTEMPTY; + goto out; + } + + name += AUFS_WH_PFX_LEN; + namelen -= AUFS_WH_PFX_LEN; + if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) + arg->err = au_nhash_append_wh + (arg->whlist, name, namelen, ino, d_type, arg->bindex, + au_ftest_testempty(arg->flags, SHWH)); + + out: + //smp_mb(); + AuTraceErr(arg->err); + return arg->err; +} + +static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err, dlgt; + struct file *h_file; + + LKTRTrace("%.*s, {%p, 0x%x, %d}\n", + AuDLNPair(dentry), arg->whlist, arg->flags, arg->bindex); + + h_file = au_h_open(dentry, arg->bindex, + O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, + /*file*/NULL); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + err = 0; + if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY) + && !h_file->f_dentry->d_inode->i_nlink)) + goto out_put; + + dlgt = au_ftest_testempty(arg->flags, DLGT); + //h_file->f_pos = 0; + do { + arg->err = 0; + au_fclr_testempty(arg->flags, CALLED); + //smp_mb(); + err = vfsub_readdir(h_file, test_empty_cb, arg, dlgt); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_testempty(arg->flags, CALLED)); + + out_put: + fput(h_file); + au_sbr_put(dentry->d_sb, arg->bindex); + out: + AuTraceErr(err); + return err; +} + +struct do_test_empty_args { + int *errp; + struct dentry *dentry; + struct test_empty_arg *arg; +}; + +static void call_do_test_empty(void *args) +{ + struct do_test_empty_args *a = args; + *a->errp = do_test_empty(a->dentry, a->arg); +} + +static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) +{ + int err, wkq_err; + struct dentry *h_dentry; + struct inode *h_inode; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + h_dentry = au_h_dptr(dentry, arg->bindex); + AuDebugOn(!h_dentry); + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode || !S_ISDIR(h_inode->i_mode)); + + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ, + au_opt_test_dlgt(au_mntflags(dentry->d_sb))); + mutex_unlock(&h_inode->i_mutex); + if (!err) + err = do_test_empty(dentry, arg); + else { + struct do_test_empty_args args = { + .errp = &err, + .dentry = dentry, + .arg = arg + }; + unsigned int flags = arg->flags; + au_fclr_testempty(arg->flags, DLGT); + au_fclr_testempty(arg->flags, DIRPERM1); + wkq_err = au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + arg->flags = flags; + } + + AuTraceErr(err); + return err; +} + +int au_test_empty_lower(struct dentry *dentry) +{ + int err; + struct inode *inode; + struct test_empty_arg arg; + struct au_nhash *whlist; + aufs_bindex_t bindex, bstart, btail; + unsigned int mnt_flags; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + inode = dentry->d_inode; + AuDebugOn(!inode || !S_ISDIR(inode->i_mode)); + + whlist = au_nhash_new(GFP_TEMPORARY); + err = PTR_ERR(whlist); + if (IS_ERR(whlist)) + goto out; + + bstart = au_dbstart(dentry); + mnt_flags = au_mntflags(dentry->d_sb); + arg.whlist = whlist; + arg.flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_testempty(arg.flags, DLGT); + if (unlikely(au_opt_test(mnt_flags, SHWH))) + au_fset_testempty(arg.flags, SHWH); + arg.bindex = bstart; + err = do_test_empty(dentry, &arg); + if (unlikely(err)) + goto out_whlist; + + au_fset_testempty(arg.flags, WHONLY); + if (unlikely(au_opt_test_dirperm1(mnt_flags))) + au_fset_testempty(arg.flags, DIRPERM1); + btail = au_dbtaildir(dentry); + for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && h_dentry->d_inode) { + AuDebugOn(!S_ISDIR(h_dentry->d_inode->i_mode)); + arg.bindex = bindex; + err = do_test_empty(dentry, &arg); + } + } + + out_whlist: + au_nhash_del(whlist); + out: + AuTraceErr(err); + return err; +} + +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) +{ + int err; + struct inode *inode; + struct test_empty_arg arg; + aufs_bindex_t bindex, btail; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + inode = dentry->d_inode; + AuDebugOn(!inode || !S_ISDIR(inode->i_mode)); + + err = 0; + arg.whlist = whlist; + arg.flags = AuTestEmpty_WHONLY; + if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), SHWH))) + au_fset_testempty(arg.flags, SHWH); + btail = au_dbtaildir(dentry); + for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { + struct dentry *h_dentry; + h_dentry = au_h_dptr(dentry, bindex); + if (h_dentry && h_dentry->d_inode) { + AuDebugOn(!S_ISDIR(h_dentry->d_inode->i_mode)); + arg.bindex = bindex; + err = sio_test_empty(dentry, &arg); + } + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct file_operations aufs_dir_fop = { + .read = generic_read_dir, + .readdir = aufs_readdir, + .open = aufs_open_dir, + .release = aufs_release_dir, + .flush = aufs_flush, + .fsync = aufs_fsync_dir, +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dir.h linux-2.6.25.4-unionfs/fs/aufs/dir.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/dir.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dir.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * directory operations + * + * $Id: dir.h,v 1.2 2008/04/21 01:34:26 sfjro Exp $ + */ + +#ifndef __AUFS_DIR_H__ +#define __AUFS_DIR_H__ + +#ifdef __KERNEL__ + +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* need to be faster and smaller */ + +#define AuSize_DEBLK 512 // todo: changeable +#define AuSize_NHASH 32 // todo: changeable +#if AuSize_DEBLK < NAME_MAX || PAGE_SIZE < AuSize_DEBLK +#error invalid size AuSize_DEBLK +#endif + +typedef char au_vdir_deblk_t[AuSize_DEBLK]; + +struct au_nhash { + struct hlist_head heads[AuSize_NHASH]; +}; + +struct au_vdir_destr { + unsigned char len; + char name[0]; +} __packed; + +struct au_vdir_dehstr { + struct hlist_node hash; + struct au_vdir_destr *str; +}; + +struct au_vdir_de { + ino_t de_ino; + unsigned char de_type; + /* caution: packed */ + struct au_vdir_destr de_str; +} __packed; + +struct au_vdir_wh { + struct hlist_node wh_hash; + aufs_bindex_t wh_bindex; +#ifdef CONFIG_AUFS_SHWH + ino_t wh_ino; + unsigned char wh_type; + /* caution: packed */ +#endif + struct au_vdir_destr wh_str; +} __packed; + +union au_vdir_deblk_p { + unsigned char *p; + au_vdir_deblk_t *deblk; + struct au_vdir_de *de; +}; + +struct au_vdir { + au_vdir_deblk_t **vd_deblk; + int vd_nblk; + struct { + int i; + union au_vdir_deblk_p p; + } vd_last; + + unsigned long vd_version; + unsigned long vd_jiffy; +}; + +/* ---------------------------------------------------------------------- */ + +/* dir.c */ +extern struct file_operations aufs_dir_fop; +int au_test_empty_lower(struct dentry *dentry); +int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); + +/* vdir.c */ +struct au_nhash *au_nhash_new(gfp_t gfp); +void au_nhash_del(struct au_nhash *nhash); +void au_nhash_init(struct au_nhash *nhash); +void au_nhash_move(struct au_nhash *dst, struct au_nhash *src); +void au_nhash_fin(struct au_nhash *nhash); +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit); +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int namelen); +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int namelen, + ino_t ino, unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh); +void au_vdir_free(struct au_vdir *vdir); +int au_vdir_init(struct file *file); +int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir); + +/* ---------------------------------------------------------------------- */ + +static inline +void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, unsigned char d_type) +{ +#ifdef CONFIG_AUFS_SHWH + wh->wh_ino = ino; + wh->wh_type = d_type; +#endif +} + +static inline void au_add_nlink(struct inode *dir, struct inode *h_dir) +{ + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + dir->i_nlink += h_dir->i_nlink - 2; + if (unlikely(h_dir->i_nlink < 2)) + dir->i_nlink += 2; +} + +static inline void au_sub_nlink(struct inode *dir, struct inode *h_dir) +{ + AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode)); + dir->i_nlink -= h_dir->i_nlink - 2; + if (unlikely(h_dir->i_nlink < 2)) + dir->i_nlink -= 2; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/dlgt.c linux-2.6.25.4-unionfs/fs/aufs/dlgt.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/dlgt.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/dlgt.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * lookup functions in 'delegate' mode + * + * $Id: dlgt.c,v 1.2 2008/04/21 02:00:37 sfjro Exp $ + */ + +//#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +struct au_lookup_one_len_args { + struct dentry **errp; + const char *name; + struct dentry *parent; + int len; +}; + +static void au_call_lookup_one_len(void *args) +{ + struct au_lookup_one_len_args *a = args; + *a->errp = vfsub_lookup_one_len(a->name, a->parent, a->len); +} + +struct dentry *au_lkup_one_dlgt(const char *name, struct dentry *parent, + int len, unsigned int flags) +{ + struct dentry *dentry; + int dirperm1; + + LKTRTrace("%.*s/%.*s, 0x%x\n", AuDLNPair(parent), len, name, flags); + + dirperm1 = au_ftest_ndx(flags, DIRPERM1); + if (!dirperm1 && !au_ftest_ndx(flags, DLGT)) + dentry = vfsub_lookup_one_len(name, parent, len); + else { + int wkq_err; + struct au_lookup_one_len_args args = { + .errp = &dentry, + .name = name, + .parent = parent, + .len = len + }; + wkq_err = au_wkq_wait(au_call_lookup_one_len, &args, + /*dlgt*/!dirperm1); + if (unlikely(wkq_err)) + dentry = ERR_PTR(wkq_err); + } + + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +struct security_inode_permission_args { + int *errp; + struct inode *h_inode; + int mask; + struct nameidata *fake_nd; +}; + +static void call_security_inode_permission(void *args) +{ + struct security_inode_permission_args *a = args; + LKTRTrace("fsuid %d\n", current->fsuid); + *a->errp = security_inode_permission(a->h_inode, a->mask, a->fake_nd); +} + +int au_security_inode_permission(struct inode *h_inode, int mask, + struct nameidata *fake_nd, int dlgt) +{ + int err; + + AuTraceEnter(); + + if (!dlgt) + err = security_inode_permission(h_inode, mask, fake_nd); + else { + int wkq_err; + struct security_inode_permission_args args = { + .errp = &err, + .h_inode = h_inode, + .mask = mask, + .fake_nd = fake_nd + }; + wkq_err = au_wkq_wait(call_security_inode_permission, &args, + /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + } + + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/export.c linux-2.6.25.4-unionfs/fs/aufs/export.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/export.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/export.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,578 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * export via nfs + * + * $Id: export.c,v 1.4 2008/05/04 23:52:08 sfjro Exp $ + */ + +#include +#include "aufs.h" + +union conv { +#if BITS_PER_LONG == 32 + __u32 a[1]; +#else + __u32 a[2]; +#endif + ino_t ino; +}; + +static ino_t decode_ino(__u32 *a) +{ + union conv u; + + u.a[0] = a[0]; +#if BITS_PER_LONG == 64 + u.a[1] = a[1]; +#endif + return u.ino; +} + +static void encode_ino(__u32 *a, ino_t ino) +{ + union conv u; + + u.ino = ino; + a[0] = u.a[0]; +#if BITS_PER_LONG == 64 + a[1] = u.a[1]; +#endif +} + +/* NFS file handle */ +enum { + Fh_br_id, + Fh_sigen, +#if BITS_PER_LONG == 64 + /* support 64bit inode number */ + Fh_ino1, + Fh_ino2, + Fh_dir_ino1, + Fh_dir_ino2, + Fh_h_ino1, + Fh_h_ino2, +#else + Fh_ino1, + Fh_dir_ino1, + Fh_h_ino1, +#endif + Fh_h_igen, + Fh_h_type, + Fh_tail, + + Fh_ino = Fh_ino1, + Fh_dir_ino = Fh_dir_ino1, + Fh_h_ino = Fh_h_ino1, +}; + +static int au_test_anon(struct dentry *dentry) +{ + return !!(dentry->d_flags & DCACHE_DISCONNECTED); +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino) +{ + struct dentry *dentry, *parent; + struct inode *inode; + + LKTRTrace("i%lu, diri%lu\n", ino, dir_ino); + + dentry = NULL; + inode = ilookup(sb, ino); + if (unlikely(!inode)) + goto out; + + dentry = ERR_PTR(-ESTALE); + if (unlikely(is_bad_inode(inode))) + goto out_iput; + + dentry = NULL; + if (!S_ISDIR(inode->i_mode)) { + struct dentry *d; + spin_lock(&dcache_lock); + list_for_each_entry(d, &inode->i_dentry, d_alias) + if (!au_test_anon(d) + && d->d_parent->d_inode->i_ino == dir_ino) { + dentry = dget_locked(d); + break; + } + spin_unlock(&dcache_lock); + } else { + dentry = d_find_alias(inode); + if (dentry && !au_test_anon(dentry)) { + int same_ino; + parent = dget_parent(dentry); + same_ino = (parent->d_inode->i_ino == dir_ino); + dput(parent); + if (same_ino) + goto out_iput; /* success */ + } + + dput(dentry); + dentry = NULL; + } + + out_iput: + iput(inode); + out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +struct find_name_by_ino { + int called, found; + ino_t ino; + char *name; + int namelen; +}; + +static int +find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset, + u64 ino, unsigned int d_type) +{ + struct find_name_by_ino *a = arg; + + a->called++; + if (a->ino != ino) + return 0; + + memcpy(a->name, name, namelen); + a->namelen = namelen; + a->found = 1; + return 1; +} + +static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, + ino_t dir_ino) +{ + struct dentry *dentry, *parent; + struct inode *dir; + struct find_name_by_ino arg; + struct file *file; + int err; + + LKTRTrace("i%lu, diri%lu\n", ino, dir_ino); + + dentry = NULL; + dir = ilookup(sb, dir_ino); + if (unlikely(!dir)) + goto out; + + dentry = ERR_PTR(-ESTALE); + if (unlikely(is_bad_inode(dir))) + goto out_iput; + + dentry = NULL; + parent = d_find_alias(dir); + if (parent) { + if (unlikely(au_test_anon(parent))) { + dput(parent); + goto out_iput; + } + } else + goto out_iput; + + file = dentry_open(parent, au_mntcache_get(sb), au_dir_roflags); + dentry = (void *)file; + if (IS_ERR(file)) + goto out_iput; + + dentry = ERR_PTR(-ENOMEM); + arg.name = __getname(); + if (unlikely(!arg.name)) + goto out_fput; + arg.ino = ino; + arg.found = 0; + + do { + arg.called = 0; + //smp_mb(); + err = vfsub_readdir(file, find_name_by_ino, &arg, /*dlgt*/0); + } while (!err && !arg.found && arg.called); + dentry = ERR_PTR(err); + if (arg.found) { + /* do not call au_lkup_one(), nor dlgt */ + mutex_lock(&dir->i_mutex); + dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); + mutex_unlock(&dir->i_mutex); + AuTraceErrPtr(dentry); + } + + //out_putname: + __putname(arg.name); + out_fput: + fput(file); + out_iput: + iput(dir); + out: + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +struct append_name { + int found, called, len; + char *h_path; + ino_t h_ino; +}; + +static int append_name(void *arg, const char *name, int len, loff_t pos, + u64 ino, unsigned int d_type) +{ + struct append_name *a = arg; + char *p; + + a->called++; + if (ino != a->h_ino) + return 0; + + AuDebugOn(len == 1 && *name == '.'); + AuDebugOn(len == 2 && name[0] == '.' && name[1] == '.'); + a->len = strlen(a->h_path); + memmove(a->h_path - len - 1, a->h_path, a->len); + a->h_path -= len + 1; + p = a->h_path + a->len; + *p++ = '/'; + memcpy(p, name, len); + a->len += 1 + len; + a->found++; + return 1; +} + +static int h_acceptable(void *expv, struct dentry *dentry) +{ + return 1; +} + +static char *au_build_path(struct super_block *sb, __u32 *fh, char *path, + struct vfsmount *h_mnt, struct dentry *h_root, + struct dentry *h_parent) +{ + char *ret; + int err, len; + struct file *h_file; + struct append_name arg; + struct path dm_path = { + .mnt = h_mnt, + .dentry = h_root + }; + + AuTraceEnter(); + + arg.h_path = d_path(&dm_path, path, PATH_MAX); + ret = arg.h_path; + if (unlikely(!arg.h_path || IS_ERR(arg.h_path))) + goto out; + + len = strlen(arg.h_path); + dm_path.dentry = h_parent; + arg.h_path = d_path(&dm_path, path, PATH_MAX); + ret = arg.h_path; + if (unlikely(!arg.h_path || IS_ERR(arg.h_path))) + goto out; + LKTRTrace("%s\n", arg.h_path); + if (len != 1) + arg.h_path += len; + LKTRTrace("%p, %s, %ld\n", + arg.h_path, arg.h_path, (long)(arg.h_path - path)); + + /* cf. fs/exportfs/expfs.c */ + h_file = dentry_open(dget(h_parent), mntget(h_mnt), au_dir_roflags); + ret = (void *)h_file; + if (IS_ERR(h_file)) + goto out; + + arg.found = 0; + arg.h_ino = decode_ino(fh + Fh_h_ino); + do { + arg.called = 0; + err = vfsub_readdir(h_file, append_name, &arg, /*dlgt*/0); + } while (!err && !arg.found && arg.called); + LKTRTrace("%p, %s, %d\n", arg.h_path, arg.h_path, arg.len); + fput(h_file); + ret = ERR_PTR(err); + if (unlikely(err)) + goto out; + + dm_path.mnt = au_mntcache_get(sb); + dm_path.dentry = sb->s_root; + ret = d_path(&dm_path, path, PATH_MAX - arg.len); + mntput(dm_path.mnt); + if (unlikely(!ret || IS_ERR(ret))) + goto out; + ret[strlen(ret)] = '/'; + LKTRTrace("%s\n", ret); + + out: + AuTraceErrPtr(ret); + return ret; +} + +static struct dentry * +decode_by_path(struct super_block *sb, aufs_bindex_t bindex, __u32 *fh, + int fh_len, void *context) +{ + struct dentry *dentry, *h_parent, *root, *h_root; + struct super_block *h_sb; + char *path, *p; + struct vfsmount *h_mnt; + int err; + struct nameidata nd; + struct au_branch *br; + + LKTRTrace("b%d\n", bindex); + SiMustAnyLock(sb); + + br = au_sbr(sb, bindex); + /* au_br_get(br); */ + h_mnt = br->br_mnt; + h_sb = h_mnt->mnt_sb; + LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb)); + /* in linux-2.6.24, it takes struct fid * as file handle */ + //todo: call lower fh_to_dentry()? fh_to_parent()? + h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), + fh_len - Fh_tail, fh[Fh_h_type], + h_acceptable, /*context*/NULL); + dentry = h_parent; + if (unlikely(!h_parent || IS_ERR(h_parent))) { + AuWarn1("%s decode_fh failed\n", au_sbtype(h_sb)); + goto out; + } + dentry = NULL; + if (unlikely(au_test_anon(h_parent))) { + AuWarn1("%s decode_fh returned a disconnected dentry\n", + au_sbtype(h_sb)); + dput(h_parent); + goto out; + } + + dentry = ERR_PTR(-ENOMEM); + path = __getname(); + if (unlikely(!path)) { + dput(h_parent); + goto out; + } + + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + h_root = au_h_dptr(root, bindex); + di_read_unlock(root, !AuLock_IR); + p = au_build_path(sb, fh, path, h_mnt, h_root, h_parent); + dput(h_parent); + if (IS_ERR(p)) + goto out_putname; + + err = vfsub_path_lookup(p, LOOKUP_FOLLOW, &nd); + dentry = ERR_PTR(err); + if (!err) { + dentry = dget(nd.path.dentry); + if (unlikely(au_test_anon(dentry))) { + dput(dentry); + dentry = ERR_PTR(-ESTALE); + } + path_put(&nd.path); + } + + out_putname: + __putname(path); + out: + /* au_br_put(br); */ + AuTraceErrPtr(dentry); + return dentry; +} + +/* ---------------------------------------------------------------------- */ + +static noinline_for_stack struct dentry * +aufs_decode_fh(struct super_block *sb, __u32 *fh, int fh_len, int fh_type, + int (*acceptable)(void *context, struct dentry *de), + void *context) +{ + struct dentry *dentry; + ino_t ino, dir_ino; + aufs_bindex_t bindex, br_id; + struct inode *inode, *h_inode; + au_gen_t sigen; + + //au_debug_on(); + LKTRTrace("%d, fh{i%u, br_id %u, sigen %u, hi%u}\n", + fh_type, fh[Fh_ino], fh[Fh_br_id], fh[Fh_sigen], + fh[Fh_h_ino]); + AuDebugOn(fh_len < Fh_tail); + + si_read_lock(sb, AuLock_FLUSH); + lockdep_off(); + + /* branch id may be wrapped around */ + dentry = ERR_PTR(-ESTALE); + br_id = fh[Fh_br_id]; + sigen = fh[Fh_sigen]; + bindex = au_br_index(sb, br_id); + if (unlikely(bindex < 0 || sigen + AUFS_BRANCH_MAX <= au_sigen(sb))) + goto out; + + /* is this inode still cached? */ + ino = decode_ino(fh + Fh_ino); + dir_ino = decode_ino(fh + Fh_dir_ino); + dentry = decode_by_ino(sb, ino, dir_ino); + if (IS_ERR(dentry)) + goto out; + if (dentry) + goto accept; + + /* is the parent dir cached? */ + dentry = decode_by_dir_ino(sb, ino, dir_ino); + if (IS_ERR(dentry)) + goto out; + if (dentry) + goto accept; + + /* lookup path */ + dentry = decode_by_path(sb, bindex, fh, fh_len, context); + if (IS_ERR(dentry)) + goto out; + if (unlikely(!dentry)) + goto out_stale; + if (unlikely(dentry->d_inode->i_ino != ino)) + goto out_dput; + + accept: + inode = dentry->d_inode; + h_inode = NULL; + ii_read_lock_child(inode); + if (au_ibstart(inode) <= bindex && bindex <= au_ibend(inode)) + h_inode = au_h_iptr(inode, bindex); + ii_read_unlock(inode); + if (h_inode + && h_inode->i_generation == fh[Fh_h_igen] + && acceptable(context, dentry)) + goto out; /* success */ + out_dput: + dput(dentry); + out_stale: + dentry = ERR_PTR(-ESTALE); + out: + lockdep_on(); + si_read_unlock(sb); + AuTraceErrPtr(dentry); + //au_debug_off(); + return dentry; +} + +static struct dentry * +aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, + int fh_type) +{ + return aufs_decode_fh(sb, fid->raw, fh_len, fh_type, h_acceptable, + /*context*/NULL); +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, + int connectable) +{ + int err; + struct super_block *sb, *h_sb; + struct inode *inode, *h_inode, *dir; + aufs_bindex_t bindex; + union conv u; + struct dentry *parent, *h_parent; + + //au_debug_on(); + BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); + LKTRTrace("%.*s, max %d, conn %d\n", + AuDLNPair(dentry), *max_len, connectable); + AuDebugOn(au_test_anon(dentry)); + inode = dentry->d_inode; + AuDebugOn(!inode); + parent = dget_parent(dentry); + AuDebugOn(au_test_anon(parent)); + + err = -ENOSPC; + if (unlikely(*max_len <= Fh_tail)) { + AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); + goto out; + } + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + di_read_lock_child(dentry, AuLock_IR); + di_read_lock_parent(parent, AuLock_IR); +#ifdef CONFIG_AUFS_DEBUG + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + AuWarn1("NFS-exporting requires xino\n"); +#endif + + err = -EPERM; + bindex = au_dbstart(dentry); + h_sb = au_sbr_sb(sb, bindex); + if (unlikely(!h_sb->s_export_op)) { + AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); + goto out_unlock; + } + + fh[Fh_br_id] = au_sbr_id(sb, bindex); + fh[Fh_sigen] = au_sigen(sb); + encode_ino(fh + Fh_ino, inode->i_ino); + dir = parent->d_inode; + encode_ino(fh + Fh_dir_ino, dir->i_ino); + h_inode = au_h_dptr(dentry, bindex)->d_inode; + encode_ino(fh + Fh_h_ino, h_inode->i_ino); + fh[Fh_h_igen] = h_inode->i_generation; + + *max_len -= Fh_tail; + //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len); + h_parent = au_h_dptr(parent, bindex); + AuDebugOn(au_test_anon(h_parent)); + /* in linux-2.6.24, it takes struct fid * as file handle */ + fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), + max_len, connectable); + err = fh[Fh_h_type]; + *max_len += Fh_tail; + if (err != 255) + err = 2; //?? + else + AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); + + out_unlock: + di_read_unlock(parent, AuLock_IR); + aufs_read_unlock(dentry, AuLock_IR); + out: + dput(parent); + AuTraceErr(err); + //au_debug_off(); + if (unlikely(err < 0)) + err = 255; + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct export_operations aufs_export_op = { + .fh_to_dentry = aufs_fh_to_dentry, + .encode_fh = aufs_encode_fh +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/file.c linux-2.6.25.4-unionfs/fs/aufs/file.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/file.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/file.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,755 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * handling file/dir, and address_space operation + * + * $Id: file.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $ + */ + +//#include +#include +//#include +//#include +#include "aufs.h" + +/* + * a dirty trick for handling FMODE_EXEC and deny_write_access(). + * because FMODE_EXEC flag is not passed to f_op->open(), + * set it to file->private_data temporary. + */ +#if !defined(CONFIG_AUFS_MODULE) || defined(CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH) +int au_store_fmode_exec(struct nameidata *nd, struct inode *inode) +{ + int err; + union { + void *p; + unsigned long ul; + } u; + + err = 0; + if (nd + && (nd->flags & LOOKUP_OPEN) + && nd->intent.open.file + && (nd->intent.open.flags & FMODE_EXEC) + && inode + && S_ISREG(inode->i_mode)) { + u.ul = nd->intent.open.flags; + nd->intent.open.file->private_data = u.p; + //smp_mb(); + err = 1; + } + + return err; +} +#endif + +/* drop flags for writing */ +unsigned int au_file_roflags(unsigned int flags) +{ + flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); + flags |= O_RDONLY | O_NOATIME; + return flags; +} + +/* common functions to regular file and dir */ +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file) +{ + struct file *h_file; + struct dentry *h_dentry; + struct inode *h_inode; + struct super_block *sb; + struct au_branch *br; + int hinotify, err; + + LKTRTrace("%.*s, b%d, flags 0%o, f %d\n", + AuDLNPair(dentry), bindex, flags, !!file); + AuDebugOn(!dentry); + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry); + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode); + + sb = dentry->d_sb; + hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY); + + br = au_sbr(sb, bindex); + au_br_get(br); + /* drop flags for writing */ + if (au_test_ro(sb, bindex, dentry->d_inode)) + flags = au_file_roflags(flags); + flags &= ~O_CREAT; + + h_file = NULL; + if (unlikely(file && au_test_nfs(h_dentry->d_sb))) + h_file = au_h_intent(dentry, bindex, file); + if (!h_file) { + //DbgSleep(3); + h_file = dentry_open(dget(h_dentry), mntget(br->br_mnt), flags); + //if (LktrCond) {fput(h_file); h_file = ERR_PTR(-1);} + } + + /* + * a dirty trick for handling FMODE_EXEC and deny_write_access(). + */ + if (file && (file->f_mode & FMODE_EXEC)) { + h_file->f_mode |= FMODE_EXEC; + smp_mb(); /* flush f_mode */ + err = au_deny_write_access(h_file); + if (unlikely(err)) { + fput(h_file); + h_file = ERR_PTR(err); + } + } + if (!IS_ERR(h_file)) + return h_file; + + au_br_put(br); + AuTraceErrPtr(h_file); + return h_file; +} + +static int do_coo(struct dentry *dentry, aufs_bindex_t bstart) +{ + int err; + struct dentry *parent, *h_parent, *h_dentry, *gparent; + aufs_bindex_t bcpup; + struct inode *h_dir, *h_inode, *dir; + struct super_block *sb; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + AuDebugOn(IS_ROOT(dentry)); + DiMustWriteLock(dentry); + + parent = dget_parent(dentry); + di_write_lock_parent(parent); + sb = dentry->d_sb; + err = AuWbrCopyup(au_sbi(sb), dentry); + bcpup = err; + if (err < 0) { + err = 0; /* stop copyup, it is not an error */ + goto out; + } + err = 0; + + h_parent = au_h_dptr(parent, bcpup); + if (!h_parent) { + err = au_cpup_dirs(dentry, bcpup, NULL); + if (unlikely(err)) + goto out; + h_parent = au_h_dptr(parent, bcpup); + } + + h_dir = h_parent->d_inode; + h_dentry = au_h_dptr(dentry, bstart); + h_inode = h_dentry->d_inode; + dir = parent->d_inode; + //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. + gparent = NULL; + if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY) + && !IS_ROOT(parent))) { + gparent = dget_parent(parent); + ii_read_lock_parent2(gparent->d_inode); + } + au_hdir_lock(h_dir, dir, bcpup); + //todo: test parent-gparent relationship + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + AuDebugOn(au_h_dptr(dentry, bcpup)); + err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME); + AuTraceErr(err); + mutex_unlock(&h_inode->i_mutex); + au_hdir_unlock(h_dir, dir, bcpup); + if (unlikely(gparent)) { + ii_read_unlock(gparent->d_inode); + dput(gparent); + } + + out: + di_write_unlock(parent); + dput(parent); + AuTraceErr(err); + return err; +} + +int au_do_open(struct inode *inode, struct file *file, + int (*open)(struct file *file, int flags)) +{ + int err, coo; + struct dentry *dentry; + struct super_block *sb; + aufs_bindex_t bstart; + //struct inode *h_dir, *dir; + + dentry = file->f_dentry; + LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry)); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + coo = 0; + switch (au_mntflags(sb) & AuOptMask_COO) { + case AuOpt_COO_LEAF: + coo = !S_ISDIR(inode->i_mode); + break; + case AuOpt_COO_ALL: + coo = 1; + break; + } + err = au_finfo_init(file); + //if (LktrCond) {fi_write_unlock(file); fin_finfo(file); err = -1;} + if (unlikely(err)) + goto out; + + if (!coo) { + di_read_lock_child(dentry, AuLock_IR); + bstart = au_dbstart(dentry); + } else { + di_write_lock_child(dentry); + bstart = au_dbstart(dentry); + if (au_test_ro(sb, bstart, dentry->d_inode)) { + err = do_coo(dentry, bstart); + if (err) { + di_write_unlock(dentry); + goto out_finfo; + } + bstart = au_dbstart(dentry); + } + di_downgrade_lock(dentry, AuLock_IR); + } + + err = open(file, file->f_flags); + //if (LktrCond) err = -1; + di_read_unlock(dentry, AuLock_IR); + + out_finfo: + fi_write_unlock(file); + if (unlikely(err)) + au_finfo_fin(file); + //DbgFile(file); + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +int au_reopen_nondir(struct file *file) +{ + int err; + struct dentry *dentry; + aufs_bindex_t bstart, bindex, bend; + struct file *h_file, *h_file_tmp; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + bstart = au_dbstart(dentry); + AuDebugOn(S_ISDIR(dentry->d_inode->i_mode) + || !au_h_dptr(dentry, bstart)->d_inode); + + h_file_tmp = NULL; + if (au_fbstart(file) == bstart) { + h_file = au_h_fptr(file, bstart); + if (file->f_mode == h_file->f_mode) + return 0; /* success */ + h_file_tmp = h_file; + get_file(h_file_tmp); + au_set_h_fptr(file, bstart, NULL); + } + AuDebugOn(au_fbstart(file) < bstart + || au_fi(file)->fi_hfile[0 + bstart].hf_file); + + h_file = au_h_open(dentry, bstart, file->f_flags & ~O_TRUNC, file); + //if (LktrCond) {fput(h_file); au_br_put(au_sbr(dentry->d_sb, bstart)); + //h_file = ERR_PTR(-1);} + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; // close all? + err = 0; + //cpup_file_flags(h_file, file); + au_set_fbstart(file, bstart); + au_set_h_fptr(file, bstart, h_file); + au_update_figen(file); + //file->f_ra = h_file->f_ra; //?? + + /* close lower files */ + bend = au_fbend(file); + for (bindex = bstart + 1; bindex <= bend; bindex++) + au_set_h_fptr(file, bindex, NULL); + au_set_fbend(file, bstart); + + out: + if (h_file_tmp) + fput(h_file_tmp); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * prepare the @file for writing. + */ +int au_ready_to_write(struct file *file, loff_t len) +{ + int err; + struct dentry *dentry, *parent, *h_dentry, *h_parent, *hi_wh, + *old_h_dentry, *gparent; + struct inode *h_inode, *h_dir, *inode, *dir; + struct super_block *sb; + aufs_bindex_t bstart, bcpup, old_bstart; + struct au_dinfo *dinfo; + + dentry = file->f_dentry; + LKTRTrace("%.*s, len %Ld\n", AuDLNPair(dentry), len); + FiMustWriteLock(file); + + sb = dentry->d_sb; + bstart = au_fbstart(file); + AuDebugOn(au_fbr(file, bstart) != au_sbr(sb, bstart)); + + inode = dentry->d_inode; + AuDebugOn(S_ISDIR(inode->i_mode)); + ii_read_lock_child(inode); + LKTRTrace("rdonly %d, bstart %d\n", + au_test_ro(sb, bstart, inode), bstart); + err = au_test_ro(sb, bstart, inode); + ii_read_unlock(inode); + if (!err && (au_h_fptr(file, bstart)->f_mode & FMODE_WRITE)) + return 0; + + /* need to cpup */ + di_write_lock_child(dentry); + parent = dget_parent(dentry); + di_write_lock_parent(parent); + err = AuWbrCopyup(au_sbi(sb), dentry); + bcpup = err; + if (unlikely(err < 0)) + goto out_unlock; + err = 0; + + h_parent = au_h_dptr(parent, bcpup); + if (!h_parent) { + err = au_cpup_dirs(dentry, bcpup, NULL); + //if (LktrCond) err = -1; + if (unlikely(err)) + goto out_unlock; + h_parent = au_h_dptr(parent, bcpup); + } + + //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. + gparent = NULL; + if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY) + && !IS_ROOT(parent))) { + gparent = dget_parent(parent); + ii_read_lock_parent2(gparent->d_inode); + } + h_dir = h_parent->d_inode; + h_dentry = au_h_fptr(file, au_fbstart(file))->f_dentry; + h_inode = h_dentry->d_inode; + dir = parent->d_inode; + au_hdir_lock(h_dir, dir, bcpup); + //todo: test parent-gparent relationship + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + if (d_unhashed(dentry) /* || d_unhashed(h_dentry) */ + /* || !h_inode->i_nlink */) { + hi_wh = au_hi_wh(inode, bcpup); + if (!hi_wh) + err = au_sio_cpup_wh(dentry, bcpup, len, file); + else { + /* already copied-up after unlink */ + dinfo = au_di(dentry); + old_bstart = dinfo->di_bstart; + dinfo->di_bstart = bcpup; + old_h_dentry = dinfo->di_hdentry[0 + bcpup].hd_dentry; + dinfo->di_hdentry[0 + bcpup].hd_dentry = hi_wh; + err = au_reopen_nondir(file); + dinfo->di_hdentry[0 + bcpup].hd_dentry = old_h_dentry; + dinfo->di_bstart = old_bstart; + } + //if (LktrCond) err = -1; + AuTraceErr(err); + } else { + if (!au_h_dptr(dentry, bcpup)) + err = au_sio_cpup_simple(dentry, bcpup, len, + AuCpup_DTIME); + //if (LktrCond) err = -1; + AuTraceErr(err); + if (!err) + err = au_reopen_nondir(file); + //if (LktrCond) err = -1; + AuTraceErr(err); + } + mutex_unlock(&h_inode->i_mutex); + au_hdir_unlock(h_dir, dir, bcpup); + if (unlikely(gparent)) { + ii_read_unlock(gparent->d_inode); + dput(gparent); + } + + out_unlock: + di_write_unlock(parent); + di_write_unlock(dentry); + dput(parent); + AuTraceErr(err); + return err; + +} + +/* ---------------------------------------------------------------------- */ + +static int refresh_file_by_inode(struct file *file, int *need_reopen) +{ + int err; + struct au_finfo *finfo; + struct dentry *dentry, *parent, *old_h_dentry, *hi_wh; + struct inode *inode, *dir, *h_dir; + aufs_bindex_t bstart, new_bstart, old_bstart; + struct super_block *sb; + struct au_dinfo *dinfo; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + FiMustWriteLock(file); + + err = 0; + finfo = au_fi(file); + inode = dentry->d_inode; + sb = dentry->d_sb; + again: + bstart = au_ibstart(inode); + if (bstart == finfo->fi_bstart) + goto out; + + new_bstart = bstart; + parent = dget_parent(dentry); + dir = parent->d_inode; + if (au_test_ro(sb, bstart, inode)) { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(au_sbi(sb), dentry); + new_bstart = err; + di_read_unlock(parent, !AuLock_IR); + //todo: err = -1; + if (unlikely(err < 0)) + goto out_put; + } + di_read_unlock(dentry, AuLock_IR); + di_write_lock_child(dentry); + /* someone else might change our inode while we were sleeping */ + if (bstart != au_ibstart(inode)) { // todo + di_downgrade_lock(dentry, AuLock_IR); + err = 0; + dput(parent); + goto again; + } + di_read_lock_parent(parent, AuLock_IR); + bstart = new_bstart; + + hi_wh = au_hi_wh(inode, bstart); + if (au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(sb, inode) + && !d_unhashed(dentry)) { + err = au_test_and_cpup_dirs(dentry, bstart, NULL); + + /* always superio. */ + h_dir = au_h_dptr(parent, bstart)->d_inode; + au_hdir_lock(h_dir, dir, bstart); + err = au_sio_cpup_simple(dentry, bstart, -1, AuCpup_DTIME); + au_hdir_unlock(h_dir, dir, bstart); + } else if (hi_wh) { + /* already copied-up after unlink */ + dinfo = au_di(dentry); + old_bstart = dinfo->di_bstart; + dinfo->di_bstart = bstart; + old_h_dentry = dinfo->di_hdentry[0 + bstart].hd_dentry; + dinfo->di_hdentry[0 + bstart].hd_dentry = hi_wh; + err = au_reopen_nondir(file); + dinfo->di_hdentry[0 + bstart].hd_dentry = old_h_dentry; + dinfo->di_bstart = old_bstart; + *need_reopen = 0; + } + di_read_unlock(parent, AuLock_IR); + di_downgrade_lock(dentry, AuLock_IR); + + out_put: + dput(parent); + out: + AuTraceErr(err); + return err; +} + +/* + * after branch manipulating, refresh the file. + */ +static int refresh_file(struct file *file, int (*reopen)(struct file *file)) +{ + int err, new_sz, need_reopen; + struct dentry *dentry; + aufs_bindex_t bend, bindex, bstart, brid; + struct au_hfile *p; + struct au_finfo *finfo; + struct super_block *sb; + struct inode *inode; + struct file *hidden_file; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + FiMustWriteLock(file); + DiMustReadLock(dentry); + inode = dentry->d_inode; + IiMustReadLock(inode); + + err = -ENOMEM; + sb = dentry->d_sb; + finfo = au_fi(file); + bstart = finfo->fi_bstart; + bend = finfo->fi_bstart; + new_sz = sizeof(*finfo->fi_hfile) * (au_sbend(sb) + 1); + p = au_kzrealloc(finfo->fi_hfile, sizeof(*p) * (finfo->fi_bend + 1), + new_sz, GFP_KERNEL); + //p = NULL; + if (unlikely(!p)) + goto out; + finfo->fi_hfile = p; + hidden_file = p[0 + bstart].hf_file; + + p = finfo->fi_hfile + finfo->fi_bstart; + brid = p->hf_br->br_id; + bend = finfo->fi_bend; + for (bindex = finfo->fi_bstart; bindex <= bend; bindex++, p++) { + struct au_hfile tmp, *q; + aufs_bindex_t new_bindex; + + if (!p->hf_file) + continue; + new_bindex = au_find_bindex(sb, p->hf_br); + if (new_bindex == bindex) + continue; + if (new_bindex < 0) { // test here + au_set_h_fptr(file, bindex, NULL); + continue; + } + + /* swap two hidden inode, and loop again */ + q = finfo->fi_hfile + new_bindex; + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hf_file) { + bindex--; + p--; + } + } + { + aufs_bindex_t s = finfo->fi_bstart, e = finfo->fi_bend; + finfo->fi_bstart = 0; + finfo->fi_bend = au_sbend(sb); + finfo->fi_bstart = s; + finfo->fi_bend = e; + } + + p = finfo->fi_hfile; + if (!au_test_mmapped(file) && !d_unhashed(dentry)) { + bend = au_sbend(sb); + for (finfo->fi_bstart = 0; finfo->fi_bstart <= bend; + finfo->fi_bstart++, p++) + if (p->hf_file) { + if (p->hf_file->f_dentry + && p->hf_file->f_dentry->d_inode) + break; + else + au_hfput(p); + } + } else { + bend = au_br_index(sb, brid); + //LKTRTrace("%d\n", bend); + for (finfo->fi_bstart = 0; finfo->fi_bstart < bend; + finfo->fi_bstart++, p++) + if (p->hf_file) + au_hfput(p); + //LKTRTrace("%d\n", finfo->fi_bstart); + bend = au_sbend(sb); + } + + p = finfo->fi_hfile + bend; + for (finfo->fi_bend = bend; finfo->fi_bend >= finfo->fi_bstart; + finfo->fi_bend--, p--) + if (p->hf_file) { + if (p->hf_file->f_dentry + && p->hf_file->f_dentry->d_inode) + break; + else + au_hfput(p); + } + AuDebugOn(finfo->fi_bend < finfo->fi_bstart); + //DbgFile(file); + //DbgDentry(file->f_dentry); + + err = 0; + need_reopen = 1; + if (!au_test_mmapped(file)) + err = refresh_file_by_inode(file, &need_reopen); + if (!err && need_reopen && !d_unhashed(dentry)) + err = reopen(file); + //err = -1; + if (!err) { + au_update_figen(file); + //DbgFile(file); + return 0; /* success */ + } + + /* error, close all hidden files */ + bend = au_fbend(file); + for (bindex = au_fbstart(file); bindex <= bend; bindex++) + au_set_h_fptr(file, bindex, NULL); + + out: + AuTraceErr(err); + return err; +} + +/* common function to regular file and dir */ +int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file), + int wlock, int locked) +{ + int err, pseudo_link; + struct dentry *dentry; + struct super_block *sb; + aufs_bindex_t bstart; + au_gen_t sgen, fgen; + + dentry = file->f_dentry; + LKTRTrace("%.*s, w %d, l %d\n", AuDLNPair(dentry), wlock, locked); + sb = dentry->d_sb; + SiMustAnyLock(sb); + + err = 0; + sgen = au_sigen(sb); + fi_write_lock(file); + fgen = au_figen(file); + di_read_lock_child(dentry, AuLock_IR); + bstart = au_dbstart(dentry); + pseudo_link = (bstart != au_ibstart(dentry->d_inode)); + di_read_unlock(dentry, AuLock_IR); + if (sgen == fgen && !pseudo_link && au_fbstart(file) == bstart) { + if (!wlock) + fi_downgrade_lock(file); + return 0; /* success */ + } + + LKTRTrace("sgen %d, fgen %d\n", sgen, fgen); + if (unlikely(sgen != au_digen(dentry) + || sgen != au_iigen(dentry->d_inode))) { + /* + * d_path() and path_lookup() is a simple and good approach + * to revalidate. but si_rwsem in DEBUG_RWSEM will cause a + * deadlock. removed the code. + */ + di_write_lock_child(dentry); + err = au_reval_dpath(dentry, sgen); + //if (LktrCond) err = -1; + di_write_unlock(dentry); + if (unlikely(err < 0)) + goto out; + AuDebugOn(au_digen(dentry) != sgen + || au_iigen(dentry->d_inode) != sgen); + } + + di_read_lock_child(dentry, AuLock_IR); + err = refresh_file(file, reopen + /* , au_opt_test(au_mnt_flags(sb), REFROF) */); + //if (LktrCond) err = -1; + di_read_unlock(dentry, AuLock_IR); + if (!err) { + if (!wlock) + fi_downgrade_lock(file); + } else + fi_write_unlock(file); + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* cf. aufs_nopage() */ +/* for madvise(2) */ +static int aufs_readpage(struct file *file, struct page *page) +{ + AuTraceEnter(); + unlock_page(page); + return 0; +} + +/* they will never be called. */ +#ifdef CONFIG_AUFS_DEBUG +static int aufs_prepare_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ AuUnsupport(); return 0; } +static int aufs_commit_write(struct file *file, struct page *page, + unsigned from, unsigned to) +{ AuUnsupport(); return 0; } +static int aufs_write_begin(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned flags, + struct page **pagep, void **fsdata) +{ AuUnsupport(); return 0; } +static int aufs_write_end(struct file *file, struct address_space *mapping, + loff_t pos, unsigned len, unsigned copied, + struct page *page, void *fsdata) +{ AuUnsupport(); return 0; } +static int aufs_writepage(struct page *page, struct writeback_control *wbc) +{ AuUnsupport(); return 0; } +static void aufs_sync_page(struct page *page) +{ AuUnsupport(); } + +static int aufs_set_page_dirty(struct page *page) +{ AuUnsupport(); return 0; } +static void aufs_invalidatepage(struct page *page, unsigned long offset) +{ AuUnsupport(); } +static int aufs_releasepage(struct page *page, gfp_t gfp) +{ AuUnsupport(); return 0; } +static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, + const struct iovec *iov, loff_t offset, + unsigned long nr_segs) +{ AuUnsupport(); return 0; } +static struct page *aufs_get_xip_page(struct address_space *mapping, + sector_t offset, int create) +{ AuUnsupport(); return NULL; } +//static int aufs_migratepage (struct page *newpage, struct page *page) +//{ AuUnsupport(); return 0; } +#endif /* CONFIG_AUFS_DEBUG */ + +struct address_space_operations aufs_aop = { + .readpage = aufs_readpage, +#ifdef CONFIG_AUFS_DEBUG + .writepage = aufs_writepage, + .sync_page = aufs_sync_page, + //.writepages = aufs_writepages, + .set_page_dirty = aufs_set_page_dirty, + //.readpages = aufs_readpages, + .prepare_write = aufs_prepare_write, + .commit_write = aufs_commit_write, + .write_begin = aufs_write_begin, + .write_end = aufs_write_end, + //.bmap = aufs_bmap, + .invalidatepage = aufs_invalidatepage, + .releasepage = aufs_releasepage, + .direct_IO = aufs_direct_IO, + .get_xip_page = aufs_get_xip_page, + //.migratepage = aufs_migratepage +#endif /* CONFIG_AUFS_DEBUG */ +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/file.h linux-2.6.25.4-unionfs/fs/aufs/file.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/file.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/file.h 2008-05-25 11:58:53 +0300 @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * file operations + * + * $Id: file.h,v 1.3 2008/05/19 01:48:04 sfjro Exp $ + */ + +#ifndef __AUFS_FILE_H__ +#define __AUFS_FILE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "dentry.h" +#include "misc.h" +#include "super.h" + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +struct au_hfile { + struct file *hf_file; + struct au_branch *hf_br; +}; + +struct au_vdir; +struct au_finfo { + atomic_t fi_generation; + + struct au_rwsem fi_rwsem; + struct au_hfile *fi_hfile; + aufs_bindex_t fi_bstart, fi_bend; + + union { + struct vm_operations_struct *fi_h_vm_ops; + struct au_vdir *fi_vdir_cache; + }; +}; + +/* ---------------------------------------------------------------------- */ + +/* file.c */ +extern struct address_space_operations aufs_aop; +unsigned int au_file_roflags(unsigned int flags); +struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, + struct file *file); +int au_do_open(struct inode *inode, struct file *file, + int (*open)(struct file *file, int flags)); +int au_reopen_nondir(struct file *file); +int au_ready_to_write(struct file *file, loff_t len); +int au_reval_and_lock_finfo(struct file *file, int (*reopen)(struct file *file), + int wlock, int locked); + +/* f_op.c */ +extern struct file_operations aufs_file_fop; +int aufs_flush(struct file *file, fl_owner_t id); + +/* finfo.c */ +struct au_finfo *au_fi(struct file *file); +struct au_branch *au_fbr(struct file *file, aufs_bindex_t bindex); +struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex); + +void au_hfput(struct au_hfile *hf); +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, + struct file *h_file); + +void au_finfo_fin(struct file *file); +int au_finfo_init(struct file *file); + +#ifdef CONFIG_AUFS_ROBR +/* robr.c */ +struct file *au_robr_safe_file(struct vm_area_struct *vma); +void au_robr_reset_file(struct vm_area_struct *vma, struct file *file); +#else +static inline struct file *au_robr_safe_file(struct vm_area_struct *vma) +{ + struct file *file; + + file = vma->vm_file; + if (file->private_data && au_test_aufs(file->f_dentry->d_sb)) + return file; + return NULL; +} + +static inline +void au_robr_reset_file(struct vm_area_struct *vma, struct file *file) +{ + vma->vm_file = file; + //smp_mb(); /* flush vm_file */ +} +#endif /* CONFIG_AUFS_ROBR */ + +/* ---------------------------------------------------------------------- */ + +//todo: memory barrier? +static inline au_gen_t au_figen(struct file *f) +{ + return atomic_read(&au_fi(f)->fi_generation); +} + +static inline int au_test_mmapped(struct file *f) +{ + return !!(au_fi(f)->fi_h_vm_ops); +} + +static inline int au_test_aufs_file(struct file *f) +{ + return !(f->f_dentry->d_inode->i_mode + & (S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK)); +} + +/* ---------------------------------------------------------------------- */ + +#if !defined(CONFIG_AUFS_MODULE) || defined(CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH) +int au_store_fmode_exec(struct nameidata *nd, struct inode *inode); + +static inline int au_deny_write_access(struct file *h_file) +{ + LKTRTrace("%.*s\n", AuDLNPair(h_file->f_dentry)); + return deny_write_access(h_file); +} + +static inline void au_allow_write_access(struct file *h_file) +{ + allow_write_access(h_file); +} + +#else + +static inline int au_store_fmode_exec(struct nameidata *nd, struct inode *inode) +{ + /* nothing */ + return 0; +} + +static inline int au_deny_write_access(struct file *h_file) +{ + /* nothing */ + return 0; +} + +static inline void au_allow_write_access(struct file *h_file) +{ + /* nothing */ +} +#endif /* CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH */ + +/* ---------------------------------------------------------------------- */ + +/* + * fi_read_lock, fi_write_lock, + * fi_read_unlock, fi_write_unlock, fi_downgrade_lock + */ +AuSimpleRwsemFuncs(fi, struct file *f, au_fi(f)->fi_rwsem); + +/* to debug easier, do not make them inlined functions */ +#define FiMustReadLock(f) do { \ + SiMustAnyLock((f)->f_dentry->d_sb); \ + AuRwMustReadLock(&au_fi(f)->fi_rwsem); \ +} while (0) + +#define FiMustWriteLock(f) do { \ + SiMustAnyLock((f)->f_dentry->d_sb); \ + AuRwMustWriteLock(&au_fi(f)->fi_rwsem); \ +} while (0) + +#define FiMustAnyLock(f) do { \ + SiMustAnyLock((f)->f_dentry->d_sb); \ + AuRwMustAnyLock(&au_fi(f)->fi_rwsem); \ +} while (0) + +#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) + +/* ---------------------------------------------------------------------- */ + +// hard/soft set +static inline aufs_bindex_t au_fbstart(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_bstart; +} + +static inline aufs_bindex_t au_fbend(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_bend; +} + +static inline struct au_vdir *au_fvdir_cache(struct file *file) +{ + FiMustAnyLock(file); + return au_fi(file)->fi_vdir_cache; +} + +static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(au_sbend(file->f_dentry->d_sb) < bindex); + au_fi(file)->fi_bstart = bindex; +} + +static inline void au_set_fbend(struct file *file, aufs_bindex_t bindex) +{ + FiMustWriteLock(file); + AuDebugOn(au_sbend(file->f_dentry->d_sb) < bindex + || bindex < au_fbstart(file)); + au_fi(file)->fi_bend = bindex; +} + +static inline void au_set_fvdir_cache(struct file *file, + struct au_vdir *vdir_cache) +{ + FiMustWriteLock(file); + AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode) + || (au_fi(file)->fi_vdir_cache && vdir_cache)); + au_fi(file)->fi_vdir_cache = vdir_cache; +} + +static inline void au_update_figen(struct file *file) +{ + atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); + //smp_mb(); /* atomic_set */ +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_FILE_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/finfo.c linux-2.6.25.4-unionfs/fs/aufs/finfo.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/finfo.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/finfo.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * file private data + * + * $Id: finfo.c,v 1.2 2008/04/21 01:35:14 sfjro Exp $ + */ + +#include "aufs.h" + +struct au_finfo *au_fi(struct file *file) +{ + struct au_finfo *finfo = file->private_data; + AuDebugOn(!finfo + || !finfo->fi_hfile + || (0 < finfo->fi_bend + && (/* au_sbi(file->f_dentry->d_sb)->si_bend + < finfo->fi_bend + || */ finfo->fi_bend < finfo->fi_bstart))); + return finfo; +} + +struct au_branch *au_fbr(struct file *file, aufs_bindex_t bindex) +{ + struct au_finfo *finfo = au_fi(file); + struct au_hfile *hf; + + FiMustAnyLock(file); + AuDebugOn(!finfo + || finfo->fi_bstart < 0 + || bindex < finfo->fi_bstart + || finfo->fi_bend < bindex); + hf = finfo->fi_hfile + bindex; + AuDebugOn(hf->hf_br && au_br_count(hf->hf_br) <= 0); + return hf->hf_br; +} + +struct file *au_h_fptr(struct file *file, aufs_bindex_t bindex) +{ + struct au_finfo *finfo = au_fi(file); + struct au_hfile *hf; + + FiMustAnyLock(file); + AuDebugOn(!finfo + || finfo->fi_bstart < 0 + || bindex < finfo->fi_bstart + || finfo->fi_bend < bindex); + hf = finfo->fi_hfile + bindex; + AuDebugOn(hf->hf_file + && file_count(hf->hf_file) <= 0 + && au_br_count(hf->hf_br) <= 0); + return hf->hf_file; +} + +void au_hfput(struct au_hfile *hf) +{ + if (hf->hf_file->f_mode & FMODE_EXEC) + au_allow_write_access(hf->hf_file); + fput(hf->hf_file); + hf->hf_file = NULL; + AuDebugOn(!hf->hf_br); + au_br_put(hf->hf_br); + hf->hf_br = NULL; +} + +void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) +{ + struct au_finfo *finfo = au_fi(file); + struct au_hfile *hf; + + FiMustWriteLock(file); + AuDebugOn(!finfo + || finfo->fi_bstart < 0 + || bindex < finfo->fi_bstart + || finfo->fi_bend < bindex); + AuDebugOn(val && file_count(val) <= 0); + hf = finfo->fi_hfile + bindex; + AuDebugOn(val && hf->hf_file); + if (hf->hf_file) + au_hfput(hf); + if (val) { + hf->hf_file = val; + hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); + } +} + +void au_finfo_fin(struct file *file) +{ + struct au_finfo *finfo; + struct dentry *dentry; + aufs_bindex_t bindex, bend; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + SiMustAnyLock(dentry->d_sb); + + fi_write_lock(file); + bend = au_fbend(file); + bindex = au_fbstart(file); + if (bindex >= 0) + for (; bindex <= bend; bindex++) + au_set_h_fptr(file, bindex, NULL); + + finfo = au_fi(file); +#ifdef CONFIG_AUFS_DEBUG + if (finfo->fi_bstart >= 0) { + bend = au_fbend(file); + for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) { + struct au_hfile *hf; + hf = finfo->fi_hfile + bindex; + AuDebugOn(hf->hf_file || hf->hf_br); + } + } +#endif + + kfree(finfo->fi_hfile); + fi_write_unlock(file); + au_cache_free_finfo(finfo); + //file->private_data = NULL; +} + +int au_finfo_init(struct file *file) +{ + struct au_finfo *finfo; + struct dentry *dentry; + union { + void *p; + unsigned long ul; + } u; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + AuDebugOn(!dentry->d_inode); + + finfo = au_cache_alloc_finfo(); + if (finfo) { + finfo->fi_hfile = kcalloc(au_sbend(dentry->d_sb) + 1, + sizeof(*finfo->fi_hfile), GFP_KERNEL); + if (finfo->fi_hfile) { + au_rw_init_wlock(&finfo->fi_rwsem); + finfo->fi_bstart = -1; + finfo->fi_bend = -1; + atomic_set(&finfo->fi_generation, au_digen(dentry)); + //smp_mb(); /* atomic_set */ + + /* + * a dirty trick for handling FMODE_EXEC and + * deny_write_access(). + * because FMODE_EXEC flag is not passed to + * f_op->open(), + * aufs set it to file->private_data temporary in lookup + * or dentry revalidation operations. + * restore the flag to f_mode here. + */ + u.p = file->private_data; + if (u.ul & FMODE_EXEC) { + file->f_mode |= FMODE_EXEC; + smp_mb(); /* flush f_mode */ + } + + file->private_data = finfo; + return 0; /* success */ + } + au_cache_free_finfo(finfo); + } + + AuTraceErr(-ENOMEM); + return -ENOMEM; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/f_op.c linux-2.6.25.4-unionfs/fs/aufs/f_op.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/f_op.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/f_op.c 2008-05-25 11:59:38 +0300 @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * file and vm operations + * + * $Id: f_op.c,v 1.5 2008/05/19 01:48:04 sfjro Exp $ + */ + +//#include +#include +//#include +#include +//#include +#include "aufs.h" + +/* common function to regular file and dir */ +int aufs_flush(struct file *file, fl_owner_t id) +{ + int err; + struct dentry *dentry; + aufs_bindex_t bindex, bend; + + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + // aufs_read_lock_file() + si_noflush_read_lock(dentry->d_sb); + fi_read_lock(file); + di_read_lock_child(dentry, AuLock_IW); + + err = 0; + bend = au_fbend(file); + for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { + struct file *h_file; + h_file = au_h_fptr(file, bindex); + if (h_file && h_file->f_op && h_file->f_op->flush) { + err = h_file->f_op->flush(h_file, id); + if (!err) + au_update_fuse_h_inode + (h_file->f_vfsmnt, h_file->f_dentry); + /*ignore*/ + } + } + au_cpup_attr_timesizes(dentry->d_inode); + + di_read_unlock(dentry, AuLock_IW); + fi_read_unlock(file); + si_read_unlock(dentry->d_sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int do_open_nondir(struct file *file, int flags) +{ + int err; + aufs_bindex_t bindex; + struct super_block *sb; + struct file *h_file; + struct dentry *dentry; + struct inode *inode; + struct au_finfo *finfo; + + dentry = file->f_dentry; + LKTRTrace("%.*s, flags 0%o\n", AuDLNPair(dentry), flags); + FiMustWriteLock(file); + inode = dentry->d_inode; + AuDebugOn(!inode || S_ISDIR(inode->i_mode)); + + err = 0; + finfo = au_fi(file); + finfo->fi_h_vm_ops = NULL; + sb = dentry->d_sb; + bindex = au_dbstart(dentry); + AuDebugOn(!au_h_dptr(dentry, bindex)->d_inode); + /* O_TRUNC is processed already */ + BUG_ON(au_test_ro(sb, bindex, inode) && (flags & O_TRUNC)); + + h_file = au_h_open(dentry, bindex, flags, file); + //if (LktrCond) {fput(h_file); au_br_put(au_sbr(dentry->d_sb, bindex)); + //h_file = ERR_PTR(-1);} + if (!IS_ERR(h_file)) { + au_set_fbstart(file, bindex); + au_set_fbend(file, bindex); + au_set_h_fptr(file, bindex, h_file); + au_update_figen(file); + //file->f_ra = h_file->f_ra; //?? + //AuDbgFile(file); + return 0; /* success */ + } + err = PTR_ERR(h_file); + AuTraceErr(err); + return err; +} + +static int aufs_open_nondir(struct inode *inode, struct file *file) +{ + return au_do_open(inode, file, do_open_nondir); +} + +static int aufs_release_nondir(struct inode *inode, struct file *file) +{ + struct super_block *sb = file->f_dentry->d_sb; + + LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(file->f_dentry)); + + si_noflush_read_lock(sb); + au_finfo_fin(file); + si_read_unlock(sb); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + ssize_t err; + struct dentry *dentry; + struct file *h_file; + struct super_block *sb; + struct inode *h_inode; + + dentry = file->f_dentry; + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(dentry), (unsigned long)count, *ppos); + //AuDbgDentry(dentry); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0, + /*locked*/0); + //if (LktrCond) {fi_read_unlock(file); err = -1;} + if (unlikely(err)) + goto out; + + /* support LSM and notify */ + h_file = au_h_fptr(file, au_fbstart(file)); + h_inode = h_file->f_dentry->d_inode; + err = vfsub_read_u(h_file, buf, count, ppos, + au_opt_test_dlgt(au_mntflags(sb))); + //file->f_ra = h_file->f_ra; //?? + fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); + + fi_read_unlock(file); + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +static ssize_t aufs_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t err; + struct dentry *dentry, *parent; + struct inode *inode, *dir; + struct super_block *sb; + unsigned int mnt_flags; + struct file *h_file; + char __user *buf = (char __user *)ubuf; + struct au_hin_ignore ign; + struct vfsub_args vargs; + + dentry = file->f_dentry; + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(dentry), (unsigned long)count, *ppos); + + inode = dentry->d_inode; + mutex_lock(&inode->i_mutex); + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1, + /*locked*/1); + //if (LktrCond) {fi_write_unlock(file); err = -1;} + if (unlikely(err)) + goto out; + err = au_ready_to_write(file, -1); + //if (LktrCond) err = -1; + if (unlikely(err)) + goto out_unlock; + + /* support LSM and notify */ + mnt_flags = au_mntflags(sb); + vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(mnt_flags), 0); + h_file = au_h_fptr(file, au_fbstart(file)); + if (!au_opt_test(mnt_flags, UDBA_INOTIFY)) + err = vfsub_write_u(h_file, buf, count, ppos, &vargs); + else { + parent = dget_parent(dentry); + dir = parent->d_inode; + ii_read_lock_parent(dir); + vfsub_ign_hinode(&vargs, IN_MODIFY, + au_hi(dir, au_fbstart(file))); + err = vfsub_write_u(h_file, buf, count, ppos, &vargs); + ii_read_unlock(dir); + dput(parent); + } + ii_write_lock_child(inode); + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + + out_unlock: + fi_write_unlock(file); + out: + si_read_unlock(sb); + mutex_unlock(&inode->i_mutex); + AuTraceErr(err); + return err; +} + +#ifdef CONFIG_AUFS_SPLICE_PATCH +static int au_test_loopback(void) +{ + const char c = current->comm[4]; + /* true if a kernel thread named 'loop[0-9].*' accesses a file */ + const int loopback = (current->mm == NULL + && '0' <= c && c <= '9' + && strncmp(current->comm, "loop", 4) == 0); + return loopback; +} + +static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + ssize_t err; + struct file *h_file; + struct dentry *dentry; + struct super_block *sb; + + dentry = file->f_dentry; + LKTRTrace("%.*s, pos %Ld, len %lu\n", + AuDLNPair(dentry), *ppos, (unsigned long)len); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0, + /*locked*/0); + if (unlikely(err)) + goto out; + + err = -EINVAL; + /* support LSM and notify */ + h_file = au_h_fptr(file, au_fbstart(file)); + if (/* unlikely */(au_test_loopback())) { + file->f_mapping = h_file->f_mapping; + smp_mb(); /* unnecessary? */ + } + err = vfsub_splice_to(h_file, ppos, pipe, len, flags, + au_opt_test_dlgt(au_mntflags(sb))); + //file->f_ra = h_file->f_ra; //?? + fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); + fi_read_unlock(file); + + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +static ssize_t +aufs_splice_write(struct pipe_inode_info *pipe, struct file *file, loff_t *ppos, + size_t len, unsigned int flags) +{ + ssize_t err; + struct dentry *dentry; + struct inode *inode, *h_inode; + struct super_block *sb; + struct file *h_file; + struct au_hin_ignore ign; + struct vfsub_args vargs; + unsigned int mnt_flags; + + dentry = file->f_dentry; + LKTRTrace("%.*s, len %lu, pos %Ld\n", + AuDLNPair(dentry), (unsigned long)len, *ppos); + + inode = dentry->d_inode; + mutex_lock(&inode->i_mutex); + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1, + /*locked*/1); + //if (LktrCond) {fi_write_unlock(file); err = -1;} + if (unlikely(err)) + goto out; + err = au_ready_to_write(file, -1); + //if (LktrCond) err = -1; + if (unlikely(err)) + goto out_unlock; + + /* support LSM and notify */ + mnt_flags = au_mntflags(sb); + vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(mnt_flags), 0); + h_file = au_h_fptr(file, au_fbstart(file)); + h_inode = h_file->f_dentry->d_inode; + /* current vfs_splice_from() doesn't fire up the inotify event */ + if (1 || !au_opt_test(mnt_flags, UDBA_INOTIFY)) + err = vfsub_splice_from(pipe, h_file, ppos, len, flags, &vargs); + else { + //struct dentry *parent = dget_parent(dentry); + //vfsub_ign_hinode(&vargs, IN_MODIFY, + //au_hi(parent->d_inode, au_fbstart(file)); + err = vfsub_splice_from(pipe, h_file, ppos, len, flags, &vargs); + //dput(parent); + } + ii_write_lock_child(inode); + au_cpup_attr_timesizes(inode); + ii_write_unlock(inode); + + out_unlock: + fi_write_unlock(file); + out: + si_read_unlock(sb); + mutex_unlock(&inode->i_mutex); + AuTraceErr(err); + return err; +} +#endif /* CONFIG_AUFS_SPLICE_PATCH */ + +/* ---------------------------------------------------------------------- */ + +static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + int err; + struct dentry *dentry; + struct file *file, *h_file; + struct inode *inode; + static DECLARE_WAIT_QUEUE_HEAD(wq); + struct au_finfo *finfo; + //struct page *page; + + AuTraceEnter(); + AuDebugOn(!vma || !vma->vm_file); + //todo: non-robr mode, user vm_file as it is. + wait_event(wq, (file = au_robr_safe_file(vma))); + AuDebugOn(!au_test_aufs(file->f_dentry->d_sb)); + dentry = file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + inode = dentry->d_inode; + AuDebugOn(!S_ISREG(inode->i_mode)); + + /* do not revalidate, nor si lock */ + finfo = au_fi(file); + h_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file; + AuDebugOn(!h_file || !au_test_mmapped(file)); + fi_write_lock(file); + vma->vm_file = h_file; + err = finfo->fi_h_vm_ops->fault(vma, vmf); + //file->f_ra = h_file->f_ra; //?? + au_robr_reset_file(vma, file); + fi_write_unlock(file); + wake_up(&wq); + + if (!(err & VM_FAULT_ERROR)) { + //page->mapping = file->f_mapping; + //get_page(page); + //file->f_mapping = h_file->f_mapping; + //touch_atime(NULL, dentry); + //inode->i_atime = h_file->f_dentry->d_inode->i_atime; + } + AuTraceErr(err); + //AuDbg("err %d\n", err); + return err; +} + +static struct vm_operations_struct aufs_vm_ops = { + //.open = aufs_vmaopen, + //.close = aufs_vmaclose, + .fault = aufs_fault, +}; + +/* ---------------------------------------------------------------------- */ + +static struct vm_operations_struct *au_vm_ops(struct file *h_file, + struct vm_area_struct *vma) +{ + struct vm_operations_struct *vm_ops; + int err; + + AuTraceEnter(); + + if (!au_test_nfs(h_file->f_vfsmnt->mnt_sb)) + err = h_file->f_op->mmap(h_file, vma); + else { + lockdep_off(); + err = h_file->f_op->mmap(h_file, vma); + lockdep_on(); + } + vm_ops = ERR_PTR(err); + if (unlikely(err)) + goto out; + vm_ops = vma->vm_ops; + err = do_munmap(current->mm, vma->vm_start, + vma->vm_end - vma->vm_start); + if (unlikely(err)) { + AuIOErr("failed internal unmapping %.*s, %d\n", + AuDLNPair(h_file->f_dentry), err); + vm_ops = ERR_PTR(-EIO); + } + + out: + AuTraceErrPtr(vm_ops); + return vm_ops; +} + +static int aufs_mmap(struct file *file, struct vm_area_struct *vma) +{ + int err, wlock, mmapped; + struct dentry *dentry; + struct super_block *sb; + struct file *h_file; + struct vm_operations_struct *vm_ops; + + dentry = file->f_dentry; + LKTRTrace("%.*s, %lx, len %lu\n", + AuDLNPair(dentry), vma->vm_start, + vma->vm_end - vma->vm_start); + AuDebugOn(!S_ISREG(dentry->d_inode->i_mode)); + AuDebugOn(down_write_trylock(&vma->vm_mm->mmap_sem)); + + mmapped = au_test_mmapped(file); /* can be harmless race condition */ + wlock = !!(file->f_mode & FMODE_WRITE); + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, + wlock | !mmapped, /*locked*/0); + //err = -1; + if (unlikely(err)) + goto out; + + if (wlock) { + err = au_ready_to_write(file, -1); + //err = -1; + if (unlikely(err)) + goto out_unlock; + } + + h_file = au_h_fptr(file, au_fbstart(file)); + if (unlikely(au_test_fuse(h_file->f_dentry->d_sb))) { + /* + * by this assignment, f_mapping will differs from aufs inode + * i_mapping. + * if someone else mixes the use of f_dentry->d_inode and + * f_mapping->host, then a problem may arise. + */ + file->f_mapping = h_file->f_mapping; + } + + if (0 && h_file->f_op->mmap == generic_file_mmap) { + err = generic_file_mmap(file, vma); /* instead of h_file */ + if (unlikely(err)) + goto out_unlock; + au_fi(file)->fi_h_vm_ops = vma->vm_ops; + } else { + vm_ops = NULL; + if (!mmapped) { + vm_ops = au_vm_ops(h_file, vma); + err = PTR_ERR(vm_ops); + if (IS_ERR(vm_ops)) + goto out_unlock; + } + + err = generic_file_mmap(file, vma); + if (unlikely(err)) + goto out_unlock; + vma->vm_ops = &aufs_vm_ops; + /* test again */ + if (!au_test_mmapped(file)) { + FiMustWriteLock(file); + au_fi(file)->fi_h_vm_ops = vm_ops; + } + } + + file_accessed(h_file); + au_update_fuse_h_inode(h_file->f_vfsmnt, h_file->f_dentry); /*ignore*/ + fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); + + out_unlock: + if (!wlock && mmapped) + fi_read_unlock(file); + else + fi_write_unlock(file); + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static unsigned int aufs_poll(struct file *file, poll_table *wait) +{ + unsigned int mask; + struct file *h_file; + int err; + struct dentry *dentry; + struct super_block *sb; + + dentry = file->f_dentry; + LKTRTrace("%.*s, wait %p\n", AuDLNPair(dentry), wait); + AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)); + + /* We should pretend an error happened. */ + mask = POLLERR /* | POLLIN | POLLOUT */; + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0, + /*locked*/0); + //err = -1; + if (unlikely(err)) + goto out; + + /* it is not an error of hidden_file has no operation */ + mask = DEFAULT_POLLMASK; + h_file = au_h_fptr(file, au_fbstart(file)); + if (h_file->f_op && h_file->f_op->poll) + mask = h_file->f_op->poll(h_file, wait); + fi_read_unlock(file); + + out: + si_read_unlock(sb); + AuTraceErr((int)mask); + return mask; +} + +static int aufs_fsync_nondir(struct file *file, struct dentry *dentry, + int datasync) +{ + int err; + struct inode *inode; + struct file *h_file; + struct super_block *sb; + + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync); + inode = dentry->d_inode; + + IMustLock(file->f_mapping->host); + if (unlikely(inode != file->f_mapping->host)) { + mutex_unlock(&file->f_mapping->host->i_mutex); + mutex_lock(&inode->i_mutex); + } + IMustLock(inode); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = 0; //-EBADF; // posix? + if (unlikely(!(file->f_mode & FMODE_WRITE))) + goto out; + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/1, + /*locked*/1); + //err = -1; + if (unlikely(err)) + goto out; + err = au_ready_to_write(file, -1); + //err = -1; + if (unlikely(err)) + goto out_unlock; + + err = -EINVAL; + h_file = au_h_fptr(file, au_fbstart(file)); + if (h_file->f_op && h_file->f_op->fsync) { + ii_write_lock_child(inode); + mutex_lock_nested(&h_file->f_dentry->d_inode->i_mutex, + AuLsc_I_CHILD); + err = h_file->f_op->fsync(h_file, h_file->f_dentry, datasync); + //err = -1; + if (!err) + au_update_fuse_h_inode(h_file->f_vfsmnt, + h_file->f_dentry); + au_cpup_attr_timesizes(inode); + mutex_unlock(&h_file->f_dentry->d_inode->i_mutex); + ii_write_unlock(inode); + } + + out_unlock: + fi_write_unlock(file); + out: + si_read_unlock(sb); + if (unlikely(inode != file->f_mapping->host)) { + mutex_unlock(&inode->i_mutex); + mutex_lock(&file->f_mapping->host->i_mutex); + } + AuTraceErr(err); + return err; +} + +static int aufs_fasync(int fd, struct file *file, int flag) +{ + int err; + struct file *h_file; + struct dentry *dentry; + struct super_block *sb; + + dentry = file->f_dentry; + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), flag); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + err = au_reval_and_lock_finfo(file, au_reopen_nondir, /*wlock*/0, + /*locked*/0); + //err = -1; + if (unlikely(err)) + goto out; + + h_file = au_h_fptr(file, au_fbstart(file)); + if (h_file->f_op && h_file->f_op->fasync) + err = h_file->f_op->fasync(fd, h_file, flag); + fi_read_unlock(file); + + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct file_operations aufs_file_fop = { + .read = aufs_read, + .write = aufs_write, + .poll = aufs_poll, + .mmap = aufs_mmap, + .open = aufs_open_nondir, + .flush = aufs_flush, + .release = aufs_release_nondir, + .fsync = aufs_fsync_nondir, + .fasync = aufs_fasync, +#ifdef CONFIG_AUFS_SPLICE_PATCH + .splice_write = aufs_splice_write, + .splice_read = aufs_splice_read, +#endif +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/hinode.h linux-2.6.25.4-unionfs/fs/aufs/hinode.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/hinode.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/hinode.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * lower (branch filesystem) inode and setting inotify + * + * $Id: hinode.h,v 1.3 2008/04/28 03:08:37 sfjro Exp $ + */ + +#ifndef __AUFS_HINODE_H__ +#define __AUFS_HINODE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +//#include "branch.h" +//#include "inode.h" +#include "super.h" +#include "vfsub.h" + +/* ---------------------------------------------------------------------- */ + +struct au_hinotify { +#ifdef CONFIG_AUFS_HINOTIFY + struct inotify_watch hin_watch; + struct inode *hin_aufs_inode; /* no get/put */ + + /* an array of atomic_t X au_hin_nignore */ + atomic_t hin_ignore[0]; +#endif +}; + +struct au_hinode { + struct inode *hi_inode; + aufs_bindex_t hi_id; +#ifdef CONFIG_AUFS_HINOTIFY + struct au_hinotify *hi_notify; +#endif + + /* reference to the copied-up whiteout with get/put */ + struct dentry *hi_whdentry; +}; + +struct au_hin_ignore { +#ifdef CONFIG_AUFS_HINOTIFY + __u32 ign_events; + struct au_hinode *ign_hinode; +#endif +}; + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_HINOTIFY +static inline +void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val) +{ + hinode->hi_notify = val; +} + +/* hinotify.c */ +int au_hin_alloc(struct au_hinode *hinode, struct inode *inode, + struct inode *h_inode); +void au_hin_free(struct au_hinode *hinode); +void au_do_hdir_lock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex, unsigned int lsc); +void au_hdir_unlock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex); +struct dentry *au_hdir_lock_rename(struct dentry **h_parents, + struct inode **dirs, aufs_bindex_t bindex, + int issamedir); +void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs, + aufs_bindex_t bindex, int issamedir); +void au_reset_hinotify(struct inode *inode, unsigned int flags); + +void au_hin_ignore(struct au_hinode *hinode, __u32 events); +void au_hin_unignore(struct au_hinode *hinode, __u32 events); + +int __init au_inotify_init(void); +void au_inotify_fin(void); + +#else + +static inline +void au_hin_init(struct au_hinode *hinode, struct au_hinotify *val) +{ + /* empty */ +} + +static inline +int au_hin_alloc(struct au_hinode *hinode, struct inode *inode, + struct inode *h_inode) +{ + return -EOPNOTSUPP; +} + +static inline void au_hin_free(struct au_hinode *hinode) +{ + /* nothing */ +} + +static inline +void au_do_hdir_lock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex, unsigned int lsc) +{ + mutex_lock_nested(&h_dir->i_mutex, lsc); +} + +static inline +void au_hdir_unlock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex) +{ + mutex_unlock(&h_dir->i_mutex); +} + +static inline +struct dentry *au_hdir_lock_rename(struct dentry **h_parents, + struct inode **dirs, aufs_bindex_t bindex, + int issamedir) +{ + return vfsub_lock_rename(h_parents[0], h_parents[1]); +} + +static inline +void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs, + aufs_bindex_t bindex, int issamedir) +{ + vfsub_unlock_rename(h_parents[0], h_parents[1]); +} + +static inline void au_reset_hinotify(struct inode *inode, unsigned int flags) +{ + /* nothing */ +} + +static inline void au_hin_ignore(struct au_hinotify *hinotify, __u32 events) +{ + /* nothing */ +} + +static inline void au_hin_unignore(struct au_hinotify *hinotify, __u32 events) +{ + /* nothing */ +} + +static inline int au_inotify_init(void) +{ + return 0; +} + +#define au_inotify_fin() do {} while (0) +#endif /* CONFIG_AUFS_HINOTIFY */ + +/* ---------------------------------------------------------------------- */ + +/* + * au_hdir_lock, au_hdir2_lock + */ +#define AuLockFunc(name, lsc) \ +static inline \ +void name##_lock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex) \ +{ au_do_hdir_lock(h_dir, dir, bindex, AuLsc_I_##lsc); } + +AuLockFunc(au_hdir, PARENT); +AuLockFunc(au_hdir2, PARENT2); + +#undef AuLockFunc + +/* ---------------------------------------------------------------------- */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_HINODE_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/hin_or_dlgt.c linux-2.6.25.4-unionfs/fs/aufs/hin_or_dlgt.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/hin_or_dlgt.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/hin_or_dlgt.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,708 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sub-routines for vfs in hinotify or dlgt mode + * + * $Id: hin_or_dlgt.c,v 1.2 2008/04/21 02:00:37 sfjro Exp $ + */ +// I'm going to slightly mad + +#include +#include "aufs.h" + +#if !defined(CONFIG_AUFS_HINOTIFY) && !defined(CONFIG_AUFS_DLGT) +#error mis-configuraion or Makefile +#endif + +/* ---------------------------------------------------------------------- */ + +struct permission_args { + int *errp; + struct inode *inode; + int mask; + struct nameidata *nd; +}; + +static void call_permission(void *args) +{ + struct permission_args *a = args; + *a->errp = do_vfsub_permission(a->inode, a->mask, a->nd); +} + +int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd, + int dlgt) +{ + if (!dlgt) + return do_vfsub_permission(inode, mask, nd); + else { + int err, wkq_err; + struct permission_args args = { + .errp = &err, + .inode = inode, + .mask = mask, + .nd = nd + }; + wkq_err = au_wkq_wait(call_permission, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +/* ---------------------------------------------------------------------- */ + +struct create_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + int mode; + struct nameidata *nd; +}; + +static void call_create(void *args) +{ + struct create_args *a = args; + *a->errp = do_vfsub_create(a->dir, a->dentry, a->mode, a->nd); +} + +int vfsub_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd, int dlgt) +{ + if (!dlgt) + return do_vfsub_create(dir, dentry, mode, nd); + else { + int err, wkq_err; + struct create_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .mode = mode, + .nd = nd + }; + wkq_err = au_wkq_wait(call_create, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct symlink_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + const char *symname; + int mode; +}; + +static void call_symlink(void *args) +{ + struct symlink_args *a = args; + *a->errp = do_vfsub_symlink(a->dir, a->dentry, a->symname, a->mode); +} + +int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname, + int mode, int dlgt) +{ + if (!dlgt) + return do_vfsub_symlink(dir, dentry, symname, mode); + else { + int err, wkq_err; + struct symlink_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .symname = symname, + .mode = mode + }; + wkq_err = au_wkq_wait(call_symlink, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct mknod_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + int mode; + dev_t dev; +}; + +static void call_mknod(void *args) +{ + struct mknod_args *a = args; + *a->errp = do_vfsub_mknod(a->dir, a->dentry, a->mode, a->dev); +} + +int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev, + int dlgt) +{ + if (!dlgt) + return do_vfsub_mknod(dir, dentry, mode, dev); + else { + int err, wkq_err; + struct mknod_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .mode = mode, + .dev = dev + }; + wkq_err = au_wkq_wait(call_mknod, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct mkdir_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + int mode; +}; + +static void call_mkdir(void *args) +{ + struct mkdir_args *a = args; + *a->errp = do_vfsub_mkdir(a->dir, a->dentry, a->mode); +} + +int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt) +{ + if (!dlgt) + return do_vfsub_mkdir(dir, dentry, mode); + else { + int err, wkq_err; + struct mkdir_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .mode = mode + }; + wkq_err = au_wkq_wait(call_mkdir, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +/* ---------------------------------------------------------------------- */ + +struct link_args { + int *errp; + struct inode *dir; + struct dentry *src_dentry, *dentry; +}; + +static void call_link(void *args) +{ + struct link_args *a = args; + *a->errp = do_vfsub_link(a->src_dentry, a->dir, a->dentry); +} + +int vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry, int dlgt) +{ + if (!dlgt) + return do_vfsub_link(src_dentry, dir, dentry); + else { + int err, wkq_err; + struct link_args args = { + .errp = &err, + .src_dentry = src_dentry, + .dir = dir, + .dentry = dentry + }; + wkq_err = au_wkq_wait(call_link, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct rename_args { + int *errp; + struct inode *src_dir, *dir; + struct dentry *src_dentry, *dentry; + struct vfsub_args *vargs; +}; + +static void call_rename(void *args) +{ + struct rename_args *a = args; + vfsub_ignore(a->vargs); + *a->errp = do_vfsub_rename(a->src_dir, a->src_dentry, a->dir, + a->dentry); + if (unlikely(*a->errp)) + vfsub_unignore(a->vargs); +} + +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs) +{ + int err; + + if (!vfsub_ftest(vargs->flags, DLGT)) { + vfsub_ignore(vargs); + err = do_vfsub_rename(src_dir, src_dentry, dir, dentry); + if (unlikely(err)) + vfsub_unignore(vargs); + } else { + int wkq_err; + struct rename_args args = { + .errp = &err, + .src_dir = src_dir, + .src_dentry = src_dentry, + .dir = dir, + .dentry = dentry, + .vargs = vargs + }; + wkq_err = au_wkq_wait(call_rename, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + } + return err; +} + +struct rmdir_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + struct vfsub_args *vargs; +}; + +static void call_rmdir(void *args) +{ + struct rmdir_args *a = args; + vfsub_ignore(a->vargs); + *a->errp = do_vfsub_rmdir(a->dir, a->dentry); + if (unlikely(*a->errp)) + vfsub_unignore(a->vargs); +} + +int vfsub_rmdir(struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs) +{ + int err; + + if (!vfsub_ftest(vargs->flags, DLGT)) { + vfsub_ignore(vargs); + err = do_vfsub_rmdir(dir, dentry); + if (unlikely(err)) + vfsub_unignore(vargs); + } else { + int wkq_err; + struct rmdir_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .vargs = vargs + }; + wkq_err = au_wkq_wait(call_rmdir, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + } + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct read_args { + ssize_t *errp; + struct file *file; + union { + void *kbuf; + char __user *ubuf; + }; + size_t count; + loff_t *ppos; +}; + +static void call_read_k(void *args) +{ + struct read_args *a = args; + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(a->file->f_dentry), (unsigned long)a->count, + *a->ppos); + *a->errp = do_vfsub_read_k(a->file, a->kbuf, a->count, a->ppos); +} + +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos, int dlgt) +{ + if (!dlgt) + return do_vfsub_read_u(file, ubuf, count, ppos); + else { + int wkq_err; + ssize_t err, read; + struct read_args args = { + .errp = &err, + .file = file, + .count = count, + .ppos = ppos + }; + + if (unlikely(!count)) + return 0; + + /* + * workaround an application bug. + * generally, read(2) or write(2) may return the value shorter + * than requested. But many applications don't support it, + * for example bash. + */ + err = -ENOMEM; + if (args.count > PAGE_SIZE) + args.count = PAGE_SIZE; + args.kbuf = kmalloc(args.count, GFP_TEMPORARY); + if (unlikely(!args.kbuf)) + goto out; + + read = 0; + do { + wkq_err = au_wkq_wait(call_read_k, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + if (unlikely(err > 0 + && copy_to_user(ubuf, args.kbuf, err))) { + err = -EFAULT; + goto out_free; + } else if (!err) + break; + else if (unlikely(err < 0)) + goto out_free; + count -= err; + /* do not read too much because of file i/o pointer */ + if (count < args.count) + args.count = count; + ubuf += err; + read += err; + } while (count); + smp_mb(); /* flush ubuf */ + err = read; + + out_free: + kfree(args.kbuf); + out: + return err; + } +} + +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + int dlgt) +{ + if (!dlgt) + return do_vfsub_read_k(file, kbuf, count, ppos); + else { + ssize_t err; + int wkq_err; + struct read_args args = { + .errp = &err, + .file = file, + .count = count, + .ppos = ppos + }; + args.kbuf = kbuf; + wkq_err = au_wkq_wait(call_read_k, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct write_args { + ssize_t *errp; + struct file *file; + union { + void *kbuf; + const char __user *ubuf; + }; + void *buf; + size_t count; + loff_t *ppos; + struct vfsub_args *vargs; +}; + +static void call_write_k(void *args) +{ + struct write_args *a = args; + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(a->file->f_dentry), (unsigned long)a->count, + *a->ppos); + vfsub_ignore(a->vargs); + *a->errp = do_vfsub_write_k(a->file, a->kbuf, a->count, a->ppos); + if (unlikely(*a->errp < 0)) + vfsub_unignore(a->vargs); +} + +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos, struct vfsub_args *vargs) +{ + ssize_t err; + + if (!vfsub_ftest(vargs->flags, DLGT)) { + vfsub_ignore(vargs); + err = do_vfsub_write_u(file, ubuf, count, ppos); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + } else { + ssize_t written; + int wkq_err; + struct write_args args = { + .errp = &err, + .file = file, + .count = count, + .ppos = ppos, + .vargs = vargs + }; + + if (unlikely(!count)) + return 0; + + /* + * workaround an application bug. + * generally, read(2) or write(2) may return the value shorter + * than requested. But many applications don't support it, + * for example bash. + */ + err = -ENOMEM; + if (args.count > PAGE_SIZE) + args.count = PAGE_SIZE; + args.kbuf = kmalloc(args.count, GFP_TEMPORARY); + if (unlikely(!args.kbuf)) + goto out; + + written = 0; + do { + if (unlikely(copy_from_user(args.kbuf, ubuf, + args.count))) { + err = -EFAULT; + goto out_free; + } + + wkq_err = au_wkq_wait(call_write_k, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + if (err > 0) { + count -= err; + if (count < args.count) + args.count = count; + ubuf += err; + written += err; + } else if (!err) + break; + else if (unlikely(err < 0)) + goto out_free; + } while (count); + err = written; + + out_free: + kfree(args.kbuf); + } + out: + return err; +} + +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + struct vfsub_args *vargs) +{ + ssize_t err; + + if (!vfsub_ftest(vargs->flags, DLGT)) { + vfsub_ignore(vargs); + err = do_vfsub_write_k(file, kbuf, count, ppos); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + } else { + int wkq_err; + struct write_args args = { + .errp = &err, + .file = file, + .count = count, + .ppos = ppos, + .vargs = vargs + }; + args.kbuf = kbuf; + wkq_err = au_wkq_wait(call_write_k, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + } + return err; +} + +struct readdir_args { + int *errp; + struct file *file; + filldir_t filldir; + void *arg; +}; + +static void call_readdir(void *args) +{ + struct readdir_args *a = args; + *a->errp = do_vfsub_readdir(a->file, a->filldir, a->arg); +} + +int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt) +{ + if (!dlgt) + return do_vfsub_readdir(file, filldir, arg); + else { + int err, wkq_err; + struct readdir_args args = { + .errp = &err, + .file = file, + .filldir = filldir, + .arg = arg + }; + wkq_err = au_wkq_wait(call_readdir, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +/* ---------------------------------------------------------------------- */ + +struct splice_to_args { + long *errp; + struct file *in; + loff_t *ppos; + struct pipe_inode_info *pipe; + size_t len; + unsigned int flags; +}; + +static void call_splice_to(void *args) +{ + struct splice_to_args *a = args; + *a->errp = do_vfsub_splice_to(a->in, a->ppos, a->pipe, a->len, + a->flags); +} + +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags, int dlgt) +{ + if (!dlgt) + return do_vfsub_splice_to(in, ppos, pipe, len, flags); + else { + long err; + int wkq_err; + struct splice_to_args args = { + .errp = &err, + .in = in, + .ppos = ppos, + .pipe = pipe, + .len = len, + .flags = flags + }; + wkq_err = au_wkq_wait(call_splice_to, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} + +struct splice_from_args { + long *errp; + struct pipe_inode_info *pipe; + struct file *out; + loff_t *ppos; + size_t len; + unsigned int flags; + struct vfsub_args *vargs; +}; + +static void call_splice_from(void *args) +{ + struct splice_from_args *a = args; + vfsub_ignore(a->vargs); + *a->errp = do_vfsub_splice_from(a->pipe, a->out, a->ppos, a->len, + a->flags); + if (unlikely(*a->errp < 0)) + vfsub_unignore(a->vargs); +} + +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags, + struct vfsub_args *vargs) +{ + long err; + + if (!vfsub_ftest(vargs->flags, DLGT)) { + vfsub_ignore(vargs); + err = do_vfsub_splice_from(pipe, out, ppos, len, flags); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + } else { + int wkq_err; + struct splice_from_args args = { + .errp = &err, + .pipe = pipe, + .out = out, + .ppos = ppos, + .len = len, + .flags = flags, + .vargs = vargs + }; + wkq_err = au_wkq_wait(call_splice_from, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + } + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct getattr_args { + int *errp; + struct vfsmount *mnt; + struct dentry *dentry; + struct kstat *st; +}; + +static void call_getattr(void *args) +{ + struct getattr_args *a = args; + *a->errp = do_vfsub_getattr(a->mnt, a->dentry, a->st); +} + +int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st, + int dlgt) +{ + if (!dlgt) + return do_vfsub_getattr(mnt, dentry, st); + else { + int err, wkq_err; + struct getattr_args args = { + .errp = &err, + .mnt = mnt, + .dentry = dentry, + .st = st + }; + wkq_err = au_wkq_wait(call_getattr, &args, /*dlgt*/1); + if (unlikely(wkq_err)) + err = wkq_err; + return err; + } +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/hinotify.c linux-2.6.25.4-unionfs/fs/aufs/hinotify.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/hinotify.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/hinotify.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,1053 @@ +/* + * Copyright (C) 2006-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * internal/hidden inotify handler + * + * $Id: hinotify.c,v 1.5 2008/05/19 01:49:11 sfjro Exp $ + */ + +#include "aufs.h" + +/* inotify events */ +static const __u32 AuInMask = (IN_MOVE | IN_DELETE | IN_CREATE + /* | IN_ACCESS */ + | IN_MODIFY | IN_ATTRIB + /* | IN_DELETE_SELF | IN_MOVE_SELF */ + ); +static struct inotify_handle *in_handle; + +/* the size of an array for ignore counter */ +static int au_hin_nignore; + +AuCacheFuncs(hinotify, AuCache_HINOTIFY); + +int au_hin_alloc(struct au_hinode *hinode, struct inode *inode, + struct inode *hidden_inode) +{ + int err, i; + struct au_hinotify *hin; + s32 wd; + + LKTRTrace("i%lu, hi%lu\n", inode->i_ino, hidden_inode->i_ino); + + err = -ENOMEM; + hin = au_cache_alloc_hinotify(); + if (hin) { + AuDebugOn(hinode->hi_notify); + hinode->hi_notify = hin; + hin->hin_aufs_inode = inode; + for (i = 0; i < au_hin_nignore; i++) + atomic_set(hin->hin_ignore + i, 0); + + inotify_init_watch(&hin->hin_watch); + wd = inotify_add_watch(in_handle, &hin->hin_watch, hidden_inode, + AuInMask); + if (wd >= 0) + return 0; /* success */ + + err = wd; + put_inotify_watch(&hin->hin_watch); + au_cache_free_hinotify(hin); + hinode->hi_notify = NULL; + } + + AuTraceErr(err); + return err; +} + +void au_hin_free(struct au_hinode *hinode) +{ + int err; + struct au_hinotify *hin; + + AuTraceEnter(); + + hin = hinode->hi_notify; + if (unlikely(hin)) { + err = 0; + if (atomic_read(&hin->hin_watch.count)) + err = inotify_rm_watch(in_handle, &hin->hin_watch); + if (unlikely(err)) + /* it means the watch is already removed */ + LKTRTrace("failed inotify_rm_watch() %d\n", err); + au_cache_free_hinotify(hin); + hinode->hi_notify = NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +static void ctl_hinotify(struct au_hinode *hinode, const __u32 mask) +{ + struct inode *h_inode; + struct inotify_watch *watch; + + h_inode = hinode->hi_inode; + LKTRTrace("hi%lu, sb %p, 0x%x\n", h_inode->i_ino, h_inode->i_sb, mask); + IMustLock(h_inode); + if (!hinode->hi_notify) + return; + + watch = &hinode->hi_notify->hin_watch; + /* struct inotify_handle is hidden */ + mutex_lock(&h_inode->inotify_mutex); + //mutex_lock(&watch->ih->mutex); + watch->mask = mask; + //mutex_unlock(&watch->ih->mutex); + mutex_unlock(&h_inode->inotify_mutex); + LKTRTrace("watch %p, mask %u\n", watch, watch->mask); +} + +#define suspend_hinotify(hi) ctl_hinotify(hi, 0) +#define resume_hinotify(hi) ctl_hinotify(hi, AuInMask) + +void au_do_hdir_lock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex, unsigned int lsc) +{ + struct au_hinode *hinode; + + LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc); + AuDebugOn(!S_ISDIR(dir->i_mode)); + hinode = au_ii(dir)->ii_hinode + bindex; + AuDebugOn(h_dir != hinode->hi_inode); + + mutex_lock_nested(&h_dir->i_mutex, lsc); + suspend_hinotify(hinode); +} + +void au_hdir_unlock(struct inode *h_dir, struct inode *dir, + aufs_bindex_t bindex) +{ + struct au_hinode *hinode; + + LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex); + AuDebugOn(!S_ISDIR(dir->i_mode)); + hinode = au_ii(dir)->ii_hinode + bindex; + AuDebugOn(h_dir != hinode->hi_inode); + + resume_hinotify(hinode); + mutex_unlock(&h_dir->i_mutex); +} + +struct dentry *au_hdir_lock_rename(struct dentry **h_parents, + struct inode **dirs, aufs_bindex_t bindex, + int issamedir) +{ + struct dentry *h_trap; + struct au_hinode *hinode; + + LKTRTrace("%.*s, %.*s\n", + AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1])); + + h_trap = vfsub_lock_rename(h_parents[0], h_parents[1]); + hinode = au_ii(dirs[0])->ii_hinode + bindex; + AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode); + suspend_hinotify(hinode); + if (!issamedir) { + hinode = au_ii(dirs[1])->ii_hinode + bindex; + AuDebugOn(h_parents[1]->d_inode != hinode->hi_inode); + suspend_hinotify(hinode); + } + + return h_trap; +} + +void au_hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs, + aufs_bindex_t bindex, int issamedir) +{ + struct au_hinode *hinode; + + LKTRTrace("%.*s, %.*s\n", + AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1])); + + hinode = au_ii(dirs[0])->ii_hinode + bindex; + AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode); + resume_hinotify(hinode); + if (!issamedir) { + hinode = au_ii(dirs[1])->ii_hinode + bindex; + AuDebugOn(h_parents[1]->d_inode != hinode->hi_inode); + resume_hinotify(hinode); + } + vfsub_unlock_rename(h_parents[0], h_parents[1]); +} + +void au_reset_hinotify(struct inode *inode, unsigned int flags) +{ + aufs_bindex_t bindex, bend; + struct inode *hi; + struct dentry *iwhdentry; + + LKTRTrace("i%lu, 0x%x\n", inode->i_ino, flags); + + bend = au_ibend(inode); + for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { + hi = au_h_iptr(inode, bindex); + if (hi) { + //mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); + iwhdentry = au_hi_wh(inode, bindex); + if (unlikely(iwhdentry)) + dget(iwhdentry); + igrab(hi); + au_set_h_iptr(inode, bindex, NULL, 0); + au_set_h_iptr(inode, bindex, igrab(hi), + flags & ~AuHi_XINO); + iput(hi); + dput(iwhdentry); + //mutex_unlock(&hi->i_mutex); + } + } +} + +/* ---------------------------------------------------------------------- */ + +/* cf. fsnotify_change() */ +__u32 vfsub_events_notify_change(struct iattr *ia) +{ + __u32 events; + const unsigned int amtime = (ATTR_ATIME | ATTR_MTIME); + + events = 0; + if ((ia->ia_valid & (ATTR_UID | ATTR_GID | ATTR_MODE)) + || (ia->ia_valid & amtime) == amtime) + events |= IN_ATTRIB; + if ((ia->ia_valid & ATTR_SIZE) + || (ia->ia_valid & amtime) == ATTR_MTIME) + events |= IN_MODIFY; + return events; +} + +void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events, + struct au_hinode *hinode) +{ + struct au_hin_ignore *ign; + + AuDebugOn(!hinode); + + ign = vargs->ignore + vargs->nignore++; + ign->ign_events = events; + ign->ign_hinode = hinode; +} + +void vfsub_ignore(struct vfsub_args *vargs) +{ + int n; + struct au_hin_ignore *ign; + + n = vargs->nignore; + ign = vargs->ignore; + while (n-- > 0) { + au_hin_ignore(ign->ign_hinode, ign->ign_events); + ign++; + } +} + +void vfsub_unignore(struct vfsub_args *vargs) +{ + int n; + struct au_hin_ignore *ign; + + n = vargs->nignore; + ign = vargs->ignore; + while (n-- > 0) { + au_hin_unignore(ign->ign_hinode, ign->ign_events); + ign++; + } +} + +/* ---------------------------------------------------------------------- */ + +void au_hin_ignore(struct au_hinode *hinode, __u32 events) +{ + int i; + atomic_t *ign; + + LKTRTrace("0x%x\n", events); + AuDebugOn(!hinode || !events); + if (unlikely(!hinode->hi_inode || !hinode->hi_notify)) { + /* + * it may happen by this scenario. + * - a file and its parent dir exist on two branches + * - a file on the upper branch is opened + * - the parent dir and the file are removed by udba + * - the parent is re-accessed, and new dentry/inode in + * aufs is generated for it, based upon the one on the lower + * branch + * - the opened file is re-accessed, re-validated, and it may be + * re-connected to the new parent dentry + * it means the file in aufs cannot get the actual removed + * parent dir on the branch. + */ + return; + } + LKTRTrace("hi%lu\n", hinode->hi_inode->i_ino); +#ifdef DbgInotify + AuDbg("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events); +#endif + AuDebugOn(!hinode->hi_notify); + + ign = hinode->hi_notify->hin_ignore; + for (i = 0; i < au_hin_nignore; i++) + if (1U << i & events) + atomic_inc_return(ign + i); +} + +void au_hin_unignore(struct au_hinode *hinode, __u32 events) +{ + int i; + atomic_t *ign; + + LKTRTrace("0x%x\n", events); + AuDebugOn(!hinode || !events); + if (unlikely(!hinode->hi_inode || !hinode->hi_notify)) + return; + LKTRTrace("hi%lu\n", hinode->hi_inode->i_ino); +#ifdef DbgInotify + AuDbg("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events); +#endif + AuDebugOn(!hinode->hi_notify); + + ign = hinode->hi_notify->hin_ignore; + for (i = 0; i < au_hin_nignore; i++) + if (1U << i & events) + atomic_dec_return(ign + i); +} + +/* ---------------------------------------------------------------------- */ + +static char *in_name(u32 mask) +{ +#ifdef CONFIG_AUFS_DEBUG +#define test_ret(flag) if (mask & flag) return #flag; + test_ret(IN_ACCESS); + test_ret(IN_MODIFY); + test_ret(IN_ATTRIB); + test_ret(IN_CLOSE_WRITE); + test_ret(IN_CLOSE_NOWRITE); + test_ret(IN_OPEN); + test_ret(IN_MOVED_FROM); + test_ret(IN_MOVED_TO); + test_ret(IN_CREATE); + test_ret(IN_DELETE); + test_ret(IN_DELETE_SELF); + test_ret(IN_MOVE_SELF); + test_ret(IN_UNMOUNT); + test_ret(IN_Q_OVERFLOW); + test_ret(IN_IGNORED); + return ""; +#undef test_ret +#else + return "??"; +#endif +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, + struct inode *dir) +{ + struct dentry *dentry, *d, *parent; + struct qstr *dname; + + LKTRTrace("%.*s, dir%lu\n", nlen, name, dir->i_ino); + + parent = d_find_alias(dir); + if (!parent) + return NULL; + + dentry = NULL; + spin_lock(&dcache_lock); + list_for_each_entry(d, &parent->d_subdirs, d_u.d_child) { + LKTRTrace("%.*s\n", AuDLNPair(d)); + dname = &d->d_name; + if (dname->len != nlen || memcmp(dname->name, name, nlen)) + continue; + if (!atomic_read(&d->d_count)) { + spin_lock(&d->d_lock); + __d_drop(d); + spin_unlock(&d->d_lock); + continue; + } + + dentry = dget(d); + break; + } + spin_unlock(&dcache_lock); + dput(parent); + + if (dentry) + di_write_lock_child(dentry); + return dentry; +} + +static struct inode *lookup_wlock_by_ino(struct super_block *sb, + aufs_bindex_t bindex, ino_t h_ino) +{ + struct inode *inode; + struct au_xino_entry xinoe; + int err; + + LKTRTrace("b%d, hi%lu\n", bindex, h_ino); + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + + inode = NULL; + err = au_xino_read(sb, bindex, h_ino, &xinoe); + if (!err && xinoe.ino) + inode = ilookup(sb, xinoe.ino); + if (!inode) + goto out; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + AuWarn("wrong root branch\n"); + iput(inode); + inode = NULL; + goto out; + } + + ii_write_lock_child(inode); + + out: + return inode; +} + +static int hin_xino(struct inode *inode, struct inode *h_inode) +{ + int err; + aufs_bindex_t bindex, bend, bfound, bstart; + struct inode *h_i; + + LKTRTrace("i%lu, hi%lu\n", inode->i_ino, h_inode->i_ino); + + err = 0; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + AuWarn("branch root dir was changed\n"); + goto out; + } + + bfound = -1; + bend = au_ibend(inode); + bstart = au_ibstart(inode); + for (bindex = bstart; bindex <= bend; bindex++) { + if (au_h_iptr(inode, bindex) == h_inode) { + bfound = bindex; + break; + } + } + if (bfound < 0) + goto out; + + for (bindex = bstart; bindex <= bend; bindex++) { + h_i = au_h_iptr(inode, bindex); + if (h_i) + err = au_xino_write0(inode->i_sb, bindex, h_i->i_ino, + 0); + /* ignore this error */ + /* bad action? */ + } + + /* children inode number will be broken */ + + out: + AuTraceErr(err); + return err; +} + +static int hin_gen_tree(struct dentry *dentry) +{ + int err, i, j, ndentry; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + err = au_dpages_init(&dpages, GFP_TEMPORARY); + if (unlikely(err)) + goto out; + err = au_dcsub_pages(&dpages, dentry, NULL, NULL); + if (unlikely(err)) + goto out_dpages; + + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + struct dentry *d; + d = dentries[j]; + LKTRTrace("%.*s\n", AuDLNPair(d)); + if (IS_ROOT(d)) + continue; + + d_drop(d); + au_digen_dec(d); + if (d->d_inode) + //reset children xino? cached children only? + au_iigen_dec(d->d_inode); + } + } + + out_dpages: + au_dpages_free(&dpages); + + /* discard children */ + dentry_unhash(dentry); + dput(dentry); + out: + AuTraceErr(err); + return err; +} + +/* + * return 0 if processed. + */ +static int hin_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, + const unsigned int isdir) +{ + int err; + struct dentry *d; + struct qstr *dname; + + LKTRTrace("%.*s, i%lu\n", nlen, name, inode->i_ino); + + err = 1; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { + AuWarn("branch root dir was changed\n"); + err = 0; + goto out; + } + + if (!isdir) { + AuDebugOn(!name); + au_iigen_dec(inode); + spin_lock(&dcache_lock); + list_for_each_entry(d, &inode->i_dentry, d_alias) { + dname = &d->d_name; + if (dname->len != nlen + && memcmp(dname->name, name, nlen)) + continue; + err = 0; + spin_lock(&d->d_lock); + __d_drop(d); + au_digen_dec(d); + spin_unlock(&d->d_lock); + break; + } + spin_unlock(&dcache_lock); + } else { + au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIRS); + d = d_find_alias(inode); + if (!d) { + au_iigen_dec(inode); + goto out; + } + + dname = &d->d_name; + if (dname->len == nlen && !memcmp(dname->name, name, nlen)) + err = hin_gen_tree(d); + dput(d); + } + + out: + AuTraceErr(err); + return err; +} + +static int hin_gen_by_name(struct dentry *dentry, const unsigned int isdir) +{ + int err; + struct inode *inode; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + inode = dentry->d_inode; + if (IS_ROOT(dentry) + //|| (inode && inode->i_ino == AUFS_ROOT_INO) + ) { + AuWarn("branch root dir was changed\n"); + return 0; + } + + err = 0; + if (!isdir) { + d_drop(dentry); + au_digen_dec(dentry); + if (inode) + au_iigen_dec(inode); + } else { + au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIRS); + if (inode) + err = hin_gen_tree(dentry); + } + + AuTraceErr(err); + return err; +} + +static void hin_attr(struct inode *inode, struct inode *h_inode) +{ + struct dentry *h_dentry; + + LKTRTrace("i%lu, hi%lu\n", inode->i_ino, h_inode->i_ino); + + if (au_h_iptr(inode, au_ibstart(inode)) != h_inode) + return; + + h_dentry = d_find_alias(h_inode); + if (h_dentry) { + au_update_fuse_h_inode(NULL, h_dentry); + /* ignore an error*/ + dput(h_dentry); + } + + au_cpup_attr_all(inode); +} + +/* ---------------------------------------------------------------------- */ + +/* hinotify job flags */ +#define AuHinJob_XINO0 1 +#define AuHinJob_GEN (1 << 1) +#define AuHinJob_DIRENT (1 << 2) +#define AuHinJob_ATTR (1 << 3) +#define AuHinJob_ISDIR (1 << 4) +#define AuHinJob_TRYXINO0 (1 << 5) +#define AuHinJob_MNTPNT (1 << 6) +#define au_ftest_hinjob(flags, name) ((flags) & AuHinJob_##name) +#define au_fset_hinjob(flags, name) { (flags) |= AuHinJob_##name; } +#define au_fclr_hinjob(flags, name) { (flags) &= ~AuHinJob_##name; } + +struct hin_job_args { + unsigned int flags; + struct inode *inode, *h_inode, *dir, *h_dir; + struct dentry *dentry; + char *h_name; + int h_nlen; +}; + +static int hin_job(struct hin_job_args *a) +{ + const unsigned int isdir = au_ftest_hinjob(a->flags, ISDIR); + + /* reset xino */ + if (au_ftest_hinjob(a->flags, XINO0) && a->inode) + hin_xino(a->inode, a->h_inode); + /* ignore this error */ + + if (au_ftest_hinjob(a->flags, TRYXINO0) + && a->inode + && a->h_inode) { + mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); + if (!a->h_inode->i_nlink) + hin_xino(a->inode, a->h_inode); + /* ignore this error */ + mutex_unlock(&a->h_inode->i_mutex); + } + + /* make the generation obsolete */ + if (au_ftest_hinjob(a->flags, GEN)) { + int err = -1; + if (a->inode) + err = hin_gen_by_inode(a->h_name, a->h_nlen, a->inode, + isdir); + if (err && a->dentry) + hin_gen_by_name(a->dentry, isdir); + /* ignore this error */ + } + + /* make dir entries obsolete */ + if (au_ftest_hinjob(a->flags, DIRENT) && a->inode) { + struct au_vdir *vdir; + IiMustWriteLock(a->inode); + vdir = au_ivdir(a->inode); + if (vdir) + vdir->vd_jiffy = 0; + //IMustLock(a->inode); + //a->inode->i_version++; + } + + /* update the attr */ + if (au_ftest_hinjob(a->flags, ATTR) && a->inode && a->h_inode) + hin_attr(a->inode, a->h_inode); + + /* can do nothing but warn */ + if (au_ftest_hinjob(a->flags, MNTPNT) + && a->dentry + && d_mountpoint(a->dentry)) + AuWarn("mount-point %.*s is removed or renamed\n", + AuDLNPair(a->dentry)); + + return 0; +} + +/* ---------------------------------------------------------------------- */ + +enum { CHILD, PARENT }; +struct postproc_args { + struct inode *h_dir, *dir, *h_child_inode; + u32 mask; + unsigned int flags[2]; + unsigned int h_child_nlen; + char h_child_name[]; +}; + +static void postproc(void *_args) +{ + struct postproc_args *a = _args; + struct super_block *sb; + aufs_bindex_t bindex, bend, bfound; + int xino, err; + struct inode *inode; + ino_t h_ino; + struct hin_job_args args; + struct dentry *dentry; + struct au_sbinfo *sbinfo; + + AuDebugOn(!_args); + AuDebugOn(!a->h_dir); + AuDebugOn(!a->dir); + AuDebugOn(!a->mask); + //au_debug_on(); + LKTRTrace("mask 0x%x %s, i%lu, hi%lu, hci%lu\n", + a->mask, in_name(a->mask), a->dir->i_ino, a->h_dir->i_ino, + a->h_child_inode ? a->h_child_inode->i_ino : 0); + + inode = NULL; + dentry = NULL; + // do not lock here because of d_revalidate() may cause a deadlock. + //mutex_lock(&a->dir->i_mutex); + sb = a->dir->i_sb; + AuDebugOn(!sb); + sbinfo = au_sbi(sb); + AuDebugOn(!sbinfo); + /* big aufs lock */ + si_noflush_write_lock(sb); + + ii_read_lock_parent(a->dir); + bfound = -1; + bend = au_ibend(a->dir); + for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) + if (au_h_iptr(a->dir, bindex) == a->h_dir) { + bfound = bindex; + break; + } + ii_read_unlock(a->dir); + if (unlikely(bfound < 0)) + goto out; + + xino = !!au_opt_test(au_mntflags(sb), XINO); + h_ino = 0; + if (a->h_child_inode) + h_ino = a->h_child_inode->i_ino; + //AuDbg("here\n"); + + if (a->h_child_nlen + && (au_ftest_hinjob(a->flags[CHILD], GEN) + || au_ftest_hinjob(a->flags[CHILD], MNTPNT))) + dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, + a->dir); + //AuDbg("here\n"); + if (dentry) + inode = dentry->d_inode; + if (xino && !inode && h_ino + && (au_ftest_hinjob(a->flags[CHILD], XINO0) + || au_ftest_hinjob(a->flags[CHILD], TRYXINO0) + || au_ftest_hinjob(a->flags[CHILD], GEN) + || au_ftest_hinjob(a->flags[CHILD], ATTR))) + inode = lookup_wlock_by_ino(sb, bfound, h_ino); + //AuDbg("here\n"); + + args.flags = a->flags[CHILD]; + args.dentry = dentry; + args.inode = inode; + args.h_inode = a->h_child_inode; + args.dir = a->dir; + args.h_dir = a->h_dir; + args.h_name = a->h_child_name; + args.h_nlen = a->h_child_nlen; + err = hin_job(&args); + if (dentry) { + di_write_unlock(dentry); + dput(dentry); + } else if (inode) { + ii_write_unlock(inode); + iput(inode); + } + //AuDbg("here\n"); + + ii_write_lock_parent(a->dir); + args.flags = a->flags[PARENT]; + args.dentry = NULL; + args.inode = a->dir; + args.h_inode = a->h_dir; + args.dir = NULL; + args.h_dir = NULL; + args.h_name = NULL; + args.h_nlen = 0; + err = hin_job(&args); + ii_write_unlock(a->dir); + //AuDbg("here\n"); + + out: + si_write_unlock(sb); + //mutex_unlock(&a->dir->i_mutex); + au_nwt_dec(&sbinfo->si_nowait); + + iput(a->h_child_inode); + iput(a->h_dir); + iput(a->dir); + kfree(a); + //au_debug_off(); +} + +//todo: endian? +#ifndef ilog2 +#define ilog2(n) ffz(~(n)) +#endif + +static void aufs_inotify(struct inotify_watch *watch, u32 wd, u32 mask, + u32 cookie, const char *h_child_name, + struct inode *h_child_inode) +{ + struct au_hinotify *hinotify; + struct postproc_args *args; + int len, wkq_err, isdir, isroot, wh, idx; + char *p; + struct inode *dir; + unsigned int flags[2]; + struct super_block *sb; + atomic_t *cnt; + + LKTRTrace("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s, hi%lu\n", + watch->inode->i_ino, wd, mask, in_name(mask), cookie, + h_child_name ? h_child_name : "", + h_child_inode ? h_child_inode->i_ino : 0); + /* if IN_UNMOUNT happens, there must be another bug */ + if (mask & (IN_IGNORED | IN_UNMOUNT)) { + //WARN_ON(watch->inode->i_ino == 15); + put_inotify_watch(watch); + return; + } + +#ifdef DbgInotify + if (!h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME)) + AuDbg("i%lu, wd %d, mask 0x%x %s, cookie 0x%x, hcname %s," + " hi%lu\n", + watch->inode->i_ino, wd, mask, in_name(mask), cookie, + h_child_name ? h_child_name : "", + h_child_inode ? h_child_inode->i_ino : 0); + //WARN_ON(1); +#endif + + hinotify = container_of(watch, struct au_hinotify, hin_watch); + AuDebugOn(!hinotify || !hinotify->hin_aufs_inode); + idx = ilog2(mask & IN_ALL_EVENTS); + AuDebugOn(au_hin_nignore <= idx); + cnt = hinotify->hin_ignore + idx; + if (0 <= atomic_dec_return(cnt)) + return; + atomic_inc_return(cnt); + + dir = igrab(hinotify->hin_aufs_inode); + if (!dir) + return; + isroot = (dir->i_ino == AUFS_ROOT_INO); + len = 0; + wh = 0; + if (h_child_name) { + len = strlen(h_child_name); + if (!memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + h_child_name += AUFS_WH_PFX_LEN; + len -= AUFS_WH_PFX_LEN; + wh = 1; + } + } + + isdir = 0; + if (h_child_inode) + isdir = !!S_ISDIR(h_child_inode->i_mode); + flags[PARENT] = AuHinJob_ISDIR; + flags[CHILD] = 0; + if (isdir) + flags[CHILD] = AuHinJob_ISDIR; + switch (mask & IN_ALL_EVENTS) { + case IN_MODIFY: + /*FALLTHROUGH*/ + case IN_ATTRIB: + if (h_child_inode) { + if (!wh) + au_fset_hinjob(flags[CHILD], ATTR); + } else + au_fset_hinjob(flags[PARENT], ATTR); + break; + + /* IN_MOVED_FROM is the first event in rename(2) */ + case IN_MOVED_FROM: + case IN_MOVED_TO: + AuDebugOn(!h_child_name || !h_child_inode); + au_fset_hinjob(flags[CHILD], GEN); + au_fset_hinjob(flags[CHILD], ATTR); + if (1 || isdir) + au_fset_hinjob(flags[CHILD], XINO0); + au_fset_hinjob(flags[CHILD], MNTPNT); + + au_fset_hinjob(flags[PARENT], ATTR); + au_fset_hinjob(flags[PARENT], DIRENT); + break; + + case IN_CREATE: + AuDebugOn(!h_child_name || !h_child_inode); + au_fset_hinjob(flags[PARENT], ATTR); + au_fset_hinjob(flags[PARENT], DIRENT); + au_fset_hinjob(flags[CHILD], GEN); + /* hard link */ + if (!isdir && h_child_inode->i_nlink > 1) + au_fset_hinjob(flags[CHILD], ATTR); + break; + + case IN_DELETE: + /* + * aufs never be able to get this child inode. + * revalidation should be in d_revalidate() + * by checking i_nlink, i_generation or d_unhashed(). + */ + AuDebugOn(!h_child_name); + au_fset_hinjob(flags[PARENT], ATTR); + au_fset_hinjob(flags[PARENT], DIRENT); + au_fset_hinjob(flags[CHILD], GEN); + au_fset_hinjob(flags[CHILD], TRYXINO0); + au_fset_hinjob(flags[CHILD], MNTPNT); + break; + + case IN_DELETE_SELF: + if (!isroot) + au_fset_hinjob(flags[PARENT], GEN); + /*FALLTHROUGH*/ + + case IN_MOVE_SELF: + /* + * when an inotify is set to an aufs inode, + * such inode can be isolated and this event can be fired + * solely. + */ + AuDebugOn(h_child_name || h_child_inode); + if (unlikely(isroot)) { + AuWarn("root branch was moved\n"); + iput(dir); + return; + } + au_fset_hinjob(flags[PARENT], XINO0); + au_fset_hinjob(flags[PARENT], GEN); + au_fset_hinjob(flags[PARENT], ATTR); + au_fset_hinjob(flags[PARENT], DIRENT); + //au_fset_hinjob(flags[PARENT], MNTPNT); + break; + case IN_ACCESS: + default: + AuDebugOn(1); + } + + if (wh) + h_child_inode = NULL; + + /* iput() and kfree() will be called in postproc() */ + /* + * inotify_mutex is already acquired and kmalloc/prune_icache may lock + * iprune_mutex. strange. + */ + lockdep_off(); + args = kmalloc(sizeof(*args) + len + 1, GFP_TEMPORARY); + lockdep_on(); + if (unlikely(!args)) { + AuErr1("no memory\n"); + iput(dir); + return; + } + args->flags[PARENT] = flags[PARENT]; + args->flags[CHILD] = flags[CHILD]; + args->mask = mask; + args->dir = dir; + args->h_dir = igrab(watch->inode); + if (h_child_inode) + igrab(h_child_inode); + args->h_child_inode = h_child_inode; + args->h_child_nlen = len; + if (len) { + p = (void *)args; + p += sizeof(*args); + memcpy(p, h_child_name, len + 1); + } + + sb = dir->i_sb; + au_nwt_inc(&au_sbi(sb)->si_nowait); + lockdep_off(); + wkq_err = au_wkq_nowait(postproc, args, sb, /*dlgt*/0); + lockdep_on(); + if (unlikely(wkq_err)) { + AuErr("wkq %d\n", wkq_err); + au_nwt_dec(&au_sbi(sb)->si_nowait); + } +} + +static void aufs_inotify_destroy(struct inotify_watch *watch) +{ + return; +} + +static struct inotify_operations aufs_inotify_ops = { + .handle_event = aufs_inotify, + .destroy_watch = aufs_inotify_destroy +}; + +/* ---------------------------------------------------------------------- */ + +static void au_hin_destroy_cache(void) +{ + kmem_cache_destroy(au_cachep[AuCache_HINOTIFY]); + au_cachep[AuCache_HINOTIFY] = NULL; +} + +int __init au_inotify_init(void) +{ + au_hin_nignore = 6; + while (1U << au_hin_nignore < AuInMask) + au_hin_nignore++; + //AuDbg("au_hin_nignore %d\n", au_hin_nignore); + AuDebugOn(au_hin_nignore != 10); + + in_handle = ERR_PTR(-ENOMEM); + au_cachep[AuCache_HINOTIFY] + = AuCacheX(au_hinotify, sizeof(atomic_t) * au_hin_nignore); + if (unlikely(!au_cachep[AuCache_HINOTIFY])) + goto out; + + in_handle = inotify_init(&aufs_inotify_ops); + if (!IS_ERR(in_handle)) + return 0; + + au_hin_destroy_cache(); + out: + AuTraceErrPtr(in_handle); + return PTR_ERR(in_handle); +} + +void au_inotify_fin(void) +{ + inotify_destroy(in_handle); + if (au_cachep[AuCache_HINOTIFY]) + au_hin_destroy_cache(); +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/iinfo.c linux-2.6.25.4-unionfs/fs/aufs/iinfo.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/iinfo.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/iinfo.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,282 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode private data + * + * $Id: iinfo.c,v 1.2 2008/04/21 01:32:05 sfjro Exp $ + */ + +#include "aufs.h" + +struct au_iinfo *au_ii(struct inode *inode) +{ + struct au_iinfo *iinfo; + + iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo); + /* bad_inode case */ + if (unlikely(!iinfo->ii_hinode)) + return NULL; + AuDebugOn(!iinfo->ii_hinode + /* || au_sbi(inode->i_sb)->si_bend < iinfo->ii_bend */ + || iinfo->ii_bend < iinfo->ii_bstart); + return iinfo; +} + +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) +{ + struct inode *hidden_inode; + + IiMustAnyLock(inode); + AuDebugOn(bindex < 0 || au_ibend(inode) < bindex); + hidden_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; + AuDebugOn(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0); + return hidden_inode; +} + +aufs_bindex_t au_ii_br_id(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustAnyLock(inode); + AuDebugOn(bindex < 0 + || au_ibend(inode) < bindex + || !au_ii(inode)->ii_hinode[0 + bindex].hi_inode); + return au_ii(inode)->ii_hinode[0 + bindex].hi_id; +} + +// hard/soft set +void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) +{ + struct au_iinfo *iinfo = au_ii(inode); + struct inode *h_inode; + + IiMustWriteLock(inode); + AuDebugOn(au_sbend(inode->i_sb) < bindex); + iinfo->ii_bstart = bindex; + h_inode = iinfo->ii_hinode[bindex + 0].hi_inode; + if (h_inode) + au_cpup_igen(inode, h_inode); +} + +unsigned int au_hi_flags(struct inode *inode, int isdir) +{ + unsigned int flags; + const unsigned int mnt_flags = au_mntflags(inode->i_sb); + + flags = 0; + if (au_opt_test(mnt_flags, XINO)) + au_fset_hi(flags, XINO); + if (unlikely(isdir && au_opt_test(mnt_flags, UDBA_INOTIFY))) + au_fset_hi(flags, NOTIFY); + return flags; +} + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags) +{ + struct au_hinode *hinode; + struct inode *hi; + struct au_iinfo *iinfo = au_ii(inode); + + LKTRTrace("i%lu, b%d, hi%lu, flags 0x%x\n", + inode->i_ino, bindex, h_inode ? h_inode->i_ino : 0, flags); + IiMustWriteLock(inode); + hinode = iinfo->ii_hinode + bindex; + hi = hinode->hi_inode; + AuDebugOn(bindex < au_ibstart(inode) || au_ibend(inode) < bindex + || (h_inode && atomic_read(&h_inode->i_count) <= 0) + || (h_inode && hi)); + + if (hi) { + au_hiput(hinode); + } + hinode->hi_inode = h_inode; + if (h_inode) { + int err; + struct super_block *sb = inode->i_sb; + + if (bindex == iinfo->ii_bstart) + au_cpup_igen(inode, h_inode); + hinode->hi_id = au_sbr_id(sb, bindex); + if (au_ftest_hi(flags, XINO)) { + struct au_xino_entry xinoe = { + .ino = inode->i_ino, + //.h_gen = h_inode->i_generation + }; + err = au_xino_write(sb, bindex, h_inode->i_ino, &xinoe); + if (unlikely(err)) + AuIOErr1("failed au_xino_write() %d\n", err); + } + + if (unlikely(au_ftest_hi(flags, NOTIFY) + && au_br_hinotifyable(au_sbr_perm(sb, bindex)))) { + err = au_hin_alloc(hinode, inode, h_inode); + if (unlikely(err)) + AuIOErr1("au_hin_alloc() %d\n", err); + } + } +} + +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh) +{ + struct au_hinode *hinode; + + IiMustWriteLock(inode); + hinode = au_ii(inode)->ii_hinode + bindex; + AuDebugOn(hinode->hi_whdentry); + hinode->hi_whdentry = h_wh; +} + +void au_update_iigen(struct inode *inode) +{ + //IiMustWriteLock(inode); + AuDebugOn(!inode->i_sb); + atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb)); + //smp_mb(); /* atomic_set */ +} + +/* it may be called at remount time, too */ +void au_update_brange(struct inode *inode, int do_put_zero) +{ + struct au_iinfo *iinfo; + + LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero); + IiMustWriteLock(inode); + + iinfo = au_ii(inode); + if (unlikely(!iinfo) || iinfo->ii_bstart < 0) + return; + + if (do_put_zero) { + aufs_bindex_t bindex; + for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; + bindex++) { + struct inode *h_i; + h_i = iinfo->ii_hinode[0 + bindex].hi_inode; + if (h_i && !h_i->i_nlink) + au_set_h_iptr(inode, bindex, NULL, 0); + } + } + + iinfo->ii_bstart = -1; + while (++iinfo->ii_bstart <= iinfo->ii_bend) + if (iinfo->ii_hinode[0 + iinfo->ii_bstart].hi_inode) + break; + if (iinfo->ii_bstart > iinfo->ii_bend) { + iinfo->ii_bstart = -1; + iinfo->ii_bend = -1; + return; + } + + iinfo->ii_bend++; + while (0 <= --iinfo->ii_bend) + if (iinfo->ii_hinode[0 + iinfo->ii_bend].hi_inode) + break; + AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend || iinfo->ii_bend < 0); +} + +/* ---------------------------------------------------------------------- */ + +int au_iinfo_init(struct inode *inode) +{ + struct au_iinfo *iinfo; + struct super_block *sb; + int nbr, i; + + sb = inode->i_sb; + AuDebugOn(!sb); + iinfo = &(container_of(inode, struct aufs_icntnr, vfs_inode)->iinfo); + AuDebugOn(iinfo->ii_hinode); + nbr = au_sbend(sb) + 1; + if (unlikely(nbr <= 0)) + nbr = 1; + iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_KERNEL); + //iinfo->ii_hinode = NULL; + if (iinfo->ii_hinode) { + for (i = 0; i < nbr; i++) + iinfo->ii_hinode[i].hi_id = -1; + atomic_set(&iinfo->ii_generation, au_sigen(sb)); + //smp_mb(); /* atomic_set */ + au_rw_init_nolock(&iinfo->ii_rwsem); + iinfo->ii_bstart = -1; + iinfo->ii_bend = -1; + iinfo->ii_vdir = NULL; + return 0; + } + return -ENOMEM; +} + +static int au_iinfo_write0(struct super_block *sb, struct au_hinode *hinode, + ino_t ino) +{ + int err, locked; + aufs_bindex_t bindex; + + err = 0; + locked = si_read_trylock(sb, !AuLock_FLUSH); // crucio! + bindex = au_br_index(sb, hinode->hi_id); + if (bindex >= 0) + err = au_xino_write0(sb, bindex, hinode->hi_inode->i_ino, ino); + /* error action? */ + if (locked) + si_read_unlock(sb); + return err; +} + +void au_iinfo_fin(struct inode *inode) +{ + struct au_iinfo *iinfo; + aufs_bindex_t bend; + struct au_hinode *hi; + struct super_block *sb; + int unlinked; + ino_t ino; + + iinfo = au_ii(inode); + /* bad_inode case */ + if (unlikely(!iinfo)) + return; + + if (unlikely(iinfo->ii_vdir)) + au_vdir_free(iinfo->ii_vdir); + + if (iinfo->ii_bstart >= 0) { + sb = inode->i_sb; + unlinked = !inode->i_nlink; + ino = 0; + if (unlinked) + ino = inode->i_ino; + hi = iinfo->ii_hinode + iinfo->ii_bstart; + bend = iinfo->ii_bend; + while (iinfo->ii_bstart++ <= bend) { + if (hi->hi_inode) { + if (unlinked || !hi->hi_inode->i_nlink) { + au_iinfo_write0(sb, hi, ino); + /* ignore this error */ + ino = 0; + } + au_hiput(hi); + } + hi++; + } + //iinfo->ii_bstart = iinfo->ii_bend = -1; + } + + kfree(iinfo->ii_hinode); + //iinfo->ii_hinode = NULL; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/inode.c linux-2.6.25.4-unionfs/fs/aufs/inode.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/inode.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/inode.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode functions + * + * $Id: inode.c,v 1.3 2008/04/28 03:04:12 sfjro Exp $ + */ + +#include "aufs.h" + +int au_refresh_hinode_self(struct inode *inode) +{ + int err, new_sz, update; + struct inode *first; + struct au_hinode *p, *q, tmp; + struct super_block *sb; + struct au_iinfo *iinfo; + aufs_bindex_t bindex, bend, new_bindex; + + LKTRTrace("i%lu\n", inode->i_ino); + IiMustWriteLock(inode); + + err = -ENOMEM; + update = 0; + sb = inode->i_sb; + bend = au_sbend(sb); + new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1); + iinfo = au_ii(inode); + p = au_kzrealloc(iinfo->ii_hinode, sizeof(*p) * (iinfo->ii_bend + 1), + new_sz, GFP_KERNEL); + //p = NULL; + if (unlikely(!p)) + goto out; + + iinfo->ii_hinode = p; + p = iinfo->ii_hinode + iinfo->ii_bstart; + first = p->hi_inode; + err = 0; + for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; + bindex++, p++) { + if (!p->hi_inode) + continue; + + new_bindex = au_br_index(sb, p->hi_id); + if (new_bindex == bindex) + continue; + if (new_bindex < 0) { + update++; + au_hiput(p); + p->hi_inode = NULL; + continue; + } + + if (new_bindex < iinfo->ii_bstart) + iinfo->ii_bstart = new_bindex; + if (iinfo->ii_bend < new_bindex) + iinfo->ii_bend = new_bindex; + /* swap two hidden inode, and loop again */ + q = iinfo->ii_hinode + new_bindex; + tmp = *q; + *q = *p; + *p = tmp; + if (tmp.hi_inode) { + bindex--; + p--; + } + } + au_update_brange(inode, /*do_put_zero*/0); + + if (unlikely(err)) + goto out; + + if (1 || first != au_h_iptr(inode, iinfo->ii_bstart)) + au_cpup_attr_all(inode); + if (update && S_ISDIR(inode->i_mode)) + inode->i_version++; + au_update_iigen(inode); + + out: + AuTraceErr(err); + return err; +} + +int au_refresh_hinode(struct inode *inode, struct dentry *dentry) +{ + int err, update, isdir; + struct inode *first; + struct au_hinode *p; + struct super_block *sb; + struct au_iinfo *iinfo; + aufs_bindex_t bindex, bend; + unsigned int flags; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + IiMustWriteLock(inode); + + err = au_refresh_hinode_self(inode); + if (unlikely(err)) + goto out; + + sb = dentry->d_sb; + bend = au_sbend(sb); + iinfo = au_ii(inode); + update = 0; + p = iinfo->ii_hinode + iinfo->ii_bstart; + first = p->hi_inode; + isdir = S_ISDIR(inode->i_mode); + flags = au_hi_flags(inode, isdir); + bend = au_dbend(dentry); + for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { + struct inode *hi; + struct dentry *hd; + + hd = au_h_dptr(dentry, bindex); + if (!hd || !hd->d_inode) + continue; + + if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { + hi = au_h_iptr(inode, bindex); + if (hi) { + if (hi == hd->d_inode) + continue; + err = -ESTALE; + break; + } + } + if (bindex < iinfo->ii_bstart) + iinfo->ii_bstart = bindex; + if (iinfo->ii_bend < bindex) + iinfo->ii_bend = bindex; + au_set_h_iptr(inode, bindex, igrab(hd->d_inode), flags); + update++; + } + au_update_brange(inode, /*do_put_zero*/0); + + if (unlikely(err)) + goto out; + + if (1 || first != au_h_iptr(inode, iinfo->ii_bstart)) + au_cpup_attr_all(inode); + if (update && isdir) + inode->i_version++; + au_update_iigen(inode); + + out: + AuTraceErr(err); + return err; +} + +static int set_inode(struct inode *inode, struct dentry *dentry) +{ + int err, isdir; + struct dentry *h_dentry; + struct inode *h_inode; + umode_t mode; + aufs_bindex_t bindex, bstart, btail; + struct au_iinfo *iinfo; + unsigned int flags; + + LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry)); + AuDebugOn(!(inode->i_state & I_NEW)); + IiMustWriteLock(inode); + bstart = au_dbstart(dentry); + h_dentry = au_h_dptr(dentry, bstart); + AuDebugOn(!h_dentry); + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode); + + err = 0; + isdir = 0; + mode = h_inode->i_mode; + switch (mode & S_IFMT) { + case S_IFREG: + btail = au_dbtail(dentry); + break; + case S_IFDIR: + isdir = 1; + btail = au_dbtaildir(dentry); + inode->i_op = &aufs_dir_iop; + inode->i_fop = &aufs_dir_fop; + break; + case S_IFLNK: + btail = au_dbtail(dentry); + inode->i_op = &aufs_symlink_iop; + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + case S_IFSOCK: + btail = au_dbtail(dentry); + init_special_inode(inode, mode, + au_h_rdev(h_inode, /*h_mnt*/NULL, h_dentry)); + break; + default: + AuIOErr("Unknown file type 0%o\n", mode); + err = -EIO; + goto out; + } + + flags = au_hi_flags(inode, isdir); + iinfo = au_ii(inode); + iinfo->ii_bstart = bstart; + iinfo->ii_bend = btail; + for (bindex = bstart; bindex <= btail; bindex++) { + h_dentry = au_h_dptr(dentry, bindex); + if (!h_dentry) + continue; + AuDebugOn(!h_dentry->d_inode); + au_set_h_iptr(inode, bindex, igrab(h_dentry->d_inode), flags); + } + au_cpup_attr_all(inode); + + out: + AuTraceErr(err); + return err; +} + +/* successful returns with iinfo write_locked */ +//todo: return with unlocked? +static int reval_inode(struct inode *inode, struct dentry *dentry, int *matched) +{ + int err; + struct inode *h_inode, *h_dinode; + aufs_bindex_t bindex, bend; + + LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry)); + + *matched = 0; + + /* + * before this function, if aufs got any iinfo lock, it must be only + * one, the parent dir. + * it can happen by UDBA and the obsoleted inode number. + */ + err = -EIO; + if (unlikely(inode->i_ino == parent_ino(dentry))) + goto out; + + err = 0; + h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; + mutex_lock_nested(&inode->i_mutex, AuLsc_I_CHILD); + ii_write_lock_new(inode); + bend = au_ibend(inode); + for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (h_inode && h_inode == h_dinode) { + //&& (ibs != bstart || !au_test_higen(inode, h_inode))); + *matched = 1; + err = 0; + if (unlikely(au_iigen(inode) != au_digen(dentry))) + err = au_refresh_hinode(inode, dentry); + break; + } + } + if (unlikely(err)) + ii_write_unlock(inode); + mutex_unlock(&inode->i_mutex); + + out: + AuTraceErr(err); + return err; +} + +/* successful returns with iinfo write_locked */ +//todo: return with unlocked? +struct inode *au_new_inode(struct dentry *dentry) +{ + struct inode *inode, *h_inode; + struct dentry *h_dentry; + ino_t h_ino; + struct super_block *sb; + int err, match; + aufs_bindex_t bstart; + struct au_xino_entry xinoe; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + sb = dentry->d_sb; + bstart = au_dbstart(dentry); + h_dentry = au_h_dptr(dentry, bstart); + AuDebugOn(!h_dentry); + h_inode = h_dentry->d_inode; + AuDebugOn(!h_inode); + + h_ino = h_inode->i_ino; + err = au_xino_read(sb, bstart, h_ino, &xinoe); + //err = -1; + inode = ERR_PTR(err); + if (unlikely(err)) + goto out; + new_ino: + if (!xinoe.ino) { + xinoe.ino = au_xino_new_ino(sb); + if (!xinoe.ino) { + inode = ERR_PTR(-EIO); + goto out; + } + } + + LKTRTrace("i%lu\n", xinoe.ino); + inode = au_iget_locked(sb, xinoe.ino); + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out; + + LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); + if (inode->i_state & I_NEW) { + ii_write_lock_new(inode); + err = set_inode(inode, dentry); + //err = -1; + unlock_new_inode(inode); + if (!err) + goto out; /* success */ + iget_failed(inode); + ii_write_unlock(inode); + goto out_iput; + } else { + //todo: remove this + AuDebugOn(inode->i_state & I_LOCK); + err = reval_inode(inode, dentry, &match); + if (!err) + goto out; /* success */ + else if (match) + goto out_iput; + } + + if (unlikely(au_test_unique_ino(h_dentry, h_ino))) + AuWarn1("Un-notified UDBA or repeatedly renamed dir," + " b%d, %s, %.*s, hi%lu, i%lu.\n", + bstart, au_sbtype(h_dentry->d_sb), AuDLNPair(dentry), + h_ino, xinoe.ino); + xinoe.ino = 0; + err = au_xino_write0(sb, bstart, h_ino, 0); + if (!err) { + iput(inode); + goto new_ino; + } + /* force noxino? */ + + out_iput: + iput(inode); + inode = ERR_PTR(err); + out: + AuTraceErrPtr(inode); + return inode; +} + +/* ---------------------------------------------------------------------- */ + +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode) +{ + int err; + + err = au_br_rdonly(au_sbr(sb, bindex)); + + /* pseudo-link after flushed may out of bounds */ + if (!err + && inode + && au_ibstart(inode) <= bindex + && bindex <= au_ibend(inode)) { + /* + * permission check is unnecessary since vfsub routine + * will be called later + */ + struct inode *hi = au_h_iptr(inode, bindex); + if (hi) + err = IS_IMMUTABLE(hi) ? -EROFS : 0; + } + + AuTraceErr(err); + return err; +} + +int au_test_h_perm(struct inode *h_inode, int mask, int dlgt) +{ + if (!current->fsuid) + return 0; + //todo: fake nameidata + return vfsub_permission(h_inode, mask, NULL, dlgt); +} + +int au_test_h_perm_sio(struct inode *h_inode, int mask, int dlgt) +{ + if (unlikely(au_test_nfs(h_inode->i_sb) + && (mask & MAY_WRITE) + && S_ISDIR(h_inode->i_mode))) + mask |= MAY_READ; /* force permission check */ + return au_test_h_perm(h_inode, mask, dlgt); +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/inode.h linux-2.6.25.4-unionfs/fs/aufs/inode.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/inode.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/inode.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode operations + * + * $Id: inode.h,v 1.2 2008/04/21 01:32:05 sfjro Exp $ + */ + +#ifndef __AUFS_INODE_H__ +#define __AUFS_INODE_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include "hinode.h" +#include "misc.h" +#include "super.h" + +struct au_hinode; +struct au_vdir; +struct au_iinfo { + atomic_t ii_generation; + struct super_block *ii_hsb1; /* no get/put */ + + struct au_rwsem ii_rwsem; + aufs_bindex_t ii_bstart, ii_bend; + struct au_hinode *ii_hinode; + struct au_vdir *ii_vdir; +}; + +struct aufs_icntnr { + struct au_iinfo iinfo; + struct inode vfs_inode; +}; + +/* ---------------------------------------------------------------------- */ + +/* inode.c */ +int au_refresh_hinode_self(struct inode *inode); +int au_refresh_hinode(struct inode *inode, struct dentry *dentry); +struct inode *au_new_inode(struct dentry *dentry); +int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode); +int au_test_h_perm(struct inode *h_inode, int mask, int dlgt); +int au_test_h_perm_sio(struct inode *h_inode, int mask, int dlgt); + +/* i_op.c */ +extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; + +/* au_wr_dir flags */ +#define AuWrDir_ADD_ENTRY 1 +#define AuWrDir_LOCK_SRCDIR (1 << 1) +#define AuWrDir_ISDIR (1 << 2) +#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) +#define au_fset_wrdir(flags, name) { (flags) |= AuWrDir_##name; } +#define au_fclr_wrdir(flags, name) { (flags) &= ~AuWrDir_##name; } + +struct au_wr_dir_args { + aufs_bindex_t force_btgt; + unsigned int flags; +}; +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args); + +/* i_op_add.c */ +struct au_ndx; +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir, struct au_ndx *ndx); +int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev); +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname); +int aufs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd); +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry); +int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode); + +/* i_op_del.c */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup, + struct dentry *locked); +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir, struct au_ndx *ndx); +int aufs_unlink(struct inode *dir, struct dentry *dentry); +int aufs_rmdir(struct inode *dir, struct dentry *dentry); + +/* i_op_ren.c */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt); +int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry); + +#ifdef CONFIG_AUFS_DLGT +/* dlgt.c */ +int au_security_inode_permission(struct inode *h_inode, int mask, + struct nameidata *fake_nd, int dlgt); +#else +static inline +int au_security_inode_permission(struct inode *h_inode, int mask, + struct nameidata *fake_nd, int dlgt) +{ + return security_inode_permission(h_inode, mask, fake_nd); +} +#endif /* CONFIG_AUFS_DLGT */ + +#ifdef CONFIG_AUFS_WORKAROUND_FUSE +/* br_fuse.c */ +int aufs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st); +#endif + +/* iinfo.c */ +struct au_iinfo *au_ii(struct inode *inode); +struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); +aufs_bindex_t au_ii_br_id(struct inode *inode, aufs_bindex_t bindex); + +void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex); +void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, + struct dentry *h_wh); +unsigned int au_hi_flags(struct inode *inode, int isdir); + +/* hinode flags */ +#define AuHi_XINO 1 +#define AuHi_NOTIFY (1 << 1) +#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) +#define au_fset_hi(flags, name) { (flags) |= AuHi_##name; } +#define au_fclr_hi(flags, name) { (flags) &= ~AuHi_##name; } +#ifndef CONFIG_AUFS_HINOTIFY +#undef AuHi_NOTIFY +#define AuHi_NOTIFY 0 +#endif + +void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, + struct inode *h_inode, unsigned int flags); + +void au_update_iigen(struct inode *inode); +void au_update_brange(struct inode *inode, int do_put_zero); + +int au_iinfo_init(struct inode *inode); +void au_iinfo_fin(struct inode *inode); + +/* plink.c */ +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb); +#else +static inline void au_plink_list(struct super_block *sb) +{ + /* nothing */ +} +#endif +int au_plink_test(struct super_block *sb, struct inode *inode); +struct dentry *au_plink_lkup(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode); +void au_plink_append(struct super_block *sb, struct inode *inode, + struct dentry *h_dentry, aufs_bindex_t bindex); +void au_plink_put(struct super_block *sb); +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for iinfo */ +enum { + AuLsc_II_CHILD, /* child first */ + AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hinotify */ + AuLsc_II_CHILD3, /* copyup dirs */ + AuLsc_II_PARENT, + AuLsc_II_PARENT2, + AuLsc_II_PARENT3, + AuLsc_II_PARENT4, + AuLsc_II_NEW /* new inode */ +}; + +/* + * ii_read_lock_child, ii_write_lock_child, + * ii_read_lock_child2, ii_write_lock_child2, + * ii_read_lock_child3, ii_write_lock_child3, + * ii_read_lock_parent, ii_write_lock_parent, + * ii_read_lock_parent2, ii_write_lock_parent2, + * ii_read_lock_parent3, ii_write_lock_parent3, + * ii_read_lock_parent4, ii_write_lock_parent4, + * ii_read_lock_new, ii_write_lock_new + */ +#define AuReadLockFunc(name, lsc) \ +static inline void ii_read_lock_##name(struct inode *i) \ +{ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); } + +#define AuWriteLockFunc(name, lsc) \ +static inline void ii_write_lock_##name(struct inode *i) \ +{ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); } + +#define AuRWLockFuncs(name, lsc) \ + AuReadLockFunc(name, lsc) \ + AuWriteLockFunc(name, lsc) + +AuRWLockFuncs(child, CHILD); +AuRWLockFuncs(child2, CHILD2); +AuRWLockFuncs(child3, CHILD3); +AuRWLockFuncs(parent, PARENT); +AuRWLockFuncs(parent2, PARENT2); +AuRWLockFuncs(parent3, PARENT3); +AuRWLockFuncs(parent4, PARENT4); +AuRWLockFuncs(new, NEW); + +#undef AuReadLockFunc +#undef AuWriteLockFunc +#undef AuRWLockFuncs + +/* + * ii_read_unlock, ii_write_unlock, ii_downgrade_lock + */ +AuSimpleUnlockRwsemFuncs(ii, struct inode *i, au_ii(i)->ii_rwsem); + +/* to debug easier, do not make them inlined functions */ +#define IiMustReadLock(i) do { \ + SiMustAnyLock((i)->i_sb); \ + AuRwMustReadLock(&au_ii(i)->ii_rwsem); \ +} while (0) + +#define IiMustWriteLock(i) do { \ + SiMustAnyLock((i)->i_sb); \ + AuRwMustWriteLock(&au_ii(i)->ii_rwsem); \ +} while (0) + +#define IiMustAnyLock(i) do { \ + SiMustAnyLock((i)->i_sb); \ + AuRwMustAnyLock(&au_ii(i)->ii_rwsem); \ +} while (0) + +#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) + +/* ---------------------------------------------------------------------- */ + +static inline aufs_bindex_t au_ibstart(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_bstart; +} + +static inline aufs_bindex_t au_ibend(struct inode *inode) +{ + IiMustAnyLock(inode); + return au_ii(inode)->ii_bend; +} + +static inline struct au_vdir *au_ivdir(struct inode *inode) +{ + IiMustAnyLock(inode); + AuDebugOn(!S_ISDIR(inode->i_mode)); + return au_ii(inode)->ii_vdir; +} + +static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) +{ + struct au_hinode *hinode; + IiMustAnyLock(inode); + hinode = au_ii(inode)->ii_hinode + bindex; + return hinode->hi_whdentry; +} + +static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) +{ + IiMustWriteLock(inode); + AuDebugOn(au_sbend(inode->i_sb) < bindex || bindex < au_ibstart(inode)); + au_ii(inode)->ii_bend = bindex; +} + +static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) +{ + IiMustWriteLock(inode); + AuDebugOn(!S_ISDIR(inode->i_mode) || (au_ii(inode)->ii_vdir && vdir)); + au_ii(inode)->ii_vdir = vdir; +} + +static inline void au_hiput(struct au_hinode *hinode) +{ + au_hin_free(hinode); + dput(hinode->hi_whdentry); + iput(hinode->hi_inode); +} + +static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) +{ + //todo: this lock check causes some unnecessary locks in callers. + IiMustAnyLock(inode); + return au_ii(inode)->ii_hinode + bindex; +} + +/* tiny test for inode number */ +/* tmpfs generation is too rough */ +static inline int au_test_higen(struct inode *inode, struct inode *h_inode) +{ + IiMustAnyLock(inode); + return !(au_ii(inode)->ii_hsb1 == h_inode->i_sb + && inode->i_generation == h_inode->i_generation); +} + +static inline au_gen_t au_iigen(struct inode *inode) +{ + return atomic_read(&au_ii(inode)->ii_generation); +} + +#ifdef CONFIG_AUFS_HINOTIFY +static inline au_gen_t au_iigen_dec(struct inode *inode) +{ + //AuDbg("i%lu\n", inode->i_ino); + return atomic_dec_return(&au_ii(inode)->ii_generation); +} +#endif + +#endif /* __KERNEL__ */ +#endif /* __AUFS_INODE_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_add.c linux-2.6.25.4-unionfs/fs/aufs/i_op_add.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_add.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/i_op_add.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,777 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode operations (add entry) + * + * $Id: i_op_add.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $ + */ + +#include "aufs.h" + +/* + * final procedure of adding a new entry, except link(2). + * remove whiteout, instantiate, copyup the parent dir's times and size + * and update version. + * if it failed, re-create the removed whiteout. + */ +static int epilog(struct inode *dir, struct dentry *wh_dentry, + struct dentry *dentry) +{ + int err, rerr; + aufs_bindex_t bwh; + struct inode *inode, *h_dir; + struct dentry *wh; + struct au_ndx ndx; + struct super_block *sb; + + LKTRTrace("wh %p, %.*s\n", wh_dentry, AuDLNPair(dentry)); + + bwh = -1; + if (wh_dentry) { + h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(h_dir); + bwh = au_dbwh(dentry); + err = au_wh_unlink_dentry(h_dir, wh_dentry, dentry, dir, + /*dlgt*/0); + //err = -1; + if (unlikely(err)) + goto out; + } + + inode = au_new_inode(dentry); + //inode = ERR_PTR(-1); + if (!IS_ERR(inode)) { + d_instantiate(dentry, inode); + dir = dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(dir); + /* or always cpup dir mtime? */ + if (au_ibstart(dir) == au_dbstart(dentry)) + au_cpup_attr_timesizes(dir); + dir->i_version++; + return 0; /* success */ + } + + err = PTR_ERR(inode); + if (!wh_dentry) + goto out; + + /* revert */ + sb = dentry->d_sb; + ndx.flags = 0; + if (unlikely(au_opt_test_dlgt(au_mntflags(sb)))) + au_fset_ndx(ndx.flags, DLGT); + ndx.nfsmnt = au_nfsmnt(sb, bwh); + ndx.nd = NULL; + //ndx.br = NULL; + /* dir inode is locked */ + wh = au_wh_create(dir, dentry, bwh, wh_dentry->d_parent, &ndx); + //wh = ERR_PTR(-1); + rerr = PTR_ERR(wh); + if (!IS_ERR(wh)) { + dput(wh); + goto out; + } + AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + + out: + AuTraceErr(err); + return err; +} + +/* + * simple tests for the adding inode operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir, struct au_ndx *ndx) +{ + int err, exist; + struct dentry *h_dentry; + struct inode *h_inode; + umode_t h_mode; + + LKTRTrace("%.*s/%.*s, b%d, dir %d\n", + AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir); + + exist = !!dentry->d_inode; + h_dentry = au_h_dptr(dentry, bindex); + h_inode = h_dentry->d_inode; + if (!exist) { + err = -EEXIST; + if (unlikely(h_inode)) + goto out; + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(!h_inode || !h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } + + err = -EIO; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + goto out; + err = 0; + + if (unlikely(au_opt_test(au_mntflags(dentry->d_sb), UDBA_INOTIFY))) { + struct dentry *h_latest; + struct qstr *qstr = &dentry->d_name; + + err = -EACCES; + if (unlikely(au_test_h_perm + (h_parent->d_inode, MAY_EXEC | MAY_WRITE, + au_ftest_ndx(ndx->flags, DLGT)))) + goto out; + + err = -EIO; + h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len, + ndx); + err = PTR_ERR(h_latest); + if (IS_ERR(h_latest)) + goto out; + err = -EIO; + dput(h_latest); + if (h_latest == h_dentry) + err = 0; + } + + out: + AuTraceErr(err); + return err; +} + +/* + * initial procedure of adding a new entry. + * prepare writable branch and the parent dir, lock it, + * lookup whiteout for the new entry. + */ +static struct dentry * +lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, + struct dentry *src_dentry, struct au_wr_dir_args *wr_dir_args) +{ + struct dentry *wh_dentry, *parent, *h_parent, *gparent; + int err; + aufs_bindex_t bstart, bcpup; + struct inode *dir, *h_dir, *gdir; + struct au_ndx ndx; + struct super_block *sb; + struct au_hinode *hgdir; + unsigned int mnt_flags; + + LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry); + + parent = dentry->d_parent; /* dir inode is locked */ + IMustLock(parent->d_inode); + bstart = au_dbstart(dentry); + err = au_wr_dir(dentry, src_dentry, wr_dir_args); + bcpup = err; + //err = -1; + wh_dentry = ERR_PTR(err); + if (unlikely(err < 0)) + goto out; + + sb = parent->d_sb; + mnt_flags = au_mntflags(sb); + //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. + hgdir = NULL; + if (unlikely(dt && au_opt_test(mnt_flags, UDBA_INOTIFY) + && !IS_ROOT(parent))) { + gparent = dget_parent(parent); + gdir = gparent->d_inode; + ii_read_lock_parent2(gdir); + hgdir = au_hi(gdir, bcpup); + ii_read_unlock(gdir); + dput(gparent); + } + dir = parent->d_inode; + h_parent = au_h_dptr(parent, bcpup); + h_dir = h_parent->d_inode; + + AuDbgSleep_UdbaRace(); + au_hdir_lock(h_dir, dir, bcpup); + //todo: revalidate the lower dentry? + + ndx.nfsmnt = au_nfsmnt(sb, bcpup); + ndx.flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_ndx(ndx.flags, DLGT); + ndx.nd = NULL; + //ndx.br = NULL; + //ndx.nd_file = NULL; + + if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) { + struct nameidata nd; + + if (unlikely(ndx.nfsmnt)) { + //todo: dirty + ndx.nd = &nd; + ndx.br = au_sbr(sb, bcpup); + memset(&nd, 0, sizeof(nd)); + nd.flags = LOOKUP_CREATE; + nd.intent.open.flags = O_EXCL; + } + err = au_may_add(dentry, bcpup, h_parent, + au_ftest_wrdir(wr_dir_args->flags, ISDIR), + &ndx); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_dir; + ndx.nd = NULL; + ndx.br = NULL; + } + + if (dt) + au_dtime_store(dt, parent, h_parent, hgdir); + wh_dentry = NULL; + if (/* bcpup != bstart || */ bcpup != au_dbwh(dentry)) + goto out; /* success */ + + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, &ndx); + //wh_dentry = ERR_PTR(-1); + + out_dir: + if (IS_ERR(wh_dentry)) + au_hdir_unlock(h_dir, dir, bcpup); + out: + AuTraceErrPtr(wh_dentry); + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +enum { Mknod, Symlink, Creat }; +struct simple_arg { + int type; + union { + struct { + int mode; + struct nameidata *nd; + } c; + struct { + const char *symname; + } s; + struct { + int mode; + dev_t dev; + } m; + } u; +}; + +static int add_simple(struct inode *dir, struct dentry *dentry, + struct simple_arg *arg) +{ + int err, dlgt, created; + struct dentry *h_dentry, *h_parent, *wh_dentry, *parent; + struct inode *h_dir; + struct au_dtime dt; + struct vfsub_args vargs; + struct super_block *sb; + aufs_bindex_t bstart; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY + }; + + LKTRTrace("type %d, %.*s\n", arg->type, AuDLNPair(dentry)); + IMustLock(dir); + + aufs_read_lock(dentry, AuLock_DW); + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, + &wr_dir_args); + //wh_dentry = ERR_PTR(-1); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + bstart = au_dbstart(dentry); + h_dentry = au_h_dptr(dentry, bstart); + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + sb = dir->i_sb; + dlgt = !!au_opt_test_dlgt(au_mntflags(sb)); + + switch (arg->type) { + case Creat: + AuDebugOn(au_test_nfs(h_dir->i_sb) && !arg->u.c.nd); + err = au_h_create(h_dir, h_dentry, arg->u.c.mode, dlgt, + arg->u.c.nd, au_nfsmnt(sb, bstart)); + break; + case Symlink: + err = vfsub_symlink(h_dir, h_dentry, arg->u.s.symname, + S_IALLUGO, dlgt); + break; + case Mknod: + err = vfsub_mknod(h_dir, h_dentry, arg->u.m.mode, arg->u.m.dev, + dlgt); + break; + default: + BUG(); + } + created = !err; + if (!err) + err = epilog(dir, wh_dentry, dentry); + //err = -1; + + /* revert */ + if (unlikely(created && err && h_dentry->d_inode)) { + int rerr; + vfsub_args_init(&vargs, NULL, dlgt, 0); + rerr = vfsub_unlink(h_dir, h_dentry, &vargs); + //rerr = -1; + if (rerr) { + AuIOErr("%.*s revert failure(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + } + //todo: inotify will be fired to the grand parent dir + au_dtime_revert(&dt); + d_drop(dentry); + } + + au_hdir_unlock(h_dir, dir, bstart); + dput(wh_dentry); + + out: + if (unlikely(err)) { + au_update_dbstart(dentry); + d_drop(dentry); + } + di_write_unlock(parent); + aufs_read_unlock(dentry, AuLock_DW); + AuTraceErr(err); + return err; +} + +int aufs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) +{ + struct simple_arg arg = { + .type = Mknod, + .u.m = { + .mode = mode, + .dev = dev + } + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) +{ + struct simple_arg arg = { + .type = Symlink, + .u.s.symname = symname + }; + return add_simple(dir, dentry, &arg); +} + +int aufs_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + struct simple_arg arg = { + .type = Creat, + .u.c = { + .mode = mode, + .nd = nd + } + }; + return add_simple(dir, dentry, &arg); +} + +/* ---------------------------------------------------------------------- */ + +struct link_arg { + aufs_bindex_t bdst, bsrc; + int issamedir, dlgt; + struct dentry *src_parent, *parent, *h_dentry; + struct inode *h_dir, *inode, *dir; +}; + +static int cpup_before_link(struct dentry *src_dentry, struct inode *dir, + struct link_arg *a) +{ + int err; + struct inode *hi, *h_dir, *src_dir, *gdir; + struct dentry *gparent; + + AuTraceEnter(); + + gparent = NULL; + gdir = NULL; + if (unlikely(au_opt_test(au_mntflags(src_dentry->d_sb), UDBA_INOTIFY) + && !IS_ROOT(a->src_parent))) { + gparent = dget_parent(a->src_parent); + gdir = gparent->d_inode; + if (gdir == dir) { + dput(gparent); + gparent = NULL; + } + } + src_dir = a->src_parent->d_inode; + h_dir = NULL; + + if (!a->issamedir) { + /* this temporary unlock/lock is safe */ + au_hdir_unlock(a->h_dir, dir, a->bdst); + di_read_lock_parent2(a->src_parent, AuLock_IR); + err = au_test_and_cpup_dirs(src_dentry, a->bdst, a->parent); + //err = -1; + if (unlikely(err)) { + au_hdir_lock(a->h_dir, dir, a->bdst); + goto out; + } + + //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. + if (unlikely(gparent)) + ii_read_lock_parent3(gdir); + h_dir = au_h_iptr(src_dir, a->bdst); + au_hdir_lock(h_dir, src_dir, a->bdst); + } else if (unlikely(gparent)) { + /* this temporary unlock/lock is safe */ + au_hdir_unlock(a->h_dir, dir, a->bdst); + ii_read_lock_parent3(gdir); + au_hdir_lock(a->h_dir, dir, a->bdst); + } + //todo: test parent-gparent relationship + + AuDebugOn(au_dbstart(src_dentry) != a->bsrc); + hi = au_h_dptr(src_dentry, a->bsrc)->d_inode; + mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); + err = au_sio_cpup_simple(src_dentry, a->bdst, -1, AuCpup_DTIME); + //err = -1; + mutex_unlock(&hi->i_mutex); + + if (unlikely(gparent)) { + ii_read_unlock(gdir); + dput(gparent); + } + + out: + if (h_dir) { + au_hdir_unlock(h_dir, src_dir, a->bdst); + au_hdir_lock(a->h_dir, dir, a->bdst); + } + if (!a->issamedir) + di_read_unlock(a->src_parent, AuLock_IR); + + AuTraceErr(err); + return err; +} + +static int cpup_or_link(struct dentry *src_dentry, struct link_arg *a) +{ + int err; + struct inode *inode, *h_inode, *h_dst_inode; + struct dentry *h_dentry; + aufs_bindex_t bstart; + struct super_block *sb; + + AuTraceEnter(); + + sb = src_dentry->d_sb; + inode = src_dentry->d_inode; + AuDebugOn(au_dbstart(src_dentry) != a->bsrc); + h_dentry = au_h_dptr(src_dentry, a->bsrc); + h_inode = h_dentry->d_inode; + bstart = au_ibstart(inode); + h_dst_inode = NULL; + if (bstart <= a->bdst) + h_dst_inode = au_h_iptr(inode, a->bdst); + + if (!h_dst_inode || !h_dst_inode->i_nlink) { + /* copyup src_dentry as the name of dentry. */ + au_set_dbstart(src_dentry, a->bdst); + au_set_h_dptr(src_dentry, a->bdst, dget(a->h_dentry)); + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1, + !AuCpup_DTIME); + //err = -1; + mutex_unlock(&h_inode->i_mutex); + au_set_h_dptr(src_dentry, a->bdst, NULL); + au_set_dbstart(src_dentry, a->bsrc); + } else { + /* the inode of src_dentry already exists on a.bdst branch */ + h_dentry = d_find_alias(h_dst_inode); + if (h_dentry) { + err = vfsub_link(h_dentry, a->h_dir, + a->h_dentry, a->dlgt); + dput(h_dentry); + } else { + AuIOErr("no dentry found for i%lu on b%d\n", + h_dst_inode->i_ino, a->bdst); + err = -EIO; + } + } + + if (!err) + au_plink_append(sb, a->inode, a->h_dentry, a->bdst); + + AuTraceErr(err); + return err; +} + +int aufs_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err, rerr; + struct dentry *h_parent, *wh_dentry, *h_src_dentry; + struct au_dtime dt; + struct link_arg a; + struct super_block *sb; + unsigned int mnt_flags; + struct vfsub_args vargs; + struct au_wr_dir_args wr_dir_args = { + //.force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY + }; + + LKTRTrace("src %.*s, i%lu, dst %.*s\n", + AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + IMustLock(src_dentry->d_inode); + AuDebugOn(S_ISDIR(src_dentry->d_inode->i_mode)); + + aufs_read_and_write_lock2(dentry, src_dentry, /*flags*/0); + sb = dentry->d_sb; + a.dir = dir; + a.src_parent = dget_parent(src_dentry); + a.parent = dentry->d_parent; /* dir inode is locked */ + a.issamedir = (a.src_parent == a.parent); + if (!a.issamedir) + au_fset_wrdir(wr_dir_args.flags, LOCK_SRCDIR); + wr_dir_args.force_btgt = au_dbstart(src_dentry); + di_write_lock_parent(a.parent); + wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); + wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &wr_dir_args); + //wh_dentry = ERR_PTR(-1); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + a.inode = src_dentry->d_inode; + a.bdst = au_dbstart(dentry); + a.h_dentry = au_h_dptr(dentry, a.bdst); + h_parent = a.h_dentry->d_parent; /* dir inode is locked */ + a.h_dir = h_parent->d_inode; + IMustLock(a.h_dir); + + err = 0; + mnt_flags = au_mntflags(sb); + a.dlgt = !!au_opt_test_dlgt(mnt_flags); + + //todo: minor optimize, their sb may be same while their bindex differs. + a.bsrc = au_dbstart(src_dentry); + h_src_dentry = au_h_dptr(src_dentry, a.bsrc); + if (unlikely(!au_opt_test(mnt_flags, PLINK))) { + /* + * copyup src_dentry to the branch we process, + * and then link(2) to it. + * gave up 'pseudo link by cpup' approach, + * since nlink may be one and some applications will not work. + */ + if (a.bdst < a.bsrc + /* && h_src_dentry->d_sb != a.h_dentry->d_sb */) + err = cpup_before_link(src_dentry, dir, &a); + if (!err) { + h_src_dentry = au_h_dptr(src_dentry, a.bdst); + err = vfsub_link(h_src_dentry, a.h_dir, a.h_dentry, + a.dlgt); + //err = -1; + } + } else { + if (a.bdst < a.bsrc + /* && h_src_dentry->d_sb != a.h_dentry->d_sb */) + err = cpup_or_link(src_dentry, &a); + else { + h_src_dentry = au_h_dptr(src_dentry, a.bdst); + err = vfsub_link(h_src_dentry, a.h_dir, a.h_dentry, + a.dlgt); + //err = -1; + } + } + if (unlikely(err)) + goto out_unlock; + if (wh_dentry) { + err = au_wh_unlink_dentry(a.h_dir, wh_dentry, dentry, + dir, /*dlgt*/0); + //err = -1; + if (unlikely(err)) + goto out_revert; + } + + dir->i_version++; + if (au_ibstart(dir) == au_dbstart(dentry)) + au_cpup_attr_timesizes(dir); + if (!d_unhashed(a.h_dentry) + /* || h_old_inode->i_nlink <= nlink */ + /* || SB_NFS(h_src_dentry->d_sb) */) { + dentry->d_inode = igrab(a.inode); + d_instantiate(dentry, a.inode); + inc_nlink(a.inode); + a.inode->i_ctime = dir->i_ctime; + } else + /* nfs case (< 2.6.15) */ + d_drop(dentry); + goto out_unlock; /* success */ + + out_revert: + vfsub_args_init(&vargs, NULL, a.dlgt, 0); + rerr = vfsub_unlink(a.h_dir, a.h_dentry, &vargs); + //rerr = -1; + if (!rerr) + goto out_dt; +// out_rerr: + AuIOErr("%.*s reverting failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + out_dt: + d_drop(dentry); + au_dtime_revert(&dt); + out_unlock: + au_hdir_unlock(a.h_dir, dir, a.bdst); + dput(wh_dentry); + out: + if (unlikely(err)) { + au_update_dbstart(dentry); + d_drop(dentry); + } + di_write_unlock(a.parent); + dput(a.src_parent); + aufs_read_and_write_unlock2(dentry, src_dentry); + AuTraceErr(err); + return err; +} + +int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err, rerr, diropq, dlgt; + struct dentry *h_dentry, *h_parent, *wh_dentry, *parent, *opq_dentry; + struct inode *h_dir, *h_inode; + struct au_dtime dt; + aufs_bindex_t bindex; + struct super_block *sb; + unsigned int mnt_flags; + struct vfsub_args vargs; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR + }; + + LKTRTrace("i%lu, %.*s, mode 0%o\n", + dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + + aufs_read_lock(dentry, AuLock_DW); + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, + &wr_dir_args); + //wh_dentry = ERR_PTR(-1); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + sb = dentry->d_sb; + bindex = au_dbstart(dentry); + h_dentry = au_h_dptr(dentry, bindex); + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + + err = vfsub_mkdir(h_dir, h_dentry, mode, dlgt); + //err = -1; + if (unlikely(err)) + goto out_unlock; + h_inode = h_dentry->d_inode; + + /* make the dir opaque */ + diropq = 0; + if (wh_dentry || au_opt_test(mnt_flags, ALWAYS_DIROPQ)) { + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + opq_dentry = au_diropq_create(dentry, bindex, /*dlgt*/0); + //opq_dentry = ERR_PTR(-1); + mutex_unlock(&h_inode->i_mutex); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) + goto out_dir; + dput(opq_dentry); + diropq = 1; + } + + err = epilog(dir, wh_dentry, dentry); + //err = -1; + if (!err) { + inc_nlink(dir); + goto out_unlock; /* success */ + } + + /* revert */ + if (diropq) { + LKTRLabel(revert opq); + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bindex, dlgt); + //rerr = -1; + mutex_unlock(&h_inode->i_mutex); + if (rerr) { + AuIOErr("%.*s reverting diropq failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + } + } + + out_dir: + LKTRLabel(revert dir); + vfsub_args_init(&vargs, NULL, dlgt, 0); + rerr = vfsub_rmdir(h_dir, h_dentry, &vargs); + //rerr = -1; + if (rerr) { + AuIOErr("%.*s reverting dir failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + err = -EIO; + } + d_drop(dentry); + au_dtime_revert(&dt); + out_unlock: + au_hdir_unlock(h_dir, dir, bindex); + dput(wh_dentry); + out: + if (unlikely(err)) { + au_update_dbstart(dentry); + d_drop(dentry); + } + di_write_unlock(parent); + aufs_read_unlock(dentry, AuLock_DW); + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/i_op.c linux-2.6.25.4-unionfs/fs/aufs/i_op.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/i_op.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/i_op.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,732 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode operations (except add/del/rename) + * + * $Id: i_op.c,v 1.5 2008/05/19 01:46:53 sfjro Exp $ + */ + +//#include +#include +//#include +//#include +#include +#include "aufs.h" + +static int h_permission(struct inode *h_inode, int mask, + struct nameidata *fake_nd, int brperm, int dlgt) +{ + int err, submask; + const int write_mask = (mask & (MAY_WRITE | MAY_APPEND)); + + LKTRTrace("ino %lu, mask 0x%x, brperm 0x%x\n", + h_inode->i_ino, mask, brperm); + + err = -EACCES; + if (unlikely((write_mask && IS_IMMUTABLE(h_inode)) + || ((mask & MAY_EXEC) && S_ISREG(h_inode->i_mode) + && fake_nd && fake_nd->path.mnt + && (fake_nd->path.mnt->mnt_flags & MNT_NOEXEC)) + )) + goto out; + + /* skip hidden fs test in the case of write to ro branch */ + submask = mask & ~MAY_APPEND; + if (unlikely((write_mask && !au_br_writable(brperm)) + || !h_inode->i_op + || !h_inode->i_op->permission)) { + //LKTRLabel(generic_permission); + err = generic_permission(h_inode, submask, NULL); + } else { + //LKTRLabel(h_inode->permission); + err = h_inode->i_op->permission(h_inode, submask, fake_nd); + AuTraceErr(err); + } + + if (!err) + err = au_security_inode_permission(h_inode, mask, fake_nd, + dlgt); + + out: + AuTraceErr(err); + return err; +} + +static int silly_lock(struct inode *inode, struct nameidata *nd) +{ + int locked = 0; + struct super_block *sb = inode->i_sb; + + LKTRTrace("i%lu, nd %p\n", inode->i_ino, nd); + + if (!nd || !nd->path.dentry) { + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); + } else if (nd->path.dentry->d_inode != inode) { + locked = 1; + /* lock child first, then parent */ + si_read_lock(sb, AuLock_FLUSH); + ii_read_lock_child(inode); + di_read_lock_parent(nd->path.dentry, 0); + } else { + locked = 2; + aufs_read_lock(nd->path.dentry, AuLock_FLUSH | AuLock_IR); + } + return locked; +} + +static void silly_unlock(int locked, struct inode *inode, struct nameidata *nd) +{ + struct super_block *sb = inode->i_sb; + + LKTRTrace("locked %d, i%lu, nd %p\n", locked, inode->i_ino, nd); + + switch (locked) { + case 0: + ii_read_unlock(inode); + si_read_unlock(sb); + break; + case 1: + di_read_unlock(nd->path.dentry, 0); + ii_read_unlock(inode); + si_read_unlock(sb); + break; + case 2: + aufs_read_unlock(nd->path.dentry, AuLock_FLUSH | AuLock_IR); + break; + default: + BUG(); + } +} + +static int aufs_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + int err, locked, dlgt; + aufs_bindex_t bindex, bend; + struct inode *h_inode; + struct super_block *sb; + unsigned int mnt_flags; + struct nameidata fake_nd, *p; + const int write_mask = (mask & (MAY_WRITE | MAY_APPEND)); + const int nondir = !S_ISDIR(inode->i_mode); + + LKTRTrace("ino %lu, mask 0x%x, nondir %d, write_mask %d, " + "nd %d{%d, %d}\n", + inode->i_ino, mask, nondir, write_mask, + !!nd, nd ? !!nd->path.dentry : 0, nd ? !!nd->path.mnt : 0); + + sb = inode->i_sb; + locked = silly_lock(inode, nd); + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + + if (nd) + fake_nd = *nd; + if (/* unlikely */(nondir || write_mask + || au_opt_test_dirperm1(mnt_flags))) { + h_inode = au_h_iptr(inode, au_ibstart(inode)); + AuDebugOn(!h_inode + || ((h_inode->i_mode & S_IFMT) + != (inode->i_mode & S_IFMT))); + err = 0; + bindex = au_ibstart(inode); + p = au_fake_dm(&fake_nd, nd, sb, bindex); + /* actual test will be delegated to LSM */ + if (IS_ERR(p)) + AuDebugOn(PTR_ERR(p) != -ENOENT); + else { + LKTRTrace("b%d\n", bindex); + err = h_permission(h_inode, mask, p, + au_sbr_perm(sb, bindex), dlgt); + au_fake_dm_release(p); + } + if (write_mask && !err) { + /* test whether the upper writable branch exists */ + err = -EROFS; + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) { + err = 0; + break; + } + } + goto out; + } + + /* non-write to dir */ + err = 0; + bend = au_ibend(inode); + for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { + h_inode = au_h_iptr(inode, bindex); + if (!h_inode) + continue; + AuDebugOn(!S_ISDIR(h_inode->i_mode)); + + p = au_fake_dm(&fake_nd, nd, sb, bindex); + /* actual test will be delegated to LSM */ + if (IS_ERR(p)) + AuDebugOn(PTR_ERR(p) != -ENOENT); + else { + LKTRTrace("b%d\n", bindex); + err = h_permission(h_inode, mask, p, + au_sbr_perm(sb, bindex), dlgt); + au_fake_dm_release(p); + } + } + + out: + silly_unlock(locked, inode, nd); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, + struct nameidata *nd) +{ + struct dentry *ret, *parent; + int err, npositive; + struct inode *inode, *h_inode; + struct nameidata tmp_nd, *ndp; + + LKTRTrace("dir %lu, %.*s, nd{0x%x}\n", + dir->i_ino, AuDLNPair(dentry), nd ? nd->flags : 0); + AuDebugOn(IS_ROOT(dentry)); + IMustLock(dir); + + /* nd can be NULL */ + parent = dentry->d_parent; /* dir inode is locked */ + aufs_read_lock(parent, AuLock_FLUSH); + err = au_alloc_dinfo(dentry); + //if (LktrCond) err = -1; + ret = ERR_PTR(err); + if (unlikely(err)) + goto out; + + ndp = au_dup_nd(au_sbi(dir->i_sb), &tmp_nd, nd); + npositive = au_lkup_dentry(dentry, au_dbstart(parent), /*type*/0, ndp); + err = npositive; + //err = -1; + ret = ERR_PTR(err); + if (unlikely(err < 0)) + goto out_unlock; + inode = NULL; + if (npositive) { + /* + * stop 'race'-ing between hardlinks under different parents. + */ + h_inode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; + AuDebugOn(!h_inode); + if (h_inode->i_nlink == 1 || S_ISDIR(h_inode->i_mode)) + inode = au_new_inode(dentry); + else { + static DEFINE_MUTEX(mtx); + mutex_lock(&mtx); + inode = au_new_inode(dentry); + mutex_unlock(&mtx); + } + ret = (void *)inode; + } + if (!IS_ERR(inode)) { + /* d_splice_alias() also supports d_add() */ + ret = d_splice_alias(inode, dentry); + if (unlikely(IS_ERR(ret) && inode)) + ii_write_unlock(inode); + //AuDbgDentry(dentry); + AuDebugOn(nd + && (nd->flags & LOOKUP_OPEN) + && nd->intent.open.file + && nd->intent.open.file->f_dentry); + au_store_fmode_exec(nd, inode); + } + + out_unlock: + di_write_unlock(dentry); + out: + aufs_read_unlock(parent, !AuLock_IR); + AuTraceErrPtr(ret); + return ret; +} + +/* ---------------------------------------------------------------------- */ + +/* + * decide the branch and the parent dir where we will create a new entry. + * returns new bindex or an error. + * copyup the parent dir if needed. + */ +int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, + struct au_wr_dir_args *args) +{ + int err; + aufs_bindex_t bcpup, bstart, src_bstart; + struct super_block *sb; + struct dentry *parent, *src_parent; + struct au_sbinfo *sbinfo; + const int add_entry = au_ftest_wrdir(args->flags, ADD_ENTRY); + const int lock_srcdir = au_ftest_wrdir(args->flags, LOCK_SRCDIR); + + LKTRTrace("%.*s, src %p, {%d, 0x%x}\n", + AuDLNPair(dentry), src_dentry, args->force_btgt, args->flags); + //AuDbgDentry(dentry); + + src_parent = NULL; + sb = dentry->d_sb; + sbinfo = au_sbi(sb); + parent = dget_parent(dentry); + bstart = au_dbstart(dentry); + bcpup = bstart; + if (args->force_btgt < 0) { + if (src_dentry) { + src_bstart = au_dbstart(src_dentry); + if (src_bstart < bstart) + bcpup = src_bstart; + } else if (add_entry) { + err = AuWbrCreate(sbinfo, dentry, + au_ftest_wrdir(args->flags, ISDIR)); + bcpup = err; + } + + if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { + if (add_entry) + err = AuWbrCopyup(sbinfo, dentry); + else { + di_read_lock_parent(parent, !AuLock_IR); + err = AuWbrCopyup(sbinfo, dentry); + di_read_unlock(parent, !AuLock_IR); + } + //err = -1; + bcpup = err; + if (unlikely(err < 0)) + goto out; + } + } else { + bcpup = args->force_btgt; + AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); + } + LKTRTrace("bstart %d, bcpup %d\n", bstart, bcpup); + if (bstart < bcpup) + au_update_dbrange(dentry, /*do_put_zero*/1); + + err = bcpup; + if (bcpup == bstart) + goto out; /* success */ + + /* copyup the new parent into the branch we process */ + if (src_dentry) { + src_parent = dget_parent(src_dentry); + if (lock_srcdir) + di_write_lock_parent2(src_parent); + } + + if (add_entry) { + au_update_dbstart(dentry); + IMustLock(parent->d_inode); + DiMustWriteLock(parent); + IiMustWriteLock(parent->d_inode); + } else + di_write_lock_parent(parent); + + err = 0; + if (!au_h_dptr(parent, bcpup)) { + if (bstart < bcpup) + err = au_cpdown_dirs(dentry, bcpup, src_parent); + else + err = au_cpup_dirs(dentry, bcpup, src_parent); + } + //err = -1; + if (!err && add_entry) { + struct dentry *h_parent; + struct inode *h_dir; + + h_parent = au_h_dptr(parent, bcpup); + AuDebugOn(!h_parent); + h_dir = h_parent->d_inode; + AuDebugOn(!h_dir); + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); + err = au_lkup_neg(dentry, bcpup); + //err = -1; + mutex_unlock(&h_dir->i_mutex); + if (bstart < bcpup && au_dbstart(dentry) < 0) { + au_set_dbstart(dentry, 0); + au_update_dbrange(dentry, /*do_put_zero*/0); + } + } + + if (!add_entry) + di_write_unlock(parent); + if (lock_srcdir) + di_write_unlock(src_parent); + dput(src_parent); + if (!err) + err = bcpup; /* success */ + //err = -EPERM; + out: + dput(parent); + LKTRTrace("err %d\n", err); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void au_hi_lock(struct inode *h_inode, int isdir, struct inode *inode, + aufs_bindex_t bindex) +{ + if (!isdir) + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + else + au_hdir2_lock(h_inode, inode, bindex); +} + +static void au_hi_unlock(struct inode *h_inode, int isdir, struct inode *inode, + aufs_bindex_t bindex) +{ + if (!isdir) + mutex_unlock(&h_inode->i_mutex); + else + au_hdir_unlock(h_inode, inode, bindex); +} + +struct au_icpup_args { + aufs_bindex_t btgt; + unsigned char isdir, did_cpup; /* flags */ + unsigned char hinotify; + struct dentry *parent, *gparent, *h_dentry; + struct inode *dir, *gdir, *h_inode, *h_dir; +}; + +static int au_lock_and_icpup(struct dentry *dentry, loff_t sz, + struct au_icpup_args *rargs) +{ + int err; + aufs_bindex_t bstart; + struct super_block *sb; + struct dentry *hi_wh; + struct inode *inode; + struct au_wr_dir_args wr_dir_args = { + .force_btgt = -1, + .flags = 0 + }; + + LKTRTrace("%.*s, %lld\n", AuDLNPair(dentry), sz); + + di_write_lock_child(dentry); + bstart = au_dbstart(dentry); + sb = dentry->d_sb; + inode = dentry->d_inode; + rargs->isdir = S_ISDIR(inode->i_mode); + if (rargs->isdir) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); + //err = -1; + if (unlikely(err < 0)) + goto out_dentry; + rargs->btgt = err; + rargs->did_cpup = (err != bstart); + err = 0; + + /* crazy udba locks */ + rargs->hinotify = !!au_opt_test(au_mntflags(sb), UDBA_INOTIFY); + if (unlikely(!IS_ROOT(dentry))) { + rargs->parent = dget_parent(dentry); + rargs->dir = rargs->parent->d_inode; + di_read_lock_parent(rargs->parent, AuLock_IR); + } + rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); + rargs->h_inode = rargs->h_dentry->d_inode; + AuDebugOn(!rargs->h_inode); + + if (!rargs->did_cpup) { + au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt); + //todo: revalidate the lower dentry? + goto out; /* success */ + } + + if (unlikely(rargs->hinotify + && rargs->parent + && !IS_ROOT(rargs->parent))) { + rargs->gparent = dget_parent(rargs->parent); + rargs->gdir = rargs->gparent->d_inode; + ii_read_lock_parent2(rargs->gdir); + } + + hi_wh = NULL; + rargs->h_dir = au_h_iptr(rargs->dir, rargs->btgt); + au_hdir_lock(rargs->h_dir, rargs->dir, rargs->btgt); + //todo: revalidate the lower dentry? + au_hi_lock(rargs->h_inode, rargs->isdir, inode, bstart); + if (!d_unhashed(dentry)) { + err = au_sio_cpup_simple(dentry, rargs->btgt, sz, AuCpup_DTIME); + if (!err) + rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); + } else { + hi_wh = au_hi_wh(inode, rargs->btgt); + if (!hi_wh) { + err = au_sio_cpup_wh(dentry, rargs->btgt, sz, + /*file*/NULL); + if (!err) + hi_wh = au_hi_wh(inode, rargs->btgt); + } + if (!hi_wh) + rargs->h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); + else + rargs->h_dentry = hi_wh; /* do not dget here */ + } + + //err = -1; + au_hi_unlock(rargs->h_inode, rargs->isdir, inode, bstart); + rargs->h_inode = rargs->h_dentry->d_inode; + AuDebugOn(!rargs->h_inode); + if (!err) + au_hi_lock(rargs->h_inode, rargs->isdir, inode, rargs->btgt); + au_hdir_unlock(rargs->h_dir, rargs->dir, rargs->btgt); + if (!err) + goto out; /* success */ + + au_hi_unlock(rargs->h_inode, rargs->isdir, inode, rargs->btgt); + if (unlikely(rargs->gdir)) { + ii_read_unlock(rargs->gdir); + dput(rargs->gparent); + } + if (unlikely(rargs->dir)) { + di_read_unlock(rargs->parent, AuLock_IR); + dput(rargs->parent); + } + + out_dentry: + di_write_unlock(dentry); + out: + AuTraceErr(err); + return err; +} + +static int aufs_setattr(struct dentry *dentry, struct iattr *ia) +{ + int err; + struct inode *inode; + struct au_hin_ignore ign; + struct vfsub_args vargs; + struct super_block *sb; + __u32 events; + struct file *file; + loff_t sz; + struct au_icpup_args rargs; + + LKTRTrace("%.*s, ia_valid 0x%x\n", AuDLNPair(dentry), ia->ia_valid); + inode = dentry->d_inode; + IMustLock(inode); + + sb = dentry->d_sb; + si_read_lock(sb, AuLock_FLUSH); + + file = NULL; + if (ia->ia_valid & ATTR_FILE) { + /* currently ftruncate(2) only */ + file = ia->ia_file; + fi_write_lock(file); + ia->ia_file = au_h_fptr(file, au_fbstart(file)); + } + + sz = -1; + if ((ia->ia_valid & ATTR_SIZE) + && ia->ia_size < i_size_read(inode)) + sz = ia->ia_size; + memset(&rargs, 0, sizeof(rargs)); + err = au_lock_and_icpup(dentry, sz, &rargs); + if (unlikely(err < 0)) + goto out; + + if ((ia->ia_valid & ATTR_SIZE) + && ia->ia_size < i_size_read(inode)) { + err = vmtruncate(inode, ia->ia_size); + if (unlikely(err)) + goto out_unlock; + } + + if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) + ia->ia_valid &= ~ATTR_MODE; + + events = 0; + vfsub_args_init(&vargs, &ign, au_opt_test_dlgt(au_mntflags(sb)), 0); + if (unlikely(rargs.hinotify && rargs.dir)) { + events = vfsub_events_notify_change(ia); + if (events) + vfsub_ign_hinode(&vargs, events, + au_hi(rargs.dir, rargs.btgt)); + } + err = vfsub_notify_change(rargs.h_dentry, ia, &vargs); + //err = -1; + if (!err) + au_cpup_attr_changeable(inode); + + out_unlock: + au_hi_unlock(rargs.h_inode, rargs.isdir, inode, rargs.btgt); + if (unlikely(rargs.gdir)) { + ii_read_unlock(rargs.gdir); + dput(rargs.gparent); + } + if (unlikely(rargs.dir)) { + di_read_unlock(rargs.parent, AuLock_IR); + dput(rargs.parent); + } + di_write_unlock(dentry); + out: + if (file) { + ia->ia_file = file; + //ia->ia_valid |= ATTR_FILE; + fi_write_unlock(file); + } + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, + int bufsiz) +{ + struct super_block *sb; + struct dentry *h_dentry; + + LKTRTrace("%.*s, b%d, %d\n", AuDLNPair(dentry), bindex, bufsiz); + + h_dentry = au_h_dptr(dentry, bindex); + if (unlikely(!h_dentry->d_inode->i_op + || !h_dentry->d_inode->i_op->readlink)) + return -EINVAL; + + sb = dentry->d_sb; + if (!au_test_ro(sb, bindex, dentry->d_inode)) { + touch_atime(au_sbr_mnt(sb, bindex), h_dentry); + au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/ + fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); + } + return h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); +} + +static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) +{ + int err; + + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), bufsiz); + + aufs_read_lock(dentry, AuLock_IR); + err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); + //err = -1; + aufs_read_unlock(dentry, AuLock_IR); + AuTraceErr(err); + return err; +} + +static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + int err; + char *buf; + mm_segment_t old_fs; + + LKTRTrace("%.*s, nd %.*s\n", + AuDLNPair(dentry), AuDLNPair(nd->path.dentry)); + + err = -ENOMEM; + buf = __getname(); + //buf = NULL; + if (unlikely(!buf)) + goto out; + + aufs_read_lock(dentry, AuLock_IR); + old_fs = get_fs(); + set_fs(KERNEL_DS); + err = h_readlink(dentry, au_dbstart(dentry), (char __user *)buf, + PATH_MAX); + //err = -1; + set_fs(old_fs); + aufs_read_unlock(dentry, AuLock_IR); + + if (err >= 0) { + buf[err] = 0; + /* will be freed by put_link */ + nd_set_link(nd, buf); + return NULL; /* success */ + } + __putname(buf); + + out: + path_put(&nd->path); + AuTraceErr(err); + return ERR_PTR(err); +} + +static void aufs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + __putname(nd_get_link(nd)); +} + +/* ---------------------------------------------------------------------- */ + +static void aufs_truncate_range(struct inode *inode, loff_t start, loff_t end) +{ + AuUnsupport(); +} + +/* ---------------------------------------------------------------------- */ + +struct inode_operations aufs_symlink_iop = { + .permission = aufs_permission, + .setattr = aufs_setattr, +#ifdef CONFIG_AUFS_WORKAROUND_FUSE + .getattr = aufs_getattr, +#endif + + .readlink = aufs_readlink, + .follow_link = aufs_follow_link, + .put_link = aufs_put_link +}; + +struct inode_operations aufs_dir_iop = { + .create = aufs_create, + .lookup = aufs_lookup, + .link = aufs_link, + .unlink = aufs_unlink, + .symlink = aufs_symlink, + .mkdir = aufs_mkdir, + .rmdir = aufs_rmdir, + .mknod = aufs_mknod, + .rename = aufs_rename, + + .permission = aufs_permission, + .setattr = aufs_setattr, +#ifdef CONFIG_AUFS_WORKAROUND_FUSE + .getattr = aufs_getattr, +#endif +}; + +struct inode_operations aufs_iop = { + .permission = aufs_permission, + .setattr = aufs_setattr, +#ifdef CONFIG_AUFS_WORKAROUND_FUSE + .getattr = aufs_getattr, +#endif + + //void (*truncate) (struct inode *); + .truncate_range = aufs_truncate_range, +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_del.c linux-2.6.25.4-unionfs/fs/aufs/i_op_del.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_del.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/i_op_del.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode operations (del entry) + * + * $Id: i_op_del.c,v 1.4 2008/05/04 23:54:53 sfjro Exp $ + */ + +#include "aufs.h" + +/* returns, + * 0: wh is unnecessary + * plus: wh is necessary + * minus: error + */ +int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup, + struct dentry *locked) +{ + int need_wh, err; + aufs_bindex_t bstart; + struct dentry *h_dentry; + struct super_block *sb; + + LKTRTrace("%.*s, isdir %d, *bcpup %d, locked %p\n", + AuDLNPair(dentry), isdir, *bcpup, locked); + sb = dentry->d_sb; + + bstart = au_dbstart(dentry); + LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart); + h_dentry = au_h_dptr(dentry, bstart); + if (*bcpup < 0) { + *bcpup = bstart; + if (au_test_ro(sb, bstart, dentry->d_inode)) { + err = AuWbrCopyup(au_sbi(sb), dentry); + *bcpup = err; + //err = -1; + if (unlikely(err < 0)) + goto out; + } + } else + AuDebugOn(bstart < *bcpup + || au_test_ro(sb, *bcpup, dentry->d_inode)); + LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart); + + if (*bcpup != bstart) { + err = au_cpup_dirs(dentry, *bcpup, locked); + //err = -1; + if (unlikely(err)) + goto out; + need_wh = 1; + } else { + //struct nameidata nd; + aufs_bindex_t old_bend, new_bend, bdiropq = -1; + old_bend = au_dbend(dentry); + if (isdir) { + bdiropq = au_dbdiropq(dentry); + au_set_dbdiropq(dentry, -1); + } + need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, + /*nd*/NULL); + err = need_wh; + //err = -1; + if (isdir) + au_set_dbdiropq(dentry, bdiropq); + if (unlikely(err < 0)) + goto out; + new_bend = au_dbend(dentry); + if (!need_wh && old_bend != new_bend) { + au_set_h_dptr(dentry, new_bend, NULL); + au_set_dbend(dentry, old_bend); + } + } + LKTRTrace("need_wh %d\n", need_wh); + err = need_wh; + + out: + AuTraceErr(err); + return err; +} + +/* + * simple tests for the removal inode operations. + * following the checks in vfs, plus the parent-child relationship. + */ +int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, + struct dentry *h_parent, int isdir, struct au_ndx *ndx) +{ + int err, exist; + struct super_block *sb; + struct dentry *h_dentry; + struct inode *h_inode; + umode_t h_mode; + + LKTRTrace("%.*s/%.*s, b%d, dir %d\n", + AuDLNPair(h_parent), AuDLNPair(dentry), bindex, isdir); + + sb = dentry->d_sb; + exist = !!dentry->d_inode; + h_dentry = au_h_dptr(dentry, bindex); + h_inode = h_dentry->d_inode; + if (exist) { + err = -ENOENT; + if (unlikely(!h_inode || !h_inode->i_nlink)) + goto out; + + h_mode = h_inode->i_mode; + if (!isdir) { + err = -EISDIR; + if (unlikely(S_ISDIR(h_mode))) + goto out; + } else if (unlikely(!S_ISDIR(h_mode))) { + err = -ENOTDIR; + goto out; + } + } else { + /* rename(2) case */ + err = -EIO; + if (unlikely(h_inode)) + goto out; + } + + err = -ENOENT; + /* expected parent dir is locked */ + if (unlikely(h_parent != h_dentry->d_parent)) + goto out; + err = 0; + + /* + * some filesystem may unlink a dir and corrupt its consistency. + * so let's try heavy test. + */ + if (1 /*unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY))*/) { + struct dentry *h_latest; + struct qstr *qstr = &dentry->d_name; + + err = -EACCES; + if (unlikely(au_test_h_perm(h_parent->d_inode, + MAY_EXEC | MAY_WRITE, + au_ftest_ndx(ndx->flags, DLGT)))) + goto out; + + h_latest = au_sio_lkup_one(qstr->name, h_parent, qstr->len, + ndx); + //err = PTR_ERR(h_latest); + err = -EIO; + if (IS_ERR(h_latest)) + goto out; + dput(h_latest); + if (h_latest == h_dentry) + err = 0; + } + + out: + AuTraceErr(err); + return err; +} + +static struct dentry * +lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, + struct au_dtime *dt) +{ + struct dentry *wh_dentry; + int err, need_wh; + struct dentry *h_parent, *parent, *gparent; + struct inode *dir, *h_dir, *gdir; + struct au_ndx ndx; + struct super_block *sb; + struct au_hinode *hgdir; + aufs_bindex_t bcpup; + unsigned int mnt_flags; + + LKTRTrace("%.*s, isdir %d\n", AuDLNPair(dentry), isdir); + + need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup, NULL); + err = need_wh; + //err = -1; + wh_dentry = ERR_PTR(err); + if (unlikely(err < 0)) + goto out; + + //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. + hgdir = NULL; + bcpup = *rbcpup; + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + parent = dentry->d_parent; /* dir inode is locked */ + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) + && !IS_ROOT(parent))) { + gparent = dget_parent(parent); + gdir = gparent->d_inode; + ii_read_lock_parent2(gdir); + hgdir = au_hi(gdir, bcpup); + ii_read_unlock(gdir); + dput(gparent); + } + dir = parent->d_inode; + IMustLock(dir); + h_parent = au_h_dptr(parent, bcpup); + h_dir = h_parent->d_inode; + + AuDbgSleep_UdbaRace(); + au_hdir_lock(h_dir, dir, bcpup); + //todo: revalidate the lower dentry? + + if (!au_opt_test(mnt_flags, UDBA_NONE) && au_dbstart(dentry) == bcpup) { + ndx.nfsmnt = au_nfsmnt(sb, bcpup); + ndx.flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_ndx(ndx.flags, DLGT); + ndx.nd = NULL; + //ndx.br = au_sbr(sb, bcpup); + //ndx.nd_file = NULL; + err = au_may_del(dentry, bcpup, h_parent, isdir, &ndx); + wh_dentry = ERR_PTR(err); + if (unlikely(err)) + goto out_dir; + } + + au_dtime_store(dt, parent, h_parent, hgdir); + wh_dentry = NULL; + if (!need_wh) + goto out; /* success, no need to create whiteout */ + + ndx.nfsmnt = au_nfsmnt(sb, bcpup); + ndx.flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_ndx(ndx.flags, DLGT); + ndx.nd = NULL; + //ndx.br = NULL; + wh_dentry = au_wh_create(dir, dentry, bcpup, h_parent, &ndx); + //wh_dentry = ERR_PTR(-1); + if (!IS_ERR(wh_dentry)) + goto out; /* success */ + /* returns with the parent is locked and wh_dentry is DGETed */ + + out_dir: + au_hdir_unlock(h_dir, dir, bcpup); + out: + AuTraceErrPtr(wh_dentry); + return wh_dentry; +} + +static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, + struct au_nhash *whlist, struct inode *dir) +{ + int rmdir_later, err; + struct dentry *h_dentry; + struct inode *inode, *h_inode; + struct super_block *sb; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex); + + inode = NULL; + h_inode = NULL; + sb = dentry->d_sb; + if (unlikely(au_opt_test(au_mntflags(sb), UDBA_INOTIFY))) { + inode = dentry->d_inode; + h_inode = au_h_iptr(inode, bindex); + au_hdir2_lock(h_inode, inode, bindex); + } + err = au_whtmp_ren(dir, dentry, bindex, /*noself*/1); + if (unlikely(inode)) + au_hdir_unlock(h_inode, inode, bindex); + //err = -1; + if (unlikely(err)) + goto out; + + h_dentry = au_h_dptr(dentry, bindex); + if (!au_test_nfs(h_dentry->d_sb)) { + const int dirwh = au_sbi(sb)->si_dirwh; + rmdir_later = (dirwh <= 1); + if (!rmdir_later) + rmdir_later = au_nhash_test_longer_wh(whlist, bindex, + dirwh); + if (rmdir_later) + return rmdir_later; + } + + err = au_whtmp_rmdir(h_dentry, whlist, bindex, dir, dentry->d_inode, + /*noself*/1); + //err = -1; + if (unlikely(err)) { + AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n", + AuDLNPair(h_dentry), bindex, err); + err = 0; + } + + out: + AuTraceErr(err); + return err; +} + +static void epilog(struct inode *dir, struct dentry *dentry, + aufs_bindex_t bindex) +{ + //todo: unnecessary? + d_drop(dentry); + dentry->d_inode->i_ctime = dir->i_ctime; + + if (atomic_read(&dentry->d_count) == 1) { + au_set_h_dptr(dentry, au_dbstart(dentry), NULL); + au_update_dbstart(dentry); + } + if (au_ibstart(dir) == bindex) + au_cpup_attr_timesizes(dir); + dir->i_version++; +} + +/* revert flags */ +#define AuRev_DLGT 1 +#define au_ftest_rev(flags, name) ((flags) & AuRev_##name) +#define au_fset_rev(flags, name) { (flags) |= AuRev_##name; } +#define au_fclr_rev(flags, name) { (flags) &= ~AuRev_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuRev_DLGT +#define AuRev_DLGT 0 +#endif + +static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry, + aufs_bindex_t bwh, struct au_dtime *dt, unsigned int flags) +{ + int rerr; + struct inode *dir; + + dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(dir); + rerr = au_wh_unlink_dentry(dir, wh_dentry, dentry, dir, + au_ftest_rev(flags, DLGT)); + //rerr = -1; + if (!rerr) { + au_set_dbwh(dentry, bwh); + au_dtime_revert(dt); + return 0; + } + + AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", + AuDLNPair(dentry), err, rerr); + return -EIO; +} + +/* ---------------------------------------------------------------------- */ + +int aufs_unlink(struct inode *dir, struct dentry *dentry) +{ + int err, dlgt; + struct inode *inode, *h_dir; + struct dentry *parent, *wh_dentry, *h_dentry; + struct au_dtime dt; + aufs_bindex_t bwh, bindex, bstart; + struct super_block *sb; + struct vfsub_args vargs; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + inode = dentry->d_inode; + if (unlikely(!inode)) + return -ENOENT; /* possible? */ + IMustLock(inode); + + aufs_read_lock(dentry, AuLock_DW); + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + + bstart = au_dbstart(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt); + //wh_dentry = ERR_PTR(-1); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + sb = dir->i_sb; + dlgt = !!au_opt_test_dlgt(au_mntflags(sb)); + AuDebugOn(au_dbstart(dentry) != bstart); + h_dentry = au_h_dptr(dentry, bstart); + dget(h_dentry); + + if (bindex == bstart) { + vfsub_args_init(&vargs, NULL, dlgt, 0); + h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(h_dir); + err = vfsub_unlink(h_dir, h_dentry, &vargs); + //err = -1; + } else { + /* dir inode is locked */ + AuDebugOn(!wh_dentry + || wh_dentry->d_parent != au_h_dptr(parent, bindex)); + h_dir = wh_dentry->d_parent->d_inode; + IMustLock(h_dir); + err = 0; + } + + if (!err) { + drop_nlink(inode); + epilog(dir, dentry, bindex); + + /* update target timestamps */ + if (bindex == bstart) { + au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/ + inode->i_ctime = h_dentry->d_inode->i_ctime; + } else + //todo: this timestamp may be reverted later + inode->i_ctime = h_dir->i_ctime; + goto out_unlock; /* success */ + } + + /* revert */ + if (wh_dentry) { + int rerr; + unsigned int rev_flags; + + rev_flags = 0; + if (unlikely(dlgt)) + au_fset_rev(rev_flags, DLGT); + rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, rev_flags); + if (rerr) + err = rerr; + } + + out_unlock: + au_hdir_unlock(h_dir, dir, bindex); + dput(wh_dentry); + dput(h_dentry); + out: + di_write_unlock(parent); + aufs_read_unlock(dentry, AuLock_DW); + AuTraceErr(err); + return err; +} + +int aufs_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err, rmdir_later; + struct inode *inode, *h_dir; + struct dentry *parent, *wh_dentry, *h_dentry; + struct au_dtime dt; + aufs_bindex_t bwh, bindex, bstart; + struct au_whtmp_rmdir_args *args; + struct au_nhash *whlist; + struct super_block *sb; + unsigned int mnt_flags; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + inode = dentry->d_inode; + if (unlikely(!inode)) + return -ENOENT; /* possible? */ + IMustLock(inode); + + whlist = au_nhash_new(GFP_TEMPORARY); + err = PTR_ERR(whlist); + if (IS_ERR(whlist)) + goto out; + + err = -ENOMEM; + args = kmalloc(sizeof(*args), GFP_TEMPORARY); + //args = NULL; + if (unlikely(!args)) + goto out_whlist; + + aufs_read_lock(dentry, AuLock_DW); + parent = dentry->d_parent; /* dir inode is locked */ + di_write_lock_parent(parent); + err = au_test_empty(dentry, whlist); + //err = -1; + if (unlikely(err)) + goto out_args; + + bstart = au_dbstart(dentry); + bwh = au_dbwh(dentry); + bindex = -1; + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt); + //wh_dentry = ERR_PTR(-1); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_args; + + AuDebugOn(au_dbstart(dentry) != bstart); + h_dentry = au_h_dptr(dentry, bstart); + dget(h_dentry); + + rmdir_later = 0; + if (bindex == bstart) { + h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(h_dir); + err = renwh_and_rmdir(dentry, bstart, whlist, dir); + //err = -1; + if (err > 0) { + rmdir_later = err; + err = 0; + } + } else { + /* dir inode is locked */ + AuDebugOn(!wh_dentry + || wh_dentry->d_parent != au_h_dptr(parent, bindex)); + h_dir = wh_dentry->d_parent->d_inode; + IMustLock(h_dir); + err = 0; + } + + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + if (!err) { + //aufs_bindex_t bi, bend; + + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) + && rmdir_later)) + au_reset_hinotify(inode, /*flags*/0); + clear_nlink(inode); + au_set_dbdiropq(dentry, -1); + epilog(dir, dentry, bindex); + + if (rmdir_later) { + au_whtmp_kick_rmdir(h_dentry, whlist, bstart, dir, + inode, /*noself*/1, args); + args = NULL; + } + + goto out_unlock; /* success */ + } + + /* revert */ + LKTRLabel(revert); + if (wh_dentry) { + int rerr; + unsigned int rev_flags; + + rev_flags = 0; + if (unlikely(au_opt_test_dlgt(mnt_flags))) + au_fset_rev(rev_flags, DLGT); + rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, rev_flags); + if (rerr) + err = rerr; + } + + out_unlock: + au_hdir_unlock(h_dir, dir, bindex); + dput(wh_dentry); + dput(h_dentry); + out_args: + di_write_unlock(parent); + aufs_read_unlock(dentry, AuLock_DW); + kfree(args); + out_whlist: + au_nhash_del(whlist); + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_ren.c linux-2.6.25.4-unionfs/fs/aufs/i_op_ren.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/i_op_ren.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/i_op_ren.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,812 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * inode operation (rename entry) + * todo: this is monster + * + * $Id: i_op_ren.c,v 1.4 2008/05/04 23:51:40 sfjro Exp $ + */ + +#include "aufs.h" + +enum { SRC, DST }; + +#define AuRen_ISDIR 1 +#define AuRen_ISSAMEDIR (1 << 1) +#define AuRen_WHSRC (1 << 2) +#define AuRen_WHDST (1 << 3) +#define AuRen_DLGT (1 << 4) +#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) +#define au_fset_ren(flags, name) { (flags) |= AuRen_##name; } +#define au_fclr_ren(flags, name) { (flags) &= ~AuRen_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuRen_DLGT +#define AuRen_DLGT 0 +#endif + +struct rename_args { + struct dentry *h_dentry[2], *parent[2], *h_parent[2], *h_trap; + struct au_nhash whlist; + aufs_bindex_t btgt, bstart[2]; + struct super_block *sb; + unsigned int flags; + unsigned int mnt_flags; +}; + +static noinline_for_stack int +do_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, struct rename_args *a) +{ + int err, need_diropq, bycpup, rerr; + struct au_whtmp_rmdir_args *thargs; + struct dentry *wh_dentry[2], *h_dst, *h_src; + struct inode *h_dir[2]; + aufs_bindex_t bindex, bend; + struct au_hin_ignore ign; + struct vfsub_args vargs; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + LKTRTrace("%.*s/%.*s, %.*s/%.*s, " + "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, " + "flags 0x%x\n", + AuDLNPair(a->parent[SRC]), AuDLNPair(src_dentry), + AuDLNPair(a->parent[DST]), AuDLNPair(dentry), + a->h_dentry[SRC], a->h_dentry[DST], + a->h_parent[SRC], a->h_parent[DST], + &a->whlist, a->btgt, + a->bstart[SRC], a->bstart[DST], + a->flags); + + h_dir[SRC] = a->h_parent[SRC]->d_inode; + h_dir[DST] = a->h_parent[DST]->d_inode; + + /* prepare workqueue args */ + h_dst = NULL; + thargs = NULL; + if (au_ftest_ren(a->flags, ISDIR) && a->h_dentry[DST]->d_inode) { + err = -ENOMEM; + thargs = kmalloc(sizeof(*thargs), GFP_TEMPORARY); + //thargs = NULL; + if (unlikely(!thargs)) + goto out; + h_dst = dget(a->h_dentry[DST]); + } + + wh_dentry[SRC] = NULL; + wh_dentry[DST] = NULL; + ndx.nfsmnt = au_nfsmnt(a->sb, a->btgt); + if (unlikely(au_ftest_ren(a->flags, DLGT))) + au_fset_ndx(ndx.flags, DLGT); + + /* create whiteout for src_dentry */ + if (au_ftest_ren(a->flags, WHSRC)) { + wh_dentry[SRC] = au_wh_create(src_dir, src_dentry, a->btgt, + a->h_parent[SRC], &ndx); + //wh_dentry[SRC] = ERR_PTR(-1); + err = PTR_ERR(wh_dentry[SRC]); + if (IS_ERR(wh_dentry[SRC])) + goto out_thargs; + } + + /* lookup whiteout for dentry */ + if (au_ftest_ren(a->flags, WHDST)) { + struct dentry *d; + + d = au_wh_lkup(a->h_parent[DST], &dentry->d_name, &ndx); + //d = ERR_PTR(-1); + err = PTR_ERR(d); + if (IS_ERR(d)) + goto out_whsrc; + if (!d->d_inode) + dput(d); + else + wh_dentry[DST] = d; + } + + /* rename dentry to tmpwh */ + if (thargs) { + err = au_whtmp_ren(dir, dentry, a->btgt, /*noself*/0); + //err = -1; + if (unlikely(err)) + goto out_whdst; + au_set_h_dptr(dentry, a->btgt, NULL); + err = au_lkup_neg(dentry, a->btgt); + //err = -1; + if (unlikely(err)) + goto out_whtmp; + a->h_dentry[DST] = au_h_dptr(dentry, a->btgt); + } + + /* cpup src */ + if (a->h_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) { + mutex_lock_nested(&a->h_dentry[SRC]->d_inode->i_mutex, + AuLsc_I_CHILD); + err = au_sio_cpup_simple(src_dentry, a->btgt, -1, + !AuCpup_DTIME); + //err = -1; // untested dir + mutex_unlock(&a->h_dentry[SRC]->d_inode->i_mutex); + if (unlikely(err)) + goto out_whtmp; + } + + /* rename by vfs_rename or cpup */ + need_diropq = au_ftest_ren(a->flags, ISDIR) + && (wh_dentry[DST] + || au_dbdiropq(dentry) == a->btgt + /* hide the lower to keep xino */ + || a->btgt < au_dbend(dentry) + || au_opt_test(a->mnt_flags, ALWAYS_DIROPQ)); + bycpup = 0; + if (au_dbstart(src_dentry) == a->btgt) { + if (need_diropq && au_dbdiropq(src_dentry) == a->btgt) + need_diropq = 0; + vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0); + if (unlikely(au_opt_test(a->mnt_flags, UDBA_INOTIFY) + && au_ftest_ren(a->flags, ISDIR))) + vfsub_ign_hinode(&vargs, IN_MOVE_SELF, + au_hi(src_dentry->d_inode, a->btgt)); + AuDebugOn(au_dbstart(src_dentry) != a->btgt); + err = vfsub_rename(h_dir[SRC], au_h_dptr(src_dentry, a->btgt), + h_dir[DST], a->h_dentry[DST], &vargs); + //err = -1; + } else { + bycpup = 1; + mutex_lock_nested(&a->h_dentry[SRC]->d_inode->i_mutex, + AuLsc_I_CHILD); + au_set_dbstart(src_dentry, a->btgt); + au_set_h_dptr(src_dentry, a->btgt, dget(a->h_dentry[DST])); + err = au_sio_cpup_single(src_dentry, a->btgt, a->bstart[SRC], + -1, !AuCpup_DTIME); + //err = -1; // untested dir + if (unlikely(err)) { + au_set_h_dptr(src_dentry, a->btgt, NULL); + au_set_dbstart(src_dentry, a->bstart[SRC]); + } + mutex_unlock(&a->h_dentry[SRC]->d_inode->i_mutex); + } + if (unlikely(err)) + goto out_whtmp; + + /* make dir opaque */ + if (need_diropq) { + struct dentry *diropq; + struct inode *h_inode; + + h_inode = au_h_dptr(src_dentry, a->btgt)->d_inode; + au_hdir_lock(h_inode, src_dentry->d_inode, a->btgt); + diropq = au_diropq_create(src_dentry, a->btgt, + au_ftest_ren(a->flags, DLGT)); + //diropq = ERR_PTR(-1); + au_hdir_unlock(h_inode, src_dentry->d_inode, a->btgt); + err = PTR_ERR(diropq); + if (IS_ERR(diropq)) + goto out_rename; + dput(diropq); + } + + /* update target timestamps */ + AuDebugOn(au_dbstart(src_dentry) != a->btgt); + h_src = au_h_dptr(src_dentry, a->btgt); + au_update_fuse_h_inode(NULL, h_src); /*ignore*/ + //fsstack_copy_attr_atime(src_dentry->d_inode, h_src->d_inode); + src_dentry->d_inode->i_ctime = h_src->d_inode->i_ctime; + + /* remove whiteout for dentry */ + if (wh_dentry[DST]) { + err = au_wh_unlink_dentry(h_dir[DST], wh_dentry[DST], + dentry, dir, /*dlgt*/0); + //err = -1; + if (unlikely(err)) + goto out_diropq; + } + + /* remove whtmp */ + if (thargs) { + if (au_test_nfs(h_dst->d_sb) + || !au_nhash_test_longer_wh(&a->whlist, a->btgt, + au_sbi(a->sb)->si_dirwh)) { + err = au_whtmp_rmdir(h_dst, &a->whlist, a->btgt, dir, + dentry->d_inode, /*noself*/0); + if (unlikely(err)) + AuWarn("failed removing whtmp dir %.*s (%d), " + "ignored.\n", AuDLNPair(h_dst), err); + } else { + au_whtmp_kick_rmdir(h_dst, &a->whlist, a->btgt, dir, + dentry->d_inode, /*noself*/0, + thargs); + dput(h_dst); + thargs = NULL; + } + } + err = 0; + goto out_success; + +#define RevertFailure(fmt, args...) do { \ + AuIOErrWhck("revert failure: " fmt " (%d, %d)\n", \ + ##args, err, rerr); \ + err = -EIO; \ + } while (0) + + out_diropq: + if (need_diropq) { + struct inode *h_inode; + + h_inode = au_h_dptr(src_dentry, a->btgt)->d_inode; + //br_wh_read_lock(au_sbr(a->sb, a->btgt)); + /* i_lock simply since inotify is not set to h_inode. */ + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_PARENT); + //au_hdir_lock(h_inode, src_dentry->d_inode, a->btgt); + rerr = au_diropq_remove(src_dentry, a->btgt, + au_ftest_ren(a->flags, DLGT)); + //rerr = -1; + //au_hdir_unlock(h_inode, src_dentry->d_inode, a->btgt); + mutex_unlock(&h_inode->i_mutex); + //br_wh_read_unlock(au_sbr(a->sb, a->btgt)); + if (rerr) + RevertFailure("remove diropq %.*s", + AuDLNPair(src_dentry)); + } + out_rename: + if (!bycpup) { + struct dentry *d; + struct qstr *name = &src_dentry->d_name; + + d = au_lkup_one(name->name, a->h_parent[SRC], name->len, &ndx); + //d = ERR_PTR(-1); + rerr = PTR_ERR(d); + if (IS_ERR(d)) { + RevertFailure("au_lkup_one %.*s", + AuDLNPair(src_dentry)); + goto out_whtmp; + } + AuDebugOn(d->d_inode); + vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0); + if (unlikely(au_opt_test(a->mnt_flags, UDBA_INOTIFY) + && au_ftest_ren(a->flags, ISDIR))) + vfsub_ign_hinode(&vargs, IN_MOVE_SELF, + au_hi(src_dentry->d_inode, a->btgt)); + rerr = vfsub_rename(h_dir[DST], au_h_dptr(src_dentry, a->btgt), + h_dir[SRC], d, &vargs); + //rerr = -1; + d_drop(d); + dput(d); + //au_set_h_dptr(src_dentry, a->btgt, NULL); + if (rerr) + RevertFailure("rename %.*s", AuDLNPair(src_dentry)); + } else { + vfsub_args_init(&vargs, NULL, au_ftest_ren(a->flags, DLGT), 0); + rerr = vfsub_unlink(h_dir[DST], a->h_dentry[DST], &vargs); + //rerr = -1; + au_set_h_dptr(src_dentry, a->btgt, NULL); + au_set_dbstart(src_dentry, a->bstart[SRC]); + if (rerr) + RevertFailure("unlink %.*s", + AuDLNPair(a->h_dentry[DST])); + } + out_whtmp: + if (thargs) { + struct dentry *d; + struct qstr *name = &dentry->d_name; + + d = au_lkup_one(name->name, a->h_parent[DST], name->len, &ndx); + //d = ERR_PTR(-1); + rerr = PTR_ERR(d); + if (IS_ERR(d)) { + RevertFailure("lookup %.*s", AuLNPair(name)); + goto out_whdst; + } + if (d->d_inode) { + d_drop(d); + dput(d); + goto out_whdst; + } + AuDebugOn(d->d_inode); + vfsub_args_init(&vargs, &ign, au_ftest_ren(a->flags, DLGT), 0); + if (unlikely(0 && au_opt_test(a->mnt_flags, UDBA_INOTIFY) + && au_ftest_ren(a->flags, ISDIR))) + vfsub_ign_hinode(&vargs, IN_MOVE_SELF, + au_hi(dentry->d_inode, a->btgt)); + rerr = vfsub_rename(h_dir[DST], h_dst, h_dir[DST], d, &vargs); + //rerr = -1; + d_drop(d); + dput(d); + if (rerr) { + RevertFailure("rename %.*s", AuDLNPair(h_dst)); + goto out_whdst; + } + au_set_h_dptr(dentry, a->btgt, NULL); + au_set_h_dptr(dentry, a->btgt, dget(h_dst)); + } + out_whdst: + dput(wh_dentry[DST]); + wh_dentry[DST] = NULL; + out_whsrc: + if (wh_dentry[SRC]) { + rerr = au_wh_unlink_dentry(h_dir[SRC], wh_dentry[SRC], + src_dentry, src_dir, /*dlgt*/0); + //rerr = -1; + if (rerr) + RevertFailure("unlink %.*s", AuDLNPair(wh_dentry[SRC])); + } +#undef RevertFailure + d_drop(src_dentry); + bend = au_dbend(src_dentry); + for (bindex = au_dbstart(src_dentry); bindex <= bend; bindex++) { + struct dentry *hd; + + hd = au_h_dptr(src_dentry, bindex); + if (hd) + d_drop(hd); + } + d_drop(dentry); + bend = au_dbend(dentry); + for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { + struct dentry *hd; + + hd = au_h_dptr(dentry, bindex); + if (hd) + d_drop(hd); + } + au_update_dbstart(dentry); + if (thargs) + d_drop(h_dst); + out_success: + dput(wh_dentry[SRC]); + dput(wh_dentry[DST]); + out_thargs: + if (thargs) { + dput(h_dst); + kfree(thargs); + } + out: + AuTraceErr(err); + return err; +} + +/* + * test if @dentry dir can be rename destination or not. + * success means, it is a logically empty dir. + */ +static int may_rename_dstdir(struct dentry *dentry, aufs_bindex_t btgt, + struct au_nhash *whlist) +{ + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + return au_test_empty(dentry, whlist); +} + +/* + * test if @dentry dir can be rename source or not. + * if it can, return 0 and @children is filled. + * success means, + * - or, it is a logically empty dir. + * - or, it exists on writable branch and has no children including whiteouts + * on the lower branch. + */ +static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt) +{ + int err; + aufs_bindex_t bstart; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + bstart = au_dbstart(dentry); + if (bstart != btgt) { + struct au_nhash *whlist; + + whlist = au_nhash_new(GFP_TEMPORARY); + err = PTR_ERR(whlist); + if (IS_ERR(whlist)) + goto out; + err = au_test_empty(dentry, whlist); + au_nhash_del(whlist); + goto out; + } + + if (bstart == au_dbtaildir(dentry)) + return 0; /* success */ + + err = au_test_empty_lower(dentry); + + out: + if (/* unlikely */(err == -ENOTEMPTY)) { + AuWarn1("renaming dir who has child(ren) on multiple branches," + " is not supported\n"); + err = -EXDEV; + } + AuTraceErr(err); + return err; +} + +/* mainly for link(2) and rename(2) */ +int au_wbr(struct dentry *dentry, aufs_bindex_t btgt) +{ + aufs_bindex_t bdiropq, bwh; + struct dentry *parent; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), btgt); + parent = dentry->d_parent; + IMustLock(parent->d_inode); /* dir is locked */ + + bdiropq = au_dbdiropq(parent); + bwh = au_dbwh(dentry); + if (au_br_rdonly(au_sbr(dentry->d_sb, btgt)) + || (0 <= bdiropq && bdiropq < btgt) + || (0 <= bwh && bwh < btgt)) + btgt = -1; + + LKTRTrace("btgt %d\n", btgt); + return btgt; +} + +//todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled. +static void au_hgdirs(struct au_hinode **hgdir, struct rename_args *a) +{ + struct dentry *gparent[2]; + struct inode *gdir; + + if (!au_opt_test(a->mnt_flags, UDBA_INOTIFY)) + return; + + gparent[SRC] = NULL; + if (!IS_ROOT(a->parent[SRC])) { + gparent[SRC] = dget_parent(a->parent[SRC]); + gdir = gparent[SRC]->d_inode; + if (gparent[SRC] != a->parent[DST]) { + ii_read_lock_parent3(gdir); + hgdir[SRC] = au_hi(gdir, a->btgt); + ii_read_unlock(gdir); + } else + hgdir[SRC] = au_hi(gdir, a->btgt); + dput(gparent[SRC]); + } + + if (!au_ftest_ren(a->flags, ISSAMEDIR) + && !IS_ROOT(a->parent[DST]) + && a->parent[DST] != gparent[SRC]) { + gparent[DST] = dget_parent(a->parent[DST]); + gdir = gparent[DST]->d_inode; + if (gparent[DST] != a->parent[SRC]) { + ii_read_lock_parent3(gdir); + hgdir[DST] = au_hi(gdir, a->btgt); + ii_read_unlock(gdir); + } else + hgdir[DST] = au_hi(gdir, a->btgt); + dput(gparent[DST]); + } +} + +/* + * simple tests for rename. + * following the checks in vfs, plus the parent-child relationship. + */ +static int au_may_ren(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + struct rename_args *a, struct au_ndx *ndx) +{ + int err; + struct inode *h_inode; + + AuTraceEnter(); + + if (a->bstart[SRC] == a->btgt) { + err = au_may_del(src_dentry, a->btgt, a->h_parent[SRC], + au_ftest_ren(a->flags, ISDIR), ndx); + if (unlikely(err)) + goto out; + err = -EINVAL; + if (unlikely(a->h_dentry[SRC] == a->h_trap)) + goto out; + } + + err = 0; + if (a->bstart[DST] != a->btgt) + goto out; + + err = -EIO; + h_inode = a->h_dentry[DST]->d_inode; + if (!dentry->d_inode) { + if (unlikely(h_inode)) + goto out; + err = au_may_add(dentry, a->btgt, a->h_parent[DST], + au_ftest_ren(a->flags, ISDIR), ndx); + } else { + if (unlikely(!h_inode || !h_inode->i_nlink)) + goto out; + err = au_may_del(dentry, a->btgt, a->h_parent[DST], + au_ftest_ren(a->flags, ISDIR), ndx); + if (unlikely(err)) + goto out; + err = -ENOTEMPTY; + if (unlikely(a->h_dentry[DST] == a->h_trap)) + goto out; + err = 0; + } + + out: + if (unlikely(err == -ENOENT || err == -EEXIST)) + err = -EIO; + AuTraceErr(err); + return err; +} + +int aufs_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry) +{ + int err, do_dt_dstdir, flags; + aufs_bindex_t bend, bindex; + struct inode *inode[2], *dirs[2]; + struct au_hinode *hgdir[2], *hdir; + enum { PARENT, CHILD }; + /* reduce stack space */ + struct { + struct rename_args a; + struct au_dtime dt[2][2]; + } *p; + struct au_wr_dir_args wr_dir_args = { + //.force_btgt = -1, + .flags = AuWrDir_ADD_ENTRY + }; + + LKTRTrace("i%lu, %.*s, i%lu, %.*s\n", + src_dir->i_ino, AuDLNPair(src_dentry), + dir->i_ino, AuDLNPair(dentry)); + IMustLock(src_dir); + IMustLock(dir); + inode[DST] = dentry->d_inode; + if (inode[DST]) { + IMustLock(inode[DST]); + igrab(inode[DST]); + } + + err = -ENOMEM; + BUILD_BUG_ON(sizeof(*p) > PAGE_SIZE); + p = kmalloc(sizeof(*p), GFP_TEMPORARY); + if (unlikely(!p)) + goto out; + + err = -ENOTDIR; + p->a.sb = src_dentry->d_sb; + inode[SRC] = src_dentry->d_inode; + flags = 0; + p->a.flags = 0; + if (S_ISDIR(inode[SRC]->i_mode)) { + flags = AuLock_DIR; + au_fset_ren(p->a.flags, ISDIR); + if (unlikely(inode[DST] && !S_ISDIR(inode[DST]->i_mode))) + goto out_free; + } + + aufs_read_and_write_lock2(dentry, src_dentry, flags); + p->a.mnt_flags = au_mntflags(p->a.sb); + if (unlikely(au_opt_test_dlgt(p->a.mnt_flags))) + au_fset_ren(p->a.flags, DLGT); + p->a.parent[SRC] = src_dentry->d_parent; /* dir inode is locked */ + p->a.parent[DST] = dentry->d_parent; /* dir inode is locked */ + if (src_dir == dir) { + au_fset_ren(p->a.flags, ISSAMEDIR); + di_write_lock_parent(p->a.parent[DST]); + } else + di_write_lock2_parent(p->a.parent[SRC], p->a.parent[DST], + /*isdir*/1); + + /* which branch we process */ + p->a.bstart[SRC] = au_dbstart(src_dentry); + p->a.bstart[DST] = au_dbstart(dentry); + if (au_ftest_ren(p->a.flags, ISDIR)) + au_fset_wrdir(wr_dir_args.flags, ISDIR); + wr_dir_args.force_btgt = p->a.bstart[SRC]; + if (dentry->d_inode && p->a.bstart[DST] < p->a.bstart[SRC]) + wr_dir_args.force_btgt = p->a.bstart[DST]; + wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); + err = au_wr_dir(dentry, src_dentry, &wr_dir_args); + p->a.btgt = err; + if (unlikely(err < 0)) + goto out_unlock; + + /* are they available to be renamed */ + err = 0; + au_nhash_init(&p->a.whlist); + if (au_ftest_ren(p->a.flags, ISDIR) && inode[DST]) { + au_set_dbstart(dentry, p->a.bstart[DST]); + err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist); + au_set_dbstart(dentry, p->a.btgt); + } + p->a.h_dentry[DST] = au_h_dptr(dentry, au_dbstart(dentry)); + if (unlikely(err)) + goto out_unlock; + //todo: minor optimize, their sb may be same while their bindex differs. + p->a.h_dentry[SRC] = au_h_dptr(src_dentry, au_dbstart(src_dentry)); + if (au_ftest_ren(p->a.flags, ISDIR)) { + err = may_rename_srcdir(src_dentry, p->a.btgt); + if (unlikely(err)) + goto out_children; + } + + /* prepare the writable parent dir on the same branch */ + err = au_wr_dir_need_wh(src_dentry, au_ftest_ren(p->a.flags, ISDIR), + &p->a.btgt, + au_ftest_ren(p->a.flags, ISSAMEDIR) + ? NULL : p->a.parent[DST]); + if (unlikely(err < 0)) + goto out_children; + if (err) + au_fset_ren(p->a.flags, WHSRC); + if (p->a.bstart[DST] == p->a.btgt) { + au_fset_ren(p->a.flags, WHDST); + } else { + err = au_cpup_dirs(dentry, p->a.btgt, + au_ftest_ren(p->a.flags, ISSAMEDIR) + ? NULL : p->a.parent[SRC]); + if (unlikely(err)) + goto out_children; + } + + hgdir[SRC] = NULL; + hgdir[DST] = NULL; + au_hgdirs(hgdir, &p->a); + p->a.h_parent[SRC] = au_h_dptr(p->a.parent[SRC], p->a.btgt); + p->a.h_parent[DST] = au_h_dptr(p->a.parent[DST], p->a.btgt); + dirs[0] = src_dir; + dirs[1] = dir; + + AuDbgSleep_UdbaRace(); + p->a.h_trap = au_hdir_lock_rename(p->a.h_parent, dirs, p->a.btgt, + au_ftest_ren(p->a.flags, ISSAMEDIR)); + //todo: revalidate the lower dentries? + + if (!au_opt_test(p->a.mnt_flags, UDBA_NONE)) { + struct au_ndx ndx = { + .nfsmnt = au_nfsmnt(p->a.sb, p->a.btgt), + .flags = 0, + .nd = NULL, + //.br = NULL + }; + if (unlikely(au_ftest_ren(p->a.flags, DLGT))) + au_fset_ndx(ndx.flags, DLGT); + err = au_may_ren(src_dir, src_dentry, dir, dentry, &p->a, &ndx); + if (unlikely(err)) + goto out_hdir; + } + + /* store timestamps to be revertible */ + au_dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC], + p->a.h_parent[SRC], hgdir[SRC]); + if (!au_ftest_ren(p->a.flags, ISSAMEDIR)) + au_dtime_store(p->dt[PARENT] + DST, p->a.parent[DST], + p->a.h_parent[DST], hgdir[DST]); + do_dt_dstdir = 0; + if (au_ftest_ren(p->a.flags, ISDIR)) { + hdir = NULL; + if (unlikely(au_opt_test(p->a.mnt_flags, UDBA_INOTIFY))) + hdir = au_hi(p->a.parent[SRC]->d_inode, p->a.btgt); + au_dtime_store(p->dt[CHILD] + SRC, src_dentry, + p->a.h_dentry[SRC], hdir); + if (p->a.h_dentry[DST]->d_inode) { + do_dt_dstdir = 1; + if (unlikely(au_opt_test(p->a.mnt_flags, UDBA_INOTIFY))) + hdir = au_hi(p->a.parent[DST]->d_inode, + p->a.btgt); + au_dtime_store(p->dt[CHILD] + DST, dentry, + p->a.h_dentry[DST], hdir); + } + } + + err = do_rename(src_dir, src_dentry, dir, dentry, &p->a); + if (unlikely(err)) + goto out_dt; + au_hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt, + au_ftest_ren(p->a.flags, ISSAMEDIR)); + + /* update dir attributes */ + dir->i_version++; + if (au_ftest_ren(p->a.flags, ISDIR)) { + /* is this updating defined in POSIX? */ + //mutex_lock(&inode[SRC]->i_mutex); + au_cpup_attr_timesizes(inode[SRC]); + //mutex_unlock(&inode[SRC]->i_mutex); + + au_cpup_attr_nlink(dir); + if (inode[DST]) { + clear_nlink(inode[DST]); + au_cpup_attr_timesizes(inode[DST]); + } + } + if (au_ibstart(dir) == p->a.btgt) + au_cpup_attr_timesizes(dir); + + if (!au_ftest_ren(p->a.flags, ISSAMEDIR)) { + src_dir->i_version++; + if (au_ftest_ren(p->a.flags, ISDIR)) + au_cpup_attr_nlink(src_dir); + if (au_ibstart(src_dir) == p->a.btgt) + au_cpup_attr_timesizes(src_dir); + } + + /* dput/iput all lower dentries */ + au_set_dbwh(src_dentry, -1); + bend = au_dbend(src_dentry); + for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) { + struct dentry *hd; + hd = au_h_dptr(src_dentry, bindex); + if (hd) + au_set_h_dptr(src_dentry, bindex, NULL); + } + au_set_dbend(src_dentry, p->a.btgt); + + bend = au_ibend(inode[SRC]); + for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) { + struct inode *hi; + hi = au_h_iptr(inode[SRC], bindex); + if (hi) { + //AuDbg("hi%lu, i%lu\n", hi->i_ino, 0LU); + au_xino_write0(p->a.sb, bindex, hi->i_ino, 0); + /* ignore this error */ + au_set_h_iptr(inode[SRC], bindex, NULL, 0); + } + } + au_set_ibend(inode[SRC], p->a.btgt); + + goto out_children; /* success */ + + out_dt: + au_dtime_revert(p->dt[PARENT] + SRC); + if (!au_ftest_ren(p->a.flags, ISSAMEDIR)) + au_dtime_revert(p->dt[PARENT] + DST); + if (au_ftest_ren(p->a.flags, ISDIR) && err != -EIO) { + struct dentry *hd; + + hd = p->dt[CHILD][SRC].dt_h_dentry; + mutex_lock_nested(&hd->d_inode->i_mutex, AuLsc_I_CHILD); + au_dtime_revert(p->dt[CHILD] + SRC); + mutex_unlock(&hd->d_inode->i_mutex); + if (do_dt_dstdir) { + hd = p->dt[CHILD][DST].dt_h_dentry; + mutex_lock_nested(&hd->d_inode->i_mutex, AuLsc_I_CHILD); + au_dtime_revert(p->dt[CHILD] + DST); + mutex_unlock(&hd->d_inode->i_mutex); + } + } + out_hdir: + au_hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt, + au_ftest_ren(p->a.flags, ISSAMEDIR)); + out_children: + au_nhash_fin(&p->a.whlist); + out_unlock: + //if (unlikely(err /* && au_ftest_ren(p->a.flags, ISDIR) */)) { + if (unlikely(err && au_ftest_ren(p->a.flags, ISDIR))) { + au_update_dbstart(dentry); + d_drop(dentry); + } + if (au_ftest_ren(p->a.flags, ISSAMEDIR)) + di_write_unlock(p->a.parent[DST]); + else + di_write_unlock2(p->a.parent[SRC], p->a.parent[DST]); + aufs_read_and_write_unlock2(dentry, src_dentry); + out_free: + kfree(p); + out: + iput(inode[DST]); + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/Kconfig linux-2.6.25.4-unionfs/fs/aufs/Kconfig --- linux-2.6.25.4-unionfs.orig/fs/aufs/Kconfig 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/Kconfig 2008-05-25 11:56:08 +0300 @@ -0,0 +1,284 @@ +config AUFS + tristate "Another unionfs" + help + Aufs is a stackable unification filesystem such as Unionfs, + which unifies several directories and provides a merged single + directory. + In the early days, aufs was entirely re-designed and + re-implemented Unionfs Version 1.x series. After many original + ideas, approaches and improvements, it becomes totally + different from Unionfs while keeping the basic features. + See Unionfs for the basic features. + +if AUFS + +config AUFS_FAKE_DM + bool "Use simplified (fake) nameidata" + depends on AUFS + default y + help + Faking nameidata (linux kernel internal data), you can get + better performance in some cases. + When you use NFS as an aufs branch or dlgt /* /dirperm1 */ aufs mount option, + you need to disable this configuration. + +choice + prompt "Maximum number of branches" + depends on AUFS + default AUFS_BRANCH_MAX_127 + help + Specifies the maximum number of branches (or member directories) in a \ + single aufs. The larger value consumes more system resources and has \ + an impact to performance. +config AUFS_BRANCH_MAX_127 + bool "127" + help + Specifies the maximum number of branches (or member directories) in a \ + single aufs. The larger value consumes more system resources and has \ + an impact to performance. +config AUFS_BRANCH_MAX_511 + bool "511" + help + Specifies the maximum number of branches (or member directories) in a \ + single aufs. The larger value consumes more system resources and has \ + an impact to performance. +config AUFS_BRANCH_MAX_1023 + bool "1023" + help + Specifies the maximum number of branches (or member directories) in a \ + single aufs. The larger value consumes more system resources and has \ + an impact to performance. +config AUFS_BRANCH_MAX_32767 + bool "32767" + help + Specifies the maximum number of branches (or member directories) in a \ + single aufs. The larger value consumes more system resources and has \ + an impact to performance. +endchoice + +config AUFS_HINOTIFY + bool "Use inotify to detect actions on a branch" + depends on AUFS + depends on INOTIFY + default n + help + If you want to modify files on branches directly, eg. bypassing aufs, + and want aufs to detect the changes of them fully, then enable this + option and use 'udba=inotify' mount option. + It will damage the performance. + See detail in aufs.5. + +comment "INOTIFY and AUFS_HINOTIFY are disabled" + depends on INOTIFY = n + +config AUFS_EXPORT + bool "NFS-exportable aufs" + depends on AUFS + depends on (AUFS = y && EXPORTFS = y) || (AUFS = m && EXPORTFS) + default n + help + If you want to export your mounted aufs, then enable this + option. There are several requirements to export aufs. + See detail in aufs.5. + +comment "EXPORTFS and AUFS_EXPORT are disabled" + depends on AUFS + depends on EXPORTFS = n + +comment "AUFS_EXPORT is disabled since EXPORTFS is a module but AUFS" + depends on AUFS + depends on EXPORTFS = m && AUFS = y + +config AUFS_ROBR + bool "Aufs as an readonly branch of another aufs" + depends on AUFS + default n + help + If you want make your aufs to be a part of another aufs, then + enable this option. In other words, you can specify your aufs + path in 'br:' mount option for another aufs, but cannot + specify 'rw' as the branch permission. + It will damage the performance. + See detail in aufs.5. + +config AUFS_DLGT + bool "Delegate the internal branch access the kernel thread" + depends on AUFS + default n + help + If you want /*to use 'dirperm1' mount option, or */ aufs to delegate + the internal access to the branches which is made by aufs, to + the kernel thread, in order to hide the access issued by your + application from your LSM or something or make your + application to be traced strictly by the task I/O accounting, + then enable this option and use 'dlgt' mount option. + It will damage the performance. + See detail in aufs.5. + +config AUFS_RR_SQUASHFS + bool "Make squashfs branch RR (real readonly) by default" + depends on AUFS + default y + help + If you use squashfs or LZMA patched squashfs as an aufs branch + and want to set '=rr' to it by default, then enable this + configuration. + 'rr' stands for real readonly and it optimizes some aspects of + 'ro.' + See detail in aufs.5. + +config AUFS_SEC_PERM_PATCH + bool "sec_perm-2.6.24.patch was applied or not" + depends on AUFS + depends on AUFS = m + depends on SECURITY + default n + help + If you build aufs as a module and enabled CONFIG_SECURITY, + then you need to apply the patch + 'CVS_TREE/aufs/patch/sec_perm-2.6.24.patch' to your kernel + source, and enable this configuration. + The sec_perm-2.6.24.patch exports a kernel function + security_inode_permission() to modules. + +comment "SECURITY and AUFS_SEC_PERM_PATCH are disabled" + depends on AUFS + depends on SECURITY = n + +config AUFS_SPLICE_PATCH + bool "splice.patch for sendfile(2) and splice(2)" + depends on AUFS + default n + help + If you use 'loopback mount' on a fs-image file, or use + splice(2) or sendfile(2) systemcall in aufs, then you need to + apply the patch 'CVS_TREE/aufs/patch/splice.patch' to your + kernel source, and enable this configuration. + The splice.patch makes the kernel function vfs_splice_to/from() + global and exports them to modules. + +config AUFS_PUT_FILP_PATCH + bool "put_filp.patch for NFS branch" + depends on AUFS + depends on AUFS = m + depends on NFS_FS + depends on AUFS_FAKE_DM = n + default n + help + If you build aufs as a module and use mounted NFS as an aufs + branch filesystem, then you need to apply the patch + 'CVS_TREE/aufs/patch/put_filp.patch' to your kernel source, + and enable this configuration. + The put_filp.patch exports a kernel function put_filp() to + modules. + +comment "NFS_FS and AUFS_PUT_FILP_PATCH are disabled" + depends on AUFS + depends on NFS_FS = n + +comment "AUFS_FILP_PATCH is disabled since AUFS_FAKE_DM is enabled" + depends on AUFS + depends on AUFS_FAKE_DM + +config AUFS_LHASH_PATCH + bool "lhash.patch for NFS branch" + depends on AUFS + depends on NFS_FS + depends on AUFS_FAKE_DM = n + default n + help + If you use mounted NFS as an aufs branch filesystem, then you + need to apply the patch 'CVS_TREE/aufs/patch/lhash.patch' (or + lhash-2.6.22.patch for linux-2.6.22 and later) to your kernel + source, and enable this configuration. + The patch file makes the kernel function __lookup_hash() global + and exports it to modules. + +comment "NFS_FS and AUFS_LHASH_PATCH are disabled" + depends on AUFS + depends on NFS_FS = n + +comment "AUFS_LHASH_PATCH is disabled since AUFS_FAKE_DM is enabled" + depends on AUFS + depends on AUFS_FAKE_DM + +config AUFS_FSYNC_SUPER_PATCH + bool "fsync_super-2.6.xx.patch was applied or not" + depends on AUFS + depends on AUFS = m + default n + help + If you build aufs as a module and want to flush everything for + branch filesystems which are not marked as 'rr' nor 'rr+wh' at + umount or remount time, then you need to apply the patch + 'CVS_TREE/aufs/patch/fsync_super-2.6.16.patch' or + '...-2.6.19.patch' to your kernel source, and enable this + configuration. + It may be helpful at shutdown time in case of your aufs is a + root filesystem. But this behaviour will not guarantee the + consistency of branch filesystems. To gurantee it, try the + approach described in the aufs manual, and don't forget + installing auplink script. + The fsync_super-2.6.xx.patch does nothing but exports a kernel + function fsync_super() to modules. + +config AUFS_DENY_WRITE_ACCESS_PATCH + bool "deny_write_access.patch was applied or not" + depends on AUFS + depends on AUFS = m + default n + help + A security enhancement to deny writing to a running executable + which exists on an aufs branch filesystem and executed through + aufs. If you applied + 'CVS_TREE/aufs/patch/deny_write_access.patch' to your kernel + and you are compiling aufs as a module, then enable this + option. + The write_deny_access.patch does nothing but export the + function. + +config AUFS_WORKAROUND_FUSE + bool "Special handling for FUSE-based filesystem" + depends on AUFS && FUSE_FS + default n + help + A FUSE-based filesystem may not initialize its inode + attributes and the FUSE developer thinks the inode attributes + in a positive dentry which is returned by VFS lookup operation + are not reliable. + If you use a FUSE-based filesystem as an aufs branch, and it + customizes the inode attribute on it without overriding + fuse_lowlevel_ops.lookup, probably you need to enable this + configuration. + If you enable this configuration, aufs calls getattr operation + in every lookup and revalidate operation for the FUSE-based + filesystem branch. + It will damage the performance even if you don't use a + FUSE-based filesystem branch. + +config AUFS_DEBUG + bool "Debug aufs" + depends on AUFS + default y + help + Enable this to compile aufs internal debug code. + The performance will be damaged. + +config AUFS_COMPAT + bool "Compatibility with Unionfs (obsolete)" + depends on AUFS + default n + help + This makes aufs compatible with unionfs-style mount options and some + behaviours. + The dirs= mount option and =nfsro branch permission flag are always + interpreted as br: mount option and =ro flag respectively. The + 'debug', 'delete' and 'imap' mount options are ignored. + If you disable this option, you will get, + - aufs issues a warning about the ignored mount options + - the default branch permission flag is set. RW for the first branch, + and RO for the rests. + - the name of a internal file which represents the directory is + 'opaque', becomes '.wh..wh..opq' + - the 'diropq=w' mount option is set by default +endif diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/Makefile linux-2.6.25.4-unionfs/fs/aufs/Makefile --- linux-2.6.25.4-unionfs.orig/fs/aufs/Makefile 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/Makefile 2008-05-25 11:56:08 +0300 @@ -0,0 +1,65 @@ +# AUFS Makefile for the Linux 2.6.25 and later +# $Id: Makefile,v 1.3 2008/04/28 03:03:10 sfjro Exp $ + +# the environment variables are not inherited since 2.6.23 +ifdef AUFS_EXTRA_CFLAGS +EXTRA_CFLAGS += ${AUFS_EXTRA_CFLAGS} +endif + +######################################## + +ifdef CONFIG_AUFS_RR_SQUASHFS +# cf. squashfs3.2-r2 and sqlzma patch. +EXTRA_CFLAGS += -DSQUASHFS_MAGIC=0x73717368 +EXTRA_CFLAGS += -DSQUASHFS_MAGIC_SWAP=0x68737173 +EXTRA_CFLAGS += -DSQUASHFS_MAGIC_LZMA=0x71736873 +EXTRA_CFLAGS += -DSQUASHFS_MAGIC_LZMA_SWAP=0x73687371 +endif + +ifdef CONFIG_AUFS_WORKAROUND_FUSE +# defined in ${srctree}/fs/fuse/inode.c +EXTRA_CFLAGS += -DFUSE_SUPER_MAGIC=0x65735546 +endif + +ifdef CONFIG_XFS_FS +# defined in ${srctree}/fs/xfs/xfs_sb.h +EXTRA_CFLAGS += -DXFS_SB_MAGIC=0x58465342 +endif + +ifdef CONFIG_TMPFS +# defined in ${srctree}/mm/shmem.c +EXTRA_CFLAGS += -DTMPFS_MAGIC=0x01021994 +endif + +-include $(dir $(lastword ${MAKEFILE_LIST}))priv.mk +#$(warning ${EXTRA_CFLAGS}) + +######################################## + +obj-$(CONFIG_AUFS) += aufs.o +aufs-y := module.o super.o sbinfo.o branch.o xino.o sysaufs.o opts.o \ + wkq.o vfsub.o dcsub.o \ + cpup.o whout.o plink.o wbr_policy.o \ + dentry.o dinfo.o \ + file.o f_op.o finfo.o \ + dir.o vdir.o \ + inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o iinfo.o \ + misc.o + +#xattr.o +aufs-$(CONFIG_SYSFS) += sysfs.o +aufs-$(CONFIG_AUFS_BR_NFS) += br_nfs.o +aufs-$(CONFIG_AUFS_BR_XFS) += br_xfs.o +aufs-$(CONFIG_AUFS_WORKAROUND_FUSE) += br_fuse.o + +aufs-$(CONFIG_AUFS_DLGT) += dlgt.o +aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o hin_or_dlgt.o +# dirty +ifndef CONFIG_AUFS_HINOTIFY +aufs-$(CONFIG_AUFS_DLGT) += hin_or_dlgt.o +endif + +aufs-$(CONFIG_AUFS_EXPORT) += export.o +aufs-$(CONFIG_AUFS_ROBR) += robr.o +aufs-$(CONFIG_AUFS_DEBUG) += debug.o +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/misc.c linux-2.6.25.4-unionfs/fs/aufs/misc.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/misc.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/misc.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * $Id: misc.c,v 1.6 2008/05/19 01:46:53 sfjro Exp $ + */ + +//#include +//#include +//#include +//#include +#include "aufs.h" + +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) +{ + void *q; + + LKTRTrace("p %p, nused %d, sz %d\n", p, nused, new_sz); + AuDebugOn(new_sz <= 0); + if (new_sz <= nused) + return p; + + q = krealloc(p, new_sz, gfp); + if (q) + memset(q + nused, 0, new_sz - nused); + return q; +} + +/* ---------------------------------------------------------------------- */ + +struct nameidata *au_dup_nd(struct au_sbinfo *sbinfo, struct nameidata *dst, + struct nameidata *src) +{ + LKTRTrace("src %p\n", src); + + if (src) { + *dst = *src; + dst->flags &= ~LOOKUP_PARENT; + if (sbinfo->si_wbr_create == AuWbrCreate_TDP) { + if ((dst->flags & LOOKUP_CREATE) + && !(dst->intent.open.flags & O_CREAT)) + dst->flags &= ~LOOKUP_CREATE; + } else { + dst->flags &= ~LOOKUP_CREATE; + dst->intent.open.flags &= ~O_CREAT; + } + } else + dst = NULL; + + return dst; +} + +struct nameidata *au_fake_dm(struct nameidata *fake_nd, struct nameidata *nd, + struct super_block *sb, aufs_bindex_t bindex) +{ + LKTRTrace("nd %p, b%d\n", nd, bindex); + + if (!nd) + return NULL; + + DiMustAnyLock(nd->path.dentry); + + fake_nd->path.dentry = NULL; + fake_nd->path.mnt = NULL; + + if (bindex <= au_dbend(nd->path.dentry)) + fake_nd->path.dentry = au_h_dptr(nd->path.dentry, bindex); + if (fake_nd->path.dentry) { + fake_nd->path.mnt = au_sbr_mnt(sb, bindex); + AuDebugOn(!fake_nd->path.mnt); + path_get(&fake_nd->path); + } else + fake_nd = ERR_PTR(-ENOENT); + + AuTraceErrPtr(fake_nd); + return fake_nd; +} + +void au_fake_dm_release(struct nameidata *fake_nd) +{ + if (fake_nd) + path_put(&fake_nd->path); +} + +int au_h_create(struct inode *h_dir, struct dentry *h_dentry, int mode, + int dlgt, struct nameidata *nd, struct vfsmount *nfsmnt) +{ + int err; + + LKTRTrace("hi%lu, %.*s, 0%o, nd %d, nfsmnt %d\n", + h_dir->i_ino, AuDLNPair(h_dentry), mode, !!nd, !!nfsmnt); + + err = -ENOSYS; + if (!nfsmnt) + err = vfsub_create(h_dir, h_dentry, mode, /*nd*/NULL, dlgt); + else { + struct nameidata fake_nd; + + if (nd) + fake_nd = *nd; + else + memset(&fake_nd, 0, sizeof(fake_nd)); + fake_nd.path.dentry = h_dentry; + fake_nd.path.mnt = nfsmnt; + path_get(&fake_nd.path); + fake_nd.flags = LOOKUP_CREATE; + fake_nd.intent.open.flags = O_CREAT | FMODE_READ; + fake_nd.intent.open.create_mode = mode; + + err = vfsub_create(h_dir, h_dentry, mode, &fake_nd, dlgt); + path_put(&fake_nd.path); + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_copy_file(struct file *dst, struct file *src, loff_t len, + struct super_block *sb) +{ + int err, all_zero; + unsigned long blksize; + char *buf; + struct vfsub_args vargs; + /* reduce stack space */ + struct iattr *ia; + + LKTRTrace("%.*s, %.*s\n", + AuDLNPair(dst->f_dentry), AuDLNPair(src->f_dentry)); + AuDebugOn(!(dst->f_mode & FMODE_WRITE)); +#ifdef CONFIG_AUFS_DEBUG + { + struct dentry *parent; + parent = dget_parent(dst->f_dentry); + IMustLock(parent->d_inode); + dput(parent); + } +#endif + + err = -ENOMEM; + blksize = dst->f_dentry->d_sb->s_blocksize; + if (!blksize || PAGE_SIZE < blksize) + blksize = PAGE_SIZE; + LKTRTrace("blksize %lu\n", blksize); + buf = kmalloc(blksize, GFP_TEMPORARY); + //buf = NULL; + if (unlikely(!buf)) + goto out; + ia = kmalloc(sizeof(*ia), GFP_TEMPORARY); + if (unlikely(!ia)) + goto out_buf; + +#ifdef CONFIG_AUFS_DEBUG + if (len > (1 << 22)) + AuWarn("copying a large file %Ld\n", (long long)len); +#endif + vfsub_args_init(&vargs, NULL, au_opt_test_dlgt(au_mntflags(sb)), 0); + err = 0; + all_zero = 0; + src->f_pos = 0; + dst->f_pos = 0; + while (len) { + size_t sz, rbytes, wbytes, i; + char *p; + + LKTRTrace("len %lld\n", len); + sz = blksize; + if (len < blksize) + sz = len; + + /* support LSM and notify */ + rbytes = 0; + // signal_pending + while (!rbytes || err == -EAGAIN || err == -EINTR) { + rbytes = vfsub_read_k(src, buf, sz, &src->f_pos, + vfsub_ftest(vargs.flags, DLGT)); + err = rbytes; + } + if (unlikely(err < 0)) + break; + + all_zero = 0; + if (len >= rbytes && rbytes == blksize) { + //todo: try bitmap or memcmp()/get_zeroed_page() + unsigned long *ulp; + size_t n; + + all_zero = 1; + ulp = (void *)buf; + n = rbytes / sizeof(*ulp); + i = n; + while (n-- > 0 && all_zero) + all_zero = !*ulp++; + p = (void *)ulp; + i *= sizeof(*ulp); + for (; all_zero && i < rbytes; i++) + all_zero = !*p++; + } + if (!all_zero) { + wbytes = rbytes; + p = buf; + while (wbytes) { + size_t b; + /* support LSM and notify */ + b = vfsub_write_k(dst, p, wbytes, &dst->f_pos, + &vargs); + err = b; + // signal_pending + if (unlikely(err == -EAGAIN || err == -EINTR)) + continue; + if (unlikely(err < 0)) + break; + wbytes -= b; + p += b; + } + } else { + loff_t res; + LKTRLabel(hole); + res = vfsub_llseek(dst, rbytes, SEEK_CUR); + err = res; + if (unlikely(res < 0)) + break; + } + len -= rbytes; + err = 0; + } + + /* the last block may be a hole */ + if (unlikely(!err && all_zero)) { + struct dentry *h_d = dst->f_dentry; + struct inode *h_i = h_d->d_inode; + + LKTRLabel(last hole); + do { + // signal_pending + err = vfsub_write_k(dst, "\0", 1, &dst->f_pos, &vargs); + } while (err == -EAGAIN || err == -EINTR); + if (err == 1) { + ia->ia_size = dst->f_pos; + ia->ia_valid = ATTR_SIZE | ATTR_FILE; + ia->ia_file = dst; + mutex_lock_nested(&h_i->i_mutex, AuLsc_I_CHILD2); + err = vfsub_notify_change(h_d, ia, &vargs); + mutex_unlock(&h_i->i_mutex); + } + } + + kfree(ia); + out_buf: + kfree(buf); + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/misc.h linux-2.6.25.4-unionfs/fs/aufs/misc.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/misc.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/misc.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * $Id: misc.h,v 1.2 2008/04/21 01:33:00 sfjro Exp $ + */ + +#ifndef __AUFS_MISC_H__ +#define __AUFS_MISC_H__ + +#ifdef __KERNEL__ + +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +typedef unsigned int au_gen_t; +/* see linux/include/linux/jiffies.h */ +#define AuGenYounger(a, b) ((int)(b) - (int)(a) < 0) +#define AuGenOlder(a, b) AufsGenYounger(b, a) + +/* ---------------------------------------------------------------------- */ + +struct au_rwsem { + struct rw_semaphore rwsem; +#ifdef CONFIG_AUFS_DEBUG + atomic_t rcnt; +#endif +}; + +#ifdef CONFIG_AUFS_DEBUG +#define AuDbgRcntInit(rw) do { \ + atomic_set(&(rw)->rcnt, 0); \ + smp_mb(); \ +} while (0) + +#define AuDbgRcntInc(rw) atomic_inc_return(&(rw)->rcnt) +#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) +#else +#define AuDbgRcntInit(rw) do {} while (0) +#define AuDbgRcntInc(rw) do {} while (0) +#define AuDbgRcntDec(rw) do {} while (0) +#endif /* CONFIG_AUFS_DEBUG */ + +static inline void au_rw_init_nolock(struct au_rwsem *rw) +{ + AuDbgRcntInit(rw); + init_rwsem(&rw->rwsem); +} + +static inline void au_rw_init_wlock(struct au_rwsem *rw) +{ + au_rw_init_nolock(rw); + down_write(&rw->rwsem); +} + +static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, + unsigned int lsc) +{ + au_rw_init_nolock(rw); + down_write_nested(&rw->rwsem, lsc); +} + +static inline void au_rw_read_lock(struct au_rwsem *rw) +{ + down_read(&rw->rwsem); + AuDbgRcntInc(rw); +} + +static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) +{ + down_read_nested(&rw->rwsem, lsc); + AuDbgRcntInc(rw); +} + +static inline void au_rw_read_unlock(struct au_rwsem *rw) +{ + AuDbgRcntDec(rw); + up_read(&rw->rwsem); +} + +static inline void au_rw_dgrade_lock(struct au_rwsem *rw) +{ + AuDbgRcntInc(rw); + downgrade_write(&rw->rwsem); +} + +static inline void au_rw_write_lock(struct au_rwsem *rw) +{ + down_write(&rw->rwsem); +} + +static inline void au_rw_write_lock_nested(struct au_rwsem *rw, + unsigned int lsc) +{ + down_write_nested(&rw->rwsem, lsc); +} + +static inline void au_rw_write_unlock(struct au_rwsem *rw) +{ + up_write(&rw->rwsem); +} + +/* why is not _nested version defined */ +static inline int au_rw_read_trylock(struct au_rwsem *rw) +{ + int ret = down_read_trylock(&rw->rwsem); + if (ret) + AuDbgRcntInc(rw); + return ret; +} + +static inline int au_rw_write_trylock(struct au_rwsem *rw) +{ + return down_write_trylock(&rw->rwsem); +} + +#undef AuDbgRcntInit +#undef AuDbgRcntInc +#undef AuDbgRcntDec + +/* to debug easier, do not make them inlined functions */ +#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) +#define AuRwMustAnyLock(rw) AuDebugOn(down_write_trylock(&(rw)->rwsem)) +#ifdef CONFIG_AUFS_DEBUG +#define AuRwMustReadLock(rw) do { \ + AuRwMustAnyLock(rw); \ + AuDebugOn(!atomic_read(&(rw)->rcnt)); \ +} while (0) + +#define AuRwMustWriteLock(rw) do { \ + AuRwMustAnyLock(rw); \ + AuDebugOn(atomic_read(&(rw)->rcnt)); \ +} while (0) +#else +#define AuRwMustReadLock(rw) AuRwMustAnyLock(rw) +#define AuRwMustWriteLock(rw) AuRwMustAnyLock(rw) +#endif /* CONFIG_AUFS_DEBUG */ + +#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ +static inline void prefix##_read_lock(param) \ +{ au_rw_read_lock(&(rwsem)); } \ +static inline void prefix##_write_lock(param) \ +{ au_rw_write_lock(&(rwsem)); } \ +static inline int prefix##_read_trylock(param) \ +{ return au_rw_read_trylock(&(rwsem)); } \ +static inline int prefix##_write_trylock(param) \ +{ return au_rw_write_trylock(&(rwsem)); } +//static inline void prefix##_read_trylock_nested(param, lsc) +//{au_rw_read_trylock_nested(&(rwsem, lsc));} +//static inline void prefix##_write_trylock_nestd(param, lsc) +//{au_rw_write_trylock_nested(&(rwsem), nested);} + +#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ +static inline void prefix##_read_unlock(param) \ +{ au_rw_read_unlock(&(rwsem)); } \ +static inline void prefix##_write_unlock(param) \ +{ au_rw_write_unlock(&(rwsem)); } \ +static inline void prefix##_downgrade_lock(param) \ +{ au_rw_dgrade_lock(&(rwsem)); } + +#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ + AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ + AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) + +/* ---------------------------------------------------------------------- */ + +void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); + +struct au_sbinfo; +struct nameidata *au_dup_nd(struct au_sbinfo *sbinfo, struct nameidata *dst, + struct nameidata *src); + +struct nameidata *au_fake_dm(struct nameidata *fake_nd, struct nameidata *nd, + struct super_block *sb, aufs_bindex_t bindex); +void au_fake_dm_release(struct nameidata *fake_nd); +int au_h_create(struct inode *h_dir, struct dentry *h_dentry, int mode, + int dlgt, struct nameidata *nd, struct vfsmount *nfsmnt); + +int au_copy_file(struct file *dst, struct file *src, loff_t len, + struct super_block *sb); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_MISC_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/module.c linux-2.6.25.4-unionfs/fs/aufs/module.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/module.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/module.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2007 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * module global variables and operations + * + * $Id: module.c,v 1.4 2008/05/19 01:49:24 sfjro Exp $ + */ + +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +/* + * aufs caches + */ +struct kmem_cache *au_cachep[AuCache_Last]; +static int __init create_cache(void) +{ + au_cachep[AuCache_DINFO] = AuCache(au_dinfo); + if (au_cachep[AuCache_DINFO]) + au_cachep[AuCache_ICNTNR] = AuCache(aufs_icntnr); + if (au_cachep[AuCache_ICNTNR]) + au_cachep[AuCache_FINFO] = AuCache(au_finfo); + //au_cachep[AuCache_FINFO] = NULL; + if (au_cachep[AuCache_FINFO]) + au_cachep[AuCache_VDIR] = AuCache(au_vdir); + if (au_cachep[AuCache_VDIR]) + au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); + if (au_cachep[AuCache_DEHSTR]) + return 0; + + return -ENOMEM; +} + +static void destroy_cache(void) +{ + int i; + for (i = 0; i < AuCache_Last; i++) + if (au_cachep[i]) { + kmem_cache_destroy(au_cachep[i]); + au_cachep[i] = NULL; + } +} + +/* ---------------------------------------------------------------------- */ + +char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ +int au_dir_roflags; + +/* + * functions for module interface. + */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Junjiro Okajima"); +MODULE_DESCRIPTION(AUFS_NAME " -- Another unionfs"); +MODULE_VERSION(AUFS_VERSION); + +/* it should be 'byte', but param_set_byte() prints it by "%c" */ +short aufs_nwkq = AUFS_NWKQ_DEF; +MODULE_PARM_DESC(nwkq, "the number of workqueue thread, " AUFS_WKQ_NAME); +module_param_named(nwkq, aufs_nwkq, short, S_IRUGO); + +int sysaufs_brs; +MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); +module_param_named(brs, sysaufs_brs, int, S_IRUGO); + +/* ---------------------------------------------------------------------- */ + +static int __init aufs_init(void) +{ + int err, i; + char *p; + + au_debug_init(); + + p = au_esc_chars; + for (i = 1; i <= ' '; i++) + *p++ = i; + *p++ = '\\'; + *p++ = '\x7f'; + *p = 0; + + au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE); + + err = -EINVAL; + if (unlikely(aufs_nwkq <= 0)) + goto out; + + err = sysaufs_init(); + if (unlikely(err)) + goto out; + err = au_wkq_init(); + if (unlikely(err)) + goto out_sysaufs; + err = au_inotify_init(); + if (unlikely(err)) + goto out_wkq; + err = au_sysrq_init(); + if (unlikely(err)) + goto out_inotify; + + err = create_cache(); + if (unlikely(err)) + goto out_sysrq; + + err = register_filesystem(&aufs_fs_type); + if (unlikely(err)) + goto out_cache; + pr_info(AUFS_NAME " " AUFS_VERSION "\n"); + return 0; /* success */ + + out_cache: + destroy_cache(); + out_sysrq: + au_sysrq_fin(); + out_inotify: + au_inotify_fin(); + out_wkq: + au_wkq_fin(); + out_sysaufs: + sysaufs_fin(); + out: + AuTraceErr(err); + return err; +} + +static void __exit aufs_exit(void) +{ + unregister_filesystem(&aufs_fs_type); + destroy_cache(); + + au_sysrq_fin(); + au_inotify_fin(); + au_wkq_fin(); + sysaufs_fin(); +} + +module_init(aufs_init); +module_exit(aufs_exit); + +/* ---------------------------------------------------------------------- */ + +/* fake Kconfig */ +#if 1 + +#ifdef CONFIG_AUFS_HINOTIFY +#ifndef CONFIG_INOTIFY +#error enable CONFIG_INOTIFY to use CONFIG_AUFS_HINOTIFY. +#endif +#endif /* CONFIG_AUFS_HINOTIFY */ + +#if AUFS_BRANCH_MAX > 511 && PAGE_SIZE > 4096 +#warning pagesize is larger than 4kb, \ + CONFIG_AUFS_BRANCH_MAX_511 or smaller is recommended. +#endif + +#ifdef CONFIG_AUFS_STAT +#ifndef CONFIG_SYSFS +#error CONFIG_AUFS_STAT requires CONFIG_SYSFS. +#endif +#endif /* CONFIG_AUFS_STAT */ + +#ifdef CONFIG_AUFS_EXPORT +#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE) +#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS +#endif +#if defined(CONFIG_EXPORTFS_MODULE) && defined(CONFIG_AUFS) +#error need CONFIG_EXPORTFS = y to link aufs statically with CONFIG_AUFS_EXPORT +#endif +#endif /* CONFIG_AUFS_EXPORT */ + +#ifdef CONFIG_AUFS_SEC_PERM_PATCH +#ifndef CONFIG_SECURITY +#warning CONFIG_AUFS_SEC_PERM_PATCH is unnecessary since CONFIG_SECURITY is disabled. +#endif +#ifdef CONFIG_AUFS +#warning CONFIG_AUFS_SEC_PERM_PATCH is unnecessary since CONFIG_AUFS is not a module. +#endif +#endif + +#ifdef CONFIG_AUFS_FAKE_DM +#warning CONFIG_AUFS_FAKE_DM is obsoleted in linux-2.6.24 and later. +#endif + +#ifdef CONFIG_AUFS_PUT_FILP_PATCH +#if !defined(CONFIG_NFS_FS) && !defined(CONFIG_NFS_FS_MODULE) +#warning CONFIG_AUFS_PUT_FILP_PATCH is unnecessary since CONFIG_NFS_FS is disabled. +#endif +#ifdef CONFIG_AUFS +#warning CONFIG_AUFS_PUT_FILP_PATCH is unnecessary since CONFIG_AUFS is not a module. +#endif +#endif /* CONFIG_AUFS_PUT_FILP_PATCH */ + +#ifdef CONFIG_AUFS_LHASH_PATCH +#if !defined(CONFIG_NFS_FS) && !defined(CONFIG_NFS_FS_MODULE) +#warning CONFIG_AUFS_LHASH_PATCH is unnecessary since CONFIG_NFS_FS is disabled. +#endif +#endif + +#ifdef CONFIG_AUFS_KSIZE_PATCH +#warning CONFIG_AUFS_KSIZE_PATCH is unnecessary for linux-2.6.22 and later. +#endif + +#ifdef CONFIG_AUFS_WORKAROUND_FUSE +#if !defined(CONFIG_FUSE_FS) && !defined(CONFIG_FUSE_FS_MODULE) +#warning CONFIG_AUFS_WORKAROUND_FUSE is enabled while FUSE is disabled. +#endif +#endif + +#ifdef CONFIG_DEBUG_PROVE_LOCKING +#if MAX_LOCKDEP_SUBCLASSES < AuLsc_I_End +#warning lockdep will not work since aufs uses deeper locks. +#endif +#endif + +#ifdef CONFIG_AUFS_COMPAT +#warning CONFIG_AUFS_COMPAT will be removed in the near future. +#endif + +#endif diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/module.h linux-2.6.25.4-unionfs/fs/aufs/module.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/module.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/module.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * module initialization and module-global + * + * $Id: module.h,v 1.2 2008/04/21 01:33:00 sfjro Exp $ + */ + +#ifndef __AUFS_MODULE_H__ +#define __AUFS_MODULE_H__ + +#ifdef __KERNEL__ + +//#include + +/* ---------------------------------------------------------------------- */ + +/* module parameters */ +extern short aufs_nwkq; +extern int sysaufs_brs; + +/* ---------------------------------------------------------------------- */ + +extern char au_esc_chars[]; +extern int au_dir_roflags; + +/* kmem cache */ +enum { + AuCache_DINFO, + AuCache_ICNTNR, + AuCache_FINFO, + AuCache_VDIR, + AuCache_DEHSTR, +#ifdef CONFIG_AUFS_HINOTIFY + AuCache_HINOTIFY, +#endif + AuCache_Last +}; + +extern struct kmem_cache *au_cachep[]; + +#define AuCacheArgs(type, sz) (type), (sz), 0, SLAB_RECLAIM_ACCOUNT, NULL +#define AuCacheX(type, extra) \ + kmem_cache_create(AuCacheArgs(#type, sizeof(struct type) + extra)) +#define AuCache(type) AuCacheX(type, 0) + +/* ---------------------------------------------------------------------- */ + +#define AuCacheFuncs(name, index) \ +static inline void *au_cache_alloc_##name(void) \ +{ return kmem_cache_alloc(au_cachep[index], GFP_KERNEL); } \ +static inline void au_cache_free_##name(void *p) \ +{ kmem_cache_free(au_cachep[index], p); } + +AuCacheFuncs(dinfo, AuCache_DINFO); +AuCacheFuncs(icntnr, AuCache_ICNTNR); +AuCacheFuncs(finfo, AuCache_FINFO); +AuCacheFuncs(vdir, AuCache_VDIR); +AuCacheFuncs(dehstr, AuCache_DEHSTR); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/opts.c linux-2.6.25.4-unionfs/fs/aufs/opts.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/opts.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/opts.c 2008-05-25 12:00:15 +0300 @@ -0,0 +1,1465 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * mount options/flags + * + * $Id: opts.c,v 1.5 2008/05/19 01:49:36 sfjro Exp $ + */ + +#include /* a distribution requires */ +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +enum { + Opt_br, + Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend, + Opt_idel, Opt_imod, Opt_ireorder, + Opt_dirwh, Opt_rdcache, Opt_deblk, Opt_nhash, Opt_rendir, + Opt_xino, Opt_zxino, Opt_noxino, + Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, + Opt_trunc_xino_path, Opt_itrunc_xino, + Opt_xinodir, Opt_xinonames, Opt_ixinonames, + Opt_trunc_xib, Opt_notrunc_xib, + Opt_dirperm1, Opt_nodirperm1, + Opt_shwh, Opt_noshwh, + Opt_plink, Opt_noplink, Opt_list_plink, Opt_clean_plink, + Opt_udba, + //Opt_lock, Opt_unlock, + Opt_cmd, Opt_cmd_args, + Opt_diropq_a, Opt_diropq_w, + Opt_warn_perm, Opt_nowarn_perm, + Opt_wbr_copyup, Opt_wbr_create, + Opt_coo, + Opt_dlgt, Opt_nodlgt, + Opt_refrof, Opt_norefrof, + Opt_verbose, Opt_noverbose, + Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err +}; + +static match_table_t options = { + {Opt_br, "br=%s"}, + {Opt_br, "br:%s"}, + + {Opt_add, "add=%d:%s"}, + {Opt_add, "add:%d:%s"}, + {Opt_add, "ins=%d:%s"}, + {Opt_add, "ins:%d:%s"}, + {Opt_append, "append=%s"}, + {Opt_append, "append:%s"}, + {Opt_prepend, "prepend=%s"}, + {Opt_prepend, "prepend:%s"}, + + {Opt_del, "del=%s"}, + {Opt_del, "del:%s"}, + //{Opt_idel, "idel:%d"}, + {Opt_mod, "mod=%s"}, + {Opt_mod, "mod:%s"}, + {Opt_imod, "imod:%d:%s"}, + + {Opt_dirwh, "dirwh=%d"}, + {Opt_dirwh, "dirwh:%d"}, + + {Opt_xino, "xino=%s"}, + {Opt_xino, "xino:%s"}, + {Opt_xinodir, "xinodir=%s"}, + {Opt_xinodir, "xinodir:%s"}, + {Opt_noxino, "noxino"}, + {Opt_trunc_xino, "trunc_xino"}, + {Opt_trunc_xino_v, "trunc_xino_v=%d:%d"}, + {Opt_notrunc_xino, "notrunc_xino"}, + {Opt_trunc_xino_path, "trunc_xino=%s"}, + {Opt_trunc_xino_path, "trunc_xino:%s"}, + {Opt_itrunc_xino, "itrunc_xino=%d"}, + {Opt_itrunc_xino, "itrunc_xino:%d"}, + //{Opt_zxino, "zxino=%s"}, + {Opt_trunc_xib, "trunc_xib"}, + {Opt_notrunc_xib, "notrunc_xib"}, + + {Opt_plink, "plink"}, + {Opt_noplink, "noplink"}, +#ifdef CONFIG_AUFS_DEBUG + {Opt_list_plink, "list_plink"}, +#endif + {Opt_clean_plink, "clean_plink"}, + + {Opt_udba, "udba=%s"}, + + {Opt_diropq_a, "diropq=always"}, + {Opt_diropq_a, "diropq=a"}, + {Opt_diropq_w, "diropq=whiteouted"}, + {Opt_diropq_w, "diropq=w"}, + + {Opt_warn_perm, "warn_perm"}, + {Opt_nowarn_perm, "nowarn_perm"}, + +#ifdef CONFIG_AUFS_DLGT + {Opt_dlgt, "dlgt"}, + {Opt_nodlgt, "nodlgt"}, + + {Opt_dirperm1, "dirperm1"}, + {Opt_nodirperm1, "nodirperm1"}, +#endif + +#ifdef CONFIG_AUFS_SHWH + {Opt_shwh, "shwh"}, + {Opt_noshwh, "noshwh"}, +#endif + + {Opt_rendir, "rendir=%d"}, + {Opt_rendir, "rendir:%d"}, + + {Opt_refrof, "refrof"}, + {Opt_norefrof, "norefrof"}, + + {Opt_verbose, "verbose"}, + {Opt_verbose, "v"}, + {Opt_noverbose, "noverbose"}, + {Opt_noverbose, "quiet"}, + {Opt_noverbose, "q"}, + {Opt_noverbose, "silent"}, + + {Opt_rdcache, "rdcache=%d"}, + {Opt_rdcache, "rdcache:%d"}, + + {Opt_coo, "coo=%s"}, + + {Opt_wbr_create, "create=%s"}, + {Opt_wbr_create, "create:%s"}, + {Opt_wbr_create, "create_policy=%s"}, + {Opt_wbr_create, "create_policy:%s"}, + {Opt_wbr_copyup, "cpup=%s"}, + {Opt_wbr_copyup, "cpup:%s"}, + {Opt_wbr_copyup, "copyup=%s"}, + {Opt_wbr_copyup, "copyup:%s"}, + {Opt_wbr_copyup, "copyup_policy=%s"}, + {Opt_wbr_copyup, "copyup_policy:%s"}, + + /* internal use for the scripts */ + {Opt_ignore_silent, "si=%s"}, + + {Opt_br, "dirs=%s"}, + {Opt_ignore, "debug=%d"}, + {Opt_ignore, "delete=whiteout"}, + {Opt_ignore, "delete=all"}, + {Opt_ignore, "imap=%s"}, + + {Opt_err, NULL} +}; + +/* ---------------------------------------------------------------------- */ + +static const char *au_parser_pattern(int val, struct match_token *token) +{ + while (token->pattern) { + if (token->token == val) + return token->pattern; + token++; + } + BUG(); + return "??"; +} + +/* ---------------------------------------------------------------------- */ + +#define RW "rw" +#define RO "ro" +#define WH "wh" +#define RR "rr" +#define NoLinkWH "nolwh" + +static match_table_t brperms = { + {AuBr_RR, RR}, + {AuBr_RO, RO}, + {AuBr_RW, RW}, + + {AuBr_RRWH, RR "+" WH}, + {AuBr_ROWH, RO "+" WH}, + {AuBr_RWNoLinkWH, RW "+" NoLinkWH}, + + {AuBr_ROWH, "nfsro"}, + {AuBr_RO, NULL} +}; + +static noinline_for_stack int br_perm_val(char *perm) +{ + int val; + substring_t args[MAX_OPT_ARGS]; + + AuDebugOn(!perm || !*perm); + LKTRTrace("perm %s\n", perm); + val = match_token(perm, brperms, args); + AuTraceErr(val); + return val; +} + +const char *au_optstr_br_perm(int brperm) +{ + return au_parser_pattern(brperm, brperms); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t udbalevel = { + {AuOpt_UDBA_REVAL, "reval"}, +#ifdef CONFIG_AUFS_HINOTIFY + {AuOpt_UDBA_INOTIFY, "inotify"}, +#endif + {AuOpt_UDBA_NONE, "none"}, + {-1, NULL} +}; + +static noinline_for_stack int udba_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + return match_token(str, udbalevel, args); +} + +const char *au_optstr_udba(int udba) +{ + return au_parser_pattern(udba, udbalevel); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t coolevel = { + {AuOpt_COO_NONE, "none"}, + {AuOpt_COO_LEAF, "leaf"}, + {AuOpt_COO_ALL, "all"}, + {-1, NULL} +}; + +static noinline_for_stack int coo_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + return match_token(str, coolevel, args); +} + +const char *au_optstr_coo(int coo) +{ + return au_parser_pattern(coo, coolevel); +} + +/* ---------------------------------------------------------------------- */ + +static match_table_t au_wbr_create_policy = { + {AuWbrCreate_TDP, "tdp"}, + {AuWbrCreate_TDP, "top-down-parent"}, + {AuWbrCreate_RR, "rr"}, + {AuWbrCreate_RR, "round-robin"}, + {AuWbrCreate_MFS, "mfs"}, + {AuWbrCreate_MFS, "most-free-space"}, + {AuWbrCreate_MFSV, "mfs:%d"}, + {AuWbrCreate_MFSV, "most-free-space:%d"}, + + {AuWbrCreate_MFSRR, "mfsrr:%d"}, + {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, + {AuWbrCreate_PMFS, "pmfs"}, + {AuWbrCreate_PMFSV, "pmfs:%d"}, + + {-1, NULL} +}; + +/* cf. linux/lib/parser.c */ +static int au_match_ull(substring_t *s, unsigned long long *result, int base) +{ + char *endp; + char *buf; + int ret; + + buf = kmalloc(s->to - s->from + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + memcpy(buf, s->from, s->to - s->from); + buf[s->to - s->from] = '\0'; + *result = simple_strtoull(buf, &endp, base); + ret = 0; + if (endp == buf) + ret = -EINVAL; + kfree(buf); + return ret; +} + +static int au_wbr_mfs_wmark(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int err; + u64 ull; + + err = 0; + if (!au_match_ull(arg, &ull, 0)) + create->mfsrr_watermark = ull; + else { + AuErr("bad integer in %s\n", str); + err = -EINVAL; + } + + AuTraceErr(err); + return err; +} + +static int au_wbr_mfs_sec(substring_t *arg, char *str, + struct au_opt_wbr_create *create) +{ + int n, err; + + err = 0; + if (!match_int(arg, &n) && 0 <= n) + create->mfs_second = n; + else { + AuErr("bad integer in %s\n", str); + err = -EINVAL; + } + + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +au_wbr_create_val(char *str, struct au_opt_wbr_create *create) +{ + int err, e; + substring_t args[MAX_OPT_ARGS]; + + err = match_token(str, au_wbr_create_policy, args); + create->wbr_create = err; + switch (err) { + case AuWbrCreate_MFSRRV: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (!e) + e = au_wbr_mfs_sec(&args[1], str, create); + if (unlikely(e)) + err = e; + break; + case AuWbrCreate_MFSRR: + e = au_wbr_mfs_wmark(&args[0], str, create); + if (unlikely(e)) { + err = e; + break; + } + /*FALLTHROUGH*/ + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + create->mfs_second = AUFS_MFS_SECOND_DEF; + break; + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + e = au_wbr_mfs_sec(&args[0], str, create); + if (unlikely(e)) + err = e; + break; + } + + return err; +} + +const char *au_optstr_wbr_create(int wbr_create) +{ + return au_parser_pattern(wbr_create, au_wbr_create_policy); +} + +static match_table_t au_wbr_copyup_policy = { + {AuWbrCopyup_TDP, "tdp"}, + {AuWbrCopyup_TDP, "top-down-parent"}, + {AuWbrCopyup_BUP, "bup"}, + {AuWbrCopyup_BUP, "bottom-up-parent"}, + {AuWbrCopyup_BU, "bu"}, + {AuWbrCopyup_BU, "bottom-up"}, + {-1, NULL} +}; + +static noinline_for_stack int au_wbr_copyup_val(char *str) +{ + substring_t args[MAX_OPT_ARGS]; + return match_token(str, au_wbr_copyup_policy, args); +} + +const char *au_optstr_wbr_copyup(int wbr_copyup) +{ + return au_parser_pattern(wbr_copyup, au_wbr_copyup_policy); +} + +/* ---------------------------------------------------------------------- */ + +static const int lkup_dirflags = LOOKUP_FOLLOW | LOOKUP_DIRECTORY; + +static noinline_for_stack void dump_opts(struct au_opts *opts) +{ +#ifdef CONFIG_AUFS_DEBUG + /* reduce stack space */ + union { + struct au_opt_add *add; + struct au_opt_del *del; + struct au_opt_mod *mod; + struct au_opt_xino *xino; + struct au_opt_xino_itrunc *xino_itrunc; + struct au_opt_wbr_create *create; + } u; + struct au_opt *opt; + + AuTraceEnter(); + + opt = opts->opt; + while (/* opt < opts_tail && */ opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + u.add = &opt->add; + LKTRTrace("add {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->path, u.add->perm, + u.add->nd.path.dentry); + break; + case Opt_del: + case Opt_idel: + u.del = &opt->del; + LKTRTrace("del {%s, %p}\n", u.del->path, u.del->h_root); + break; + case Opt_mod: + case Opt_imod: + u.mod = &opt->mod; + LKTRTrace("mod {%s, 0x%x, %p}\n", + u.mod->path, u.mod->perm, u.mod->h_root); + break; + case Opt_append: + u.add = &opt->add; + LKTRTrace("append {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->path, u.add->perm, + u.add->nd.path.dentry); + break; + case Opt_prepend: + u.add = &opt->add; + LKTRTrace("prepend {b%d, %s, 0x%x, %p}\n", + u.add->bindex, u.add->path, u.add->perm, + u.add->nd.path.dentry); + break; + case Opt_dirwh: + LKTRTrace("dirwh %d\n", opt->dirwh); + break; + case Opt_rdcache: + LKTRTrace("rdcache %d\n", opt->rdcache); + break; + case Opt_xino: + u.xino = &opt->xino; + LKTRTrace("xino {%s %.*s}\n", + u.xino->path, + AuDLNPair(u.xino->file->f_dentry)); + break; + case Opt_trunc_xino: + LKTRLabel(trunc_xino); + break; + case Opt_notrunc_xino: + LKTRLabel(notrunc_xino); + break; + case Opt_trunc_xino_path: + case Opt_itrunc_xino: + u.xino_itrunc = &opt->xino_itrunc; + LKTRTrace("trunc_xino %d\n", u.xino_itrunc->bindex); + break; + + case Opt_noxino: + LKTRLabel(noxino); + break; + case Opt_trunc_xib: + LKTRLabel(trunc_xib); + break; + case Opt_notrunc_xib: + LKTRLabel(notrunc_xib); + break; + case Opt_dirperm1: + LKTRLabel(dirperm1); + break; + case Opt_nodirperm1: + LKTRLabel(nodirperm1); + break; + case Opt_shwh: + LKTRLabel(shwh); + break; + case Opt_noshwh: + LKTRLabel(noshwh); + break; + case Opt_plink: + LKTRLabel(plink); + break; + case Opt_noplink: + LKTRLabel(noplink); + break; + case Opt_list_plink: + LKTRLabel(list_plink); + break; + case Opt_clean_plink: + LKTRLabel(clean_plink); + break; + case Opt_udba: + LKTRTrace("udba %d, %s\n", + opt->udba, au_optstr_udba(opt->udba)); + break; + case Opt_diropq_a: + LKTRLabel(diropq_a); + break; + case Opt_diropq_w: + LKTRLabel(diropq_w); + break; + case Opt_warn_perm: + LKTRLabel(warn_perm); + break; + case Opt_nowarn_perm: + LKTRLabel(nowarn_perm); + break; + case Opt_dlgt: + LKTRLabel(dlgt); + break; + case Opt_nodlgt: + LKTRLabel(nodlgt); + break; + case Opt_refrof: + LKTRLabel(refrof); + break; + case Opt_norefrof: + LKTRLabel(norefrof); + break; + case Opt_verbose: + LKTRLabel(verbose); + break; + case Opt_noverbose: + LKTRLabel(noverbose); + break; + case Opt_coo: + LKTRTrace("coo %d, %s\n", + opt->coo, au_optstr_coo(opt->coo)); + break; + case Opt_wbr_create: + u.create = &opt->wbr_create; + LKTRTrace("create %d, %s\n", u.create->wbr_create, + au_optstr_wbr_create(u.create->wbr_create)); + switch (u.create->wbr_create) { + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFSV: + LKTRTrace("%d sec\n", u.create->mfs_second); + break; + case AuWbrCreate_MFSRR: + LKTRTrace("%Lu watermark\n", + u.create->mfsrr_watermark); + break; + case AuWbrCreate_MFSRRV: + LKTRTrace("%Lu watermark, %d sec\n", + u.create->mfsrr_watermark, + u.create->mfs_second); + break; + } + break; + case Opt_wbr_copyup: + LKTRTrace("copyup %d, %s\n", opt->wbr_copyup, + au_optstr_wbr_copyup(opt->wbr_copyup)); + break; + default: + BUG(); + } + opt++; + } +#endif +} + +void au_opts_free(struct au_opts *opts) +{ + struct au_opt *opt; + + AuTraceEnter(); + + opt = opts->opt; + while (opt->type != Opt_tail) { + switch (opt->type) { + case Opt_add: + case Opt_append: + case Opt_prepend: + path_put(&opt->add.nd.path); + break; + case Opt_del: + case Opt_idel: + dput(opt->del.h_root); + break; + case Opt_mod: + case Opt_imod: + dput(opt->mod.h_root); + break; + case Opt_xino: + fput(opt->xino.file); + break; + } + opt++; + } +} + +static int opt_add(struct au_opt *opt, char *opt_str, struct super_block *sb, + aufs_bindex_t bindex) +{ + int err; + struct au_opt_add *add = &opt->add; + char *p; + + LKTRTrace("%s, b%d\n", opt_str, bindex); + + add->bindex = bindex; + add->perm = AuBr_Last; + add->path = opt_str; + p = strchr(opt_str, '='); + if (unlikely(p)) { + *p++ = 0; + if (*p) + add->perm = br_perm_val(p); + } + + /* LSM may detect it */ + /* do not superio. */ + err = vfsub_path_lookup(add->path, lkup_dirflags, &add->nd); + //err = -1; + if (!err) { + if (!p /* && add->perm == AuBr_Last */) { + add->perm = AuBr_RO; + if (au_test_def_rr(add->nd.path.dentry->d_sb)) + add->perm = AuBr_RR; + if (!bindex && !(sb->s_flags & MS_RDONLY)) + add->perm = AuBr_RW; +#ifdef CONFIG_AUFS_COMPAT + add->perm = AuBr_RW; +#endif + } + opt->type = Opt_add; + goto out; + } + AuErr("lookup failed %s (%d)\n", add->path, err); + err = -EINVAL; + + out: + AuTraceErr(err); + return err; +} + +/* called without aufs lock */ +int au_opts_parse(struct super_block *sb, unsigned long flags, char *str, + struct au_opts *opts) +{ + int err, n, token, skipped; + struct dentry *root; + struct au_opt *opt, *opt_tail; + char *opt_str, *p; + substring_t args[MAX_OPT_ARGS]; + aufs_bindex_t bindex, bend; + struct nameidata nd; + union { + struct au_opt_del *del; + struct au_opt_mod *mod; + struct au_opt_xino *xino; + struct au_opt_xino_itrunc *xino_itrunc; + struct au_opt_wbr_create *create; + } u; + struct file *file; + + LKTRTrace("%s, nopts %d\n", str, opts->max_opt); + + root = sb->s_root; + err = 0; + bindex = 0; + opt = opts->opt; + opt_tail = opt + opts->max_opt - 1; + opt->type = Opt_tail; + while (!err && (opt_str = strsep(&str, ",")) && *opt_str) { + err = -EINVAL; + token = match_token(opt_str, options, args); + LKTRTrace("%s, token %d, args[0]{%p, %p}\n", + opt_str, token, args[0].from, args[0].to); + + skipped = 0; + switch (token) { + case Opt_br: + err = 0; + while (!err && (opt_str = strsep(&args[0].from, ":")) + && *opt_str) { + err = opt_add(opt, opt_str, sb, bindex++); + //if (LktrCond) err = -1; + if (unlikely(!err && ++opt > opt_tail)) { + err = -E2BIG; + break; + } + opt->type = Opt_tail; + skipped = 1; + } + break; + case Opt_add: + if (unlikely(match_int(&args[0], &n))) { + AuErr("bad integer in %s\n", opt_str); + break; + } + bindex = n; + err = opt_add(opt, args[1].from, sb, bindex); + break; + case Opt_append: + err = opt_add(opt, args[0].from, sb, /*dummy bindex*/1); + if (!err) + opt->type = token; + break; + case Opt_prepend: + err = opt_add(opt, args[0].from, sb, /*bindex*/0); + if (!err) + opt->type = token; + break; + case Opt_del: + u.del = &opt->del; + u.del->path = args[0].from; + LKTRTrace("del path %s\n", u.del->path); + /* LSM may detect it */ + /* do not superio. */ + err = vfsub_path_lookup(u.del->path, lkup_dirflags, + &nd); + if (unlikely(err)) { + AuErr("lookup failed %s (%d)\n", + u.del->path, err); + break; + } + u.del->h_root = dget(nd.path.dentry); + path_put(&nd.path); + opt->type = token; + break; + case Opt_mod: + u.mod = &opt->mod; + u.mod->path = args[0].from; + p = strchr(u.mod->path, '='); + if (unlikely(!p)) { + AuErr("no permssion %s\n", opt_str); + break; + } + *p++ = 0; + u.mod->perm = br_perm_val(p); + LKTRTrace("mod path %s, perm 0x%x, %s\n", + u.mod->path, u.mod->perm, p); + /* LSM may detect it */ + /* do not superio. */ + err = vfsub_path_lookup(u.mod->path, lkup_dirflags, + &nd); + if (unlikely(err)) { + AuErr("lookup failed %s (%d)\n", + u.mod->path, err); + break; + } + u.mod->h_root = dget(nd.path.dentry); + path_put(&nd.path); + opt->type = token; + break; +#ifdef IMOD + case Opt_imod: + u.mod = &opt->mod; + u.mod->path = "(indexed)"; + if (unlikely(match_int(&args[0], &n))) { + AuErr("bad integer in %s\n", opt_str); + break; + } + bindex = n; + aufs_read_lock(root, AuLock_FLUSH); + if (bindex < 0 || au_sbend(sb) < bindex) { + AuErr("out of bounds, %d\n", bindex); + aufs_read_unlock(root, !AuLock_IR); + break; + } + u.mod->perm = br_perm_val(args[1].from); + LKTRTrace("mod path %s, perm 0x%x, %s\n", + u.mod->path, u.mod->perm, args[1].from); + err = 0; + u.mod->h_root = dget(au_h_dptr(root, bindex)); + opt->type = token; + aufs_read_unlock(root, !AuLock_IR); + break; +#endif + case Opt_xino: + u.xino = &opt->xino; + file = au_xino_create(sb, args[0].from, /*silent*/0, + /*parent*/NULL); + err = PTR_ERR(file); + if (IS_ERR(file)) + break; + err = -EINVAL; + if (unlikely(file->f_dentry->d_sb == sb)) { + fput(file); + AuErr("%s must be outside\n", args[0].from); + break; + } + err = 0; + u.xino->file = file; + u.xino->path = args[0].from; + opt->type = token; + break; + + case Opt_trunc_xino_path: + u.xino_itrunc = &opt->xino_itrunc; + p = args[0].from; + LKTRTrace("trunc_xino path %s\n", p); + /* LSM may detect it */ + /* do not superio. */ + err = vfsub_path_lookup(p, lkup_dirflags, &nd); + if (unlikely(err)) { + AuErr("lookup failed %s (%d)\n", p , err); + break; + } + u.xino_itrunc->bindex = -1; + aufs_read_lock(root, AuLock_FLUSH); + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + if (au_h_dptr(root, bindex) == nd.path.dentry) { + u.xino_itrunc->bindex = bindex; + break; + } + } + aufs_read_unlock(root, !AuLock_IR); + path_put(&nd.path); + if (unlikely(u.xino_itrunc->bindex < 0)) { + AuErr("no such branch %s\n", p); + err = -EINVAL; + break; + } + opt->type = token; + break; + + case Opt_itrunc_xino: + u.xino_itrunc = &opt->xino_itrunc; + if (unlikely(match_int(&args[0], &n))) { + AuErr("bad integer in %s\n", opt_str); + break; + } + u.xino_itrunc->bindex = n; + aufs_read_lock(root, AuLock_FLUSH); + if (n < 0 || au_sbend(sb) < n) { + AuErr("out of bounds, %d\n", n); + aufs_read_unlock(root, !AuLock_IR); + break; + } + aufs_read_unlock(root, !AuLock_IR); + err = 0; + opt->type = token; + break; + + case Opt_dirwh: + if (unlikely(match_int(&args[0], &opt->dirwh))) + break; + err = 0; + opt->type = token; + break; + + case Opt_rdcache: + if (unlikely(match_int(&args[0], &opt->rdcache))) + break; + err = 0; + opt->type = token; + break; + + case Opt_shwh: + if (flags & MS_RDONLY) { + err = 0; + opt->type = token; + } else + AuErr("shwh requires ro\n"); + break; + + case Opt_trunc_xino: + case Opt_notrunc_xino: + case Opt_noxino: + case Opt_trunc_xib: + case Opt_notrunc_xib: + case Opt_dirperm1: + case Opt_nodirperm1: + case Opt_noshwh: + case Opt_plink: + case Opt_noplink: + case Opt_list_plink: + case Opt_clean_plink: + case Opt_diropq_a: + case Opt_diropq_w: + case Opt_warn_perm: + case Opt_nowarn_perm: + case Opt_dlgt: + case Opt_nodlgt: + case Opt_refrof: + case Opt_norefrof: + case Opt_verbose: + case Opt_noverbose: + err = 0; + opt->type = token; + break; + + case Opt_udba: + opt->udba = udba_val(args[0].from); + if (opt->udba >= 0) { + err = 0; + opt->type = token; + } + break; + + case Opt_wbr_create: + u.create = &opt->wbr_create; + u.create->wbr_create + = au_wbr_create_val(args[0].from, u.create); + if (u.create->wbr_create >= 0) { + err = 0; + opt->type = token; + } + break; + case Opt_wbr_copyup: + opt->wbr_copyup = au_wbr_copyup_val(args[0].from); + if (opt->wbr_copyup >= 0) { + err = 0; + opt->type = token; + } + break; + + case Opt_coo: + opt->coo = coo_val(args[0].from); + if (opt->coo >= 0) { + err = 0; + opt->type = token; + } + break; + + case Opt_ignore: +#ifndef CONFIG_AUFS_COMPAT + AuWarn("ignored %s\n", opt_str); +#endif + case Opt_ignore_silent: + skipped = 1; + err = 0; + break; + case Opt_err: + AuErr("unknown option %s\n", opt_str); + break; + } + + if (!err && !skipped) { + if (unlikely(++opt > opt_tail)) { + err = -E2BIG; + opt--; + opt->type = Opt_tail; + break; + } + opt->type = Opt_tail; + } + } + + dump_opts(opts); + if (unlikely(err)) + au_opts_free(opts); + AuTraceErr(err); + return err; +} + +/* + * returns, + * plus: processed without an error + * zero: unprocessed + */ +static int au_opt_simple(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err; + struct au_sbinfo *sbinfo; + struct au_opt_wbr_create *create; + + AuTraceEnter(); + + err = 1; /* handled */ + sbinfo = au_sbi(sb); + switch (opt->type) { + case Opt_udba: + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= opt->udba; + opts->given_udba |= opt->udba; + break; + + case Opt_plink: + au_opt_set(sbinfo->si_mntflags, PLINK); + break; + case Opt_noplink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb); + au_opt_clr(sbinfo->si_mntflags, PLINK); + break; + case Opt_list_plink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_list(sb); + break; + case Opt_clean_plink: + if (au_opt_test(sbinfo->si_mntflags, PLINK)) + au_plink_put(sb); + break; + + case Opt_diropq_a: + au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + case Opt_diropq_w: + au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); + break; + + case Opt_dlgt: + au_opt_set(sbinfo->si_mntflags, DLGT); + break; + case Opt_nodlgt: + au_opt_clr(sbinfo->si_mntflags, DLGT); + break; + + case Opt_warn_perm: + au_opt_set(sbinfo->si_mntflags, WARN_PERM); + break; + case Opt_nowarn_perm: + au_opt_clr(sbinfo->si_mntflags, WARN_PERM); + break; + + case Opt_refrof: + au_opt_set(sbinfo->si_mntflags, REFROF); + break; + case Opt_norefrof: + //au_opt_set(sbinfo->si_mntflags, COO_LEAF); + au_opt_clr(sbinfo->si_mntflags, REFROF); + break; + + case Opt_verbose: + au_opt_set(sbinfo->si_mntflags, VERBOSE); + break; + case Opt_noverbose: + au_opt_clr(sbinfo->si_mntflags, VERBOSE); + break; + + case Opt_wbr_create: + create = &opt->wbr_create; + if (sbinfo->si_wbr_create_ops->fin) { + err = sbinfo->si_wbr_create_ops->fin(sb); + if (!err) + err = 1; + } + sbinfo->si_wbr_create = create->wbr_create; + sbinfo->si_wbr_create_ops + = au_wbr_create_ops + create->wbr_create; + switch (create->wbr_create) { + case AuWbrCreate_MFSRRV: + case AuWbrCreate_MFSRR: + sbinfo->si_wbr_mfs.mfsrr_watermark + = create->mfsrr_watermark; + /*FALLTHROUGH*/ + case AuWbrCreate_MFS: + case AuWbrCreate_MFSV: + case AuWbrCreate_PMFS: + case AuWbrCreate_PMFSV: + sbinfo->si_wbr_mfs.mfs_expire = create->mfs_second * HZ; + break; + } + if (sbinfo->si_wbr_create_ops->init) + sbinfo->si_wbr_create_ops->init(sb); /* ignore */ + break; + case Opt_wbr_copyup: + sbinfo->si_wbr_copyup = opt->wbr_copyup; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; + break; + + case Opt_coo: + sbinfo->si_mntflags &= ~AuOptMask_COO; + sbinfo->si_mntflags |= opt->coo; + break; + + case Opt_dirwh: + sbinfo->si_dirwh = opt->dirwh; + break; + + case Opt_rdcache: + sbinfo->si_rdcache = opt->rdcache * HZ; + break; + + case Opt_trunc_xino: + au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); + break; + case Opt_notrunc_xino: + au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); + break; + + case Opt_dirperm1: + au_opt_set(sbinfo->si_mntflags, DIRPERM1); + break; + case Opt_nodirperm1: + au_opt_clr(sbinfo->si_mntflags, DIRPERM1); + break; + + case Opt_shwh: + au_opt_set(sbinfo->si_mntflags, SHWH); + break; + case Opt_noshwh: + au_opt_clr(sbinfo->si_mntflags, SHWH); + break; + + case Opt_trunc_xino_path: + case Opt_itrunc_xino: + err = au_xino_trunc(sb, opt->xino_itrunc.bindex); + if (!err) + err = 1; + break; + + case Opt_trunc_xib: + au_fset_opts(opts->flags, TRUNC_XIB); + break; + case Opt_notrunc_xib: + au_fclr_opts(opts->flags, TRUNC_XIB); + break; + + default: + err = 0; + break; + } + + AuTraceErr(err); + return err; +} + +/* + * returns tri-state. + * plus: processed without an error + * zero: unprocessed + * minus: error + */ +static int au_opt_br(struct super_block *sb, struct au_opt *opt, + struct au_opts *opts) +{ + int err, do_refresh; + + AuTraceEnter(); + + err = 0; + switch (opt->type) { + case Opt_append: + opt->add.bindex = au_sbend(sb) + 1; + if (unlikely(opt->add.bindex < 0)) + opt->add.bindex = 0; + goto add; + case Opt_prepend: + opt->add.bindex = 0; + add: + case Opt_add: + err = au_br_add(sb, &opt->add, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, REFRESH_DIR); + if (unlikely(au_br_whable(opt->add.perm))) + au_fset_opts(opts->flags, REFRESH_NONDIR); + } + break; + + case Opt_del: + case Opt_idel: + err = au_br_del(sb, &opt->del, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) { + err = 1; + au_fset_opts(opts->flags, TRUNC_XIB); + au_fset_opts(opts->flags, REFRESH_DIR); + au_fset_opts(opts->flags, REFRESH_NONDIR); + } + break; + + case Opt_mod: + case Opt_imod: + err = au_br_mod(sb, &opt->mod, + au_ftest_opts(opts->flags, REMOUNT), + &do_refresh); + if (!err) { + err = 1; + if (unlikely(do_refresh)) { + au_fset_opts(opts->flags, REFRESH_DIR); + au_fset_opts(opts->flags, REFRESH_NONDIR); + } + } + break; + } + + AuTraceErr(err); + return err; +} + +static int au_opt_xino(struct super_block *sb, struct au_opt *opt, + struct au_opt_xino **opt_xino, struct au_opts *opts) +{ + int err; + + AuTraceEnter(); + + err = 0; + switch (opt->type) { + case Opt_xino: + err = au_xino_set(sb, &opt->xino, + au_ftest_opts(opts->flags, REMOUNT)); + if (!err) + *opt_xino = &opt->xino; + break; + case Opt_noxino: + au_xino_clr(sb); + *opt_xino = (void *)-1; + break; + } + + AuTraceErr(err); + return err; +} + +static noinline_for_stack int +verify_opts(struct super_block *sb, unsigned int pending, int remount) +{ + int err; + aufs_bindex_t bindex, bend; + struct au_branch *br; + struct dentry *root; + struct inode *dir; + unsigned int do_plink; + unsigned int mnt_flags; + + AuTraceEnter(); + mnt_flags = au_mntflags(sb); + AuDebugOn(!(mnt_flags & AuOptMask_COO)); + AuDebugOn(!(mnt_flags & AuOptMask_UDBA)); + + if (!(sb->s_flags & MS_RDONLY)) { + if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) + AuWarn("first branch should be rw\n"); + if (unlikely(au_opt_test(mnt_flags, SHWH))) + AuWarn("shwh should be used with ro\n"); + } + + if (unlikely(au_opt_test(mnt_flags | pending, UDBA_INOTIFY) + && !au_opt_test(mnt_flags, XINO))) + AuWarn("udba=inotify requires xino\n"); + + err = 0; + root = sb->s_root; + dir = sb->s_root->d_inode; + do_plink = !!au_opt_test(mnt_flags, PLINK); + bend = au_sbend(sb); + for (bindex = 0; !err && bindex <= bend; bindex++) { + struct inode *h_dir; + int skip; + + skip = 0; + h_dir = au_h_iptr(dir, bindex); + br = au_sbr(sb, bindex); + br_wh_read_lock(br); + switch (br->br_perm) { + case AuBr_RR: + case AuBr_RO: + case AuBr_RRWH: + case AuBr_ROWH: + skip = (!br->br_wh && !br->br_plink); + break; + + case AuBr_RWNoLinkWH: + skip = !br->br_wh; + if (skip) { + if (do_plink) + skip = !!br->br_plink; + else + skip = !br->br_plink; + } + break; + + case AuBr_RW: + skip = !!br->br_wh; + if (skip) { + if (do_plink) + skip = !!br->br_plink; + else + skip = !br->br_plink; + } + break; + + default: + BUG(); + } + br_wh_read_unlock(br); + + if (skip) + continue; + + au_hdir_lock(h_dir, dir, bindex); + br_wh_write_lock(br); + err = au_wh_init(au_h_dptr(root, bindex), br, + au_nfsmnt(sb, bindex), sb); + br_wh_write_unlock(br); + au_hdir_unlock(h_dir, dir, bindex); + } + + AuTraceErr(err); + return err; +} + +int au_opts_mount(struct super_block *sb, struct au_opts *opts) +{ + int err; + struct inode *dir; + struct au_opt *opt; + struct au_opt_xino *opt_xino; + aufs_bindex_t bend; + struct au_sbinfo *sbinfo; + unsigned int tmp; + + AuTraceEnter(); + SiMustWriteLock(sb); + DiMustWriteLock(sb->s_root); + dir = sb->s_root->d_inode; + IiMustWriteLock(dir); + + err = 0; + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_simple(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + /* disable xino, hinotify, dlgt temporary */ + sbinfo = au_sbi(sb); + tmp = sbinfo->si_mntflags; + au_opt_clr(sbinfo->si_mntflags, XINO); + au_opt_clr(sbinfo->si_mntflags, DLGT); + au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); + + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) + err = au_opt_br(sb, opt++, opts); + if (err > 0) + err = 0; + else if (unlikely(err < 0)) + goto out; + + bend = au_sbend(sb); + if (unlikely(bend < 0)) { + err = -EINVAL; + AuErr("no branches\n"); + goto out; + } + + if (au_opt_test(tmp, XINO)) + au_opt_set(sbinfo->si_mntflags, XINO); + opt = opts->opt; + while (!err && opt->type != Opt_tail) + err = au_opt_xino(sb, opt++, &opt_xino, opts); + if (unlikely(err)) + goto out; + + //todo: test this error case. + err = verify_opts(sb, tmp, /*remount*/0); + if (unlikely(err)) + goto out; + + /* enable xino */ + if (au_opt_test(tmp, XINO) && !opt_xino) { + struct au_opt_xino xino; + + xino.file = au_xino_def(sb); + err = PTR_ERR(xino.file); + if (IS_ERR(xino.file)) + goto out; + + err = au_xino_set(sb, &xino, /*remount*/0); + fput(xino.file); + if (unlikely(err)) + goto out; + } + + /* restore hinotify */ + sbinfo->si_mntflags &= ~AuOptMask_UDBA; + sbinfo->si_mntflags |= (tmp & AuOptMask_UDBA); + if (au_opt_test(tmp, UDBA_INOTIFY)) + au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AuHi_XINO); + + /* restore dlgt */ + if (au_opt_test_dlgt(tmp)) + au_opt_set(sbinfo->si_mntflags, DLGT); + + out: + AuTraceErr(err); + return err; +} + +int au_opts_remount(struct super_block *sb, struct au_opts *opts) +{ + int err, rerr; + struct inode *dir; + struct au_opt_xino *opt_xino; + struct au_opt *opt; + unsigned int dlgt; + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + SiMustWriteLock(sb); + DiMustWriteLock(sb->s_root); + dir = sb->s_root->d_inode; + IiMustWriteLock(dir); + sbinfo = au_sbi(sb); + //AuDebugOn(au_opt_test(sbifno->si_mntflags, UDBA_INOTIFY)); + + err = 0; + dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags); + opt_xino = NULL; + opt = opts->opt; + while (err >= 0 && opt->type != Opt_tail) { + err = au_opt_simple(sb, opt, opts); + + /* disable it temporary */ + dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags); + au_opt_clr(sbinfo->si_mntflags, DLGT); + + if (!err) + err = au_opt_br(sb, opt, opts); + if (!err) + err = au_opt_xino(sb, opt, &opt_xino, opts); + + /* restore it */ + if (unlikely(dlgt)) + au_opt_set(sbinfo->si_mntflags, DLGT); + opt++; + } + if (err > 0) + err = 0; + AuTraceErr(err); + + /* go on even err */ + + //todo: test this error case. + au_opt_clr(sbinfo->si_mntflags, DLGT); + rerr = verify_opts(sb, sbinfo->si_mntflags, /*remount*/1); + if (unlikely(dlgt)) + au_opt_set(sbinfo->si_mntflags, DLGT); + if (unlikely(rerr && !err)) + err = rerr; + + if (unlikely(au_ftest_opts(opts->flags, TRUNC_XIB))) { + rerr = au_xib_trunc(sb); + if (unlikely(rerr && !err)) + err = rerr; + } + + /* they are handled by the caller */ + if (!au_ftest_opts(opts->flags, REFRESH_DIR) + && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) + au_fset_opts(opts->flags, REFRESH_DIR); + + LKTRTrace("status 0x%x\n", opts->flags); + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/opts.h linux-2.6.25.4-unionfs/fs/aufs/opts.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/opts.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/opts.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * mount options/flags + * + * $Id: opts.h,v 1.2 2008/04/21 01:46:26 sfjro Exp $ + */ + +#ifndef __AUFS_OPTS_H__ +#define __AUFS_OPTS_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include "wkq.h" + +/* ---------------------------------------------------------------------- */ +/* mount flags */ + +/* external inode number bitmap and translation table */ +#define AuOpt_XINO 1 +#define AuOpt_TRUNC_XINO (1 << 1) +#define AuOpt_LINO (1 << 2) +#define AuOpt_UDBA_NONE (1 << 3) /* users direct branch access */ +#define AuOpt_UDBA_REVAL (1 << 4) +#define AuOpt_UDBA_INOTIFY (1 << 5) +#define AuOpt_SHWH (1 << 6) +#define AuOpt_PLINK (1 << 7) +#define AuOpt_WARN_PERM (1 << 8) +#define AuOpt_DIRPERM1 (1 << 9) +#define AuOpt_DLGT (1 << 10) +#define AuOpt_COO_NONE (1 << 11) /* copyup on open */ +#define AuOpt_COO_LEAF (1 << 12) +#define AuOpt_COO_ALL (1 << 13) +#define AuOpt_ALWAYS_DIROPQ (1 << 14) +#define AuOpt_REFROF (1 << 15) +#define AuOpt_VERBOSE (1 << 16) +#ifndef CONFIG_AUFS_HINOTIFY +#undef AuOpt_UDBA_INOTIFY +#define AuOpt_UDBA_INOTIFY 0 +#endif +#ifndef CONFIG_AUFS_SHWH +#undef AuOpt_SHWH +#define AuOpt_SHWH 0 +#endif +#ifndef CONFIG_AUFS_DLGT +#undef AuOpt_DIRPERM1 +#define AuOpt_DIRPERM1 0 +#undef AuOpt_DLGT +#define AuOpt_DLGT 0 +#endif + +/* policies to select one among multiple writable branches */ +enum { + AuWbrCreate_TDP, /* top down parent */ + AuWbrCreate_RR, /* round robin */ + AuWbrCreate_MFS, /* most free space */ + AuWbrCreate_MFSV, /* mfs with seconds */ + AuWbrCreate_MFSRR, /* mfs then rr */ + AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ + AuWbrCreate_PMFS, /* parent and mfs */ + AuWbrCreate_PMFSV, /* parent and mfs with seconds */ + + AuWbrCreate_Def = AuWbrCreate_TDP +}; + +enum { + AuWbrCopyup_TDP, /* top down parent */ + AuWbrCopyup_BUP, /* bottom up parent */ + AuWbrCopyup_BU, /* bottom up */ + + AuWbrCopyup_Def = AuWbrCopyup_TDP +}; + +#define AuOptMask_COO (AuOpt_COO_NONE \ + | AuOpt_COO_LEAF \ + | AuOpt_COO_ALL) +#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ + | AuOpt_UDBA_REVAL \ + | AuOpt_UDBA_INOTIFY) + +#ifdef CONFIG_AUFS_COMPAT +#define AuOpt_DefExtra1 AuOpt_ALWAYS_DIROPQ +#else +#define AuOpt_DefExtra1 0 +#endif + +#define AuOpt_Def (AuOpt_XINO \ + | AuOpt_UDBA_REVAL \ + | AuOpt_WARN_PERM \ + | AuOpt_COO_NONE \ + | AuOpt_PLINK \ + | AuOpt_DefExtra1) + +/* ---------------------------------------------------------------------- */ + +struct au_opt_add { + aufs_bindex_t bindex; + char *path; + int perm; + struct nameidata nd; +}; + +struct au_opt_del { + char *path; + struct dentry *h_root; +}; + +struct au_opt_mod { + char *path; + int perm; + struct dentry *h_root; +}; + +struct au_opt_xino { + char *path; + struct file *file; +}; + +struct au_opt_xino_itrunc { + aufs_bindex_t bindex; +}; + +struct au_opt_xino_trunc_v { + u64 upper; + int step; +}; + +struct au_opt_wbr_create { + int wbr_create; + int mfs_second; + u64 mfsrr_watermark; +}; + +struct au_opt { + int type; + union { + struct au_opt_xino xino; + struct au_opt_xino_itrunc xino_itrunc; + struct au_opt_add add; + struct au_opt_del del; + struct au_opt_mod mod; + int dirwh; + int rdcache; + int deblk; + int nhash; + int udba; + int coo; + struct au_opt_wbr_create wbr_create; + int wbr_copyup; + }; +}; + +/* opts flags */ +#define AuOpts_REMOUNT 1 +#define AuOpts_REFRESH_DIR (1 << 1) +#define AuOpts_REFRESH_NONDIR (1 << 2) +#define AuOpts_TRUNC_XIB (1 << 3) +#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) +#define au_fset_opts(flags, name) { (flags) |= AuOpts_##name; } +#define au_fclr_opts(flags, name) { (flags) &= ~AuOpts_##name; } + +struct au_opts { + struct au_opt *opt; + int max_opt; + + unsigned int given_udba; + unsigned int flags; +}; + +/* ---------------------------------------------------------------------- */ + +const char *au_optstr_br_perm(int brperm); +const char *au_optstr_udba(int udba); +const char *au_optstr_coo(int coo); +const char *au_optstr_wbr_copyup(int wbr_copyup); +const char *au_optstr_wbr_create(int wbr_create); + +void au_opts_free(struct au_opts *opts); +int au_opts_parse(struct super_block *sb, unsigned long flags, char *str, + struct au_opts *opts); +int au_opts_mount(struct super_block *sb, struct au_opts *opts); +int au_opts_remount(struct super_block *sb, struct au_opts *opts); + +/* ---------------------------------------------------------------------- */ + +static inline unsigned int au_opt_do_test(unsigned int flags, unsigned int bit) +{ + AuDebugOn(bit & (AuOpt_DLGT | AuOpt_DIRPERM1)); + return (flags & bit); +} + +#define au_opt_test(flags, name) au_opt_do_test(flags, AuOpt_##name) + +#define au_opt_set(flags, name) do { \ + BUILD_BUG_ON(AuOpt_##name & (AuOptMask_COO | AuOptMask_UDBA)); \ + ((flags) |= AuOpt_##name); \ +} while (0) + +#define au_opt_clr(flags, name) { ((flags) &= ~AuOpt_##name); } + +#define au_opt_set_coo(flags, name) do { \ + (flags) &= ~AuOptMask_COO; \ + ((flags) |= AuOpt_##name); \ +} while(0) + +#define au_opt_set_udba(flags, name) do { \ + (flags) &= ~AuOptMask_UDBA; \ + ((flags) |= AuOpt_##name); \ +} while(0) + +static inline unsigned int au_opt_test_dlgt(unsigned int flags) +{ + if (!au_test_wkq(current)) + return (flags & AuOpt_DLGT); + return 0; +} + +static inline unsigned int au_opt_test_dirperm1(unsigned int flags) +{ + if (!au_test_wkq(current)) + return (flags & AuOpt_DIRPERM1); + return 0; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/plink.c linux-2.6.25.4-unionfs/fs/aufs/plink.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/plink.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/plink.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * pseudo-link + * + * $Id: plink.c,v 1.2 2008/04/21 01:45:16 sfjro Exp $ + */ + +#include "aufs.h" + +struct pseudo_link { + struct list_head list; + struct inode *inode; +}; + +#ifdef CONFIG_AUFS_DEBUG +void au_plink_list(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct list_head *plink_list; + struct pseudo_link *plink; + + AuTraceEnter(); + SiMustAnyLock(sb); + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + + plink_list = &sbinfo->si_plink; + spin_lock(&sbinfo->si_plink_lock); + list_for_each_entry(plink, plink_list, list) + AuDbg("%lu\n", plink->inode->i_ino); + spin_unlock(&sbinfo->si_plink_lock); +} +#endif + +int au_plink_test(struct super_block *sb, struct inode *inode) +{ + int found; + struct au_sbinfo *sbinfo; + struct list_head *plink_list; + struct pseudo_link *plink; + + LKTRTrace("i%lu\n", inode->i_ino); + SiMustAnyLock(sb); + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + + found = 0; + plink_list = &sbinfo->si_plink; + spin_lock(&sbinfo->si_plink_lock); + list_for_each_entry(plink, plink_list, list) + if (plink->inode == inode) { + found = 1; + break; + } + spin_unlock(&sbinfo->si_plink_lock); + return found; +} + +/* 20 is max digits length of ulong 64 */ +#define PLINK_NAME_LEN ((20 + 1) * 2) + +static int plink_name(char *name, int len, struct inode *inode, + aufs_bindex_t bindex) +{ + int rlen; + struct inode *h_inode; + + LKTRTrace("i%lu, b%d\n", inode->i_ino, bindex); + AuDebugOn(len != PLINK_NAME_LEN); + h_inode = au_h_iptr(inode, bindex); + AuDebugOn(!h_inode); + rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); + AuDebugOn(rlen >= len); + return rlen; +} + +struct dentry *au_plink_lkup(struct super_block *sb, aufs_bindex_t bindex, + struct inode *inode) +{ + struct dentry *h_dentry, *h_parent; + struct au_branch *br; + struct inode *h_dir; + char tgtname[PLINK_NAME_LEN]; + int len; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino); + br = au_sbr(sb, bindex); + h_parent = br->br_plink; + AuDebugOn(!h_parent); + h_dir = h_parent->d_inode; + AuDebugOn(!h_dir); + + len = plink_name(tgtname, sizeof(tgtname), inode, bindex); + + /* always superio. */ + ndx.nfsmnt = au_do_nfsmnt(br->br_mnt); + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); + h_dentry = au_sio_lkup_one(tgtname, h_parent, len, &ndx); + mutex_unlock(&h_dir->i_mutex); + return h_dentry; +} + +static int do_whplink(char *tgt, int len, struct dentry *h_parent, + struct dentry *h_dentry, struct vfsmount *nfsmnt, + struct super_block *sb) +{ + int err, dlgt; + struct dentry *h_tgt; + struct inode *h_dir; + struct vfsub_args vargs; + struct au_ndx ndx = { + .nfsmnt = nfsmnt, + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + dlgt = !!au_opt_test_dlgt(au_mntflags(sb)); + if (unlikely(dlgt)) + au_fset_ndx(ndx.flags, DLGT); + h_tgt = au_lkup_one(tgt, h_parent, len, &ndx); + err = PTR_ERR(h_tgt); + if (IS_ERR(h_tgt)) + goto out; + + err = 0; + vfsub_args_init(&vargs, NULL, dlgt, 0); + h_dir = h_parent->d_inode; + if (unlikely(h_tgt->d_inode && h_tgt->d_inode != h_dentry->d_inode)) + err = vfsub_unlink(h_dir, h_tgt, &vargs); + if (!err && !h_tgt->d_inode) { + err = vfsub_link(h_dentry, h_dir, h_tgt, dlgt); + //inode->i_nlink++; + } + dput(h_tgt); + + out: + AuTraceErr(err); + return err; +} + +struct do_whplink_args { + int *errp; + char *tgt; + int len; + struct dentry *h_parent; + struct dentry *h_dentry; + struct vfsmount *nfsmnt; + struct super_block *sb; +}; + +static void call_do_whplink(void *args) +{ + struct do_whplink_args *a = args; + *a->errp = do_whplink(a->tgt, a->len, a->h_parent, a->h_dentry, + a->nfsmnt, a->sb); +} + +static int whplink(struct dentry *h_dentry, struct inode *inode, + aufs_bindex_t bindex, struct super_block *sb) +{ + int err, len, wkq_err; + struct au_branch *br; + struct dentry *h_parent; + struct inode *h_dir; + char tgtname[PLINK_NAME_LEN]; + + LKTRTrace("%.*s\n", AuDLNPair(h_dentry)); + br = au_sbr(inode->i_sb, bindex); + h_parent = br->br_plink; + AuDebugOn(!h_parent); + h_dir = h_parent->d_inode; + AuDebugOn(!h_dir); + + len = plink_name(tgtname, sizeof(tgtname), inode, bindex); + + /* always superio. */ + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); + if (!au_test_wkq(current)) { + struct do_whplink_args args = { + .errp = &err, + .tgt = tgtname, + .len = len, + .h_parent = h_parent, + .h_dentry = h_dentry, + .nfsmnt = au_do_nfsmnt(br->br_mnt), + .sb = sb + }; + wkq_err = au_wkq_wait(call_do_whplink, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } else + err = do_whplink(tgtname, len, h_parent, h_dentry, + au_do_nfsmnt(br->br_mnt), sb); + mutex_unlock(&h_dir->i_mutex); + + AuTraceErr(err); + return err; +} + +void au_plink_append(struct super_block *sb, struct inode *inode, + struct dentry *h_dentry, aufs_bindex_t bindex) +{ + struct au_sbinfo *sbinfo; + struct list_head *plink_list; + struct pseudo_link *plink; + int found, err, cnt; + + LKTRTrace("i%lu\n", inode->i_ino); + SiMustAnyLock(sb); + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + + cnt = 0; + found = 0; + plink_list = &sbinfo->si_plink; + spin_lock(&sbinfo->si_plink_lock); + list_for_each_entry(plink, plink_list, list) { + cnt++; + if (plink->inode == inode) { + found = 1; + break; + } + } + + err = 0; + if (!found) { + plink = kmalloc(sizeof(*plink), GFP_ATOMIC); + if (plink) { + plink->inode = igrab(inode); + list_add(&plink->list, plink_list); + cnt++; + } else + err = -ENOMEM; + } + spin_unlock(&sbinfo->si_plink_lock); + + if (!err) + err = whplink(h_dentry, inode, bindex, sb); + + if (unlikely(cnt > AUFS_PLINK_WARN)) + AuWarn1("unexpectedly many pseudo links, %d\n", cnt); + if (unlikely(err)) + AuWarn("err %d, damaged pseudo link. ignored.\n", err); +} + +static void do_put_plink(struct pseudo_link *plink, int do_del) +{ + AuTraceEnter(); + + iput(plink->inode); + if (do_del) + list_del(&plink->list); + kfree(plink); +} + +void au_plink_put(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct list_head *plink_list; + struct pseudo_link *plink, *tmp; + + AuTraceEnter(); + SiMustWriteLock(sb); + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + + plink_list = &sbinfo->si_plink; + //spin_lock(&sbinfo->si_plink_lock); + list_for_each_entry_safe(plink, tmp, plink_list, list) + do_put_plink(plink, 0); + INIT_LIST_HEAD(plink_list); + //spin_unlock(&sbinfo->si_plink_lock); +} + +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) +{ + struct au_sbinfo *sbinfo; + struct list_head *plink_list; + struct pseudo_link *plink, *tmp; + struct inode *inode; + aufs_bindex_t bstart, bend, bindex; + int do_put; + + AuTraceEnter(); + SiMustWriteLock(sb); + sbinfo = au_sbi(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); + + plink_list = &sbinfo->si_plink; + //spin_lock(&sbinfo->si_plink_lock); + list_for_each_entry_safe(plink, tmp, plink_list, list) { + do_put = 0; + inode = igrab(plink->inode); + ii_write_lock_child(inode); + bstart = au_ibstart(inode); + bend = au_ibend(inode); + if (bstart >= 0) { + for (bindex = bstart; bindex <= bend; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; + au_set_h_iptr(inode, bindex, NULL, 0); + do_put = 1; + break; + } + } else + do_put_plink(plink, 1); + + if (do_put) { + for (bindex = bstart; bindex <= bend; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; + } + if (do_put) + do_put_plink(plink, 1); + } + ii_write_unlock(inode); + iput(inode); + } + //spin_unlock(&sbinfo->si_plink_lock); +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/robr.c linux-2.6.25.4-unionfs/fs/aufs/robr.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/robr.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/robr.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * 'robr', aufs as readonly branch of another aufs + * + * $Id: robr.c,v 1.3 2008/05/19 01:48:04 sfjro Exp $ + */ + +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +int au_test_robr_wh(struct qstr *name, struct dentry *h_parent, + struct qstr *wh_name, int try_sio, struct au_ndx *ndx) +{ + if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) + return au_wh_test(h_parent, wh_name, try_sio, ndx); + return -EPERM; +} + +int au_test_robr_shwh(struct super_block *sb, const struct qstr *name) +{ + return 0; +} + +/* ---------------------------------------------------------------------- */ + +struct au_robr_lvma { + struct list_head list; + struct vm_area_struct *vma; +}; + +struct file *au_robr_safe_file(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct super_block *sb = file->f_dentry->d_sb; + struct au_robr_lvma *lvma, *entry; + struct au_sbinfo *sbinfo; + int found, warn; + + AuTraceEnter(); + AuDebugOn(!au_test_aufs(sb)); + + warn = 0; + found = 0; + sbinfo = au_sbi(sb); + spin_lock(&sbinfo->si_lvma_lock); + list_for_each_entry(entry, &sbinfo->si_lvma, list) { + found = (entry->vma == vma); + if (unlikely(found)) + break; + } + if (!found) { + lvma = kmalloc(sizeof(*lvma), GFP_ATOMIC); + if (lvma) { + lvma->vma = vma; + list_add(&lvma->list, &sbinfo->si_lvma); + } else { + warn = 1; + file = NULL; + } + } else + file = NULL; + spin_unlock(&sbinfo->si_lvma_lock); + + if (unlikely(warn)) + AuWarn1("no memory for lvma\n"); + return file; +} + +void au_robr_reset_file(struct vm_area_struct *vma, struct file *file) +{ + struct super_block *sb = file->f_dentry->d_sb; + struct au_robr_lvma *entry, *found; + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + AuDebugOn(!au_test_aufs(sb)); + + vma->vm_file = file; + //smp_mb(); /* flush vm_file */ + + found = NULL; + sbinfo = au_sbi(sb); + spin_lock(&sbinfo->si_lvma_lock); + list_for_each_entry(entry, &sbinfo->si_lvma, list) + if (entry->vma == vma) { + found = entry; + break; + } + AuDebugOn(!found); + list_del(&found->list); + spin_unlock(&sbinfo->si_lvma_lock); + kfree(found); +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/sbinfo.c linux-2.6.25.4-unionfs/fs/aufs/sbinfo.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/sbinfo.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/sbinfo.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * superblock private data + * + * $Id: sbinfo.c,v 1.5 2008/05/19 01:49:56 sfjro Exp $ + */ + +#include +#include +#include "aufs.h" + +/* + * they are necessary regardless sysfs is disabled. + */ +void au_si_free(struct kobject *kobj) +{ + struct au_sbinfo *sbinfo; + struct super_block *sb; + + LKTRTrace("kobj %p\n", kobj); + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + LKTRTrace("sbinfo %p\n", sbinfo); + AuDebugOn(!list_empty(&sbinfo->si_plink)); + + sb = sbinfo->si_sb; + if (unlikely(!au_ftest_si(sbinfo, FAILED_INIT))) { + au_sbilist_lock(); + au_sbilist_del(sbinfo); + au_sbilist_unlock(); + } + + si_write_lock(sb); + au_xino_clr(sb); + au_br_free(sbinfo); + kfree(sbinfo->si_branch); + si_write_unlock(sb); + + //AuDbg("free\n"); + kfree(sbinfo); +} + +int au_si_alloc(struct super_block *sb) +{ + int err; + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + + err = -ENOMEM; + sbinfo = kmalloc(sizeof(*sbinfo), GFP_KERNEL); + //if (LktrCond) {kfree(sbinfo); sbinfo = NULL;} + if (unlikely(!sbinfo)) + goto out; + sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_KERNEL); + //if (LktrCond) {kfree(sbinfo->si_branch); sbinfo->si_branch = NULL;} + if (unlikely(!sbinfo->si_branch)) + goto out_sbinfo; + + memset(&sbinfo->si_kobj, 0, sizeof(sbinfo->si_kobj)); + err = sysaufs_si_init(sbinfo); + if (unlikely(err)) + goto out_br; + + au_rw_init_wlock(&sbinfo->si_rwsem); + sbinfo->si_generation = 0; + //sbinfo->si_generation = INT_MAX - 2; + sbinfo->au_si_status = 0; + sbinfo->si_bend = -1; + sbinfo->si_last_br_id = 0; + + sbinfo->si_wbr_copyup = AuWbrCopyup_Def; + sbinfo->si_wbr_create = AuWbrCreate_Def; + sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + AuWbrCopyup_Def; + sbinfo->si_wbr_create_ops = au_wbr_create_ops + AuWbrCreate_Def; + + sbinfo->si_mntflags = AuOpt_Def; + + sbinfo->si_xread = NULL; + sbinfo->si_xwrite = NULL; + sbinfo->si_xib = NULL; + mutex_init(&sbinfo->si_xib_mtx); + sbinfo->si_xib_buf = NULL; + /* leave si_xib_last_pindex and si_xib_next_bit */ + + au_nwt_init(&sbinfo->si_nowait); + + sbinfo->si_rdcache = AUFS_RDCACHE_DEF * HZ; + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + + spin_lock_init(&sbinfo->si_plink_lock); + INIT_LIST_HEAD(&sbinfo->si_plink); + + au_robr_lvma_init(sbinfo); + + /* leave other members for sysaufs and si_mnt. */ + sbinfo->si_sb = sb; + + sb->s_fs_info = sbinfo; + //sysaufs_si_get(sb); + + au_debug_sbinfo_init(sbinfo); + return 0; /* success */ + + out_br: + kfree(sbinfo->si_branch); + out_sbinfo: + kfree(sbinfo); + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_branch *au_sbr(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_branch *br; + + SiMustAnyLock(sb); + AuDebugOn(bindex < 0 || au_sbend(sb) < bindex); + br = au_sbi(sb)->si_branch[0 + bindex]; + AuDebugOn(!br); + return br; +} + +au_gen_t au_sigen_inc(struct super_block *sb) +{ + au_gen_t gen; + + SiMustWriteLock(sb); + gen = ++au_sbi(sb)->si_generation; + au_update_digen(sb->s_root); + au_update_iigen(sb->s_root->d_inode); + sb->s_root->d_inode->i_version++; + return gen; +} + +int au_find_bindex(struct super_block *sb, struct au_branch *br) +{ + aufs_bindex_t bindex, bend; + + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) + if (au_sbr(sb, bindex) == br) + return bindex; + return -1; +} + +/* ---------------------------------------------------------------------- */ + +/* dentry and super_block lock. call at entry point */ +void aufs_read_lock(struct dentry *dentry, int flags) +{ + si_read_lock(dentry->d_sb, flags); + if (au_ftest_lock(flags, DW)) + di_write_lock_child(dentry); + else + di_read_lock_child(dentry, flags); +} + +void aufs_read_unlock(struct dentry *dentry, int flags) +{ + if (au_ftest_lock(flags, DW)) + di_write_unlock(dentry); + else + di_read_unlock(dentry, flags); + si_read_unlock(dentry->d_sb); +} + +void aufs_write_lock(struct dentry *dentry) +{ + si_write_lock(dentry->d_sb); + di_write_lock_child(dentry); +} + +void aufs_write_unlock(struct dentry *dentry) +{ + di_write_unlock(dentry); + si_write_unlock(dentry->d_sb); +} + +void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) +{ + AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb); + si_read_lock(d1->d_sb, flags); + di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); +} + +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) +{ + AuDebugOn(d1 == d2 || d1->d_sb != d2->d_sb); + di_write_unlock2(d1, d2); + si_read_unlock(d1->d_sb); +} + +/* ---------------------------------------------------------------------- */ + +aufs_bindex_t au_new_br_id(struct super_block *sb) +{ + aufs_bindex_t br_id; + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + while (1) { + br_id = ++sbinfo->si_last_br_id; + if (br_id && au_br_index(sb, br_id) < 0) + return br_id; + } +} + +//todo: dirty +/* + * when you mntput() for the return value of this function, + * you have to store it to your local var. + * ie. never mntput si_mntcache directly. + */ +struct vfsmount *au_mntcache_get(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + struct mnt_namespace *ns; + struct vfsmount *pos, *mnt; + + AuTraceEnter(); + + sbinfo = au_sbi(sb); + + spin_lock(&sbinfo->si_mntcache_lock); + if (sbinfo->si_mntcache) + goto out; + + /* vfsmount_lock is not exported */ + //root_mnt = current->fs->root.mnt; + /* no get/put */ + AuDebugOn(!current->nsproxy); + ns = current->nsproxy->mnt_ns; + AuDebugOn(!ns); + list_for_each_entry(pos, &ns->list, mnt_list) + if (pos->mnt_sb == sb) { + sbinfo->si_mntcache = pos; + break; + } + AuDebugOn(!sbinfo->si_mntcache); + + out: + mnt = mntget(sbinfo->si_mntcache); + spin_unlock(&sbinfo->si_mntcache_lock); + return mnt; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/super.c linux-2.6.25.4-unionfs/fs/aufs/super.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/super.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/super.c 2008-05-25 12:00:53 +0300 @@ -0,0 +1,877 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * mount and super_block operations + * + * $Id: super.c,v 1.6 2008/05/19 01:50:10 sfjro Exp $ + */ + +#include +#include +#include +#include +#include + +#include "aufs.h" + +/* + * super_operations + */ +static struct inode *aufs_alloc_inode(struct super_block *sb) +{ + struct aufs_icntnr *c; + + AuTraceEnter(); + + c = au_cache_alloc_icntnr(); + //if (LktrCond) {au_cache_free_icntnr(c); c = NULL;} + if (c) { + inode_init_once(&c->vfs_inode); + c->vfs_inode.i_version = 1; //sigen(sb); + c->iinfo.ii_hinode = NULL; + return &c->vfs_inode; + } + return NULL; +} + +static void aufs_destroy_inode(struct inode *inode) +{ + LKTRTrace("i%lu\n", inode->i_ino); + au_iinfo_fin(inode); + au_cache_free_icntnr(container_of(inode, struct aufs_icntnr, + vfs_inode)); +} + +struct inode *au_iget_locked(struct super_block *sb, ino_t ino) +{ + struct inode *inode; + int err; + + LKTRTrace("i%lu\n", ino); + + inode = iget_locked(sb, ino); + if (unlikely(!inode)) { + inode = ERR_PTR(-ENOMEM); + goto out; + } + AuDebugOn(IS_ERR(inode)); + if (unlikely(!(inode->i_state & I_NEW))) + goto out; + + err = au_iinfo_init(inode); + //if (LktrCond) err = -1; + if (!err) { + inode->i_version++; + inode->i_op = &aufs_iop; + inode->i_fop = &aufs_file_fop; + inode->i_mapping->a_ops = &aufs_aop; + //inode->i_mapping->backing_dev_info = &bdi; + } else { + iget_failed(inode); + inode = ERR_PTR(err); + } + + out: + /* never return NULL */ + AuDebugOn(!inode); + AuTraceErrPtr(inode); + return inode; +} + +static int au_show_brs(struct seq_file *seq, struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bend; + struct dentry *root; + struct path path; + + AuTraceEnter(); + + err = 0; + root = sb->s_root; + bend = au_sbend(sb); + for (bindex = 0; !err && bindex <= bend; bindex++) { + path.mnt = au_sbr_mnt(sb, bindex); + path.dentry = au_h_dptr(root, bindex); + err = seq_path(seq, &path, au_esc_chars); + if (err > 0) + err = seq_printf + (seq, "=%s", + au_optstr_br_perm(au_sbr_perm(sb, bindex))); + if (!err && bindex != bend) + err = seq_putc(seq, ':'); + } + + AuTraceErr(err); + return err; +} + +static void au_show_wbr_create(struct seq_file *m, int v, + struct au_sbinfo *sbinfo) +{ + const char *pat; + + AuDebugOn(v == AuWbrCreate_Def); + + seq_printf(m, ",create="); + pat = au_optstr_wbr_create(v); + switch (v) { + case AuWbrCreate_TDP: + case AuWbrCreate_RR: + case AuWbrCreate_MFS: + case AuWbrCreate_PMFS: + seq_printf(m, pat); + break; + case AuWbrCreate_MFSV: + seq_printf(m, /*pat*/"mfs:%lu", + sbinfo->si_wbr_mfs.mfs_expire / HZ); + break; + case AuWbrCreate_PMFSV: + seq_printf(m, /*pat*/"pmfs:%lu", + sbinfo->si_wbr_mfs.mfs_expire / HZ); + break; + case AuWbrCreate_MFSRR: + seq_printf(m, /*pat*/"mfsrr:%Lu", + sbinfo->si_wbr_mfs.mfsrr_watermark); + break; + case AuWbrCreate_MFSRRV: + seq_printf(m, /*pat*/"mfsrr:%Lu:%lu", + sbinfo->si_wbr_mfs.mfsrr_watermark, + sbinfo->si_wbr_mfs.mfs_expire / HZ); + break; + } +} + +/* seq_file will re-call me in case of too long string */ +static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt) +{ + int err, n; + struct super_block *sb; + struct au_sbinfo *sbinfo; + struct dentry *root; + struct file *xino; + unsigned int mnt_flags, v; + struct path path; + + //au_debug_on(); + AuTraceEnter(); + + sb = mnt->mnt_sb; + root = sb->s_root; + if (!sysaufs_brs) + aufs_read_lock(root, !AuLock_IR); + else + si_noflush_read_lock(sb); + sbinfo = au_sbi(sb); + seq_printf(m, ",si=%p", sbinfo); + mnt_flags = au_mntflags(sb); + if (au_opt_test(mnt_flags, XINO)) { + seq_puts(m, ",xino="); + xino = sbinfo->si_xib; + path.mnt = xino->f_vfsmnt; + path.dentry = xino->f_dentry; + err = seq_path(m, &path, au_esc_chars); + if (unlikely(err <= 0)) + goto out; + err = 0; +#define Deleted "\\040(deleted)" + m->count -= sizeof(Deleted) - 1; + AuDebugOn(memcmp(m->buf + m->count, Deleted, + sizeof(Deleted) - 1)); +#undef Deleted + } else + seq_puts(m, ",noxino"); + +#define AuBool(name, str) do { \ + v = au_opt_test(mnt_flags, name); \ + if (v != au_opt_test(AuOpt_Def, name)) \ + seq_printf(m, ",%s" #str, v ? "" : "no"); \ +} while (0) + +#define AuStr(name, str) do { \ + v = mnt_flags & AuOptMask_##name; \ + if (v != (AuOpt_Def & AuOptMask_##name)) \ + seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ +} while (0) + +#ifdef CONFIG_AUFS_COMPAT +#define AuStr_BrOpt "dirs=" +#else +#define AuStr_BrOpt "br:" +#endif + + AuBool(TRUNC_XINO, trunc_xino); + AuBool(DIRPERM1, dirperm1); + AuBool(SHWH, shwh); + AuBool(PLINK, plink); + AuStr(UDBA, udba); + + v = sbinfo->si_wbr_create; + if (v != AuWbrCreate_Def) + au_show_wbr_create(m, v, sbinfo); + + v = sbinfo->si_wbr_copyup; + if (v != AuWbrCopyup_Def) + seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); + + v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); + if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) + seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); + AuBool(REFROF, refrof); + AuBool(DLGT, dlgt); + AuBool(WARN_PERM, warn_perm); + AuBool(VERBOSE, verbose); + + n = sbinfo->si_dirwh; + if (n != AUFS_DIRWH_DEF) + seq_printf(m, ",dirwh=%d", n); + n = sbinfo->si_rdcache / HZ; + if (n != AUFS_RDCACHE_DEF) + seq_printf(m, ",rdcache=%d", n); + + AuStr(COO, coo); + + out: + if (!sysaufs_brs) { + seq_puts(m, "," AuStr_BrOpt); + au_show_brs(m, sb); + aufs_read_unlock(root, !AuLock_IR); + } else + si_read_unlock(sb); + //au_debug_off(); + return 0; + +#undef AuBool +#undef AuStr +#undef AuStr_BrOpt +} + +static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) +{ + int err; + + AuTraceEnter(); + + aufs_read_lock(dentry->d_sb->s_root, 0); + err = vfsub_statfs(au_h_dptr(dentry->d_sb->s_root, 0), buf, + !!au_opt_test_dlgt(au_mntflags(dentry->d_sb))); + //if (LktrCond) err = -1; + aufs_read_unlock(dentry->d_sb->s_root, 0); + if (!err) { + buf->f_type = AUFS_SUPER_MAGIC; + buf->f_namelen -= AUFS_WH_PFX_LEN; + //todo: support uuid? + memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); + } + /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ + + AuTraceErr(err); + return err; +} + +static void au_fsync_br(struct super_block *sb) +{ +#ifdef CONFIG_AUFS_FSYNC_SUPER_PATCH + aufs_bindex_t bend, bindex; + int brperm; + struct super_block *h_sb; + + AuTraceEnter(); + + si_write_lock(sb); + bend = au_sbend(sb); + for (bindex = 0; bindex < bend; bindex++) { + brperm = au_sbr_perm(sb, bindex); + if (brperm == AuBr_RR || brperm == AuBr_RRWH) + continue; + h_sb = au_sbr_sb(sb, bindex); + if (bdev_read_only(h_sb->s_bdev)) + continue; + + lockdep_off(); + down_write(&h_sb->s_umount); + shrink_dcache_sb(h_sb); + fsync_super(h_sb); + up_write(&h_sb->s_umount); + lockdep_on(); + } + si_write_unlock(sb); +#endif +} + +static void aufs_umount_begin(struct vfsmount *mnt, int flags) +{ + struct super_block *sb = mnt->mnt_sb; + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + //todo: dont trust BKL. + AuDebugOn(!kernel_locked()); + + sbinfo = au_sbi(sb); + if (unlikely(!sbinfo)) + return; + + au_fsync_br(sb); + + si_write_lock(sb); + if (au_opt_test(au_mntflags(sb), PLINK)) + au_plink_put(sb); + spin_lock(&sbinfo->si_mntcache_lock); + sbinfo->si_mntcache = NULL; + spin_unlock(&sbinfo->si_mntcache_lock); + si_write_unlock(sb); +} + +/* final actions when unmounting a file system */ +static void aufs_put_super(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + + sbinfo = au_sbi(sb); + if (unlikely(!sbinfo)) + return; + kobject_put(&sbinfo->si_kobj); +} + +/* ---------------------------------------------------------------------- */ + +/* + * refresh dentry and inode at remount time. + */ +static int do_refresh(struct dentry *dentry, mode_t type, + unsigned int dir_flags) +{ + int err; + struct dentry *parent; + struct inode *inode; + + LKTRTrace("%.*s, 0%o\n", AuDLNPair(dentry), type); + inode = dentry->d_inode; + AuDebugOn(!inode); + + di_write_lock_child(dentry); + parent = dget_parent(dentry); + di_read_lock_parent(parent, AuLock_IR); + /* returns a number of positive dentries */ + err = au_refresh_hdentry(dentry, type); + //err = -1; + if (err >= 0) { + err = au_refresh_hinode(inode, dentry); + //err = -1; + if (!err && type == S_IFDIR) + au_reset_hinotify(inode, dir_flags); + } + if (unlikely(err)) + AuErr("unrecoverable error %d, %.*s\n", err, AuDLNPair(dentry)); + di_read_unlock(parent, AuLock_IR); + dput(parent); + di_write_unlock(dentry); + + AuTraceErr(err); + return err; +} + +static int test_dir(struct dentry *dentry, void *arg) +{ + return S_ISDIR(dentry->d_inode->i_mode); +} + +//todo: merge with refresh_nondir() +static int refresh_dir(struct dentry *root, au_gen_t sgen) +{ + int err, i, j, ndentry, e; + const unsigned int flags = au_hi_flags(root->d_inode, /*isdir*/1); + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + struct inode *inode; + + LKTRTrace("sgen %d\n", sgen); + SiMustWriteLock(root->d_sb); + //todo: dont trust BKL. + AuDebugOn(au_digen(root) != sgen || !kernel_locked()); + + err = 0; + list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) + if (S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) { + ii_write_lock_child(inode); + e = au_refresh_hinode_self(inode); + //e = -1; + ii_write_unlock(inode); + if (unlikely(e)) { + LKTRTrace("e %d, i%lu\n", e, inode->i_ino); + if (!err) + err = e; + /* go on even if err */ + } + } + + e = au_dpages_init(&dpages, GFP_TEMPORARY); + if (unlikely(e)) { + if (!err) + err = e; + goto out; + } + e = au_dcsub_pages(&dpages, root, test_dir, NULL); + if (unlikely(e)) { + if (!err) + err = e; + goto out_dpages; + } + + for (i = 0; !e && i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; !e && j < ndentry; j++) { + struct dentry *d; + d = dentries[j]; +#ifdef CONFIG_AUFS_DEBUG + { + struct dentry *parent; + parent = dget_parent(d); + AuDebugOn(!S_ISDIR(d->d_inode->i_mode) + || IS_ROOT(d) + || au_digen(parent) != sgen); + dput(parent); + } +#endif + if (au_digen(d) != sgen) { + e = do_refresh(d, S_IFDIR, flags); + //e = -1; + if (unlikely(e && !err)) + err = e; + /* break on err */ + } + } + } + + out_dpages: + au_dpages_free(&dpages); + out: + AuTraceErr(err); + return err; +} + +static int test_nondir(struct dentry *dentry, void *arg) +{ + return !S_ISDIR(dentry->d_inode->i_mode); +} + +static int refresh_nondir(struct dentry *root, au_gen_t sgen, int do_dentry) +{ + int err, i, j, ndentry, e; + struct au_dcsub_pages dpages; + struct au_dpage *dpage; + struct dentry **dentries; + struct inode *inode; + + LKTRTrace("sgen %d\n", sgen); + SiMustWriteLock(root->d_sb); + //todo: dont trust BKL. + AuDebugOn(au_digen(root) != sgen || !kernel_locked()); + + err = 0; + list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list) + if (!S_ISDIR(inode->i_mode) && au_iigen(inode) != sgen) { + ii_write_lock_child(inode); + e = au_refresh_hinode_self(inode); + //e = -1; + ii_write_unlock(inode); + if (unlikely(e)) { + LKTRTrace("e %d, i%lu\n", e, inode->i_ino); + if (!err) + err = e; + /* go on even if err */ + } + } + + if (!do_dentry) + goto out; + + e = au_dpages_init(&dpages, GFP_TEMPORARY); + if (unlikely(e)) { + if (!err) + err = e; + goto out; + } + e = au_dcsub_pages(&dpages, root, test_nondir, NULL); + if (unlikely(e)) { + if (!err) + err = e; + goto out_dpages; + } + + for (i = 0; i < dpages.ndpage; i++) { + dpage = dpages.dpages + i; + dentries = dpage->dentries; + ndentry = dpage->ndentry; + for (j = 0; j < ndentry; j++) { + struct dentry *d; + d = dentries[j]; +#ifdef CONFIG_AUFS_DEBUG + { + struct dentry *parent; + parent = dget_parent(d); + AuDebugOn(S_ISDIR(d->d_inode->i_mode) + || au_digen(parent) != sgen); + dput(parent); + } +#endif + inode = d->d_inode; + if (inode && au_digen(d) != sgen) { + e = do_refresh(d, inode->i_mode & S_IFMT, 0); + //e = -1; + if (unlikely(e && !err)) + err = e; + /* go on even err */ + } + } + } + + out_dpages: + au_dpages_free(&dpages); + out: + AuTraceErr(err); + return err; +} + +/* stop extra interpretation of errno in mount(8), and strange error messages */ +static int cvt_err(int err) +{ + AuTraceErr(err); + + switch (err) { + case -ENOENT: + case -ENOTDIR: + case -EEXIST: + case -EIO: + err = -EINVAL; + } + return err; +} + +/* protected by s_umount */ +static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) +{ + int err; + struct dentry *root; + struct inode *inode; + struct au_opts opts; + unsigned int dlgt; + struct au_sbinfo *sbinfo; + + //au_debug_on(); + LKTRTrace("flags 0x%x, data %s, len %lu\n", + *flags, data ? data : "NULL", + (unsigned long)(data ? strlen(data) : 0)); + + au_fsync_br(sb); + err = 0; + if (!data || !*data) + goto out; /* success */ + + err = -ENOMEM; + memset(&opts, 0, sizeof(opts)); + opts.opt = (void *)__get_free_page(GFP_TEMPORARY); + //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;} + if (unlikely(!opts.opt)) + goto out; + opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); + opts.flags = AuOpts_REMOUNT; + + /* parse it before aufs lock */ + err = au_opts_parse(sb, *flags, data, &opts); + //if (LktrCond) {au_free_opts(&opts); err = -1;} + if (unlikely(err)) + goto out_opts; + + sbinfo = au_sbi(sb); + root = sb->s_root; + inode = root->d_inode; + mutex_lock(&inode->i_mutex); + aufs_write_lock(root); + + //DbgSleep(3); + + /* au_do_opts() may return an error */ + err = au_opts_remount(sb, &opts); + //if (LktrCond) err = -1; + au_opts_free(&opts); + + if (au_ftest_opts(opts.flags, REFRESH_DIR) + || au_ftest_opts(opts.flags, REFRESH_NONDIR)) { + int rerr; + au_gen_t sigen; + + dlgt = !!au_opt_test_dlgt(sbinfo->si_mntflags); + au_opt_clr(sbinfo->si_mntflags, DLGT); + au_sigen_inc(sb); + au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1)); + sigen = au_sigen(sb); + au_fclr_si(sbinfo, FAILED_REFRESH_DIRS); + + DiMustNoWaiters(root); + IiMustNoWaiters(root->d_inode); + di_write_unlock(root); + + rerr = refresh_dir(root, sigen); + if (unlikely(rerr)) { + au_fset_si(sbinfo, FAILED_REFRESH_DIRS); + AuWarn("Refreshing directories failed, ignores (%d)\n", + rerr); + } + + if (unlikely(au_ftest_opts(opts.flags, REFRESH_NONDIR))) { + //au_debug_on(); + rerr = refresh_nondir(root, sigen, !rerr); + if (unlikely(rerr)) + AuWarn("Refreshing non-directories failed," + " ignores (%d)\n", rerr); + //au_debug_off(); + } + + /* aufs_write_lock() calls ..._child() */ + di_write_lock_child(root); + + au_cpup_attr_all(inode); + if (unlikely(dlgt)) + au_opt_set(sbinfo->si_mntflags, DLGT); + } + + aufs_write_unlock(root); + mutex_unlock(&inode->i_mutex); + + out_opts: + free_page((unsigned long)opts.opt); + out: + err = cvt_err(err); + AuTraceErr(err); + //au_debug_off(); + return err; +} + +static struct super_operations aufs_sop = { + .alloc_inode = aufs_alloc_inode, + .destroy_inode = aufs_destroy_inode, + //.dirty_inode = aufs_dirty_inode, + //.write_inode = aufs_write_inode, + //void (*put_inode) (struct inode *); + .drop_inode = generic_delete_inode, + //.delete_inode = aufs_delete_inode, + //.clear_inode = aufs_clear_inode, + + .show_options = aufs_show_options, + .statfs = aufs_statfs, + + .put_super = aufs_put_super, + //void (*write_super) (struct super_block *); + //int (*sync_fs)(struct super_block *sb, int wait); + //void (*write_super_lockfs) (struct super_block *); + //void (*unlockfs) (struct super_block *); + .remount_fs = aufs_remount_fs, + /* depends upon umount flags. also use put_super() (< 2.6.18) */ + .umount_begin = aufs_umount_begin +}; + +/* ---------------------------------------------------------------------- */ + +static int alloc_root(struct super_block *sb) +{ + int err; + struct inode *inode; + struct dentry *root; + + AuTraceEnter(); + + err = -ENOMEM; + inode = au_iget_locked(sb, AUFS_ROOT_INO); + //if (LktrCond) {iput(inode); inode = NULL;} + err = PTR_ERR(inode); + if (IS_ERR(inode)) + goto out; + unlock_new_inode(inode); + inode->i_mode = S_IFDIR; + root = d_alloc_root(inode); + //if (LktrCond) {igrab(inode); dput(root); root = NULL;} + if (unlikely(!root)) + goto out_iput; + err = PTR_ERR(root); + if (IS_ERR(root)) + goto out_iput; + + err = au_alloc_dinfo(root); + //if (LktrCond){au_rw_write_unlock(&au_di(root)->di_rwsem);err=-1;} + if (!err) { + sb->s_root = root; + return 0; /* success */ + } + dput(root); + goto out; /* do not iput */ + + out_iput: + iget_failed(inode); + iput(inode); + out: + AuTraceErr(err); + return err; + +} + +static int aufs_fill_super(struct super_block *sb, void *raw_data, int silent) +{ + int err; + struct dentry *root; + struct inode *inode; + struct au_opts opts; + char *arg = raw_data; + + //au_debug_on(); + if (unlikely(!arg || !*arg)) { + err = -EINVAL; + AuErr("no arg\n"); + goto out; + } + LKTRTrace("%s, silent %d\n", arg, silent); + + err = -ENOMEM; + memset(&opts, 0, sizeof(opts)); + opts.opt = (void *)__get_free_page(GFP_TEMPORARY); + //if (LktrCond) {free_page((unsigned long)opts.opt); opts.opt = NULL;} + if (unlikely(!opts.opt)) + goto out; + opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); + + err = au_si_alloc(sb); + //if (LktrCond) {si_write_unlock(sb);free_sbinfo(sb);err=-1;} + if (unlikely(err)) + goto out_opts; + SiMustWriteLock(sb); + /* all timestamps always follow the ones on the branch */ + sb->s_flags |= MS_NOATIME | MS_NODIRATIME; + sb->s_op = &aufs_sop; + sb->s_magic = AUFS_SUPER_MAGIC; + au_init_export_op(sb); + + err = alloc_root(sb); + //if (LktrCond) {au_rw_write_unlock(&au_di(sb->s_root)->di_rwsem); + //dput(sb->s_root);sb->s_root=NULL;err=-1;} + if (unlikely(err)) { + AuDebugOn(sb->s_root); + si_write_unlock(sb); + goto out_info; + } + root = sb->s_root; + DiMustWriteLock(root); + inode = root->d_inode; + inode->i_nlink = 2; + + /* + * actually we can parse options regardless aufs lock here. + * but at remount time, parsing must be done before aufs lock. + * so we follow the same rule. + */ + ii_write_lock_parent(inode); + aufs_write_unlock(root); + err = au_opts_parse(sb, sb->s_flags, arg, &opts); + //if (LktrCond) {au_opts_free(&opts); err = -1;} + if (unlikely(err)) + goto out_root; + + /* lock vfs_inode first, then aufs. */ + mutex_lock(&inode->i_mutex); + inode->i_op = &aufs_dir_iop; + inode->i_fop = &aufs_dir_fop; + aufs_write_lock(root); + + sb->s_maxbytes = 0; + err = au_opts_mount(sb, &opts); + //if (LktrCond) err = -1; + au_opts_free(&opts); + if (unlikely(err)) + goto out_unlock; + AuDebugOn(!sb->s_maxbytes); + + //AuDbgDentry(root); + aufs_write_unlock(root); + mutex_unlock(&inode->i_mutex); + //AuDbgSb(sb); + goto out_opts; /* success */ + + out_unlock: + aufs_write_unlock(root); + mutex_unlock(&inode->i_mutex); + out_root: + dput(root); + sb->s_root = NULL; + out_info: + au_fset_si(au_sbi(sb), FAILED_INIT); + kobject_put(&au_sbi(sb)->si_kobj); + sb->s_fs_info = NULL; + out_opts: + free_page((unsigned long)opts.opt); + out: + AuTraceErr(err); + err = cvt_err(err); + AuTraceErr(err); + //au_debug_off(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int aufs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *raw_data, + struct vfsmount *mnt) +{ + int err; + + /* all timestamps always follow the ones on the branch */ + /* mnt->mnt_flags |= MNT_NOATIME | MNT_NODIRATIME; */ + err = get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super, mnt); + if (!err) { + struct super_block *sb = mnt->mnt_sb; + struct au_sbinfo *sbinfo = au_sbi(sb); + + /* no get/put */ + spin_lock_init(&sbinfo->si_mntcache_lock); + sbinfo->si_mntcache = mnt; + + au_sbilist_lock(); + au_sbilist_add(sbinfo); + si_write_lock(sb); + sysaufs_brs_add(sb, 0); + si_write_unlock(sb); + au_sbilist_unlock(); + } + return err; +} + +struct file_system_type aufs_fs_type = { + .name = AUFS_FSTYPE, + .fs_flags = FS_REVAL_DOT, /* for UDBA and NFS branch */ + .get_sb = aufs_get_sb, + .kill_sb = generic_shutdown_super, + //.kill_sb = kill_anon_super, + /* no need to __module_get() and module_put(). */ + .owner = THIS_MODULE, +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/super.h linux-2.6.25.4-unionfs/fs/aufs/super.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/super.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/super.h 2008-05-25 11:58:53 +0300 @@ -0,0 +1,410 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * super_block operations + * + * $Id: super.h,v 1.5 2008/05/19 01:50:18 sfjro Exp $ + */ + +#ifndef __AUFS_SUPER_H__ +#define __AUFS_SUPER_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +//#include "hinode.h" +#include "misc.h" +//#include "opts.h" +#include "wkq.h" + +typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); +typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, + loff_t *); + +struct au_wbr_copyup_operations { + int (*copyup)(struct dentry *dentry); +}; + +struct au_wbr_create_operations { + int (*create)(struct dentry *dentry, int isdir); + int (*init)(struct super_block *sb); + int (*fin)(struct super_block *sb); +}; + +struct au_wbr_mfs { + struct mutex mfs_lock; /* protect this structure */ + unsigned long mfs_jiffy; + unsigned long mfs_expire; + aufs_bindex_t mfs_bindex; + + u64 mfsrr_bytes; + u64 mfsrr_watermark; +}; + +/* sbinfo status flags */ +/* + * set true when refresh_dirs() failed at remount time. + * then try refreshing dirs at access time again. + * if it is false, refreshing dirs at access time is unnecesary + */ +#define AuSi_FAILED_REFRESH_DIRS 1 +#define AuSi_FAILED_INIT (1 << 1) +#define au_ftest_si(sbinfo, name) ((sbinfo)->au_si_status & AuSi_##name) +#define au_fset_si(sbinfo, name) \ + { (sbinfo)->au_si_status |= AuSi_##name; } +#define au_fclr_si(sbinfo, name) \ + { (sbinfo)->au_si_status &= ~AuSi_##name; } + +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ + struct au_nowait_tasks si_nowait; + + struct au_rwsem si_rwsem; + + /* branch management */ + au_gen_t si_generation; + + /* see above flags */ + unsigned char au_si_status; + + aufs_bindex_t si_bend; + aufs_bindex_t si_last_br_id; + struct au_branch **si_branch; + + /* policy to select a writable branch */ + unsigned char si_wbr_copyup; + unsigned char si_wbr_create; + struct au_wbr_copyup_operations *si_wbr_copyup_ops; + struct au_wbr_create_operations *si_wbr_create_ops; + + /* round robin */ + atomic_t si_wbr_rr_next; + + /* most free space */ + struct au_wbr_mfs si_wbr_mfs; + + /* mount flags */ + /* include/asm-ia64/siginfo.h defines a macro named si_flags */ + unsigned int si_mntflags; + + /* external inode number (bitmap and translation table) */ + au_readf_t si_xread; + au_writef_t si_xwrite; + struct file *si_xib; + struct mutex si_xib_mtx; /* protect xib members */ + unsigned long *si_xib_buf; + unsigned long si_xib_last_pindex; + int si_xib_next_bit; + //unsigned long long si_xib_limit; /* Max xib file size */ + + /* readdir cache time, max, in HZ */ + unsigned long si_rdcache; + + /* + * If the number of whiteouts are larger than si_dirwh, leave all of + * them after au_whtmp_ren to reduce the cost of rmdir(2). + * future fsck.aufs or kernel thread will remove them later. + * Otherwise, remove all whiteouts and the dir in rmdir(2). + */ + unsigned int si_dirwh; + + /* + * rename(2) a directory with all children. + */ + //int si_rendir; + + /* pseudo_link list */ // dirty + spinlock_t si_plink_lock; + struct list_head si_plink; + + /* dirty, for export, async ops, and sysfs */ + spinlock_t si_mntcache_lock; + struct vfsmount *si_mntcache; /* no get/put */ + + /* + * sysfs and lifetime management. + * this is not a small structure and it may be a waste of memory in case + * of sysfs is disabled, particulary when many aufs-es are mounted. + */ + struct kobject si_kobj; + + //todo: remove this list. + /* super_blocks list is not exported */ + struct list_head si_list; + +#ifdef CONFIG_AUFS_ROBR + /* locked vma list for mmap() */ // dirty + spinlock_t si_lvma_lock; + struct list_head si_lvma; +#endif + + /* dirty, necessary for unmounting, sysfs and sysrq */ + struct super_block *si_sb; +}; + +/* ---------------------------------------------------------------------- */ + +/* policy to select one among writable branches */ +#define AuWbrCopyup(sbinfo, args... ) \ + (sbinfo)->si_wbr_copyup_ops->copyup(args) +#define AuWbrCreate(sbinfo, args... ) \ + (sbinfo)->si_wbr_create_ops->create(args) + +/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ +#define AuLock_DW 1 /* write-lock dentry */ +#define AuLock_IR (1 << 1) /* read-lock inode */ +#define AuLock_IW (1 << 2) /* write-lock inode */ +#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ +#define AuLock_DIR (1 << 4) /* target is a dir */ +#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) +#define au_fset_lock(flags, name) { (flags) |= AuLock_##name; } +#define au_fclr_lock(flags, name) { (flags) &= ~AuLock_##name; } + +/* ---------------------------------------------------------------------- */ + +/* super.c */ +extern struct file_system_type aufs_fs_type; +struct inode *au_iget_locked(struct super_block *sb, ino_t ino); + +/* sbinfo.c */ +void au_si_free(struct kobject *kobj); +int au_si_alloc(struct super_block *sb); +struct au_branch *au_sbr(struct super_block *sb, aufs_bindex_t bindex); +au_gen_t au_sigen_inc(struct super_block *sb); +int au_find_bindex(struct super_block *sb, struct au_branch *br); + +void aufs_read_lock(struct dentry *dentry, int flags); +void aufs_read_unlock(struct dentry *dentry, int flags); +void aufs_write_lock(struct dentry *dentry); +void aufs_write_unlock(struct dentry *dentry); +void aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int isdir); +void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); + +aufs_bindex_t au_new_br_id(struct super_block *sb); +struct vfsmount *au_mntcache_get(struct super_block *sb); + +/* wbr_policy.c */ +extern struct au_wbr_copyup_operations au_wbr_copyup_ops[]; +extern struct au_wbr_create_operations au_wbr_create_ops[]; +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked); + +/* ---------------------------------------------------------------------- */ + +static inline struct au_sbinfo *au_sbi(struct super_block *sb) +{ + return sb->s_fs_info; +} + +static inline const char *au_sbtype(struct super_block *sb) +{ + return sb->s_type->name; +} + +static inline int au_test_aufs(struct super_block *sb) +{ + return (sb->s_magic == AUFS_SUPER_MAGIC); +} + +static inline int au_test_nfs(struct super_block *sb) +{ +#ifdef CONFIG_AUFS_BR_NFS + return (sb->s_magic == NFS_SUPER_MAGIC); +#else + return 0; +#endif +} + +static inline int au_test_fuse(struct super_block *sb) +{ +#ifdef CONFIG_AUFS_WORKAROUND_FUSE +#ifdef FUSE_SUPER_MAGIC + BUILD_BUG_ON(FUSE_SUPER_MAGIC != 0x65735546); + return (sb->s_magic == FUSE_SUPER_MAGIC); +#else + return !strcmp(au_sbtype(sb), "fuse"); +#endif +#endif + return 0; +} + +static inline int au_test_xfs(struct super_block *sb) +{ +#ifdef CONFIG_AUFS_BR_XFS +#ifdef XFS_SB_MAGIC + BUILD_BUG_ON(XFS_SB_MAGIC != 0x58465342); + return (sb->s_magic == XFS_SB_MAGIC); +#else + return !strcmp(au_sbtype(sb), "xfs"); +#endif +#endif + return 0; +} + +static inline int au_test_tmpfs(struct super_block *sb) +{ +#ifdef CONFIG_TMPFS +#ifdef TMPFS_MAGIC + BUILD_BUG_ON(TMPFS_MAGIC != 0x01021994); + return (sb->s_magic == TMPFS_MAGIC); +#else + return !strcmp(au_sbtype(sb), "tmpfs"); +#endif +#endif + return 0; +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT +extern struct export_operations aufs_export_op; +static inline void au_init_export_op(struct super_block *sb) +{ + sb->s_export_op = &aufs_export_op; +} + +static inline int au_test_nfsd(struct task_struct *tsk) +{ + return (!tsk->mm && !strcmp(tsk->comm, "nfsd")); +} + +static inline void au_nfsd_lockdep_off(void) +{ + if (au_test_nfsd(current)) + lockdep_off(); +} + +static inline void au_nfsd_lockdep_on(void) +{ + if (au_test_nfsd(current)) + lockdep_on(); +} +#else +static inline int au_test_nfsd(struct task_struct *tsk) +{ + return 0; +} + +static inline void au_init_export_op(struct super_block *sb) +{ + /* nothing */ +} + +#define au_nfsd_lockdep_off() do {} while (0) +#define au_nfsd_lockdep_on() do {} while (0) +#endif /* CONFIG_AUFS_EXPORT */ + +#ifdef CONFIG_AUFS_ROBR +static inline int au_test_nested(struct super_block *h_sb) +{ + return 0; +} + +static inline void au_robr_lvma_init(struct au_sbinfo *sbinfo) +{ + spin_lock_init(&sbinfo->si_lvma_lock); + INIT_LIST_HEAD(&sbinfo->si_lvma); +} +#else +static inline int au_test_nested(struct super_block *h_sb) +{ + int err = 0; + if (unlikely(au_test_aufs(h_sb))) { + err = -EINVAL; + AuTraceErr(err); + } + return err; +} + +static inline void au_robr_lvma_init(struct au_sbinfo *sbinfo) +{ + /* empty */ +} +#endif /* CONFIG_AUFS_ROBR */ + +/* ---------------------------------------------------------------------- */ + +/* lock superblock. mainly for entry point functions */ +/* + * si_noflush_read_lock, si_noflush_write_lock, + * si_read_unlock, si_write_unlock, si_downgrade_lock + */ +AuSimpleLockRwsemFuncs(si_noflush, struct super_block *sb, + au_sbi(sb)->si_rwsem); +AuSimpleUnlockRwsemFuncs(si, struct super_block *sb, au_sbi(sb)->si_rwsem); + +static inline void si_read_lock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + si_noflush_read_lock(sb); +} + +static inline void si_write_lock(struct super_block *sb) +{ + au_nwt_flush(&au_sbi(sb)->si_nowait); + si_noflush_write_lock(sb); +} + +static inline int si_read_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_read_trylock(sb); +} + +static inline int si_write_trylock(struct super_block *sb, int flags) +{ + if (au_ftest_lock(flags, FLUSH)) + au_nwt_flush(&au_sbi(sb)->si_nowait); + return si_noflush_write_trylock(sb); +} + +/* to debug easier, do not make them inlined functions */ +#define SiMustReadLock(sb) AuRwMustReadLock(&au_sbi(sb)->si_rwsem) +#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) +#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) + +/* ---------------------------------------------------------------------- */ + +static inline aufs_bindex_t au_sbend(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_bend; +} + +static inline unsigned int au_mntflags(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_mntflags; +} + +static inline au_gen_t au_sigen(struct super_block *sb) +{ + SiMustAnyLock(sb); + return au_sbi(sb)->si_generation; +} + +#endif /* __KERNEL__ */ +#endif /* __AUFS_SUPER_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/sysaufs.c linux-2.6.25.4-unionfs/fs/aufs/sysaufs.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/sysaufs.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/sysaufs.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sysfs interface and lifetime management + * they are necessary regardless sysfs is disabled. + * + * $Id: sysaufs.c,v 1.5 2008/05/19 01:50:47 sfjro Exp $ + */ + +#include +#include +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +/* super_blocks list is not exported */ +DEFINE_MUTEX(au_sbilist_mtx); +LIST_HEAD(au_sbilist); + +/* ---------------------------------------------------------------------- */ + +static struct kset au_kset; + +#define AuSbiAttr(_name) { \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = sysaufs_sbi_##_name, \ +} + +static struct au_sbi_attr au_sbi_attr_xino = AuSbiAttr(xino); +struct attribute *au_sbi_attrs[] = { + &au_sbi_attr_xino.attr, + NULL, +}; + +static struct sysfs_ops au_sbi_ops = { + .show = sysaufs_sbi_show +}; + +static struct kobj_type au_sbi_ktype = { + .release = au_si_free, + .sysfs_ops = &au_sbi_ops, + .default_attrs = au_sbi_attrs, +}; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_si_init(struct au_sbinfo *sbinfo) +{ + int err; + + sbinfo->si_kobj.kset = &au_kset; + err = kobject_init_and_add(&sbinfo->si_kobj, &au_sbi_ktype, + NULL/*&au_kset.kobj*/, + SysaufsSb_PREFIX "%p", sbinfo); + AuTraceErr(err); + return err; +} + + +/* ---------------------------------------------------------------------- */ + +void sysaufs_fin(void) +{ + AuDebugOn(!list_empty(&au_sbilist)); + sysfs_remove_group(&au_kset.kobj, au_attr_group); + kset_unregister(&au_kset); +} + +int __init sysaufs_init(void) +{ + int err; + + sysaufs_brs_init(); + au_kset.kobj.parent = fs_kobj; + kobject_set_name(&au_kset.kobj, AUFS_NAME); + au_kset.kobj.ktype = au_ktype; + err = kset_register(&au_kset); + if (unlikely(err)) + goto out; + err = sysfs_create_group(&au_kset.kobj, au_attr_group); + if (unlikely(err)) + kset_unregister(&au_kset); + + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/sysaufs.h linux-2.6.25.4-unionfs/fs/aufs/sysaufs.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/sysaufs.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/sysaufs.h 2008-05-25 11:58:53 +0300 @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sysfs interface and lifetime management + * + * $Id: sysaufs.h,v 1.6 2008/05/19 01:50:47 sfjro Exp $ + */ + +#ifndef __SYSAUFS_H__ +#define __SYSAUFS_H__ + +#ifdef __KERNEL__ + +#include +#include +#include "module.h" +#include "super.h" + +/* entries under sysfs per super block */ +enum { + SysaufsSb_XINO, + SysaufsSb_MNTPNT1, + SysaufsSb_Last +}; + +#define SysaufsSb_PREFIX "si_" /* followed by %p */ + +struct au_sbi_attr { + struct attribute attr; + int (*show)(struct seq_file *seq, struct super_block *sb); +}; + +/* ---------------------------------------------------------------------- */ + +/* sysaufs.c */ +extern struct mutex au_sbilist_mtx; +extern struct list_head au_sbilist; +extern struct attribute *au_sbi_attrs[]; +int sysaufs_si_init(struct au_sbinfo *sbinfo); +int __init sysaufs_init(void); +void sysaufs_fin(void); + +/* ---------------------------------------------------------------------- */ + +static inline void au_sbilist_lock(void) +{ + mutex_lock(&au_sbilist_mtx); +} + +static inline void au_sbilist_unlock(void) +{ + mutex_unlock(&au_sbilist_mtx); +} + +static inline void au_sbilist_del(struct au_sbinfo *sbinfo) +{ + list_del(&sbinfo->si_list); +} + +static inline void au_sbilist_add(struct au_sbinfo *sbinfo) +{ + /* the order in this list is important */ + list_add_tail(&sbinfo->si_list, &au_sbilist); +} + +/* ---------------------------------------------------------------------- */ + +struct au_branch; +#ifdef CONFIG_SYSFS +/* sysfs.c */ +extern struct attribute_group *au_attr_group; +extern struct kobj_type *au_ktype; + +int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb); +int sysaufs_sbi_mntpnt1(struct seq_file *seq, struct super_block *sb); +ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr, + char *buf); + +void sysaufs_br_init(struct au_branch *br); +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); + +#define sysaufs_brs_init() do {} while (0) + +#else +#define au_attr_group NULL +#define au_ktype NULL + +static inline +int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb) +{ + return 0; +} + +static inline +int sysaufs_sbi_mntpnt1(struct seq_file *seq, struct super_block *sb) +{ + return 0; +} + +static inline +ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + return 0; +} + +static inline void sysaufs_br_init(struct au_branch *br) +{ + /* empty */ +} + +static inline void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + /* nothing */ +} + +static inline void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + /* nothing */ +} + +static inline void sysaufs_brs_init(void) +{ + sysaufs_brs = 0; +} +#endif /* CONFIG_SYSFS */ + +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/sysfs.c linux-2.6.25.4-unionfs/fs/aufs/sysfs.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/sysfs.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/sysfs.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,456 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sysfs interface + * + * $Id: sysfs.c,v 1.4 2008/05/19 01:51:16 sfjro Exp $ + */ + +#include +#include +#include +#include +#include "aufs.h" + + +#ifdef CONFIG_AUFS_LOCAL +static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ +#define conf_bool(name) "CONFIG_AUFS_" #name "=y\n" + static const char opt[] = +#ifdef CONFIG_AUFS + "CONFIG_AUFS=y\n" +#else + "CONFIG_AUFS=m\n" +#endif +#ifdef CONFIG_AUFS_BRANCH_MAX_127 + conf_bool(BRANCH_MAX_127) +#elif defined(CONFIG_AUFS_BRANCH_MAX_511) + conf_bool(BRANCH_MAX_511) +#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) + conf_bool(BRANCH_MAX_1023) +#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) + conf_bool(BRANCH_MAX_32767) +#endif +#ifdef CONFIG_AUFS_HINOTIFY + conf_bool(HINOTIFY) +#endif +#ifdef CONFIG_AUFS_EXPORT + conf_bool(EXPORT) +#endif +#ifdef CONFIG_AUFS_ROBR + conf_bool(ROBR) +#endif +#ifdef CONFIG_AUFS_SHWH + conf_bool(SHWH) +#endif +#ifdef CONFIG_AUFS_DLGT + conf_bool(DLGT) +#endif +#ifdef CONFIG_AUFS_RR_SQUASHFS + conf_bool(RR_SQUASHFS) +#endif +#ifdef CONFIG_AUFS_SEC_PERM_PATCH + conf_bool(SEC_PERM_PATCH) +#endif +#ifdef CONFIG_AUFS_SPLICE_PATCH + conf_bool(SPLICE_PATCH) +#endif +#ifdef CONFIG_AUFS_PUT_FILP_PATCH + conf_bool(PUT_FILP_PATCH) +#endif +#ifdef CONFIG_AUFS_LHASH_PATCH + conf_bool(LHASH_PATCH) +#endif +#ifdef CONFIG_AUFS_FSYNC_SUPER_PATCH + conf_bool(FSYNC_SUPER_PATCH) +#endif +#ifdef CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH + conf_bool(DENY_WRITE_ACCESS_PATCH) +#endif +#ifdef CONFIG_AUFS_KSIZE_PATCH + conf_bool(KSIZE_PATCH) +#endif +#ifdef CONFIG_AUFS_WORKAROUND_FUSE + conf_bool(WORKAROUND_FUSE) +#endif +#ifdef CONFIG_AUFS_STAT + conf_bool(STAT) +#endif +#ifdef CONFIG_AUFS_DEBUG + conf_bool(DEBUG) +#endif +#ifdef CONFIG_AUFS_COMPAT + conf_bool(COMPAT) +#endif + +/* automatic configurations */ +#ifdef CONFIG_AUFS_BR_NFS + conf_bool(BR_NFS) +#endif +#ifdef CONFIG_AUFS_MAGIC_SYSRQ + conf_bool(MAGIC_SYSRQ) +#endif + ; +#undef conf_bool + + char *p = buf; + const char *end = buf + PAGE_SIZE; + + p += snprintf(p, end - p, "%s", opt); +#ifdef DbgUdbaRace + if (p < end) + p += snprintf(p, end - p, "DbgUdbaRace=%d\n", DbgUdbaRace); +#endif + if (p < end) + return p - buf; + else + return -EFBIG; +} + +static struct kobj_attribute au_config_attr = __ATTR_RO(config); +#endif + +#ifdef CONFIG_AUFS_STAT +static ssize_t stat_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + char *p = buf; + const char *end = buf + PAGE_SIZE; + int i; + + p += snprintf(p, end - p, "wkq max_busy:"); + for (i = 0; p < end && i < aufs_nwkq; i++) + p += snprintf(p, end - p, " %u", au_wkq[i].max_busy); + if (p < end) + p += snprintf(p, end - p, ", %u(generic)\n", + au_wkq[aufs_nwkq].max_busy); + + if (p < end) + return p - buf; + else + return -EFBIG; +} + +static struct kobj_attribute au_stat_attr = __ATTR_RO(stat); +#endif + +#ifdef CONFIG_AUFS_DEBUG +static ssize_t debug_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", au_debug_test()); +} + +static ssize_t debug_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t sz) +{ + LKTRTrace("%.*s\n", (unsigned int)sz, buf); + + if (unlikely(!sz || (*buf != '0' && *buf != '1'))) + return -EOPNOTSUPP; + + if (*buf == '0') + au_debug_off(); + else if (*buf == '1') + au_debug_on(); + return sz; +} + +static struct kobj_attribute au_debug_attr = __ATTR(debug, S_IRUGO | S_IWUSR, + debug_show, debug_store); +#endif + +static struct attribute *au_attr[] = { +#ifdef CONFIG_AUFS_LOCAL + &au_config_attr.attr, +#endif +#ifdef CONFIG_AUFS_STAT + &au_stat_attr.attr, +#endif +#ifdef CONFIG_AUFS_DEBUG + &au_debug_attr.attr, +#endif + NULL, /* need to NULL terminate the list of attributes */ +}; + +static struct attribute_group au_attr_group_body = { + .attrs = au_attr +}; + +struct attribute_group *au_attr_group = &au_attr_group_body; + +/* ---------------------------------------------------------------------- */ + +/* + * they are copied from linux/lib/kobject.c, + * and will be exported in the future. + */ +static ssize_t au_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->show) + ret = kattr->show(kobj, kattr, buf); + return ret; +} + +#ifdef CONFIG_AUFS_DEBUG +static ssize_t au_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct kobj_attribute *kattr; + ssize_t ret = -EIO; + + kattr = container_of(attr, struct kobj_attribute, attr); + if (kattr->store) + ret = kattr->store(kobj, kattr, buf, count); + return ret; +} +#endif + +static struct sysfs_ops sysaufs_ops = { + .show = au_attr_show, +#ifdef CONFIG_AUFS_DEBUG + .store = au_attr_store +#endif +}; + +static struct kobj_type au_ktype_body = { + .sysfs_ops = &sysaufs_ops +}; +struct kobj_type *au_ktype = &au_ktype_body; + +/* ---------------------------------------------------------------------- */ + +int sysaufs_sbi_xino(struct seq_file *seq, struct super_block *sb) +{ + int err, dlgt; + struct au_sbinfo *sbinfo; + unsigned int mnt_flags; + aufs_bindex_t bend, bindex; + struct file *xf; + struct kstat st; + + AuTraceEnter(); + + sbinfo = au_sbi(sb); + mnt_flags = au_mntflags(sb); + if (unlikely(!au_opt_test(mnt_flags, XINO))) { +#ifdef CONFIG_AUFS_DEBUG + AuDebugOn(sbinfo->si_xib); + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) + AuDebugOn(au_sbr(sb, bindex)->br_xino); +#endif + err = 0; + goto out; /* success */ + } + + dlgt = !!au_opt_test_dlgt(mnt_flags); + xf = sbinfo->si_xib; + err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st, dlgt); + if (!err) + seq_printf(seq, "%Lux%lu %Ld\n", + st.blocks, st.blksize, (long long)st.size); + else + seq_printf(seq, "err %d\n", err); + + bend = au_sbend(sb); + for (bindex = 0; !err && bindex <= bend; bindex++) { + xf = au_sbr(sb, bindex)->br_xino; + if (!xf) + continue; + seq_printf(seq, "%d: ", bindex); + err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st, dlgt); + if (!err) + seq_printf(seq, "%d, %Lux%lu %Ld\n", + file_count(xf), st.blocks, st.blksize, + (long long)st.size); + else + seq_printf(seq, "err %d\n", err); + } + + out: + AuTraceErr(err); + return err; +} + +/* + * the lifetime of branch is independent from the entry under sysfs. + * sysfs handles the lifetime of the entry, and never call ->show() after it is + * unlinked. + */ +#define SysaufsBr_PREFIX "br" +static int sysaufs_sbi_br(struct seq_file *seq, struct super_block *sb, + aufs_bindex_t bindex) +{ + int err; + struct dentry *root; + struct au_branch *br; + struct path path; + + LKTRTrace("b%d\n", bindex); + + err = -ENOENT; + if (unlikely(au_sbend(sb) < bindex)) + goto out; + + err = 0; + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + br = au_sbr(sb, bindex); + path.mnt = br->br_mnt; + path.dentry = au_h_dptr(root, bindex); + seq_path(seq, &path, au_esc_chars); + di_read_unlock(root, !AuLock_IR); + seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm)); + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static struct seq_file *au_seq(char *p, ssize_t len) +{ + struct seq_file *seq; + + seq = kzalloc(sizeof(*seq), GFP_TEMPORARY); + if (seq) { + //mutex_init(&seq.lock); + seq->buf = p; + seq->count = 0; + seq->size = len; + return seq; /* success */ + } + + seq = ERR_PTR(-ENOMEM); + AuTraceErrPtr(seq); + return seq; +} + +//todo: file size may exceed PAGE_SIZE +ssize_t sysaufs_sbi_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + ssize_t err; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct seq_file *seq; + char *name; + struct attribute **cattr; + + LKTRTrace("%s/%s\n", kobject_name(kobj), attr->name); + + sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + + seq = au_seq(buf, PAGE_SIZE); + err = PTR_ERR(seq); + if (IS_ERR(seq)) + goto out; + + name = (void *)attr->name; + cattr = au_sbi_attrs; + while (*cattr) { + if (!strcmp(name, (*cattr)->name)) { + err = container_of(*cattr, struct au_sbi_attr, attr) + ->show(seq, sb); + goto out_seq; + } + cattr++; + } + + if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) { + name += sizeof(SysaufsBr_PREFIX) - 1; + err = sysaufs_sbi_br(seq, sb, simple_strtol(name, NULL, 10)); + goto out_seq; + } + BUG(); + + out_seq: + if (!err) { + err = seq->count; + /* sysfs limit */ + if (unlikely(err == PAGE_SIZE)) + err = -EFBIG; + } + kfree(seq); + out: + si_read_unlock(sb); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void sysaufs_br_init(struct au_branch *br) +{ + br->br_attr.name = br->br_name; + br->br_attr.mode = S_IRUGO; + br->br_attr.owner = THIS_MODULE; +} + +void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) +{ + struct au_sbinfo *sbinfo; + aufs_bindex_t bend; + + LKTRTrace("b%d\n", bindex); + + if (!sysaufs_brs) + return; + + sbinfo = au_sbi(sb); + bend = au_sbend(sb); + for (; bindex <= bend; bindex++) + sysfs_remove_file(&sbinfo->si_kobj, + &au_sbr(sb, bindex)->br_attr); +} + +void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) +{ + int err; + struct kobject *kobj; + aufs_bindex_t bend; + struct au_branch *br; + + LKTRTrace("b%d\n", bindex); + + if (!sysaufs_brs) + return; + + kobj = &au_sbi(sb)->si_kobj; + bend = au_sbend(sb); + for (; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + snprintf(br->br_name, sizeof(br->br_name), + SysaufsBr_PREFIX "%d", bindex); + err = sysfs_create_file(kobj, &br->br_attr); + if (unlikely(err)) + AuWarn("failed %s under sysfs(%d)\n", br->br_name, err); + } +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/sysrq.c linux-2.6.25.4-unionfs/fs/aufs/sysrq.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/sysrq.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/sysrq.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * magic sysrq hanlder + * + * $Id: sysrq.c,v 1.4 2008/05/04 23:53:27 sfjro Exp $ + */ + +#include +#include +#include +#include +#include "aufs.h" + +static void sysrq_sb(struct super_block *sb) +{ + char *plevel; + struct inode *i; + + plevel = au_plevel; + au_plevel = KERN_WARNING; + au_debug_on(); + + pr_warning(AUFS_NAME ": superblock\n"); + au_dpri_sb(sb); + pr_warning(AUFS_NAME ": root dentry\n"); + au_dpri_dentry(sb->s_root); + pr_warning(AUFS_NAME ": isolated inode\n"); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) + if (list_empty(&i->i_dentry)) + au_dpri_inode(i); + + au_plevel = plevel; + au_debug_off(); +} + +/* ---------------------------------------------------------------------- */ + +/* module parameter */ +static char *aufs_sysrq_key = "a"; +module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); +MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); + +static void au_sysrq(int key, struct tty_struct *tty) +{ + struct au_sbinfo *sbinfo; + + //mutex_lock(&au_sbilist_mtx); + list_for_each_entry(sbinfo, &au_sbilist, si_list) + sysrq_sb(sbinfo->si_sb); + //mutex_unlock(&au_sbilist_mtx); +} + +static struct sysrq_key_op au_sysrq_op = { + .handler = au_sysrq, + .help_msg = "Aufs", + .action_msg = "Aufs", + .enable_mask = SYSRQ_ENABLE_DUMP //?? +}; + +/* ---------------------------------------------------------------------- */ + +int __init au_sysrq_init(void) +{ + int err; + char key; + + err = -1; + key = *aufs_sysrq_key; + if ('a' <= key && key <= 'z') + err = register_sysrq_key(key, &au_sysrq_op); + if (unlikely(err)) + AuErr("err %d, sysrq=%c\n", err, key); + return err; +} + +void au_sysrq_fin(void) +{ + int err; + err = unregister_sysrq_key(*aufs_sysrq_key, &au_sysrq_op); + if (unlikely(err)) + AuErr("err %d (ignored)\n", err); +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/vdir.c linux-2.6.25.4-unionfs/fs/aufs/vdir.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/vdir.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/vdir.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,934 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * virtual or vertical directory + * + * $Id: vdir.c,v 1.3 2008/04/28 03:05:48 sfjro Exp $ + */ + +#include "aufs.h" + +static int calc_size(int namelen) +{ + int sz; + + sz = sizeof(struct au_vdir_de) + namelen; + if (sizeof(ino_t) == sizeof(long)) { + const int mask = sizeof(ino_t) - 1; + if (sz & mask) { + sz += sizeof(ino_t); + sz &= ~mask; + } + } else { + } + + AuDebugOn(sz % sizeof(ino_t)); + return sz; +} + +static int set_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->p - p->p) { + p->de->de_str.len = 0; + //smp_mb(); + return 0; + } + return -1; /* error */ +} + +/* returns true or false */ +static int is_deblk_end(union au_vdir_deblk_p *p, + union au_vdir_deblk_p *deblk_end) +{ + if (calc_size(0) <= deblk_end->p - p->p) + return !p->de->de_str.len; + return 1; +} + +static au_vdir_deblk_t *last_deblk(struct au_vdir *vdir) +{ + return vdir->vd_deblk[vdir->vd_nblk - 1]; +} + +void au_nhash_init(struct au_nhash *nhash) +{ + int i; + for (i = 0; i < AuSize_NHASH; i++) + INIT_HLIST_HEAD(nhash->heads + i); +} + +struct au_nhash *au_nhash_new(gfp_t gfp) +{ + struct au_nhash *nhash; + + nhash = kmalloc(sizeof(*nhash), gfp); + if (nhash) { + au_nhash_init(nhash); + return nhash; + } + return ERR_PTR(-ENOMEM); +} + +void au_nhash_del(struct au_nhash *nhash) +{ + au_nhash_fin(nhash); + kfree(nhash); +} + +void au_nhash_move(struct au_nhash *dst, struct au_nhash *src) +{ + int i; + + AuTraceEnter(); + + //DbgWhlist(src); + *dst = *src; + for (i = 0; i < AuSize_NHASH; i++) { + struct hlist_head *h; + h = dst->heads + i; + if (h->first) + h->first->pprev = &h->first; + INIT_HLIST_HEAD(src->heads + i); + } + //DbgWhlist(src); + //DbgWhlist(dst); + //smp_mb(); +} + +/* ---------------------------------------------------------------------- */ + +void au_nhash_fin(struct au_nhash *whlist) +{ + int i; + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos, *n; + + AuTraceEnter(); + + for (i = 0; i < AuSize_NHASH; i++) { + head = whlist->heads + i; + hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) { + //hlist_del(pos); + kfree(tpos); + } + } +} + +int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, + int limit) +{ + int n, i; + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos; + + LKTRTrace("limit %d\n", limit); + //return 1; + + n = 0; + for (i = 0; i < AuSize_NHASH; i++) { + head = whlist->heads + i; + hlist_for_each_entry(tpos, pos, head, wh_hash) + if (tpos->wh_bindex == btgt && ++n > limit) + return 1; + } + return 0; +} + +static unsigned int au_name_hash(const unsigned char *name, unsigned int len) +{ + return (full_name_hash(name, len) % AuSize_NHASH); +} + +/* returns found(true) or not */ +int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int namelen) +{ + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos; + struct au_vdir_destr *str; + + LKTRTrace("%.*s\n", namelen, name); + + head = whlist->heads + au_name_hash(name, namelen); + hlist_for_each_entry(tpos, pos, head, wh_hash) { + str = &tpos->wh_str; + LKTRTrace("%.*s\n", str->len, str->name); + if (str->len == namelen && !memcmp(str->name, name, namelen)) + return 1; + } + return 0; +} + +int au_nhash_append_wh(struct au_nhash *whlist, char *name, int namelen, + ino_t ino, unsigned int d_type, aufs_bindex_t bindex, + unsigned char shwh) +{ + int err; + struct au_vdir_destr *str; + struct au_vdir_wh *wh; + + LKTRTrace("%.*s\n", namelen, name); + + err = -ENOMEM; + wh = kmalloc(sizeof(*wh) + namelen, GFP_TEMPORARY); + if (unlikely(!wh)) + goto out; + err = 0; + wh->wh_bindex = bindex; + if (unlikely(shwh)) + au_shwh_init_wh(wh, ino, d_type); + str = &wh->wh_str; + str->len = namelen; + memcpy(str->name, name, namelen); + hlist_add_head(&wh->wh_hash, + whlist->heads + au_name_hash(name, namelen)); + //smp_mb(); + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_vdir_free(struct au_vdir *vdir) +{ + au_vdir_deblk_t **deblk; + + AuTraceEnter(); + + deblk = vdir->vd_deblk; + while (vdir->vd_nblk--) { + kfree(*deblk); + deblk++; + } + kfree(vdir->vd_deblk); + au_cache_free_vdir(vdir); +} + +static int append_deblk(struct au_vdir *vdir) +{ + int err, sz, i; + au_vdir_deblk_t **o; + union au_vdir_deblk_p p, deblk_end; + + AuTraceEnter(); + + err = -ENOMEM; + sz = sizeof(*o) * vdir->vd_nblk; + o = au_kzrealloc(vdir->vd_deblk, sz, sz + sizeof(*o), GFP_KERNEL); + if (unlikely(!o)) + goto out; + vdir->vd_deblk = o; + p.deblk = kmalloc(sizeof(*p.deblk), GFP_KERNEL); + if (p.deblk) { + i = vdir->vd_nblk++; + vdir->vd_deblk[i] = p.deblk; + vdir->vd_last.i = i; + vdir->vd_last.p.p = p.p; + deblk_end.deblk = p.deblk + 1; + err = set_deblk_end(&p, &deblk_end); + AuDebugOn(err); + } + + out: + AuTraceErr(err); + return err; +} + +static struct au_vdir *alloc_vdir(void) +{ + struct au_vdir *vdir; + int err; + + AuTraceEnter(); + + err = -ENOMEM; + vdir = au_cache_alloc_vdir(); + if (unlikely(!vdir)) + goto out; + vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_KERNEL); + if (unlikely(!vdir->vd_deblk)) + goto out_free; + + vdir->vd_nblk = 0; + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + err = append_deblk(vdir); + if (!err) + return vdir; /* success */ + + kfree(vdir->vd_deblk); + + out_free: + au_cache_free_vdir(vdir); + out: + vdir = ERR_PTR(err); + AuTraceErrPtr(vdir); + return vdir; +} + +static int reinit_vdir(struct au_vdir *vdir) +{ + int err; + union au_vdir_deblk_p p, deblk_end; + + AuTraceEnter(); + + while (vdir->vd_nblk > 1) { + kfree(vdir->vd_deblk[vdir->vd_nblk - 1]); + vdir->vd_deblk[vdir->vd_nblk - 1] = NULL; + vdir->vd_nblk--; + } + p.deblk = vdir->vd_deblk[0]; + deblk_end.deblk = p.deblk + 1; + err = set_deblk_end(&p, &deblk_end); + AuDebugOn(err); + vdir->vd_version = 0; + vdir->vd_jiffy = 0; + vdir->vd_last.i = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + //smp_mb(); + //DbgVdir(vdir); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void free_dehlist(struct au_nhash *dehlist) +{ + int i; + struct hlist_head *head; + struct au_vdir_dehstr *tpos; + struct hlist_node *pos, *n; + + AuTraceEnter(); + + for (i = 0; i < AuSize_NHASH; i++) { + head = dehlist->heads + i; + hlist_for_each_entry_safe(tpos, pos, n, head, hash) { + //hlist_del(pos); + au_cache_free_dehstr(tpos); + } + } +} + +/* returns found(true) or not */ +static int test_known(struct au_nhash *delist, char *name, int namelen) +{ + struct hlist_head *head; + struct au_vdir_dehstr *tpos; + struct hlist_node *pos; + struct au_vdir_destr *str; + + LKTRTrace("%.*s\n", namelen, name); + + head = delist->heads + au_name_hash(name, namelen); + hlist_for_each_entry(tpos, pos, head, hash) { + str = tpos->str; + LKTRTrace("%.*s\n", str->len, str->name); + if (str->len == namelen && !memcmp(str->name, name, namelen)) + return 1; + } + return 0; + +} + +static int append_de(struct au_vdir *vdir, char *name, int namelen, ino_t ino, + unsigned int d_type, struct au_nhash *delist) +{ + int err, sz; + union au_vdir_deblk_p p, *room, deblk_end; + struct au_vdir_dehstr *dehstr; + + LKTRTrace("%.*s %d, i%lu, dt%u\n", namelen, name, namelen, ino, d_type); + + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + 1; + room = &vdir->vd_last.p; + AuDebugOn(room->p < p.p || deblk_end.p <= room->p + || !is_deblk_end(room, &deblk_end)); + + sz = calc_size(namelen); + if (unlikely(sz > deblk_end.p - room->p)) { + err = append_deblk(vdir); + if (unlikely(err)) + goto out; + p.deblk = last_deblk(vdir); + deblk_end.deblk = p.deblk + 1; + //smp_mb(); + AuDebugOn(room->p != p.p); + } + + err = -ENOMEM; + dehstr = au_cache_alloc_dehstr(); + if (unlikely(!dehstr)) + goto out; + dehstr->str = &room->de->de_str; + hlist_add_head(&dehstr->hash, + delist->heads + au_name_hash(name, namelen)); + + room->de->de_ino = ino; + room->de->de_type = d_type; + room->de->de_str.len = namelen; + memcpy(room->de->de_str.name, name, namelen); + + err = 0; + room->p += sz; + if (unlikely(set_deblk_end(room, &deblk_end))) + err = append_deblk(vdir); + //smp_mb(); + + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino) +{ + int err; + struct au_xino_entry xinoe; + static DEFINE_MUTEX(mtx); + + /* a race condition for hardlinks */ + mutex_lock(&mtx); + err = au_xino_read(sb, bindex, h_ino, &xinoe); + if (unlikely(err)) + goto out; + + if (!xinoe.ino) { + //struct inode *h_inode; + err = -EIO; + xinoe.ino = au_xino_new_ino(sb); + if (unlikely(!xinoe.ino)) + goto out; + err = au_xino_write(sb, bindex, h_ino, &xinoe); + if (unlikely(err)) + goto out; + } + + *ino = xinoe.ino; + + out: + mutex_unlock(&mtx); + AuTraceErr(err); + return err; +} + +static int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t *ino) +{ +#ifdef CONFIG_AUFS_SHWH + return au_ino(sb, bindex, h_ino, ino); +#else + return 0; +#endif +} + +#define AuFillVdir_CALLED 1 +#define AuFillVdir_SHWH (1 << 1) +#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) +#define au_fset_fillvdir(flags, name) { (flags) |= AuFillVdir_##name; } +#define au_fclr_fillvdir(flags, name) { (flags) &= ~AuFillVdir_##name; } +#ifndef CONFIG_AUFS_SHWH +#undef AuFillVdir_SHWH +#define AuFillVdir_SHWH 0 +#endif + +struct fillvdir_arg { + struct file *file; + struct au_vdir *vdir; + struct au_nhash *delist; + struct au_nhash *whlist; + aufs_bindex_t bindex; + unsigned int flags; + int err; +}; + +static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset, + u64 h_ino, unsigned int d_type) +{ + struct fillvdir_arg *arg = __arg; + char *name = (void *)__name; + aufs_bindex_t bindex, bend; + struct super_block *sb; + ino_t ino; + + LKTRTrace("%.*s, namelen %d, i%Lu, dt%u\n", + namelen, name, namelen, (u64)h_ino, d_type); + + sb = arg->file->f_dentry->d_sb; + bend = arg->bindex; + arg->err = 0; + au_fset_fillvdir(arg->flags, CALLED); + //smp_mb(); + if (namelen <= AUFS_WH_PFX_LEN + || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { + for (bindex = 0; bindex < bend; bindex++) + if (test_known(arg->delist + bindex, name, namelen) + || au_nhash_test_known_wh(arg->whlist + bindex, + name, namelen)) + goto out; /* already exists or whiteouted */ + + ino = 1; /* why does gcc warns? */ + arg->err = au_ino(sb, bend, h_ino, &ino); + if (!arg->err) + arg->err = append_de(arg->vdir, name, namelen, ino, + d_type, arg->delist + bend); + } else { + name += AUFS_WH_PFX_LEN; + namelen -= AUFS_WH_PFX_LEN; + for (bindex = 0; bindex < bend; bindex++) + if (au_nhash_test_known_wh(arg->whlist + bend, name, + namelen)) + goto out; /* already whiteouted */ + + ino = 1; /* dummy */ + if (unlikely(au_ftest_fillvdir(arg->flags, SHWH))) + arg->err = au_wh_ino(sb, bend, h_ino, &ino); + if (!arg->err) + arg->err = au_nhash_append_wh + (arg->whlist + bend, name, namelen, ino, d_type, + bend, au_ftest_fillvdir(arg->flags, SHWH)); + } + + out: + if (!arg->err) + arg->vdir->vd_jiffy = jiffies; + //smp_mb(); + AuTraceErr(arg->err); + return arg->err; +} + +static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, + aufs_bindex_t bstart, aufs_bindex_t bend, + struct au_nhash *_whlist, struct au_nhash *_delist) +{ +#ifdef CONFIG_AUFS_SHWH + int err, i; + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos, *n; + char *p, *o; + struct au_nhash *whlist, *delist; + struct au_vdir_destr *destr; + aufs_bindex_t bindex; + + AuTraceEnter(); + AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); + + err = -ENOMEM; + o = p = __getname(); + if (unlikely(!p)) + goto out; + + err = 0; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + for (bindex = bstart; !err && bindex <= bend; bindex++) { + whlist = _whlist + bindex; + delist = _delist + bindex; + + for (i = 0; i < AuSize_NHASH; i++) { + head = whlist->heads + i; + hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) { + destr = &tpos->wh_str; + memcpy(p, destr->name, destr->len); + err = append_de(vdir, o, + destr->len + AUFS_WH_PFX_LEN, + tpos->wh_ino, tpos->wh_type, + delist); + if (unlikely(err)) + break; + } + } + } + + __putname(o); + + out: + AuTraceErr(err); + return err; +#else + return 0; +#endif +} + +static int au_do_read_vdir(struct fillvdir_arg *arg) +{ + int err, dlgt, shwh; + aufs_bindex_t bend, bindex, bstart; + struct super_block *sb; + unsigned int mnt_flags; + struct file *hf; + loff_t offset; + + AuTraceEnter(); + + err = -ENOMEM; + bend = au_fbend(arg->file); + arg->delist = kmalloc(sizeof(*arg->delist) * (bend + 1), GFP_TEMPORARY); + if (unlikely(!arg->delist)) + goto out; + arg->whlist = kmalloc(sizeof(*arg->whlist) * (bend + 1), GFP_TEMPORARY); + if (unlikely(!arg->whlist)) + goto out_delist; + err = 0; + for (bindex = 0; bindex <= bend; bindex++) { + au_nhash_init(arg->delist + bindex); + au_nhash_init(arg->whlist + bindex); + } + + sb = arg->file->f_dentry->d_sb; + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + arg->flags = 0; + shwh = 0; + if (unlikely(au_opt_test(mnt_flags, SHWH))) { + shwh = 1; + au_fset_fillvdir(arg->flags, SHWH); + } + bstart = au_fbstart(arg->file); + for (bindex = bstart; !err && bindex <= bend; bindex++) { + hf = au_h_fptr(arg->file, bindex); + if (!hf) + continue; + + offset = vfsub_llseek(hf, 0, SEEK_SET); + err = offset; + if (unlikely(offset)) + break; + arg->bindex = bindex; + do { + arg->err = 0; + au_fclr_fillvdir(arg->flags, CALLED); + //smp_mb(); + err = vfsub_readdir(hf, fillvdir, arg, dlgt); + if (err >= 0) + err = arg->err; + } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); + } + + if (unlikely(!err && shwh)) + err = au_handle_shwh(sb, arg->vdir, bstart, bend, arg->whlist, + arg->delist); + + for (bindex = bstart; bindex <= bend; bindex++) { + free_dehlist(arg->delist + bindex); + au_nhash_fin(arg->whlist + bindex); + } + kfree(arg->whlist); + + out_delist: + kfree(arg->delist); + out: + AuTraceErr(err); + return err; +} + +static int read_vdir(struct file *file, int may_read) +{ + int err, do_read; + struct dentry *dentry; + struct inode *inode; + struct au_vdir *vdir, *allocated; + unsigned long expire; + struct fillvdir_arg arg; + struct super_block *sb; + + dentry = file->f_dentry; + LKTRTrace("%.*s, may %d\n", AuDLNPair(dentry), may_read); + FiMustWriteLock(file); + inode = dentry->d_inode; + IMustLock(inode); + IiMustWriteLock(inode); + AuDebugOn(!S_ISDIR(inode->i_mode)); + + err = 0; + allocated = NULL; + do_read = 0; + sb = inode->i_sb; + expire = au_sbi(sb)->si_rdcache; + vdir = au_ivdir(inode); + if (!vdir) { + AuDebugOn(au_fvdir_cache(file)); + do_read = 1; + vdir = alloc_vdir(); + err = PTR_ERR(vdir); + if (IS_ERR(vdir)) + goto out; + err = 0; + allocated = vdir; + } else if (may_read + && (inode->i_version != vdir->vd_version + || time_after(jiffies, vdir->vd_jiffy + expire))) { + LKTRTrace("iver %Lu, vdver %lu, exp %lu\n", + inode->i_version, vdir->vd_version, + vdir->vd_jiffy + expire); + do_read = 1; + err = reinit_vdir(vdir); + if (unlikely(err)) + goto out; + } + //AuDbgVdir(vdir); goto out; + + if (!do_read) + return 0; /* success */ + + arg.file = file; + arg.vdir = vdir; + err = au_do_read_vdir(&arg); + if (!err) { + //file->f_pos = 0; + vdir->vd_version = inode->i_version; + vdir->vd_last.i = 0; + vdir->vd_last.p.deblk = vdir->vd_deblk[0]; + if (allocated) + au_set_ivdir(inode, allocated); + //AuDbgVdir(vdir); + } else if (allocated) + au_vdir_free(allocated); + + out: + AuTraceErr(err); + return err; +} + +static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) +{ + int err, i, rerr, n; + + AuTraceEnter(); + AuDebugOn(tgt->vd_nblk != 1); + //AuDbgVdir(tgt); + + err = -ENOMEM; + if (tgt->vd_nblk < src->vd_nblk) { + au_vdir_deblk_t **p; + p = au_kzrealloc(tgt->vd_deblk, sizeof(*p) * tgt->vd_nblk, + sizeof(*p) * src->vd_nblk, GFP_KERNEL); + if (unlikely(!p)) + goto out; + tgt->vd_deblk = p; + } + + tgt->vd_nblk = src->vd_nblk; + n = src->vd_nblk; + memcpy(tgt->vd_deblk[0], src->vd_deblk[0], AuSize_DEBLK); + //tgt->vd_last.i = 0; + //tgt->vd_last.p.deblk = tgt->vd_deblk[0]; + tgt->vd_version = src->vd_version; + tgt->vd_jiffy = src->vd_jiffy; + + for (i = 1; i < n; i++) { + tgt->vd_deblk[i] = kmalloc(AuSize_DEBLK, GFP_KERNEL); + if (tgt->vd_deblk[i]) + memcpy(tgt->vd_deblk[i], src->vd_deblk[i], + AuSize_DEBLK); + else + goto out; + } + //smp_mb(); + //AuDbgVdir(tgt); + return 0; /* success */ + + out: + rerr = reinit_vdir(tgt); + BUG_ON(rerr); + AuTraceErr(err); + return err; +} + +int au_vdir_init(struct file *file) +{ + int err; + struct dentry *dentry; + struct inode *inode; + struct au_vdir *vdir_cache, *allocated; + + dentry = file->f_dentry; + LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos); + FiMustWriteLock(file); + inode = dentry->d_inode; + IiMustWriteLock(inode); + AuDebugOn(!S_ISDIR(inode->i_mode)); + + err = read_vdir(file, !file->f_pos); + if (unlikely(err)) + goto out; + //AuDbgVdir(au_ivdir(inode)); goto out; + + allocated = NULL; + vdir_cache = au_fvdir_cache(file); + if (!vdir_cache) { + vdir_cache = alloc_vdir(); + err = PTR_ERR(vdir_cache); + if (IS_ERR(vdir_cache)) + goto out; + allocated = vdir_cache; + } else if (!file->f_pos && vdir_cache->vd_version != file->f_version) { + err = reinit_vdir(vdir_cache); + if (unlikely(err)) + goto out; + } else + return 0; /* success */ + //err = 0; AuDbgVdir(vdir_cache); goto out; + + err = copy_vdir(vdir_cache, au_ivdir(inode)); + if (!err) { + file->f_version = inode->i_version; + if (allocated) + au_set_fvdir_cache(file, allocated); + } else if (allocated) + au_vdir_free(allocated); + + out: + //au_debug_on(); + AuTraceErr(err); + //au_debug_off(); + return err; +} + +static loff_t calc_offset(struct au_vdir *vdir) +{ + loff_t offset; + union au_vdir_deblk_p p; + + p.deblk = vdir->vd_deblk[vdir->vd_last.i]; + offset = vdir->vd_last.p.p - p.p; + offset += sizeof(*p.deblk) * vdir->vd_last.i; + return offset; +} + +/* returns true or false */ +static int seek_vdir(struct file *file) +{ + int valid, i, n; + struct dentry *dentry; + struct au_vdir *vdir_cache; + loff_t offset; + union au_vdir_deblk_p p, deblk_end; + + dentry = file->f_dentry; + LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos); + vdir_cache = au_fvdir_cache(file); + AuDebugOn(!vdir_cache); + //AuDbgVdir(vdir_cache); + + valid = 1; + offset = calc_offset(vdir_cache); + LKTRTrace("offset %Ld\n", offset); + if (file->f_pos == offset) + goto out; + + vdir_cache->vd_last.i = 0; + vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; + if (!file->f_pos) + goto out; + + valid = 0; + i = file->f_pos / AuSize_DEBLK; + LKTRTrace("i %d\n", i); + if (i >= vdir_cache->vd_nblk) + goto out; + + n = vdir_cache->vd_nblk; + for (; i < n; i++) { + p.deblk = vdir_cache->vd_deblk[i]; + deblk_end.deblk = p.deblk + 1; + offset = i; + offset *= AuSize_DEBLK; + while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) { + int l; + l = calc_size(p.de->de_str.len); + offset += l; + p.p += l; + } + if (!is_deblk_end(&p, &deblk_end)) { + valid = 1; + vdir_cache->vd_last.i = i; + vdir_cache->vd_last.p = p; + break; + } + } + + out: + //smp_mb(); + AuTraceErr(!valid); + return valid; +} + +int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir) +{ + int err, l; + struct dentry *dentry; + struct au_vdir *vdir_cache; + struct au_vdir_de *de; + union au_vdir_deblk_p deblk_end; + + dentry = file->f_dentry; + LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos); + vdir_cache = au_fvdir_cache(file); + AuDebugOn(!vdir_cache); + //AuDbgVdir(vdir_cache); + + if (!seek_vdir(file)) + return 0; + + while (1) { + deblk_end.deblk + = vdir_cache->vd_deblk[vdir_cache->vd_last.i] + 1; + while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { + de = vdir_cache->vd_last.p.de; + LKTRTrace("%.*s, off%Ld, i%lu, dt%d\n", + de->de_str.len, de->de_str.name, + file->f_pos, de->de_ino, de->de_type); + err = filldir(dirent, de->de_str.name, de->de_str.len, + file->f_pos, de->de_ino, de->de_type); + if (unlikely(err)) { + AuTraceErr(err); + //return err; + //todo: ignore the error caused by udba. + return 0; + } + + l = calc_size(de->de_str.len); + vdir_cache->vd_last.p.p += l; + file->f_pos += l; + } + if (vdir_cache->vd_last.i < vdir_cache->vd_nblk - 1) { + vdir_cache->vd_last.i++; + vdir_cache->vd_last.p.deblk + = vdir_cache->vd_deblk[vdir_cache->vd_last.i]; + file->f_pos = sizeof(*vdir_cache->vd_last.p.deblk) + * vdir_cache->vd_last.i; + continue; + } + break; + } + + //smp_mb(); + return 0; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/vfsub.c linux-2.6.25.4-unionfs/fs/aufs/vfsub.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/vfsub.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/vfsub.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sub-routines for VFS + * + * $Id: vfsub.c,v 1.3 2008/05/12 00:29:11 sfjro Exp $ + */ +// I'm going to slightly mad + +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +void vfsub_args_init(struct vfsub_args *vargs, struct au_hin_ignore *ign, + int dlgt, int force_unlink) +{ + do_vfsub_args_reinit(vargs, ign); + vargs->flags = 0; + if (unlikely(dlgt)) + vfsub_fset(vargs->flags, DLGT); + if (force_unlink) + vfsub_fset(vargs->flags, FORCE_UNLINK); +} + +/* ---------------------------------------------------------------------- */ + +struct file *vfsub_filp_open(const char *path, int oflags, int mode) +{ + struct file *err; + + LKTRTrace("%s\n", path); + + lockdep_off(); + err = filp_open(path, oflags, mode); + lockdep_on(); + if (!IS_ERR(err)) + au_update_fuse_h_inode(err->f_vfsmnt, err->f_dentry); /*ignore*/ + return err; +} + +int vfsub_path_lookup(const char *name, unsigned int flags, + struct nameidata *nd) +{ + int err; + + LKTRTrace("%s\n", name); + + /* lockdep_off(); */ + err = path_lookup(name, flags, nd); + /* lockdep_on(); */ + if (!err) + au_update_fuse_h_inode(nd->path.mnt, nd->path.dentry); + /*ignore*/ + return err; +} + +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len) +{ + struct dentry *d; + + LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name); + IMustLock(parent->d_inode); + + d = lookup_one_len(name, parent, len); + if (!IS_ERR(d)) + au_update_fuse_h_inode(NULL, d); /*ignore*/ + return d; +} + +#ifdef CONFIG_AUFS_LHASH_PATCH +struct dentry *vfsub__lookup_hash(struct qstr *name, struct dentry *parent, + struct nameidata *nd) +{ + struct dentry *d; + + LKTRTrace("%.*s/%.*s, nd %d\n", + AuDLNPair(parent), AuLNPair(name), !!nd); + if (nd) + LKTRTrace("nd{0x%x}\n", nd->flags); + IMustLock(parent->d_inode); + + d = __lookup_hash(name, parent, nd); + if (!IS_ERR(d)) + au_update_fuse_h_inode(NULL, d); /*ignore*/ + return d; +} +#endif + +/* ---------------------------------------------------------------------- */ + +int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd) +{ + int err; + struct vfsmount *mnt; + + LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + + err = vfs_create(dir, dentry, mode, nd); + if (!err) { + mnt = NULL; + if (nd) + mnt = nd->path.mnt; + /* dir inode is locked */ + au_update_fuse_h_inode(mnt, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(mnt, dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_symlink(struct inode *dir, struct dentry *dentry, + const char *symname, int mode) +{ + int err; + + LKTRTrace("i%lu, %.*s, %s, 0x%x\n", + dir->i_ino, AuDLNPair(dentry), symname, mode); + IMustLock(dir); + + err = vfs_symlink(dir, dentry, symname, mode); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev) +{ + int err; + + LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + + err = vfs_mknod(dir, dentry, mode, dev); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry) +{ + int err; + + LKTRTrace("%.*s, i%lu, %.*s\n", + AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + + lockdep_off(); + err = vfs_link(src_dentry, dir, dentry); + lockdep_on(); + if (!err) { + LKTRTrace("src_i %p, dst_i %p\n", + src_dentry->d_inode, dentry->d_inode); + /* fuse has different memory inode for the same inumber */ + au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/ + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry) +{ + int err; + + LKTRTrace("i%lu, %.*s, i%lu, %.*s\n", + src_dir->i_ino, AuDLNPair(src_dentry), + dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + IMustLock(src_dir); + + lockdep_off(); + err = vfs_rename(src_dir, src_dentry, dir, dentry); + lockdep_on(); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, src_dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode) +{ + int err; + + LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode); + IMustLock(dir); + + err = vfs_mkdir(dir, dentry, mode); + if (!err) { + /* dir inode is locked */ + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + au_update_fuse_h_inode(NULL, dentry); /*ignore*/ + } + return err; +} + +int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry) +{ + int err; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + + lockdep_off(); + err = vfs_rmdir(dir, dentry); + lockdep_on(); + /* dir inode is locked */ + if (!err) + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + return err; +} + +int do_vfsub_unlink(struct inode *dir, struct dentry *dentry) +{ + int err; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + IMustLock(dir); + + /* vfs_unlink() locks inode */ + lockdep_off(); + err = vfs_unlink(dir, dentry); + lockdep_on(); + /* dir inode is locked */ + if (!err) + au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/ + return err; +} + +/* ---------------------------------------------------------------------- */ + +ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(file->f_dentry), (unsigned long)count, *ppos); + + if (0 /*!au_test_nfs(file->f_vfsmnt->mnt_sb)*/) + err = vfs_read(file, ubuf, count, ppos); + else { + lockdep_off(); + err = vfs_read(file, ubuf, count, ppos); + lockdep_on(); + } + if (err >= 0) + au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); + /*ignore*/ + return err; +} + +// kernel_read() ?? +ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = do_vfsub_read_u(file, (char __user *)kbuf, count, ppos); + set_fs(oldfs); + return err; +} + +ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + ssize_t err; + + LKTRTrace("%.*s, cnt %lu, pos %Ld\n", + AuDLNPair(file->f_dentry), (unsigned long)count, *ppos); + + lockdep_off(); + err = vfs_write(file, ubuf, count, ppos); + lockdep_on(); + if (err >= 0) + au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); + /*ignore*/ + return err; +} + +ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos) +{ + ssize_t err; + mm_segment_t oldfs; + + oldfs = get_fs(); + set_fs(KERNEL_DS); + err = do_vfsub_write_u(file, (const char __user *)kbuf, count, ppos); + set_fs(oldfs); + return err; +} + +int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg) +{ + int err; + + LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry)); + + lockdep_off(); + err = vfs_readdir(file, filldir, arg); + lockdep_on(); + if (err >= 0) + au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); + /*ignore*/ + return err; +} + +#ifdef CONFIG_AUFS_SPLICE_PATCH +long do_vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + long err; + + LKTRTrace("%.*s, pos %Ld, len %lu, 0x%x\n", + AuDLNPair(in->f_dentry), *ppos, (unsigned long)len, flags); + + lockdep_off(); + err = vfs_splice_to(in, ppos, pipe, len, flags); + lockdep_on(); + if (err >= 0) + au_update_fuse_h_inode(in->f_vfsmnt, in->f_dentry); /*ignore*/ + return err; +} + +long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + long err; + + LKTRTrace("%.*s, pos %Ld, len %lu, 0x%x\n", + AuDLNPair(out->f_dentry), *ppos, (unsigned long)len, flags); + + lockdep_off(); + err = vfs_splice_from(pipe, out, ppos, len, flags); + lockdep_on(); + if (err >= 0) + au_update_fuse_h_inode(out->f_vfsmnt, out->f_dentry); /*ignore*/ + return err; +} +#endif + +/* ---------------------------------------------------------------------- */ + +struct au_vfsub_mkdir_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + int mode; + int dlgt; +}; + +static void au_call_vfsub_mkdir(void *args) +{ + struct au_vfsub_mkdir_args *a = args; + *a->errp = vfsub_mkdir(a->dir, a->dentry, a->mode, a->dlgt); +} + +int vfsub_sio_mkdir(struct inode *dir, struct dentry *dentry, int mode, + int dlgt) +{ + int err, do_sio, wkq_err; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE, dlgt); + if (!do_sio) + err = vfsub_mkdir(dir, dentry, mode, dlgt); + else { + struct au_vfsub_mkdir_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .mode = mode, + .dlgt = dlgt + }; + wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } + + AuTraceErr(err); + return err; +} + +struct au_vfsub_rmdir_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + struct vfsub_args *vargs; +}; + +static void au_call_vfsub_rmdir(void *args) +{ + struct au_vfsub_rmdir_args *a = args; + *a->errp = vfsub_rmdir(a->dir, a->dentry, a->vargs); +} + +int vfsub_sio_rmdir(struct inode *dir, struct dentry *dentry, int dlgt) +{ + int err, do_sio, wkq_err; + struct vfsub_args vargs; + + LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry)); + + vfsub_args_init(&vargs, /*ign*/NULL, dlgt, /*force_unlink*/0); + do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE, dlgt); + if (!do_sio) + err = vfsub_rmdir(dir, dentry, &vargs); + else { + struct au_vfsub_rmdir_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .vargs = &vargs + }; + wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct notify_change_args { + int *errp; + struct dentry *h_dentry; + struct iattr *ia; + struct vfsub_args *vargs; +}; + +static void call_notify_change(void *args) +{ + struct notify_change_args *a = args; + struct inode *h_inode; + + LKTRTrace("%.*s, ia_valid 0x%x\n", + AuDLNPair(a->h_dentry), a->ia->ia_valid); + h_inode = a->h_dentry->d_inode; + IMustLock(h_inode); + + *a->errp = -EPERM; + if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { + vfsub_ignore(a->vargs); + lockdep_off(); + *a->errp = notify_change(a->h_dentry, a->ia); + lockdep_on(); + if (!*a->errp) + au_update_fuse_h_inode(NULL, a->h_dentry); /*ignore*/ + else + vfsub_unignore(a->vargs); + } + AuTraceErr(*a->errp); +} + +#ifdef CONFIG_AUFS_DLGT +static void vfsub_notify_change_dlgt(struct notify_change_args *args, + unsigned int flags) +{ + if (!vfsub_ftest(flags, DLGT)) + call_notify_change(args); + else { + int wkq_err; + wkq_err = au_wkq_wait(call_notify_change, args, /*dlgt*/1); + if (unlikely(wkq_err)) + *args->errp = wkq_err; + } +} +#else +static void vfsub_notify_change_dlgt(struct notify_change_args *args, + unsigned int flags) +{ + call_notify_change(args); +} +#endif + +int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, + struct vfsub_args *vargs) +{ + int err; + struct notify_change_args args = { + .errp = &err, + .h_dentry = dentry, + .ia = ia, + .vargs = vargs + }; + + vfsub_notify_change_dlgt(&args, vargs->flags); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct unlink_args { + int *errp; + struct inode *dir; + struct dentry *dentry; + struct vfsub_args *vargs; +}; + +static void call_unlink(void *args) +{ + struct unlink_args *a = args; + struct inode *h_inode; + const int stop_sillyrename = (au_test_nfs(a->dentry->d_sb) + && atomic_read(&a->dentry->d_count) == 1); + + LKTRTrace("%.*s, stop_silly %d, cnt %d\n", + AuDLNPair(a->dentry), stop_sillyrename, + atomic_read(&a->dentry->d_count)); + //IMustLock(a->dir); + + if (!stop_sillyrename) + dget(a->dentry); + h_inode = a->dentry->d_inode; + if (h_inode) + atomic_inc_return(&h_inode->i_count); + *a->errp = do_vfsub_unlink(a->dir, a->dentry); + + if (!stop_sillyrename) + dput(a->dentry); + if (h_inode) + iput(h_inode); + + AuTraceErr(*a->errp); +} + +/* + * @dir: must be locked. + * @dentry: target dentry. + */ +int vfsub_unlink(struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs) +{ + int err; + struct unlink_args args = { + .errp = &err, + .dir = dir, + .dentry = dentry, + .vargs = vargs + }; + + if (!vfsub_ftest(vargs->flags, DLGT) + && !vfsub_ftest(vargs->flags, FORCE_UNLINK)) + call_unlink(&args); + else { + int wkq_err; + wkq_err = au_wkq_wait(call_unlink, &args, + vfsub_ftest(vargs->flags, DLGT)); + if (unlikely(wkq_err)) + err = wkq_err; + } + + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct statfs_args { + int *errp; + void *arg; + struct kstatfs *buf; +}; + +static void call_statfs(void *args) +{ + struct statfs_args *a = args; + *a->errp = vfs_statfs(a->arg, a->buf); +} + +#ifdef CONFIG_AUFS_DLGT +static void vfsub_statfs_dlgt(struct statfs_args *args, int dlgt) +{ + if (!dlgt) + call_statfs(args); + else { + int wkq_err; + wkq_err = au_wkq_wait(call_statfs, args, /*dlgt*/1); + if (unlikely(wkq_err)) + *args->errp = wkq_err; + } +} +#else +static void vfsub_statfs_dlgt(struct statfs_args *args, int dlgt) +{ + call_statfs(args); +} +#endif + +int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt) +{ + int err; + struct statfs_args args = { + .errp = &err, + .arg = arg, + .buf = buf + }; + + vfsub_statfs_dlgt(&args, dlgt); + + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/vfsub.h linux-2.6.25.4-unionfs/fs/aufs/vfsub.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/vfsub.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/vfsub.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * sub-routines for VFS + * + * $Id: vfsub.h,v 1.2 2008/04/21 01:49:22 sfjro Exp $ + */ + +#ifndef __AUFS_VFSUB_H__ +#define __AUFS_VFSUB_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* vfsub flags */ +#define Vfsub_DLGT 1 /* operation with delegation */ +#define Vfsub_FORCE_UNLINK (1 << 1) /* force unlinking */ +#define vfsub_ftest(flags, name) ((flags) & Vfsub_##name) +#define vfsub_fset(flags, name) { (flags) |= Vfsub_##name; } +#define vfsub_fclr(flags, name) { (flags) &= ~Vfsub_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef Vfsub_DLGT +#define Vfsub_DLGT 0 +#endif + +struct au_hin_ignore; +struct vfsub_args { +#ifdef CONFIG_AUFS_HINOTIFY + /* inotify events to be ignored */ + int nignore; + struct au_hin_ignore *ignore; +#endif + + unsigned int flags; +}; + +struct au_hinode; +#ifdef CONFIG_AUFS_HINOTIFY +static inline +void do_vfsub_args_reinit(struct vfsub_args *vargs, struct au_hin_ignore *ign) +{ + vargs->nignore = 0; + vargs->ignore = ign; +} + +static inline void vfsub_args_reinit(struct vfsub_args *vargs) +{ + vargs->nignore = 0; +} + +__u32 vfsub_events_notify_change(struct iattr *ia); +void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events, + struct au_hinode *hinode); +void vfsub_ign_inode(struct vfsub_args *vargs, __u32 events, + struct inode *inode, struct inode *h_inode); + +void vfsub_ignore(struct vfsub_args *vargs); +void vfsub_unignore(struct vfsub_args *vargs); +#else +static inline +void do_vfsub_args_reinit(struct vfsub_args *vargs, struct au_hin_ignore *ign) +{ + /* empty */ +} + +static inline void vfsub_args_reinit(struct vfsub_args *vargs) +{ + /* empty */ +} + +static inline __u32 vfsub_events_notify_change(struct iattr *ia) +{ + return 0; +} + +static inline void vfsub_ign_hinode(struct vfsub_args *vargs, __u32 events, + struct au_hinode *hinode) +{ + /* empty */ +} + +static inline void vfsub_ign_inode(struct vfsub_args *vargs, __u32 events, + struct inode *inode, struct inode *h_inode) +{ + /* empty */ +} + +static inline void vfsub_ignore(struct vfsub_args *vargs) +{ + /* empty */ +} + +static inline void vfsub_unignore(struct vfsub_args *vargs) +{ + /* empty */ +} +#endif /* CONFIG_AUFS_HINOTIFY */ + +void vfsub_args_init(struct vfsub_args *vargs, struct au_hin_ignore *ign, + int dlgt, int force_unlink); + +/* ---------------------------------------------------------------------- */ + +/* inotify_inode_watched() is not exported */ +static inline int au_test_inotify(struct inode *inode) +{ +#ifdef CONFIG_INOTIFY + return !list_empty(&inode->inotify_watches); +#endif + return 0; +} + +/* ---------------------------------------------------------------------- */ + +/* lock subclass for hidden inode */ +/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ +// todo: reduce it +enum { + AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */ + AuLsc_I_PARENT, /* hidden inode, parent first */ + AuLsc_I_CHILD, + AuLsc_I_PARENT2, /* copyup dirs */ + AuLsc_I_CHILD2, + AuLsc_I_End +}; + +#define IMustLock(i) MtxMustLock(&(i)->i_mutex) + +static inline +struct dentry *vfsub_lock_rename(struct dentry *d1, struct dentry *d2) +{ + struct dentry *d; + + lockdep_off(); + d = lock_rename(d1, d2); + lockdep_on(); + return d; +} + +static inline void vfsub_unlock_rename(struct dentry *d1, struct dentry *d2) +{ + lockdep_off(); + unlock_rename(d1, d2); + lockdep_on(); +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_WORKAROUND_FUSE +/* br_fuse.c */ +int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry); +#else +static inline +int au_update_fuse_h_inode(struct vfsmount *h_mnt, struct dentry *h_dentry) +{ + return 0; +} +#endif + +#ifdef CONFIG_AUFS_BR_XFS +/* br_xfs.c */ +dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt, + struct dentry *h_dentry); +#else +static inline +dev_t au_h_rdev(struct inode *h_inode, struct vfsmount *h_mnt, + struct dentry *h_dentry) +{ + return h_inode->i_rdev; +} +#endif + +/* simple abstractions, for future use */ +static inline +int do_vfsub_permission(struct inode *inode, int mask, struct nameidata *nd) +{ + LKTRTrace("i%lu, mask 0x%x, nd %d\n", inode->i_ino, mask, !!nd); + IMustLock(inode); + return permission(inode, mask, nd); +} + +/* ---------------------------------------------------------------------- */ + +struct file *vfsub_filp_open(const char *path, int oflags, int mode); +int vfsub_path_lookup(const char *name, unsigned int flags, + struct nameidata *nd); +struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, + int len); + +#ifdef CONFIG_AUFS_LHASH_PATCH +struct dentry *vfsub__lookup_hash(struct qstr *name, struct dentry *parent, + struct nameidata *nd); +#endif + +/* ---------------------------------------------------------------------- */ + +int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd); +int do_vfsub_symlink(struct inode *dir, struct dentry *dentry, + const char *symname, int mode); +int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, + dev_t dev); +int do_vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry); +int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry); +int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode); +int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry); +int do_vfsub_unlink(struct inode *dir, struct dentry *dentry); + +/* ---------------------------------------------------------------------- */ + +ssize_t do_vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos); +// kernel_read() ?? +ssize_t do_vfsub_read_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +ssize_t do_vfsub_write_u(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos); +ssize_t do_vfsub_write_k(struct file *file, void *kbuf, size_t count, + loff_t *ppos); +int do_vfsub_readdir(struct file *file, filldir_t filldir, void *arg); + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_SPLICE_PATCH +long do_vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags); +long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); +#else +static inline +long do_vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags) +{ + return -ENOSYS; +} + +static inline +long do_vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags) +{ + return -ENOSYS; +} +#endif /* CONFIG_AUFS_SPLICE_PATCH */ + +/* ---------------------------------------------------------------------- */ + +static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) +{ + loff_t err; + + LKTRTrace("%.*s\n", AuDLNPair(file->f_dentry)); + + lockdep_off(); + err = vfs_llseek(file, offset, origin); + lockdep_on(); + return err; +} + +static inline int do_vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *st) +{ + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + return vfs_getattr(mnt, dentry, st); +} + +/* ---------------------------------------------------------------------- */ + +#if defined(CONFIG_AUFS_HINOTIFY) || defined(CONFIG_AUFS_DLGT) +/* hin_or_dlgt.c */ +int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd, + int dlgt); + +int vfsub_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd, int dlgt); +int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname, + int mode, int dlgt); +int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev, + int dlgt); +int vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry, int dlgt); +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs); +int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt); +int vfsub_rmdir(struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs); + +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos, int dlgt); +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + int dlgt); +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos, struct vfsub_args *vargs); +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + struct vfsub_args *vargs); +int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt); +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags, int dlgt); +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags, + struct vfsub_args *vargs); + +int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st, + int dlgt); +#else + +static inline +int vfsub_permission(struct inode *inode, int mask, struct nameidata *nd, + int dlgt) +{ + return do_vfsub_permission(inode, mask, nd); +} + +static inline +int vfsub_create(struct inode *dir, struct dentry *dentry, int mode, + struct nameidata *nd, int dlgt) +{ + return do_vfsub_create(dir, dentry, mode, nd); +} + +static inline +int vfsub_symlink(struct inode *dir, struct dentry *dentry, const char *symname, + int mode, int dlgt) +{ + return do_vfsub_symlink(dir, dentry, symname, mode); +} + +static inline +int vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev, + int dlgt) +{ + return do_vfsub_mknod(dir, dentry, mode, dev); +} + +static inline +int vfsub_link(struct dentry *src_dentry, struct inode *dir, + struct dentry *dentry, int dlgt) +{ + return do_vfsub_link(src_dentry, dir, dentry); +} + +static inline +int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, + struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs) +{ + int err; + + vfsub_ignore(vargs); + err = do_vfsub_rename(src_dir, src_dentry, dir, dentry); + if (unlikely(err)) + vfsub_unignore(vargs); + return err; +} + +static inline +int vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode, int dlgt) +{ + return do_vfsub_mkdir(dir, dentry, mode); +} + +static inline +int vfsub_rmdir(struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs) +{ + int err; + + vfsub_ignore(vargs); + err = do_vfsub_rmdir(dir, dentry); + if (unlikely(err)) + vfsub_unignore(vargs); + return err; +} + +static inline +ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, + loff_t *ppos, int dlgt) +{ + return do_vfsub_read_u(file, ubuf, count, ppos); +} + +static inline +ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + int dlgt) +{ + return do_vfsub_read_k(file, kbuf, count, ppos); +} + +static inline +ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, + loff_t *ppos, struct vfsub_args *vargs) +{ + int err; + + vfsub_ignore(vargs); + err = do_vfsub_write_u(file, ubuf, count, ppos); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + return err; +} + +static inline +ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos, + struct vfsub_args *vargs) +{ + int err; + + vfsub_ignore(vargs); + err = do_vfsub_write_k(file, kbuf, count, ppos); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + return err; +} + +static inline +int vfsub_readdir(struct file *file, filldir_t filldir, void *arg, int dlgt) +{ + return do_vfsub_readdir(file, filldir, arg); +} + +static inline +long vfsub_splice_to(struct file *in, loff_t *ppos, + struct pipe_inode_info *pipe, size_t len, + unsigned int flags, int dlgt) +{ + return do_vfsub_splice_to(in, ppos, pipe, len, flags); +} + +static inline +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags, + struct vfsub_args *vargs) +{ + long err; + + vfsub_ignore(vargs); + err = do_vfsub_splice_from(pipe, out, ppos, len, flags); + if (unlikely(err < 0)) + vfsub_unignore(vargs); + return err; +} + +static inline +int vfsub_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *st, + int dlgt) +{ + return do_vfsub_getattr(mnt, dentry, st); +} +#endif /* CONFIG_AUFS_DLGT || CONFIG_AUFS_HINOTIFY */ + +/* ---------------------------------------------------------------------- */ + +int vfsub_sio_mkdir(struct inode *dir, struct dentry *dentry, int mode, + int dlgt); +int vfsub_sio_rmdir(struct inode *dir, struct dentry *dentry, int dlgt); + +/* ---------------------------------------------------------------------- */ + +int vfsub_notify_change(struct dentry *dentry, struct iattr *ia, + struct vfsub_args *vargs); +int vfsub_unlink(struct inode *dir, struct dentry *dentry, + struct vfsub_args *vargs); +int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt); + +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/wbr_policy.c linux-2.6.25.4-unionfs/fs/aufs/wbr_policy.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/wbr_policy.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/wbr_policy.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,693 @@ +/* + * Copyright (C) 2007-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * policies for selecting one among multiple writable branches + * + * $Id: wbr_policy.c,v 1.3 2008/04/28 03:04:12 sfjro Exp $ + */ + +#include +#include "aufs.h" + +static int au_cpdown_attr(struct dentry *h_dst, struct dentry *h_src, int dlgt) +{ + int err, sbits; + struct iattr ia; + struct inode *h_idst, *h_isrc; + struct vfsub_args vargs; + + LKTRTrace("%.*s\n", AuDLNPair(h_dst)); + h_idst = h_dst->d_inode; + //IMustLock(h_idst); + h_isrc = h_src->d_inode; + //IMustLock(h_isrc); + + ia.ia_valid = ATTR_FORCE | ATTR_MODE | ATTR_UID | ATTR_GID; + ia.ia_mode = h_isrc->i_mode; + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); + + vfsub_args_init(&vargs, NULL, dlgt, /*force_unlink*/0); + err = vfsub_notify_change(h_dst, &ia, &vargs); + + /* is this nfs only? */ + if (!err && sbits && au_test_nfs(h_dst->d_sb)) { + ia.ia_valid = ATTR_FORCE | ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + err = vfsub_notify_change(h_dst, &ia, &vargs); + } + + if (!err) + h_idst->i_flags = h_isrc->i_flags; //?? + + AuTraceErr(err); + return err; +} + +struct au_cpdown_dir_args { + struct dentry *parent; + unsigned int parent_opq; // bit-flags +}; + +static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *h_parent, void *arg) +{ + int err, parent_opq, whed, dlgt, do_opq, made_dir, diropq, rerr; + struct au_cpdown_dir_args *args = arg; + aufs_bindex_t bend, bopq, bstart; + struct dentry *h_dentry, *opq_dentry, *wh_dentry; + struct inode *h_dir, *h_inode, *inode; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bdst); + bstart = au_dbstart(dentry); + AuDebugOn(bstart <= bdst + && bdst <= au_dbend(dentry) + && au_h_dptr(dentry, bdst)); + AuDebugOn(!h_parent); + h_dir = h_parent->d_inode; + AuDebugOn(!h_dir); + IMustLock(h_dir); + + err = au_lkup_neg(dentry, bdst); + if (unlikely(err < 0)) + goto out; + h_dentry = au_h_dptr(dentry, bdst); + dlgt = !!au_opt_test_dlgt(au_mntflags(dentry->d_sb)); + err = vfsub_sio_mkdir(h_dir, h_dentry, S_IRWXU | S_IRUGO | S_IXUGO, + dlgt); + if (unlikely(err)) + goto out_put; + + made_dir = 1; + bend = au_dbend(dentry); + bopq = au_dbdiropq(dentry); + whed = (au_dbwh(dentry) == bdst); + if (!args->parent_opq) + args->parent_opq |= (bopq <= bdst); + parent_opq = (args->parent_opq && args->parent == dentry); + do_opq = 0; + diropq = 0; + h_inode = h_dentry->d_inode; + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + if (whed || (parent_opq && do_opq)) { + opq_dentry = au_diropq_create(dentry, bdst, dlgt); + err = PTR_ERR(opq_dentry); + if (IS_ERR(opq_dentry)) { + mutex_unlock(&h_inode->i_mutex); + goto out_dir; + } + dput(opq_dentry); + diropq = 1; + } + + err = au_cpdown_attr(h_dentry, au_h_dptr(dentry, bstart), dlgt); + mutex_unlock(&h_inode->i_mutex); + if (unlikely(err)) + goto out_opq; + + wh_dentry = NULL; + if (whed) { + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, /*ndx*/NULL); + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out_opq; + err = 0; + if (wh_dentry->d_inode) + err = au_wh_unlink_dentry(h_dir, wh_dentry, dentry, + NULL, dlgt); + dput(wh_dentry); + if (unlikely(err)) + goto out_opq; + } + + inode = dentry->d_inode; + if (au_ibend(inode) < bdst) + au_set_ibend(inode, bdst); + au_set_h_iptr(inode, bdst, igrab(h_inode), au_hi_flags(inode, 1)); + goto out; /* success */ + + /* revert */ + out_opq: + if (diropq) { + mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); + rerr = au_diropq_remove(dentry, bdst, dlgt); + mutex_unlock(&h_inode->i_mutex); + if (unlikely(rerr)) { + AuIOErr("failed removing diropq for %.*s b%d (%d)\n", + AuDLNPair(dentry), bdst, rerr); + err = -EIO; + goto out; + } + } + out_dir: + if (made_dir) { + rerr = vfsub_sio_rmdir(h_dir, h_dentry, dlgt); + if (unlikely(rerr)) { + AuIOErr("failed removing %.*s b%d (%d)\n", + AuDLNPair(dentry), bdst, rerr); + err = -EIO; + } + } + out_put: + au_set_h_dptr(dentry, bdst, NULL); + if (au_dbend(dentry) == bdst) + au_update_dbend(dentry); + out: + AuTraceErr(err); + return err; +} + +int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *locked) +{ + int err; + struct au_cpdown_dir_args args = { + .parent = dget_parent(dentry), + .parent_opq = 0 + }; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bdst); + + err = au_cp_dirs(dentry, bdst, locked, au_cpdown_dir, &args); + dput(args.parent); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for create */ + +static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) +{ + for (; bindex >= 0; bindex--) + if (!au_br_rdonly(au_sbr(sb, bindex))) + return bindex; + return -EROFS; +} + +/* top down parent */ +static int au_wbr_create_tdp(struct dentry *dentry, int isdir) +{ + int err, dirperm1; + struct super_block *sb; + aufs_bindex_t bstart, bindex; + struct dentry *parent, *h_parent; + struct inode *h_dir; + + LKTRTrace("%.*s, dir %d\n", AuDLNPair(dentry), isdir); + + sb = dentry->d_sb; + dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb)); + bstart = au_dbstart(dentry); + err = bstart; + if (!au_br_rdonly(au_sbr(sb, bstart))) + goto out; + + err = -EROFS; + parent = dget_parent(dentry); + for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent) + continue; + h_dir = h_parent->d_inode; + if (!h_dir) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex)) + && (!dirperm1 + || au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC, + /*dlgt*/0))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) + err = au_wbr_bu(sb, bstart - 1); + + out: + LKTRTrace("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* an exception for the policy other than tdp */ +static int au_wbr_create_exp(struct dentry *dentry) +{ + int err; + struct dentry *parent; + aufs_bindex_t bwh, bdiropq; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + err = -1; + bwh = au_dbwh(dentry); + parent = dget_parent(dentry); + bdiropq = au_dbdiropq(parent); + if (bwh >= 0) { + if (bdiropq >= 0) + err = min(bdiropq, bwh); + else + err = bwh; + LKTRTrace("%d\n", err); + } else if (bdiropq >= 0) { + err = bdiropq; + LKTRTrace("%d\n", err); + } + dput(parent); + + if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) + err = -1; + + LKTRTrace("%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* round robin */ +static int au_wbr_create_init_rr(struct super_block *sb) +{ + int err; + + err = au_wbr_bu(sb, au_sbend(sb)); + atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ + + LKTRTrace("b%d\n", err); + return err; +} + +static int au_wbr_create_rr(struct dentry *dentry, int isdir) +{ + int err, nbr; + struct super_block *sb; + atomic_t *next; + unsigned int u; + aufs_bindex_t bindex, bend; + + //au_debug_on(); + LKTRTrace("%.*s, dir %d\n", AuDLNPair(dentry), isdir); + + sb = dentry->d_sb; + next = NULL; + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + next = &au_sbi(sb)->si_wbr_rr_next; + bend = au_sbend(sb); + nbr = bend + 1; + for (bindex = 0; bindex <= bend; bindex++) { + if (!isdir) { + err = atomic_dec_return(next) + 1; + /* modulo for 0 is meaningless */ + if (unlikely(!err)) + err = atomic_dec_return(next) + 1; + } else + err = atomic_read(next); + LKTRTrace("%d\n", err); + u = err; + err = u % nbr; + LKTRTrace("%d\n", err); + if (!au_br_rdonly(au_sbr(sb, err))) + break; + err = -EROFS; + } + + out: + LKTRTrace("%d\n", err); + //au_debug_off(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space */ +static void *au_wbr_statfs_arg(struct au_branch *br, struct super_block *sb, + aufs_bindex_t bindex) +{ + struct super_block *h_sb; + + h_sb = br->br_mnt->mnt_sb; + + if (!au_test_nfs(h_sb)) + return h_sb->s_root; + + /* sigh,,, why nfs s_root has wrong inode? */ + return au_di(sb->s_root)->di_hdentry[0 + bindex].hd_dentry; +} + +static void au_mfs(struct dentry *dentry) +{ + struct super_block *sb; + aufs_bindex_t bindex, bend; + int dlgt, err; + struct kstatfs st; + u64 b, bavail; + void *arg; + struct au_branch *br; + struct au_wbr_mfs *mfs; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + bavail = 0; + sb = dentry->d_sb; + mfs = &au_sbi(sb)->si_wbr_mfs; + mfs->mfs_bindex = -EROFS; + mfs->mfsrr_bytes = 0; + dlgt = !!au_opt_test_dlgt(au_mntflags(sb)); + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + if (au_br_rdonly(br)) + continue; + arg = au_wbr_statfs_arg(br, sb, bindex); + if (!arg) + continue; + + err = vfsub_statfs(arg, &st, dlgt); + LKTRTrace("b%d, %d, %Lu\n", + bindex, err, (unsigned long long)st.f_bavail); + if (unlikely(err)) { + AuWarn1("failed statfs, b%d, %d\n", bindex, err); + continue; + } + + /* when the available size is equal, select lower one */ + b = st.f_bavail * st.f_bsize; + br->br_bytes = b; + if (b >= bavail) { + bavail = b; + mfs->mfs_bindex = bindex; + mfs->mfs_jiffy = jiffies; + } + } + + mfs->mfsrr_bytes = bavail; + LKTRTrace("b%d\n", mfs->mfs_bindex); +} + +static int au_wbr_create_mfs(struct dentry *dentry, int isdir) +{ + int err; + struct super_block *sb; + struct au_wbr_mfs *mfs; + + //au_debug_on(); + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + sb = dentry->d_sb; + err = au_wbr_create_exp(dentry); + if (err >= 0) + goto out; + + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + if (unlikely(time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) + || mfs->mfs_bindex < 0 + || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex)))) + au_mfs(dentry); + mutex_unlock(&mfs->mfs_lock); + err = mfs->mfs_bindex; + + out: + LKTRTrace("b%d\n", err); + //au_debug_off(); + return err; +} + +static int au_wbr_create_init_mfs(struct super_block *sb) +{ + struct au_wbr_mfs *mfs; + + mfs = &au_sbi(sb)->si_wbr_mfs; + LKTRTrace("expire %lu\n", mfs->mfs_expire); + + mutex_init(&mfs->mfs_lock); + mfs->mfs_jiffy = 0; + mfs->mfs_bindex = -EROFS; + + return 0; +} + +static int au_wbr_create_fin_mfs(struct super_block *sb) +{ + AuTraceEnter(); + mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); + return 0; +} + +/* ---------------------------------------------------------------------- */ + +/* most free space and then round robin */ +static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir) +{ + int err; + struct au_wbr_mfs *mfs; + + //au_debug_on(); + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), isdir); + + err = au_wbr_create_mfs(dentry, isdir); + if (err >= 0) { + mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; + LKTRTrace("%Lu bytes, %Lu wmark\n", + mfs->mfsrr_bytes, mfs->mfsrr_watermark); + if (unlikely(mfs->mfsrr_bytes < mfs->mfsrr_watermark)) + err = au_wbr_create_rr(dentry, isdir); + } + + LKTRTrace("b%d\n", err); + //au_debug_off(); + return err; +} + +static int au_wbr_create_init_mfsrr(struct super_block *sb) +{ + int err; + //au_debug_on(); + au_wbr_create_init_mfs(sb); /* ignore */ + err = au_wbr_create_init_rr(sb); + //au_debug_off(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* top down parent and most free space */ +static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) +{ + int err, e2, dirperm1; + struct super_block *sb; + struct dentry *parent, *h_parent; + aufs_bindex_t bindex, bstart, bend; + struct au_branch *br; + u64 b; + struct inode *h_dir; + + //au_debug_on(); + LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), isdir); + + err = au_wbr_create_tdp(dentry, isdir); + if (unlikely(err < 0)) + goto out; + parent = dget_parent(dentry); + bstart = au_dbstart(parent); + bend = au_dbtaildir(parent); + if (bstart == bend) + goto out_parent; /* success */ + + e2 = au_wbr_create_mfs(dentry, isdir); + if (e2 < 0) + goto out_parent; /* success */ + + /* when the available size is equal, select upper one */ + sb = dentry->d_sb; + br = au_sbr(sb, err); + dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb)); + b = br->br_bytes; + LKTRTrace("b%d, %Lu\n", err, b); + + if (unlikely(dirperm1)) { + for (bindex = bstart; bindex <= bend; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent) + continue; + h_dir = h_parent->d_inode; + if (!h_dir) + continue; + + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br) + && au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC, + /*dlgt*/0) + && br->br_bytes > b) { + b = br->br_bytes; + err = bindex; + LKTRTrace("b%d, %Lu\n", err, b); + } + } + if (err >= 0) + goto out_parent; + } + for (bindex = bstart; bindex <= bend; bindex++) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || !h_parent->d_inode) + continue; + + br = au_sbr(sb, bindex); + if (!au_br_rdonly(br) && br->br_bytes > b) { + b = br->br_bytes; + err = bindex; + LKTRTrace("b%d, %Lu\n", err, b); + } + } + + out_parent: + dput(parent); + out: + LKTRTrace("b%d\n", err); + //au_debug_off(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* policies for copyup */ + +/* top down parent */ +static int au_wbr_copyup_tdp(struct dentry *dentry) +{ + return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0); +} + +/* bottom up parent */ +static int au_wbr_copyup_bup(struct dentry *dentry) +{ + int err, dirperm1; + struct dentry *parent, *h_parent; + aufs_bindex_t bindex, bstart; + struct super_block *sb; + struct inode *h_dir; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + err = -EROFS; + sb = dentry->d_sb; + dirperm1 = !!au_opt_test_dirperm1(au_mntflags(sb)); + parent = dget_parent(dentry); + bstart = au_dbstart(parent); + for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent) + continue; + h_dir = h_parent->d_inode; + if (!h_dir) + continue; + + if (!au_br_rdonly(au_sbr(sb, bindex)) + && (!dirperm1 + || au_test_h_perm(h_dir, MAY_WRITE | MAY_EXEC, + /*dlgt*/0))) { + err = bindex; + break; + } + } + dput(parent); + + /* bottom up here */ + if (unlikely(err < 0)) + err = au_wbr_bu(sb, bstart - 1); + + LKTRTrace("b%d\n", err); + return err; +} + +/* bottom up */ +static int au_wbr_copyup_bu(struct dentry *dentry) +{ + int err; + + LKTRTrace("%.*s\n", AuDLNPair(dentry)); + + err = au_wbr_bu(dentry->d_sb, au_dbstart(dentry)); + + LKTRTrace("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct au_wbr_copyup_operations au_wbr_copyup_ops[] = { + [AuWbrCopyup_TDP] = { + .copyup = au_wbr_copyup_tdp + }, + [AuWbrCopyup_BUP] = { + .copyup = au_wbr_copyup_bup + }, + [AuWbrCopyup_BU] = { + .copyup = au_wbr_copyup_bu + } +}; + +struct au_wbr_create_operations au_wbr_create_ops[] = { + [AuWbrCreate_TDP] = { + .create = au_wbr_create_tdp + }, + [AuWbrCreate_RR] = { + .create = au_wbr_create_rr, + .init = au_wbr_create_init_rr + }, + [AuWbrCreate_MFS] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSV] = { + .create = au_wbr_create_mfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRR] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_MFSRRV] = { + .create = au_wbr_create_mfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFS] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSV] = { + .create = au_wbr_create_pmfs, + .init = au_wbr_create_init_mfs, + .fin = au_wbr_create_fin_mfs + } +}; diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/whout.c linux-2.6.25.4-unionfs/fs/aufs/whout.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/whout.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/whout.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,1052 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * whiteout for logical deletion and opaque directory + * + * $Id: whout.c,v 1.4 2008/05/12 00:27:58 sfjro Exp $ + */ + +#include +#include +//#include +//#include +#include "aufs.h" + +#define WH_MASK S_IRUGO + +/* If a directory contains this file, then it is opaque. We start with the + * .wh. flag so that it is blocked by lookup. + */ +static struct qstr diropq_name = { + .name = AUFS_WH_DIROPQ, + .len = sizeof(AUFS_WH_DIROPQ) - 1 +}; + +/* + * generate whiteout name, which is NOT terminated by NULL. + * @name: original d_name.name + * @len: original d_name.len + * @wh: whiteout qstr + * returns zero when succeeds, otherwise error. + * succeeded value as wh->name should be freed by au_wh_name_free(). + */ +int au_wh_name_alloc(const char *name, int len, struct qstr *wh) +{ + char *p; + + AuDebugOn(!name || !len || !wh); + + if (unlikely(len > PATH_MAX - AUFS_WH_PFX_LEN)) + return -ENAMETOOLONG; + + wh->len = len + AUFS_WH_PFX_LEN; + p = kmalloc(wh->len, GFP_KERNEL); + wh->name = p; + //if (LktrCond) {kfree(p); wh->name = p = NULL;} + if (p) { + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + memcpy(p + AUFS_WH_PFX_LEN, name, len); + //smp_mb(); + return 0; + } + return -ENOMEM; +} + +void au_wh_name_free(struct qstr *wh) +{ + AuDebugOn(!wh || !wh->name); + kfree(wh->name); +#ifdef CONFIG_AUFS_DEBUG + wh->name = NULL; +#endif +} + +/* ---------------------------------------------------------------------- */ + +/* + * test if the @wh_name exists under @h_parent. + * @try_sio specifies the necessary of super-io. + */ +int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio, + struct au_ndx *ndx) +{ + int err; + struct dentry *wh_dentry; + struct inode *h_dir; + unsigned int flags; + + LKTRTrace("%.*s/%.*s, ndx{%p, 0x%x}\n", AuDLNPair(h_parent), + wh_name->len, wh_name->name, ndx->nfsmnt, ndx->flags); + h_dir = h_parent->d_inode; + AuDebugOn(!S_ISDIR(h_dir->i_mode)); + + flags = 0; + if (ndx && ndx->nd) { + flags = ndx->nd->flags; + ndx->nd->flags &= ~(LOOKUP_OPEN | LOOKUP_CREATE); + } + + if (!try_sio) + wh_dentry = au_lkup_one(wh_name->name, h_parent, + wh_name->len, ndx); + else + wh_dentry = au_sio_lkup_one(wh_name->name, h_parent, + wh_name->len, ndx); + if (flags) + ndx->nd->flags = flags; + //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);} + err = PTR_ERR(wh_dentry); + if (IS_ERR(wh_dentry)) + goto out; + + err = 0; + if (!wh_dentry->d_inode) + goto out_wh; /* success */ + + err = 1; + if (S_ISREG(wh_dentry->d_inode->i_mode)) + goto out_wh; /* success */ + + err = -EIO; + AuIOErr("%.*s Invalid whiteout entry type 0%o.\n", + AuDLNPair(wh_dentry), wh_dentry->d_inode->i_mode); + + out_wh: + dput(wh_dentry); + out: + AuTraceErr(err); + return err; +} + +/* + * test if the @h_dentry sets opaque or not. + */ +int au_diropq_test(struct dentry *h_dentry, struct au_ndx *ndx) +{ + int err, try_sio; + struct inode *h_dir; + + LKTRTrace("dentry %.*s\n", AuDLNPair(h_dentry)); + h_dir = h_dentry->d_inode; + AuDebugOn(!S_ISDIR(h_dir->i_mode)); + + try_sio = au_test_h_perm_sio(h_dir, MAY_EXEC, + au_ftest_ndx(ndx->flags, DLGT)); + err = au_wh_test(h_dentry, &diropq_name, try_sio, ndx); + AuTraceErr(err); + return err; +} + +/* + * returns a negative dentry whose name is unique and temporary. + */ +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct qstr *prefix, + struct au_ndx *ndx) +{ +#define HEX_LEN 4 + struct dentry *dentry; + int len, i; + char defname[AUFS_WH_PFX_LEN * 2 + DNAME_INLINE_LEN_MIN + 1 + + HEX_LEN + 1], *name, *p; + static unsigned char cnt; + + LKTRTrace("hp %.*s, prefix %.*s\n", + AuDLNPair(h_parent), prefix->len, prefix->name); + AuDebugOn(!h_parent->d_inode); + + name = defname; + len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1; + if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) { + dentry = ERR_PTR(-ENAMETOOLONG); + if (unlikely(len >= PATH_MAX)) + goto out; + dentry = ERR_PTR(-ENOMEM); + name = kmalloc(len + 1, GFP_KERNEL); + //if (LktrCond) {kfree(name); name = NULL;} + if (unlikely(!name)) + goto out; + } + + /* doubly whiteout-ed */ + memcpy(name, AUFS_WH_PFX AUFS_WH_PFX, AUFS_WH_PFX_LEN * 2); + p = name + AUFS_WH_PFX_LEN * 2; + memcpy(p, prefix->name, prefix->len); + p += prefix->len; + *p++ = '.'; + AuDebugOn(name + len + 1 - p <= HEX_LEN); + + for (i = 0; i < 3; i++) { + sprintf(p, "%.*d", HEX_LEN, cnt++); + dentry = au_sio_lkup_one(name, h_parent, len, ndx); + //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);} + if (IS_ERR(dentry) || !dentry->d_inode) + goto out_name; + dput(dentry); + } + /* AuWarn("could not get random name\n"); */ + dentry = ERR_PTR(-EEXIST); + AuDbg("%.*s\n", len, name); + BUG(); + + out_name: + if (unlikely(name != defname)) + kfree(name); + out: + AuTraceErrPtr(dentry); + return dentry; +#undef HEX_LEN +} + +/* + * rename the @dentry of @bindex to the whiteouted temporary name. + */ +int au_whtmp_ren(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex, + int noself) +{ + int err, dlgt; + struct inode *h_dir; + struct dentry *h_dentry, *h_parent, *tmp_dentry; + struct super_block *sb; + unsigned int mnt_flags; + struct au_hin_ignore ign; + struct vfsub_args vargs; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex); + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry || !h_dentry->d_inode); + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + + sb = dentry->d_sb; + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + if (unlikely(dlgt)) + au_fset_ndx(ndx.flags, DLGT); + ndx.nfsmnt = au_nfsmnt(sb, bindex); + tmp_dentry = au_whtmp_lkup(h_parent, &h_dentry->d_name, &ndx); + //if (LktrCond) {dput(tmp_dentry); tmp_dentry = ERR_PTR(-1);} + err = PTR_ERR(tmp_dentry); + if (!IS_ERR(tmp_dentry)) { + /* under the same dir, no need to lock_rename() */ + vfsub_args_init(&vargs, &ign, dlgt, 0); + AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) && !noself)) + vfsub_ign_hinode(&vargs, IN_MOVE_SELF, + au_hi(dentry->d_inode, bindex)); + err = vfsub_rename(h_dir, h_dentry, h_dir, tmp_dentry, &vargs); + //if (LktrCond) err = -1; //unavailable + AuTraceErr(err); + dput(tmp_dentry); + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static int do_unlink_wh(struct inode *h_dir, struct dentry *wh_dentry, + struct inode *dir, int dlgt) +{ + struct vfsub_args vargs; + + LKTRTrace("hi%lu, wh %.*s\n", h_dir->i_ino, AuDLNPair(wh_dentry)); + AuDebugOn(!wh_dentry->d_inode || !S_ISREG(wh_dentry->d_inode->i_mode)); + + /* + * forces superio when the dir has a sticky bit. + * this may be a violation of unix fs semantics. + */ + vfsub_args_init(&vargs, NULL, dlgt, + (h_dir->i_mode & S_ISVTX) + && wh_dentry->d_inode->i_uid != current->fsuid); + return vfsub_unlink(h_dir, wh_dentry, &vargs); +} + +int au_wh_unlink_dentry(struct inode *h_dir, struct dentry *wh_dentry, + struct dentry *dentry, struct inode *dir, int dlgt) +{ + int err; + + LKTRTrace("hi%lu, wh %.*s, d %p\n", h_dir->i_ino, + AuDLNPair(wh_dentry), dentry); + AuDebugOn((dentry && au_dbwh(dentry) < 0) + || !wh_dentry->d_inode + || !S_ISREG(wh_dentry->d_inode->i_mode)); + + err = do_unlink_wh(h_dir, wh_dentry, dir, dlgt); + //if (LktrCond) err = -1; // unavailable + if (!err && dentry) + au_set_dbwh(dentry, -1); + + AuTraceErr(err); + return err; +} + +static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, + struct inode *dir, struct au_ndx *ndx) +{ + int err; + struct inode *h_dir; + struct dentry *h_dentry; + + LKTRTrace("%.*s/%.*s\n", AuDLNPair(h_parent), AuLNPair(wh)); + h_dir = h_parent->d_inode; + + /* au_test_h_perm() is already done */ + h_dentry = au_lkup_one(wh->name, h_parent, wh->len, ndx); + //if (LktrCond) {dput(h_dentry); h_dentry = ERR_PTR(-1);} + if (!IS_ERR(h_dentry)) { + err = 0; + if (h_dentry->d_inode && S_ISREG(h_dentry->d_inode->i_mode)) + err = do_unlink_wh(h_dir, h_dentry, dir, + au_ftest_ndx(ndx->flags, DLGT)); + dput(h_dentry); + } else + err = PTR_ERR(h_dentry); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static void clean_wh(struct inode *h_dir, struct dentry *wh) +{ + int err; + struct vfsub_args vargs; + + AuTraceEnter(); + + if (wh->d_inode) { + vfsub_args_init(&vargs, NULL, 0, 0); + err = vfsub_unlink(h_dir, wh, &vargs); + if (unlikely(err)) + AuWarn("failed unlink %.*s (%d), ignored.\n", + AuDLNPair(wh), err); + } +} + +static void clean_plink(struct inode *h_dir, struct dentry *plink) +{ + int err; + struct vfsub_args vargs; + + AuTraceEnter(); + + if (plink->d_inode) { + vfsub_args_init(&vargs, NULL, 0, 0); + err = vfsub_rmdir(h_dir, plink, &vargs); + if (unlikely(err)) + AuWarn("failed rmdir %.*s (%d), ignored.\n", + AuDLNPair(plink), err); + } +} + +static int test_linkable(struct inode *h_dir) +{ + if (h_dir->i_op && h_dir->i_op->link) + return 0; + return -ENOSYS; +} + +static int plink_dir(struct inode *h_dir, struct dentry *plink) +{ + int err; + + err = -EEXIST; + if (!plink->d_inode) { + int mode = S_IRWXU; + if (unlikely(au_test_nfs(plink->d_sb))) + mode |= S_IXUGO; + err = vfsub_mkdir(h_dir, plink, mode, /*dlgt*/0); + } else if (S_ISDIR(plink->d_inode->i_mode)) + err = 0; + else + AuErr("unknown %.*s exists\n", AuDLNPair(plink)); + + return err; +} + +/* + * initialize the whiteout base file/dir for @br. + */ +int au_wh_init(struct dentry *h_root, struct au_branch *br, + struct vfsmount *nfsmnt, struct super_block *sb) +{ + int err; + struct dentry *wh, *plink; + struct inode *h_dir; + static struct qstr base_name[] = { + { + .name = AUFS_WH_BASENAME, + .len = sizeof(AUFS_WH_BASENAME) - 1 + }, + { + .name = AUFS_WH_PLINKDIR, + .len = sizeof(AUFS_WH_PLINKDIR) - 1 + } + }; + struct au_ndx ndx = { + .nfsmnt = nfsmnt, + .flags = 0, /* always no dlgt */ + .nd = NULL, + //.br = NULL + }; + const int do_plink = au_opt_test(au_mntflags(sb), PLINK); + + LKTRTrace("nfsmnt %p\n", nfsmnt); + BrWhMustWriteLock(br); + SiMustWriteLock(sb); + h_dir = h_root->d_inode; + + /* doubly whiteouted */ + wh = au_wh_lkup(h_root, base_name + 0, &ndx); + //if (LktrCond) {dput(wh); wh = ERR_PTR(-1);} + err = PTR_ERR(wh); + if (IS_ERR(wh)) + goto out; + AuDebugOn(br->br_wh && br->br_wh != wh); + + plink = au_wh_lkup(h_root, base_name + 1, &ndx); + err = PTR_ERR(plink); + if (IS_ERR(plink)) + goto out_dput_wh; + AuDebugOn(br->br_plink && br->br_plink != plink); + + dput(br->br_wh); + dput(br->br_plink); + br->br_wh = NULL; + br->br_plink = NULL; + + err = 0; + switch (br->br_perm) { + case AuBr_RR: + case AuBr_RO: + case AuBr_RRWH: + case AuBr_ROWH: + clean_wh(h_dir, wh); + clean_plink(h_dir, plink); + break; + + case AuBr_RWNoLinkWH: + clean_wh(h_dir, wh); + if (do_plink) { + err = test_linkable(h_dir); + if (unlikely(err)) + goto out_nolink; + + err = plink_dir(h_dir, plink); + if (unlikely(err)) + goto out_err; + br->br_plink = dget(plink); + } else + clean_plink(h_dir, plink); + break; + + case AuBr_RW: + /* + * for the moment, aufs supports the branch filesystem + * which does not support link(2). + * testing on FAT which does not support i_op->setattr() fully + * either, copyup failed. + * finally, such filesystem will not be used as the writable + * branch. + */ + err = test_linkable(h_dir); + if (unlikely(err)) + goto out_nolink; + + err = -EEXIST; + if (!wh->d_inode) + err = au_h_create(h_dir, wh, WH_MASK, /*dlgt*/0, + /*nd*/NULL, nfsmnt); + else if (S_ISREG(wh->d_inode->i_mode)) + err = 0; + else + AuErr("unknown %.*s/%.*s exists\n", + AuDLNPair(h_root), AuDLNPair(wh)); + if (unlikely(err)) + goto out_err; + + if (do_plink) { + err = plink_dir(h_dir, plink); + if (unlikely(err)) + goto out_err; + br->br_plink = dget(plink); + } else + clean_plink(h_dir, plink); + br->br_wh = dget(wh); + break; + + default: + BUG(); + } + + out_dput: + dput(plink); + out_dput_wh: + dput(wh); + out: + AuTraceErr(err); + return err; + out_nolink: + AuErr("%.*s doesn't support link(2), use noplink and rw+nolwh\n", + AuDLNPair(h_root)); + goto out_dput; + out_err: + AuErr("an error(%d) on the writable branch %.*s(%s)\n", + err, AuDLNPair(h_root), au_sbtype(h_root->d_sb)); + goto out_dput; +} + +struct reinit_br_wh { + struct super_block *sb; + struct au_branch *br; +}; + +static void reinit_br_wh(void *arg) +{ + int err; + struct reinit_br_wh *a = arg; + struct inode *h_dir, *dir; + struct dentry *h_root; + aufs_bindex_t bindex; + struct vfsub_args vargs; + + AuTraceEnter(); + AuDebugOn(!a->br->br_wh || !a->br->br_wh->d_inode || current->fsuid); + + err = 0; + /* big aufs lock */ + si_write_lock(a->sb); + if (unlikely(!au_br_writable(a->br->br_perm))) + goto out; + bindex = au_br_index(a->sb, a->br->br_id); + if (unlikely(bindex < 0)) + goto out; + + dir = a->sb->s_root->d_inode; + h_root = dget_parent(a->br->br_wh); + h_dir = h_root->d_inode; + AuDebugOn(!h_dir->i_op || !h_dir->i_op->link); + vfsub_args_init(&vargs, NULL, /*dlgt*/0, 0); + au_hdir_lock(h_dir, dir, bindex); + br_wh_write_lock(a->br); + err = vfsub_unlink(h_dir, a->br->br_wh, &vargs); + //if (LktrCond) err = -1; + dput(a->br->br_wh); + a->br->br_wh = NULL; + if (!err) + err = au_wh_init(h_root, a->br, au_do_nfsmnt(a->br->br_mnt), + a->sb); + br_wh_write_unlock(a->br); + au_hdir_unlock(h_dir, dir, bindex); + dput(h_root); + + out: + atomic_dec_return(&a->br->br_wh_running); + au_br_put(a->br); + si_write_unlock(a->sb); + kfree(arg); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) +{ + int do_dec, wkq_err; + struct reinit_br_wh *arg; + + do_dec = 1; + if (atomic_inc_return(&br->br_wh_running) != 1) + goto out; + + /* ignore ENOMEM */ + arg = kmalloc(sizeof(*arg), GFP_TEMPORARY); + if (arg) { + /* + * dec(wh_running), kfree(arg) and au_br_put() + * in reinit function + */ + arg->sb = sb; + arg->br = br; + au_br_get(br); + wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb, /*dlgt*/0); + if (unlikely(wkq_err)) { + atomic_dec_return(&br->br_wh_running); + au_br_put(br); + kfree(arg); + } + do_dec = 0; + } + + out: + if (do_dec) + atomic_dec_return(&br->br_wh_running); +} + +/* + * create the whiteout @wh. + */ +static int link_or_create_wh(struct dentry *wh, struct super_block *sb, + aufs_bindex_t bindex, struct inode *dir) +{ + int err, dlgt; + struct au_branch *br; + struct dentry *h_parent; + struct inode *h_dir; + + LKTRTrace("%.*s\n", AuDLNPair(wh)); + SiMustReadLock(sb); + h_parent = wh->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + + dlgt = !!au_opt_test_dlgt(au_mntflags(sb)); + br = au_sbr(sb, bindex); + br_wh_read_lock(br); + if (br->br_wh) { + err = vfsub_link(br->br_wh, h_dir, wh, dlgt); + if (!err || err != -EMLINK) + goto out; + + /* link count full. re-initialize br_wh. */ + kick_reinit_br_wh(sb, br); + } + + /* return this error in this context */ + err = au_h_create(h_dir, wh, WH_MASK, dlgt, /*nd*/NULL, + au_do_nfsmnt(br->br_mnt)); + + out: + br_wh_read_unlock(br); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * create or remove the diropq. + */ +static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *opq_dentry, *h_dentry; + struct inode *h_dir; + int err, dlgt; + struct super_block *sb; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + LKTRTrace("%.*s, bindex %d, flags 0x%x\n", + AuDLNPair(dentry), bindex, flags); + h_dentry = au_h_dptr(dentry, bindex); + AuDebugOn(!h_dentry); + h_dir = h_dentry->d_inode; + AuDebugOn(!h_dir || !S_ISDIR(h_dir->i_mode)); + + /* already checked by au_test_h_perm(). */ + sb = dentry->d_sb; + ndx.nfsmnt = au_nfsmnt(sb, bindex); + dlgt = 0; + if (unlikely(au_ftest_diropq(flags, DLGT))) { + dlgt = 1; + au_fset_ndx(ndx.flags, DLGT); + } + opq_dentry = au_lkup_one(diropq_name.name, h_dentry, diropq_name.len, + &ndx); + //if (LktrCond) {dput(opq_dentry); opq_dentry = ERR_PTR(-1);} + if (IS_ERR(opq_dentry)) + goto out; + + if (au_ftest_diropq(flags, CREATE)) { + AuDebugOn(opq_dentry->d_inode); + err = link_or_create_wh(opq_dentry, sb, bindex, + dentry->d_inode); + //if (LktrCond) {vfs_unlink(h_dir, opq_dentry); err = -1;} + if (!err) { + au_set_dbdiropq(dentry, bindex); + goto out; /* success */ + } + } else { + AuDebugOn(/* !S_ISDIR(dentry->d_inode->i_mode) + * || */!opq_dentry->d_inode); + err = do_unlink_wh(h_dir, opq_dentry, dentry->d_inode, dlgt); + //if (LktrCond) err = -1; + if (!err) + au_set_dbdiropq(dentry, -1); + } + dput(opq_dentry); + opq_dentry = ERR_PTR(err); + + out: + AuTraceErrPtr(opq_dentry); + return opq_dentry; +} + +struct do_diropq_args { + struct dentry **errp; + struct dentry *dentry; + aufs_bindex_t bindex; + unsigned int flags; +}; + +static void call_do_diropq(void *args) +{ + struct do_diropq_args *a = args; + *a->errp = do_diropq(a->dentry, a->bindex, a->flags); +} + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags) +{ + struct dentry *diropq, *h_dentry; + + LKTRTrace("%.*s, bindex %d, flags 0x%x\n", + AuDLNPair(dentry), bindex, flags); + + h_dentry = au_h_dptr(dentry, bindex); + if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE, + au_ftest_diropq(flags, DLGT))) + diropq = do_diropq(dentry, bindex, flags); + else { + int wkq_err; + struct do_diropq_args args = { + .errp = &diropq, + .dentry = dentry, + .bindex = bindex, + .flags = flags + }; + wkq_err = au_wkq_wait(call_do_diropq, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + diropq = ERR_PTR(wkq_err); + } + + AuTraceErrPtr(diropq); + return diropq; +} + +/* ---------------------------------------------------------------------- */ + +/* + * lookup whiteout dentry. + * @h_parent: hidden parent dentry which must exist and be locked + * @base_name: name of dentry which will be whiteouted + * returns dentry for whiteout. + */ +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_ndx *ndx) +{ + int err; + struct qstr wh_name; + struct dentry *wh_dentry; + + LKTRTrace("%.*s/%.*s\n", AuDLNPair(h_parent), AuLNPair(base_name)); + + err = au_wh_name_alloc(base_name->name, base_name->len, &wh_name); + //if (LktrCond) {au_wh_name_free(&wh_name); err = -1;} + wh_dentry = ERR_PTR(err); + if (!err) { + /* do not superio. */ + wh_dentry = au_lkup_one(wh_name.name, h_parent, + wh_name.len, ndx); + au_wh_name_free(&wh_name); + } + AuTraceErrPtr(wh_dentry); + return wh_dentry; +} + +/* + * link/create a whiteout for @dentry on @bindex. + */ +struct dentry *au_wh_create(struct inode *dir, struct dentry *dentry, + aufs_bindex_t bindex, struct dentry *h_parent, + struct au_ndx *ndx) +{ + struct dentry *wh_dentry; + int err; + struct super_block *sb; + + LKTRTrace("%.*s/%.*s on b%d\n", AuDLNPair(h_parent), + AuDLNPair(dentry), bindex); + + sb = dentry->d_sb; + wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, ndx); + //au_nfsmnt(sb, bindex), need_dlgt(sb)); + //if (LktrCond) {dput(wh_dentry); wh_dentry = ERR_PTR(-1);} + if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { + err = link_or_create_wh(wh_dentry, sb, bindex, dir); + if (!err) + au_set_dbwh(dentry, bindex); + else { + dput(wh_dentry); + wh_dentry = ERR_PTR(err); + } + } + + AuTraceErrPtr(wh_dentry); + return wh_dentry; +} + +/* ---------------------------------------------------------------------- */ + +/* Delete all whiteouts in this directory on branch bindex. */ +static int del_wh_children(struct au_nhash *whlist, struct dentry *h_parent, + aufs_bindex_t bindex, struct inode *inode, + struct au_ndx *ndx) +{ + int err, i; + struct qstr wh_name; + char *p; + struct inode *h_dir; + struct hlist_head *head; + struct au_vdir_wh *tpos; + struct hlist_node *pos; + struct au_vdir_destr *str; + + LKTRTrace("%.*s\n", AuDLNPair(h_parent)); + h_dir = h_parent->d_inode; + AuDebugOn(IS_RDONLY(h_dir)); + //SiMustReadLock(??); + + err = -ENOMEM; + p = __getname(); + wh_name.name = p; + //if (LktrCond) {__putname(p); wh_name.name = p = NULL;} + if (unlikely(!wh_name.name)) + goto out; + memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); + p += AUFS_WH_PFX_LEN; + + /* already checked by au_test_h_perm(). */ + err = 0; + for (i = 0; !err && i < AuSize_NHASH; i++) { + head = whlist->heads + i; + hlist_for_each_entry(tpos, pos, head, wh_hash) { + if (tpos->wh_bindex != bindex) + continue; + str = &tpos->wh_str; + if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) { + memcpy(p, str->name, str->len); + wh_name.len = AUFS_WH_PFX_LEN + str->len; + err = unlink_wh_name(h_parent, &wh_name, inode, + ndx); + //if (LktrCond) err = -1; + if (!err) + continue; + break; + } + AuIOErr("whiteout name too long %.*s\n", + str->len, str->name); + err = -EIO; + break; + } + } + __putname(wh_name.name); + + out: + AuTraceErr(err); + return err; +} + +struct del_wh_children_args { + int *errp; + struct au_nhash *whlist; + struct dentry *h_parent; + aufs_bindex_t bindex; + struct inode *inode; + struct au_ndx *ndx; +}; + +static void call_del_wh_children(void *args) +{ + struct del_wh_children_args *a = args; + *a->errp = del_wh_children(a->whlist, a->h_parent, a->bindex, + a->inode, a->ndx); +} + +/* ---------------------------------------------------------------------- */ + +/* + * rmdir the whiteouted temporary named dir @h_dentry. + * @whlist: whiteouted children. + */ +int au_whtmp_rmdir(struct dentry *h_dentry, struct au_nhash *whlist, + aufs_bindex_t bindex, struct inode *dir, struct inode *inode, + int noself) +{ + int err, dlgt; + struct inode *h_inode, *h_dir; + struct super_block *sb; + unsigned int mnt_flags; + struct au_hin_ignore ign; + struct vfsub_args vargs; + struct au_ndx ndx = { + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + LKTRTrace("hd %.*s, b%d, i%lu\n", + AuDLNPair(h_dentry), bindex, dir->i_ino); + IMustLock(dir); + IiMustAnyLock(dir); + h_dir = h_dentry->d_parent->d_inode; /* dir inode is locked */ + IMustLock(h_dir); + + sb = inode->i_sb; + mnt_flags = au_mntflags(sb); + dlgt = !!au_opt_test_dlgt(mnt_flags); + if (unlikely(dlgt)) + au_fset_ndx(ndx.flags, DLGT); + ndx.nfsmnt = au_nfsmnt(sb, bindex); + h_inode = h_dentry->d_inode; + AuDebugOn(h_inode != au_h_iptr(inode, bindex)); + au_hdir2_lock(h_inode, inode, bindex); + + /* + * someone else might change some whiteouts while we were sleeping. + * it means this whlist may have an obsoleted entry. + */ + if (!au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_WRITE, dlgt)) + err = del_wh_children(whlist, h_dentry, bindex, inode, &ndx); + else { + int wkq_err; + /* ugly */ + unsigned int flags = ndx.flags; + struct del_wh_children_args args = { + .errp = &err, + .whlist = whlist, + .h_parent = h_dentry, + .bindex = bindex, + .inode = inode, + .ndx = &ndx + }; + + ndx.flags = 0; + wkq_err = au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + ndx.flags = flags; + } + au_hdir_unlock(h_inode, inode, bindex); + + if (!err) { + vfsub_args_init(&vargs, &ign, dlgt, 0); + if (unlikely(au_opt_test(mnt_flags, UDBA_INOTIFY) && !noself)) + vfsub_ign_hinode(&vargs, IN_DELETE_SELF, + au_hi(inode, bindex)); + err = vfsub_rmdir(h_dir, h_dentry, &vargs); + //d_drop(h_dentry); + //if (LktrCond) err = -1; + } + + if (!err) { + if (au_ibstart(dir) == bindex) { + au_cpup_attr_timesizes(dir); + //au_cpup_attr_nlink(dir); + drop_nlink(dir); + } + return 0; /* success */ + } + + AuWarn("failed removing %.*s(%d), ignored\n", AuDLNPair(h_dentry), err); + return err; +} + +static void au_whtmp_rmdir_free_args(struct au_whtmp_rmdir_args *args) +{ + dput(args->h_dentry); + au_nhash_fin(&args->whlist); + iput(args->inode); + mutex_unlock(&args->dir->i_mutex); + iput(args->dir); + kfree(args); +} + +static void do_rmdir_whtmp(void *args) +{ + int err; + struct au_whtmp_rmdir_args *a = args; + struct super_block *sb; + + LKTRTrace("%.*s, b%d, dir i%lu\n", + AuDLNPair(a->h_dentry), a->bindex, a->dir->i_ino); + + mutex_lock(&a->dir->i_mutex); + sb = a->dir->i_sb; + //AuDbgSleep(3); + si_noflush_read_lock(sb); + err = au_test_ro(sb, a->bindex, NULL); + if (!err) { + struct dentry *h_parent = dget_parent(a->h_dentry); + struct inode *h_dir = h_parent->d_inode; + + ii_write_lock_child(a->inode); + ii_write_lock_parent(a->dir); + au_hdir_lock(h_dir, a->dir, a->bindex); + err = au_whtmp_rmdir(a->h_dentry, &a->whlist, a->bindex, + a->dir, a->inode, a->noself); + au_hdir_unlock(h_dir, a->dir, a->bindex); + ii_write_unlock(a->dir); + ii_write_unlock(a->inode); + dput(h_parent); + } + si_read_unlock(sb); + au_whtmp_rmdir_free_args(a); + if (unlikely(err)) + AuIOErr("err %d\n", err); +} + +void au_whtmp_kick_rmdir(struct dentry *h_dentry, struct au_nhash *whlist, + aufs_bindex_t bindex, struct inode *dir, + struct inode *inode, int noself, + struct au_whtmp_rmdir_args *args) +{ + int wkq_err; + + LKTRTrace("%.*s\n", AuDLNPair(h_dentry)); + IMustLock(dir); + + /* all post-process will be done in do_rmdir_whtmp(). */ + args->h_dentry = dget(h_dentry); + au_nhash_init(&args->whlist); + au_nhash_move(&args->whlist, whlist); + args->bindex = bindex; + args->dir = igrab(dir); + args->inode = igrab(inode); + args->noself = noself; + wkq_err = au_wkq_nowait(do_rmdir_whtmp, args, dir->i_sb, /*dlgt*/0); + if (unlikely(wkq_err)) { + AuWarn("rmdir error %.*s (%d), ignored\n", + AuDLNPair(h_dentry), wkq_err); + au_whtmp_rmdir_free_args(args); + } +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/whout.h linux-2.6.25.4-unionfs/fs/aufs/whout.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/whout.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/whout.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * whiteout for logical deletion and opaque directory + * + * $Id: whout.h,v 1.2 2008/04/21 02:00:37 sfjro Exp $ + */ + +#ifndef __AUFS_WHOUT_H__ +#define __AUFS_WHOUT_H__ + +#ifdef __KERNEL__ + +#include +#include +#include "dir.h" +#include "opts.h" +#include "super.h" + +int au_wh_name_alloc(const char *name, int len, struct qstr *wh); +void au_wh_name_free(struct qstr *wh); + +struct au_ndx; +int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio, + struct au_ndx *ndx); +int au_diropq_test(struct dentry *h_dentry, struct au_ndx *ndx); + +struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct qstr *prefix, + struct au_ndx *ndx); +int au_whtmp_ren(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex, + int noself); +int au_wh_unlink_dentry(struct inode *h_dir, struct dentry *wh_dentry, + struct dentry *dentry, struct inode *dir, int dlgt); + +struct au_branch; +int au_wh_init(struct dentry *h_parent, struct au_branch *br, + struct vfsmount *nfsmnt, struct super_block *sb); + +/* diropq flags */ +#define AuDiropq_CREATE 1 +#define AuDiropq_DLGT (1 << 1) +#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) +#define au_fset_diropq(flags, name) { (flags) |= AuDiropq_##name; } +#define au_fclr_diropq(flags, name) { (flags) &= ~AuDiropq_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuDiropq_DLGT +#define AuDiropq_DLGT 0 +#endif + +struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, + unsigned int flags); + +struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, + struct au_ndx *ndx); +struct dentry *au_wh_create(struct inode *dir, struct dentry *dentry, + aufs_bindex_t bindex, struct dentry *h_parent, + struct au_ndx *ndx); + +/* real rmdir the whiteout-ed dir */ +struct au_whtmp_rmdir_args { + struct dentry *h_dentry; + struct au_nhash whlist; + aufs_bindex_t bindex; + struct inode *dir, *inode; + int noself; +}; + +struct au_nhash; +int au_whtmp_rmdir(struct dentry *h_dentry, struct au_nhash *whlist, + aufs_bindex_t bindex, struct inode *dir, struct inode *inode, + int noself); +void au_whtmp_kick_rmdir(struct dentry *h_dentry, struct au_nhash *whlist, + aufs_bindex_t bindex, struct inode *dir, + struct inode *inode, int noself, + struct au_whtmp_rmdir_args *args); + +/* ---------------------------------------------------------------------- */ + +static inline +struct dentry *au_diropq_create(struct dentry *dentry, aufs_bindex_t bindex, + int dlgt) +{ + unsigned int flags = AuDiropq_CREATE; + if (unlikely(dlgt)) + au_fset_diropq(flags, DLGT); + return au_diropq_sio(dentry, bindex, flags); +} + +static inline +int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex, int dlgt) +{ + unsigned int flags = !AuDiropq_CREATE; + if (unlikely(dlgt)) + au_fset_diropq(flags, DLGT); + return PTR_ERR(au_diropq_sio(dentry, bindex, flags)); +} + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_ROBR +/* robr.c */ +int au_test_robr_wh(struct qstr *name, struct dentry *h_parent, + struct qstr *wh_name, int try_sio, struct au_ndx *ndx); +int au_test_robr_shwh(struct super_block *sb, const struct qstr *name); +#else +static inline +int au_test_robr_wh(struct qstr *name, struct dentry *h_parent, + struct qstr *wh_name, int try_sio, struct au_ndx *ndx) +{ + return au_wh_test(h_parent, wh_name, try_sio, ndx); +} + +static inline +int au_test_robr_shwh(struct super_block *sb, const struct qstr *name) +{ + if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) + && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) + return -EPERM; + return 0; +} +#endif /* CONFIG_AUFS_ROBR */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WHOUT_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/wkq.c linux-2.6.25.4-unionfs/fs/aufs/wkq.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/wkq.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/wkq.c 2008-05-25 11:56:08 +0300 @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * workqueue for asynchronous/super-io/delegated operations + * + * $Id: wkq.c,v 1.4 2008/05/04 23:53:38 sfjro Exp $ + */ + +#include +#include "aufs.h" + +struct au_wkq *au_wkq; + +struct au_cred { +#ifdef CONFIG_AUFS_DLGT + int umask; + uid_t fsuid; + gid_t fsgid; + kernel_cap_t cap_effective, cap_inheritable, cap_permitted; + //unsigned keep_capabilities:1; + //struct user_struct *user; + //struct fs_struct *fs; + //struct nsproxy *nsproxy; +#endif +}; + +struct au_wkinfo { + struct work_struct wk; + struct vfsmount *mnt; + + unsigned int flags; + struct au_cred cred; + + au_wkq_func_t func; + void *args; + + atomic_t *busyp; + struct completion *comp; +}; + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_DLGT +static void cred_store(struct au_cred *cred) +{ + cred->umask = current->fs->umask; + cred->fsuid = current->fsuid; + cred->fsgid = current->fsgid; + cred->cap_effective = current->cap_effective; + cred->cap_inheritable = current->cap_inheritable; + cred->cap_permitted = current->cap_permitted; +} + +static void cred_revert(struct au_cred *cred) +{ + AuDebugOn(!au_test_wkq(current)); + current->fs->umask = cred->umask; + current->fsuid = cred->fsuid; + current->fsgid = cred->fsgid; + current->cap_effective = cred->cap_effective; + current->cap_inheritable = cred->cap_inheritable; + current->cap_permitted = cred->cap_permitted; +} + +static void cred_switch(struct au_cred *old, struct au_cred *new) +{ + cred_store(old); + cred_revert(new); +} + +static void dlgt_cred_store(unsigned int flags, struct au_wkinfo *wkinfo) +{ + if (unlikely(au_ftest_wkq(flags, DLGT))) + cred_store(&wkinfo->cred); +} + +static void dlgt_func(struct au_wkinfo *wkinfo) +{ + if (!au_ftest_wkq(wkinfo->flags, DLGT)) + wkinfo->func(wkinfo->args); + else { + struct au_cred cred; + cred_switch(&cred, &wkinfo->cred); + wkinfo->func(wkinfo->args); + cred_revert(&cred); + } +} +#else +static void dlgt_cred_store(unsigned int flags, struct au_wkinfo *wkinfo) +{ + /* empty */ +} + +static void dlgt_func(struct au_wkinfo *wkinfo) +{ + wkinfo->func(wkinfo->args); +} +#endif /* CONFIG_AUFS_DLGT */ + +/* ---------------------------------------------------------------------- */ + +static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo) +{ +#ifdef CONFIG_AUFS_STAT + unsigned int new, old; + + do { + new = atomic_read(wkinfo->busyp); + old = wkq->max_busy; + if (new <= old) + break; + } while (cmpxchg(&wkq->max_busy, old, new) == old); +#endif +} + +static int enqueue(struct au_wkq *wkq, struct au_wkinfo *wkinfo) +{ + AuTraceEnter(); + wkinfo->busyp = &wkq->busy; + update_busy(wkq, wkinfo); + if (au_ftest_wkq(wkinfo->flags, WAIT)) + return !queue_work(wkq->q, &wkinfo->wk); + else + return !schedule_work(&wkinfo->wk); +} + +static void do_wkq(struct au_wkinfo *wkinfo) +{ + unsigned int idle, n; + int i, idle_idx; + + AuTraceEnter(); + + while (1) { + if (au_ftest_wkq(wkinfo->flags, WAIT)) { + idle_idx = 0; + idle = UINT_MAX; + for (i = 0; i < aufs_nwkq; i++) { + n = atomic_inc_return(&au_wkq[i].busy); + if (n == 1 && !enqueue(au_wkq + i, wkinfo)) + return; /* success */ + + if (n < idle) { + idle_idx = i; + idle = n; + } + atomic_dec_return(&au_wkq[i].busy); + } + } else + idle_idx = aufs_nwkq; + + atomic_inc_return(&au_wkq[idle_idx].busy); + if (!enqueue(au_wkq + idle_idx, wkinfo)) + return; /* success */ + + /* impossible? */ + AuWarn1("failed to queue_work()\n"); + yield(); + } +} + +static void wkq_func(struct work_struct *wk) +{ + struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); + + LKTRTrace("wkinfo{0x%x, %p, %p, %p}\n", + wkinfo->flags, wkinfo->func, wkinfo->busyp, wkinfo->comp); + + dlgt_func(wkinfo); + atomic_dec_return(wkinfo->busyp); + if (au_ftest_wkq(wkinfo->flags, WAIT)) + complete(wkinfo->comp); + else { + mntput(wkinfo->mnt); + module_put(THIS_MODULE); + kfree(wkinfo); + } +} + +int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags) +{ + int err; + //int queued; + //struct workqueue_struct *wkq; + DECLARE_COMPLETION_ONSTACK(comp); + struct au_wkinfo _wkinfo = { + //.sb = sb, + .flags = flags, + .func = func, + .args = args, + .comp = &comp + }, *wkinfo = &_wkinfo; + + LKTRTrace("0x%x\n", flags); + AuDebugOn(au_test_wkq(current)); + + err = 0; + if (unlikely(!au_ftest_wkq(flags, WAIT))) { + AuDebugOn(!sb); + /* + * wkq_func() must free this wkinfo. + * it highly depends upon the implementation of workqueue. + */ + err = -ENOMEM; + wkinfo = kmalloc(sizeof(*wkinfo), GFP_TEMPORARY); + if (unlikely(!wkinfo)) + goto out; + err = 0; + /* prohibit umount */ + wkinfo->mnt = au_mntcache_get(sb); + wkinfo->flags = flags; + wkinfo->func = func; + wkinfo->args = args; + wkinfo->comp = NULL; + __module_get(THIS_MODULE); + } + + INIT_WORK(&wkinfo->wk, wkq_func); + dlgt_cred_store(flags, wkinfo); + do_wkq(wkinfo); + if (au_ftest_wkq(flags, WAIT)) + /* no timeout, no interrupt */ + wait_for_completion(wkinfo->comp); + out: + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +void au_wkq_fin(void) +{ + int i; + + AuTraceEnter(); + + for (i = 0; i < aufs_nwkq; i++) + if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) + destroy_workqueue(au_wkq[i].q); + kfree(au_wkq); +} + +int __init au_wkq_init(void) +{ + int err, i; + struct au_wkq *nowaitq; + + LKTRTrace("%d\n", aufs_nwkq); + + /* '+1' is for accounting of nowait queue */ + err = -ENOMEM; + au_wkq = kcalloc(aufs_nwkq + 1, sizeof(*au_wkq), GFP_KERNEL); + if (unlikely(!au_wkq)) + goto out; + + err = 0; + for (i = 0; i < aufs_nwkq; i++) { + au_wkq[i].q = create_singlethread_workqueue(AUFS_WKQ_NAME); + if (au_wkq[i].q && !IS_ERR(au_wkq[i].q)) { + atomic_set(&au_wkq[i].busy, 0); + au_wkq[i].max_busy = 0; + continue; + } + + err = PTR_ERR(au_wkq[i].q); + au_wkq_fin(); + break; + } + + /* nowait accounting */ + nowaitq = au_wkq + aufs_nwkq; + atomic_set(&nowaitq->busy, 0); + nowaitq->max_busy = 0; + nowaitq->q = NULL; + //smp_mb(); /* atomic_set */ + + out: + AuTraceErr(err); + return err; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/wkq.h linux-2.6.25.4-unionfs/fs/aufs/wkq.h --- linux-2.6.25.4-unionfs.orig/fs/aufs/wkq.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/wkq.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * workqueue for asynchronous/super-io/delegated operations + * + * $Id: wkq.h,v 1.3 2008/04/28 03:18:21 sfjro Exp $ + */ + +#ifndef __AUFS_WKQ_H__ +#define __AUFS_WKQ_H__ + +#ifdef __KERNEL__ + +#include +#include +#include +#include + +/* ---------------------------------------------------------------------- */ + +/* internal workqueue named AUFS_WKQ_NAME */ +struct au_wkq { + struct workqueue_struct *q; + + /* accounting */ + atomic_t busy; + unsigned int max_busy; // todo: STAT only +}; + +/* + * in the next operation, wait for the 'nowait' tasks in system-wide workqueue + */ +struct au_nowait_tasks { +#ifdef CONFIG_AUFS_HINOTIFY + /* + * currently, the 'nowait' task which should be waited in the next + * operation is only hinotify. + */ + atomic_t nw_len; + wait_queue_head_t nw_wq; +#endif +}; + +/* ---------------------------------------------------------------------- */ + +extern struct au_wkq *au_wkq; +typedef void (*au_wkq_func_t)(void *args); + +/* wkq flags */ +#define AuWkq_WAIT 1 +#define AuWkq_DLGT (1 << 1) +#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) +#define au_fset_wkq(flags, name) { (flags) |= AuWkq_##name; } +#define au_fclr_wkq(flags, name) { (flags) &= ~AuWkq_##name; } +#ifndef CONFIG_AUFS_DLGT +#undef AuWkq_DLGT +#define AuWkq_DLGT 0 +#endif + +int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb, + unsigned int flags); +int __init au_wkq_init(void); +void au_wkq_fin(void); + +/* ---------------------------------------------------------------------- */ + +static inline int au_test_wkq(struct task_struct *tsk) +{ + return (!tsk->mm && !strcmp(tsk->comm, AUFS_WKQ_NAME)); +} + +static inline int au_wkq_wait(au_wkq_func_t func, void *args, int dlgt) +{ + unsigned int flags = AuWkq_WAIT; + if (unlikely(dlgt)) + au_fset_wkq(flags, DLGT); + return au_wkq_run(func, args, /*sb*/NULL, flags); +} + +static inline int au_wkq_nowait(au_wkq_func_t func, void *args, + struct super_block *sb, int dlgt) +{ + unsigned int flags = !AuWkq_WAIT; + if (unlikely(dlgt)) + au_fset_wkq(flags, DLGT); + return au_wkq_run(func, args, sb, flags); +} + +#ifdef CONFIG_AUFS_HINOTIFY +//todo: memory barrier? +static inline void au_nwt_init(struct au_nowait_tasks *nwt) +{ + atomic_set(&nwt->nw_len, 0); + smp_mb(); /* atomic_set */ + init_waitqueue_head(&nwt->nw_wq); +} + +static inline int au_nwt_inc(struct au_nowait_tasks *nwt) +{ + return atomic_inc_return(&nwt->nw_len); +} + +static inline int au_nwt_dec(struct au_nowait_tasks *nwt) +{ + int ret = atomic_dec_return(&nwt->nw_len); + if (!ret) + wake_up_all(&nwt->nw_wq); + return ret; +} + +static inline int au_nwt_flush(struct au_nowait_tasks *nwt) +{ + wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); + return 0; +} +#else +static inline void au_nwt_init(struct au_nowait_tasks *nwt) +{ + /* nothing */ +} + +static inline int au_nwt_inc(struct au_nowait_tasks *nwt) +{ + return 0; +} + +static inline int au_nwt_dec(struct au_nowait_tasks *nwt) +{ + return 0; +} + +static inline int au_nwt_flush(struct au_nowait_tasks *nwt) +{ + return 0; +} +#endif /* CONFIG_AUFS_HINOTIFY */ + +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/fs/aufs/xino.c linux-2.6.25.4-unionfs/fs/aufs/xino.c --- linux-2.6.25.4-unionfs.orig/fs/aufs/xino.c 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/fs/aufs/xino.c 2008-05-25 11:58:53 +0300 @@ -0,0 +1,1225 @@ +/* + * Copyright (C) 2005-2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * external inode number translation table and bitmap + * + * $Id: xino.c,v 1.6 2008/05/19 01:51:23 sfjro Exp $ + */ + +//#include +//#include +#include +#include "aufs.h" + +/* ---------------------------------------------------------------------- */ + +static ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, + size_t size, loff_t *pos) +{ + ssize_t err; + mm_segment_t oldfs; + + LKTRTrace("%.*s, sz %lu, *pos %Ld\n", + AuDLNPair(file->f_dentry), (unsigned long)size, *pos); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + do { + // signal_pending + err = func(file, (char __user *)buf, size, pos); + } while (err == -EAGAIN || err == -EINTR); + set_fs(oldfs); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *buf, + size_t size, loff_t *pos) +{ + ssize_t err; + mm_segment_t oldfs; + + lockdep_off(); + oldfs = get_fs(); + set_fs(KERNEL_DS); + do { + // signal_pending + err = func(file, (const char __user *)buf, size, pos); + } while (err == -EAGAIN || err == -EINTR); + set_fs(oldfs); + lockdep_on(); + + if (err >= 0) + au_update_fuse_h_inode(file->f_vfsmnt, file->f_dentry); + /*ignore*/ + + AuTraceErr(err); + return err; +} + +struct do_xino_fwrite_args { + ssize_t *errp; + au_writef_t func; + struct file *file; + void *buf; + size_t size; + loff_t *pos; +}; + +static void call_do_xino_fwrite(void *args) +{ + struct do_xino_fwrite_args *a = args; + *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); +} + +static ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, + size_t size, loff_t *pos) +{ + ssize_t err; + + LKTRTrace("%.*s, sz %lu, *pos %Ld\n", + AuDLNPair(file->f_dentry), (unsigned long)size, *pos); + + // signal block and no wkq? + /* + * it breaks RLIMIT_FSIZE and normal user's limit, + * users should care about quota and real 'filesystem full.' + */ + if (!au_test_wkq(current)) { + int wkq_err; + struct do_xino_fwrite_args args = { + .errp = &err, + .func = func, + .file = file, + .buf = buf, + .size = size, + .pos = pos + }; + wkq_err = au_wkq_wait(call_do_xino_fwrite, &args, /*dlgt*/0); + if (unlikely(wkq_err)) + err = wkq_err; + } else + err = do_xino_fwrite(func, file, buf, size, pos); + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct xino_do_trunc_args { + struct vfsmount *mnt; + struct au_branch *br; +}; + +static void xino_do_trunc(void *_args) +{ + struct xino_do_trunc_args *args = _args; + struct super_block *sb; + aufs_bindex_t bindex; + int err; + struct file *file; + + err = 0; + sb = args->mnt->mnt_sb; + si_noflush_write_lock(sb); + bindex = au_br_index(sb, args->br->br_id); + AuDebugOn(bindex < 0); + err = au_xino_trunc(sb, bindex); + if (unlikely(err)) + goto out; + + file = args->br->br_xino; + au_update_fuse_h_inode(args->br->br_mnt, file->f_dentry); /*ignore*/ + if (file->f_dentry->d_inode->i_blocks >= args->br->br_xino_upper) + args->br->br_xino_upper += AUFS_XINO_TRUNC_STEP; + //AuDbg("%Lu\n", (unsigned long long)args->br->br_xino_upper); + + out: + si_write_unlock(sb); + if (unlikely(err)) + AuWarn("err b%d, (%d)\n", bindex, err); + au_nwt_dec(&au_sbi(sb)->si_nowait); + atomic_dec_return(&args->br->br_xino_running); + au_br_put(args->br); + mntput(args->mnt); + kfree(args); +} + +static void xino_try_trunc(struct super_block *sb, struct au_branch *br) +{ + struct xino_do_trunc_args *args; + struct au_sbinfo *sbinfo; + struct file *file = br->br_xino; + int wkq_err; + + au_update_fuse_h_inode(br->br_mnt, file->f_dentry); /*ignore*/ + if (file->f_dentry->d_inode->i_blocks < br->br_xino_upper) + return; + if (atomic_inc_return(&br->br_xino_running) > 1) + goto out; + + /* lock and kfree() will be called in trunc_xino() */ + args = kmalloc(sizeof(*args), GFP_TEMPORARY); + if (args) { + args->mnt = au_mntcache_get(sb); + args->br = br; + au_br_get(br); + sbinfo = au_sbi(sb); + au_nwt_inc(&sbinfo->si_nowait); + wkq_err = au_wkq_nowait(xino_do_trunc, args, sb, /*dlgt*/0); + if (!wkq_err) + return; /* success */ + + AuErr("wkq %d\n", wkq_err); + au_nwt_dec(&sbinfo->si_nowait); + mntput(args->mnt); + au_br_put(br); + kfree(args); + } else + AuErr1("no memory\n"); + out: + atomic_dec_return(&br->br_xino_running); +} + +/* ---------------------------------------------------------------------- */ + +#define Au_LOFF_MAX ((loff_t)LLONG_MAX) + +static int au_xino_do_write(au_writef_t write, struct file *file, + ino_t h_ino, struct au_xino_entry *xinoe) +{ + loff_t pos; + ssize_t sz; + + AuTraceEnter(); + + pos = h_ino; + if (unlikely(Au_LOFF_MAX / sizeof(*xinoe) - 1 < pos)) { + AuIOErr1("too large hi%lu\n", h_ino); + return -EFBIG; + } + pos *= sizeof(*xinoe); + sz = xino_fwrite(write, file, xinoe, sizeof(*xinoe), &pos); + if (sz == sizeof(*xinoe)) + return 0; /* success */ + + AuIOErr("write failed (%ld)\n", (long)sz); + return -EIO; +} + +/* + * write @ino to the xinofile for the specified branch{@sb, @bindex} + * at the position of @_ino. + * when @ino is zero, it is written to the xinofile and means no entry. + */ +int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + struct au_xino_entry *xinoe) +{ + int err; + struct file *file; + struct au_branch *br; + unsigned int mnt_flags; + + LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, xinoe->ino); + BUILD_BUG_ON(sizeof(long long) != sizeof(Au_LOFF_MAX) + || ((loff_t) - 1) > 0); + + mnt_flags = au_mntflags(sb); + if (unlikely(!au_opt_test(mnt_flags, XINO))) + return 0; + + br = au_sbr(sb, bindex); + file = br->br_xino; + AuDebugOn(!file); + + err = au_xino_do_write(au_sbi(sb)->si_xwrite, file, h_ino, xinoe); + if (!err) { + if (unlikely(au_opt_test(mnt_flags, TRUNC_XINO) + && au_test_trunc_xino(br->br_mnt->mnt_sb))) + xino_try_trunc(sb, br); + return 0; /* success */ + } + + AuIOErr("write failed (%d)\n", err); + return -EIO; +} + +/* ---------------------------------------------------------------------- */ + +static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; +//static const int page_bits = 4; +static ino_t xib_calc_ino(unsigned long pindex, int bit) +{ + ino_t ino; + AuDebugOn(bit < 0 || page_bits <= bit); + ino = AUFS_FIRST_INO + pindex * page_bits + bit; + return ino; +} + +static void xib_calc_bit(ino_t ino, unsigned long *pindex, int *bit) +{ + AuDebugOn(ino < AUFS_FIRST_INO); + ino -= AUFS_FIRST_INO; + *pindex = ino / page_bits; + *bit = ino % page_bits; +} + +static int xib_pindex(struct super_block *sb, unsigned long pindex) +{ + int err; + struct au_sbinfo *sbinfo; + loff_t pos; + ssize_t sz; + struct file *xib; + unsigned long *p; + + LKTRTrace("pindex %lu\n", pindex); + sbinfo = au_sbi(sb); + MtxMustLock(&sbinfo->si_xib_mtx); + AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE + || !au_opt_test(sbinfo->si_mntflags, XINO)); + + if (pindex == sbinfo->si_xib_last_pindex) + return 0; + + xib = sbinfo->si_xib; + p = sbinfo->si_xib_buf; + pos = sbinfo->si_xib_last_pindex; + pos *= PAGE_SIZE; + sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) + goto out; + + pos = pindex; + pos *= PAGE_SIZE; + if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE) + sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); + else { + memset(p, 0, PAGE_SIZE); + sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); + } + if (sz == PAGE_SIZE) { + sbinfo->si_xib_last_pindex = pindex; + return 0; /* success */ + } + + out: + AuIOErr1("write failed (%ld)\n", (long)sz); + err = sz; + if (sz >= 0) + err = -EIO; + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +int au_xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + ino_t ino) +{ + int err, bit; + unsigned long pindex; + struct au_sbinfo *sbinfo; + struct au_xino_entry xinoe = { + .ino = 0 + }; + + LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, ino); + + sbinfo = au_sbi(sb); + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + return 0; + + err = 0; + if (unlikely(ino)) { + AuDebugOn(ino < AUFS_FIRST_INO); + xib_calc_bit(ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + mutex_lock(&sbinfo->si_xib_mtx); + err = xib_pindex(sb, pindex); + if (!err) { + clear_bit(bit, sbinfo->si_xib_buf); + sbinfo->si_xib_next_bit = bit; + } + mutex_unlock(&sbinfo->si_xib_mtx); + } + + if (!err) + err = au_xino_write(sb, bindex, h_ino, &xinoe); + return err; +} + +ino_t au_xino_new_ino(struct super_block *sb) +{ + ino_t ino; + struct au_sbinfo *sbinfo; + int free_bit, err; + unsigned long *p, pindex, ul, pend; + struct file *file; + + //au_debug_on(); + AuTraceEnter(); + + sbinfo = au_sbi(sb); + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + return iunique(sb, AUFS_FIRST_INO); + + mutex_lock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + free_bit = sbinfo->si_xib_next_bit; + //AuDbg("bit %d, pindex %d\n", free_bit, sbinfo->si_xib_last_pindex); + //AuDbg("bit %d, %d\n", free_bit, test_bit(free_bit, p)); + if (free_bit < page_bits && !test_bit(free_bit, p)) + goto out; /* success */ + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + + pindex = sbinfo->si_xib_last_pindex; + for (ul = pindex - 1; ul < ULONG_MAX; ul--) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + + file = sbinfo->si_xib; + pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE; + for (ul = pindex + 1; ul <= pend; ul++) { + err = xib_pindex(sb, ul); + if (unlikely(err)) + goto out_err; + free_bit = find_first_zero_bit(p, page_bits); + if (free_bit < page_bits) + goto out; /* success */ + } + BUG(); + + out: + set_bit(free_bit, p); + sbinfo->si_xib_next_bit++; + pindex = sbinfo->si_xib_last_pindex; + mutex_unlock(&sbinfo->si_xib_mtx); + ino = xib_calc_ino(pindex, free_bit); + //AuDbg("i%lu\n", ino); + LKTRTrace("i%lu\n", ino); + //au_debug_off(); + return ino; + out_err: + mutex_unlock(&sbinfo->si_xib_mtx); + LKTRTrace("i0\n"); + //au_debug_off(); + return 0; +} + +/* + * read @ino from xinofile for the specified branch{@sb, @bindex} + * at the position of @h_ino. + * if @ino does not exist and @do_new is true, get new one. + */ +int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, + struct au_xino_entry *xinoe) +{ + int err; + struct file *file; + loff_t pos; + ssize_t sz; + struct au_sbinfo *sbinfo; + + LKTRTrace("b%d, hi%lu\n", bindex, h_ino); + + err = 0; + xinoe->ino = 0; + sbinfo = au_sbi(sb); + if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) + return 0; /* no ino */ + + pos = h_ino; + if (unlikely(Au_LOFF_MAX / sizeof(*xinoe) - 1 < pos)) { + AuIOErr1("too large hi%lu\n", h_ino); + return -EFBIG; + } + pos *= sizeof(*xinoe); + + file = au_sbr(sb, bindex)->br_xino; + AuDebugOn(!file); + if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*xinoe)) + return 0; /* no ino */ + + sz = xino_fread(sbinfo->si_xread, file, xinoe, sizeof(*xinoe), &pos); + if (sz == sizeof(*xinoe)) + return 0; /* success */ + + err = sz; + if (unlikely(sz >= 0)) { + err = -EIO; + AuIOErr("xino read error (%ld)\n", (long)sz); + } + + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +struct file *au_xino_create(struct super_block *sb, char *fname, int silent, + struct dentry *parent) +{ + struct file *file; + int err; + struct dentry *h_parent; + struct inode *h_dir; + struct vfsub_args vargs; + //const int hinotify = au_opt_test(au_mntflags(sb), UDBA_INOTIFY); + + LKTRTrace("%s\n", fname); + //AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + + /* LSM may detect it */ + file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE, + S_IRUGO | S_IWUGO); + //file = ERR_PTR(-1); + if (IS_ERR(file)) { + if (!silent) + AuErr("open %s(%ld)\n", fname, PTR_ERR(file)); + return file; + } + + /* keep file count */ + h_parent = dget_parent(file->f_dentry); + h_dir = h_parent->d_inode; + vfsub_args_init(&vargs, NULL, 0, 0); + mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); + err = vfsub_unlink(h_dir, file->f_dentry, &vargs); + mutex_unlock(&h_dir->i_mutex); + dput(h_parent); + if (unlikely(err)) { + if (!silent) + AuErr("unlink %s(%d)\n", fname, err); + goto out; + } + + if (sb != file->f_dentry->d_sb) + return file; /* success */ + + if (!silent) + AuErr("%s must be outside\n", fname); + err = -EINVAL; + + out: + fput(file); + file = ERR_PTR(err); + return file; +} + +/* + * find another branch who is on the same filesystem of the specified + * branch{@btgt}. search until @bend. + */ +static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, + aufs_bindex_t bend) +{ + aufs_bindex_t bindex; + struct super_block *tgt_sb = au_sbr_sb(sb, btgt); + + for (bindex = 0; bindex <= bend; bindex++) + if (unlikely(btgt != bindex && tgt_sb == au_sbr_sb(sb, bindex))) + return bindex; + return -1; +} + +/* + * create a new xinofile at the same place/path as @base_file. + */ +static +struct file *au_xino_create2(struct super_block *sb, struct file *base_file, + struct file *copy_src) +{ + struct file *file; + int err; + struct dentry *base, *dentry, *parent; + struct inode *dir, *inode; + struct qstr *name; + struct vfsub_args vargs; + struct au_ndx ndx = { + .nfsmnt = NULL, + .flags = 0, + .nd = NULL, + //.br = NULL + }; + + base = base_file->f_dentry; + LKTRTrace("%.*s\n", AuDLNPair(base)); + parent = base->d_parent; /* dir inode is locked */ + dir = parent->d_inode; + IMustLock(dir); + + file = ERR_PTR(-EINVAL); + if (unlikely(au_test_nfs(parent->d_sb))) + goto out; + + /* do not superio, nor NFS. */ + name = &base->d_name; + dentry = au_lkup_one(name->name, parent, name->len, &ndx); + //if (LktrCond) {dput(dentry); dentry = ERR_PTR(-1);} + if (IS_ERR(dentry)) { + file = (void *)dentry; + AuErr("%.*s lookup err %ld\n", AuLNPair(name), PTR_ERR(dentry)); + goto out; + } + err = vfsub_create(dir, dentry, S_IRUGO | S_IWUGO, NULL, /*dlgt*/0); + //if (LktrCond) {vfs_unlink(dir, dentry); err = -1;} + if (unlikely(err)) { + file = ERR_PTR(err); + AuErr("%.*s create err %d\n", AuLNPair(name), err); + goto out_dput; + } + file = dentry_open(dget(dentry), mntget(base_file->f_vfsmnt), + O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE); + //if (LktrCond) {fput(file); file = ERR_PTR(-1);} + if (IS_ERR(file)) { + AuErr("%.*s open err %ld\n", AuLNPair(name), PTR_ERR(file)); + goto out_dput; + } + vfsub_args_init(&vargs, NULL, 0, 0); + err = vfsub_unlink(dir, dentry, &vargs); + //if (LktrCond) err = -1; + if (unlikely(err)) { + AuErr("%.*s unlink err %d\n", AuLNPair(name), err); + goto out_fput; + } + + if (copy_src) { + inode = copy_src->f_dentry->d_inode; + err = au_copy_file(file, copy_src, i_size_read(inode), sb); + if (unlikely(err)) { + AuErr("%.*s copy err %d\n", AuLNPair(name), err); + goto out_fput; + } + } + goto out_dput; /* success */ + + out_fput: + fput(file); + file = ERR_PTR(err); + out_dput: + dput(dentry); + out: + AuTraceErrPtr(file); + return file; +} + +/* ---------------------------------------------------------------------- */ + +/* + * initialize the xinofile for the specified branch{@sb, @bindex} + * at the place/path where @base_file indicates. + * test whether another branch is on the same filesystem or not, + * if @do_test is true. + */ +int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, + struct file *base_file, int do_test) +{ + int err, do_create; + struct au_branch *shared_br; + aufs_bindex_t bshared, bend; + struct inode *dir; + struct au_xino_entry xinoe; + struct dentry *parent; + struct file *file; + + LKTRTrace("base_file %p, do_test %d\n", base_file, do_test); + SiMustWriteLock(sb); + AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); + AuDebugOn(br->br_xino); + + do_create = 1; + bshared = -1; + shared_br = NULL; + bend = au_sbend(sb); + if (do_test) { + aufs_bindex_t bindex; + + struct super_block *tgt_sb = br->br_mnt->mnt_sb; + for (bindex = 0; bindex <= bend; bindex++) + if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) { + bshared = bindex; + break; + } + } + if (unlikely(bshared >= 0)) { + shared_br = au_sbr(sb, bshared); + do_create = !shared_br->br_xino; + } + + if (do_create) { + parent = dget_parent(base_file->f_dentry); + dir = parent->d_inode; + + mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); + file = au_xino_create2(sb, base_file, NULL); + err = PTR_ERR(file); + mutex_unlock(&dir->i_mutex); + dput(parent); + if (IS_ERR(file)) + goto out; + br->br_xino = file; + } else { + br->br_xino = shared_br->br_xino; + get_file(br->br_xino); + } + + xinoe.ino = AUFS_ROOT_INO; + //xinoe.h_gen = h_inode->i_generation; + //WARN_ON(xinoe.h_gen == AuXino_INVALID_HGEN); + err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino, h_ino, + &xinoe); + //if (LktrCond) err = -1; + if (!err) + return 0; /* success */ + + //fput(br->br_xino); + //br->br_xino = NULL; + + out: + AuTraceErr(err); + return err; +} + +/* too slow */ +static int do_xib_restore(struct super_block *sb, struct file *file, void *page) +{ + int err, bit; + struct au_sbinfo *sbinfo; + au_readf_t func; + loff_t pos, pend; + ssize_t sz; + struct au_xino_entry *xinoe; + unsigned long pindex; + + AuTraceEnter(); + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + func = sbinfo->si_xread; + pend = i_size_read(file->f_dentry->d_inode); +#ifdef CONFIG_AUFS_DEBUG + if (unlikely(pend > (1 << 22))) + AuWarn("testing a large xino file %Ld\n", (long long)pend); +#endif + pos = 0; + while (pos < pend) { + sz = xino_fread(func, file, page, PAGE_SIZE, &pos); + err = sz; + if (unlikely(sz <= 0)) + goto out; + + err = 0; + for (xinoe = page; sz > 0; xinoe++, sz -= sizeof(xinoe)) { + //AuDbg("i%lu\n", xinoe->ino); + if (unlikely(xinoe->ino < AUFS_FIRST_INO)) + continue; + + xib_calc_bit(xinoe->ino, &pindex, &bit); + AuDebugOn(page_bits <= bit); + err = xib_pindex(sb, pindex); + if (!err) + set_bit(bit, sbinfo->si_xib_buf); + else + goto out; + //AuDbg("i%lu, bit %d\n", xinoe->ino, bit); + } + } + + out: + AuTraceErr(err); + return err; +} + +static int xib_restore(struct super_block *sb) +{ + int err; + aufs_bindex_t bindex, bend; + void *page; + + //au_debug_on(); + AuTraceEnter(); + + err = -ENOMEM; + page = (void *)__get_free_page(GFP_TEMPORARY); + if (unlikely(!page)) + goto out; + + err = 0; + bend = au_sbend(sb); + for (bindex = 0; !err && bindex <= bend; bindex++) + if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) + err = do_xib_restore + (sb, au_sbr(sb, bindex)->br_xino, page); + else + LKTRTrace("b%d\n", bindex); + free_page((unsigned long)page); + + out: + AuTraceErr(err); + //au_debug_off(); + return err; +} + +int au_xib_trunc(struct super_block *sb) +{ + int err; + struct au_sbinfo *sbinfo; + unsigned long *p; + //ino_t ino; + loff_t pos; + ssize_t sz; + struct dentry *parent; + struct inode *dir; + struct file *file; + unsigned int mnt_flags; + + AuTraceEnter(); + SiMustWriteLock(sb); + + mnt_flags = au_mntflags(sb); + if (unlikely(!au_opt_test(mnt_flags, XINO))) + return 0; + + //aufs_debug_on(); + sbinfo = au_sbi(sb); + parent = dget_parent(sbinfo->si_xib->f_dentry); + dir = parent->d_inode; + mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); + file = au_xino_create2(sb, sbinfo->si_xib, NULL); + mutex_unlock(&dir->i_mutex); + dput(parent); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + + p = sbinfo->si_xib_buf; + memset(p, 0, PAGE_SIZE); + pos = 0; + sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xib, p, PAGE_SIZE, &pos); + if (unlikely(sz != PAGE_SIZE)) { + err = sz; + AuIOErr("err %d\n", err); + if (sz >= 0) + err = -EIO; + goto out; + } + + if (au_opt_test(mnt_flags, XINO)) { + mutex_lock(&sbinfo->si_xib_mtx); + err = xib_restore(sb); + mutex_unlock(&sbinfo->si_xib_mtx); + } + +out: + //aufs_debug_off(); + AuTraceErr(err); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * xino mount option handlers + */ +static au_readf_t find_readf(struct file *h_file) +{ + const struct file_operations *fop = h_file->f_op; + + if (fop) { + if (fop->read) + return fop->read; + if (fop->aio_read) + return do_sync_read; + } + return ERR_PTR(-ENOSYS); +} + +static au_writef_t find_writef(struct file *h_file) +{ + const struct file_operations *fop = h_file->f_op; + + if (fop) { + if (fop->write) + return fop->write; + if (fop->aio_write) + return do_sync_write; + } + return ERR_PTR(-ENOSYS); +} + +/* xino bitmap */ +static void xino_clear_xib(struct super_block *sb) +{ + struct au_sbinfo *sbinfo; + + AuTraceEnter(); + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + sbinfo->si_xread = NULL; + sbinfo->si_xwrite = NULL; + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; +} + +static int au_xino_set_xib(struct super_block *sb, struct file *base) +{ + int err; + struct au_sbinfo *sbinfo; + struct file *file; + loff_t pos; + + LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry)); + SiMustWriteLock(sb); + + sbinfo = au_sbi(sb); + file = au_xino_create2(sb, base, sbinfo->si_xib); + err = PTR_ERR(file); + if (IS_ERR(file)) + goto out; + if (sbinfo->si_xib) + fput(sbinfo->si_xib); + sbinfo->si_xib = file; + sbinfo->si_xread = find_readf(file); + AuDebugOn(IS_ERR(sbinfo->si_xread)); + sbinfo->si_xwrite = find_writef(file); + AuDebugOn(IS_ERR(sbinfo->si_xwrite)); + + err = -ENOMEM; + if (!sbinfo->si_xib_buf) + sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_KERNEL); + if (unlikely(!sbinfo->si_xib_buf)) + goto out_unset; + + sbinfo->si_xib_last_pindex = 0; + sbinfo->si_xib_next_bit = 0; + + /* no need to lock for i_size_read() */ + if (i_size_read(file->f_dentry->d_inode) < PAGE_SIZE) { + pos = 0; + err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, + PAGE_SIZE, &pos); + if (unlikely(err != PAGE_SIZE)) + goto out_free; + } + err = 0; + goto out; /* success */ + + out_free: + free_page((unsigned long)sbinfo->si_xib_buf); + sbinfo->si_xib_buf = NULL; + if (err >= 0) + err = -EIO; + out_unset: + fput(sbinfo->si_xib); + sbinfo->si_xib = NULL; + sbinfo->si_xread = NULL; + sbinfo->si_xwrite = NULL; + out: + AuTraceErr(err); + return err; +} + +/* xino for each branch */ +static void xino_clear_br(struct super_block *sb) +{ + aufs_bindex_t bindex, bend; + struct au_branch *br; + + AuTraceEnter(); + SiMustWriteLock(sb); + + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + if (unlikely(!br || !br->br_xino)) + continue; + + fput(br->br_xino); + br->br_xino = NULL; + } +} + +static int au_xino_set_br(struct super_block *sb, struct file *base) +{ + int err; + aufs_bindex_t bindex, bend, bshared; + struct { + struct file *old, *new; + } *fpair, *p; + struct au_branch *br; + struct au_xino_entry xinoe; + struct inode *inode; + au_writef_t writef; + + //au_debug_on(); + LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry)); + SiMustWriteLock(sb); + + err = -ENOMEM; + bend = au_sbend(sb); + fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_TEMPORARY); + if (unlikely(!fpair)) + goto out; + + inode = sb->s_root->d_inode; + xinoe.ino = AUFS_ROOT_INO; + writef = au_sbi(sb)->si_xwrite; + for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { + br = au_sbr(sb, bindex); + bshared = is_sb_shared(sb, bindex, bindex - 1); + if (bshared >= 0) { + /* shared xino */ + *p = fpair[bshared]; + get_file(p->new); + } + + if (!p->new) { + /* new xino */ + p->old = br->br_xino; + p->new = au_xino_create2(sb, base, br->br_xino); + err = PTR_ERR(p->new); + if (IS_ERR(p->new)) { + p->new = NULL; + goto out_pair; + } + } + + err = au_xino_do_write(writef, p->new, + au_h_iptr(inode, bindex)->i_ino, &xinoe); + if (unlikely(err)) + goto out_pair; + } + + for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { + br = au_sbr(sb, bindex); + AuDebugOn(p->old != br->br_xino); + if (br->br_xino) + fput(br->br_xino); + get_file(p->new); + br->br_xino = p->new; + } + + out_pair: + for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) + if (p->new) + fput(p->new); + else + break; + kfree(fpair); + out: + AuTraceErr(err); + //au_debug_off(); + return err; +} + +void au_xino_clr(struct super_block *sb) +{ + AuTraceEnter(); + SiMustWriteLock(sb); + + xino_clear_xib(sb); + xino_clear_br(sb); + au_opt_clr(au_sbi(sb)->si_mntflags, XINO); +} + +int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) +{ + int err, skip; + struct dentry *parent, *cur_parent; + struct qstr *dname, *cur_name; + struct file *cur_xino; + struct inode *dir; + struct au_sbinfo *sbinfo; + + LKTRTrace("remount %d\n", remount); + SiMustWriteLock(sb); + + err = 0; + sbinfo = au_sbi(sb); + parent = dget_parent(xino->file->f_dentry); + if (remount) { + skip = 0; + dname = &xino->file->f_dentry->d_name; + cur_xino = sbinfo->si_xib; + if (cur_xino) { + cur_parent = dget_parent(cur_xino->f_dentry); + cur_name = &cur_xino->f_dentry->d_name; + skip = (cur_parent == parent + && dname->len == cur_name->len + && !memcmp(dname->name, cur_name->name, + dname->len)); + dput(cur_parent); + } + if (skip) + goto out; + } + + au_opt_set(sbinfo->si_mntflags, XINO); + dir = parent->d_inode; + mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); + err = au_xino_set_xib(sb, xino->file); + if (!err) + err = au_xino_set_br(sb, xino->file); + mutex_unlock(&dir->i_mutex); + if (!err) + goto out; /* success */ + + /* reset all */ + AuIOErr("failed creating xino, forcing noxino (%d).\n", err); + err = -EIO; + au_xino_clr(sb); + + out: + dput(parent); + AuTraceErr(err); + return err; +} + +int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) +{ + int err; + struct au_branch *br; + struct file *new_xino; + struct super_block *h_sb; + aufs_bindex_t bi, bend; + struct dentry *parent; + struct inode *dir; + + //au_debug_on(); + LKTRTrace("b%d\n", bindex); + SiMustWriteLock(sb); + + err = -EINVAL; + bend = au_sbend(sb); + if (unlikely(bindex < 0 || bend < bindex)) + goto out; + br = au_sbr(sb, bindex); + if (unlikely(!br->br_xino)) + goto out; + + parent = dget_parent(br->br_xino->f_dentry); + dir = parent->d_inode; + //AuDbgFile(br->br_xino); + mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); + new_xino = au_xino_create2(sb, br->br_xino, br->br_xino); + mutex_unlock(&dir->i_mutex); + //AuDbgFile(new_xino); + dput(parent); + err = PTR_ERR(new_xino); + if (IS_ERR(new_xino)) + goto out; + err = 0; + fput(br->br_xino); + br->br_xino = new_xino; + + h_sb = br->br_mnt->mnt_sb; + for (bi = 0; bi <= bend; bi++) { + if (unlikely(bi == bindex)) + continue; + br = au_sbr(sb, bi); + if (br->br_mnt->mnt_sb != h_sb) + continue; + + fput(br->br_xino); + br->br_xino = new_xino; + get_file(new_xino); + } + + out: + AuTraceErr(err); + //au_debug_off(); + return err; +} + +/* ---------------------------------------------------------------------- */ + +/* + * create a xinofile at the default place/path. + */ +struct file *au_xino_def(struct super_block *sb) +{ + struct file *file; + aufs_bindex_t bend, bindex, bwr; + char *page, *p; + struct path path; + + AuTraceEnter(); + + bend = au_sbend(sb); + bwr = -1; + for (bindex = 0; bindex <= bend; bindex++) + if (au_br_writable(au_sbr_perm(sb, bindex)) + && !au_test_nfs(au_h_dptr(sb->s_root, bindex)->d_sb)) { + bwr = bindex; + break; + } + + if (bwr >= 0) { + file = ERR_PTR(-ENOMEM); + page = __getname(); + //if (LktrCond) {__putname(page); page = NULL;} + if (unlikely(!page)) + goto out; + path.mnt = au_sbr_mnt(sb, bwr); + path.dentry = au_h_dptr(sb->s_root, bwr); + p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); + //if (LktrCond) p = ERR_PTR(-1); + file = (void *)p; + if (p && !IS_ERR(p)) { + strcat(p, "/" AUFS_XINO_FNAME); + LKTRTrace("%s\n", p); + file = au_xino_create(sb, p, /*silent*/0, sb->s_root); + //if (LktrCond) {fput(file); file = ERR_PTR(-1);} + } + __putname(page); + } else { + file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0, + /*parent*/NULL); + //if (LktrCond) {fput(file); file = ERR_PTR(-1);} + if (unlikely(au_test_nfs(file->f_dentry->d_sb))) { + AuErr("xino or noxino option is required " + "since %s is NFS\n", AUFS_XINO_DEFPATH); + fput(file); + file = ERR_PTR(-EINVAL); + } + } + + out: + AuTraceErrPtr(file); + return file; +} diff -urN linux-2.6.25.4-unionfs.orig/fs/file_table.c linux-2.6.25.4-unionfs/fs/file_table.c --- linux-2.6.25.4-unionfs.orig/fs/file_table.c 2008-04-18 01:20:10 +0300 +++ linux-2.6.25.4-unionfs/fs/file_table.c 2008-05-25 11:56:08 +0300 @@ -308,6 +308,7 @@ file_free(file); } } +EXPORT_SYMBOL(put_filp); void file_move(struct file *file, struct list_head *list) { diff -urN linux-2.6.25.4-unionfs.orig/fs/Kconfig linux-2.6.25.4-unionfs/fs/Kconfig --- linux-2.6.25.4-unionfs.orig/fs/Kconfig 2008-05-25 11:53:37 +0300 +++ linux-2.6.25.4-unionfs/fs/Kconfig 2008-05-25 11:56:08 +0300 @@ -614,6 +614,8 @@ If you want to develop a userspace FS, or if you want to use a filesystem based on FUSE, answer Y or M. +source "fs/aufs/Kconfig" + config GENERIC_ACL bool select FS_POSIX_ACL diff -urN linux-2.6.25.4-unionfs.orig/fs/Makefile linux-2.6.25.4-unionfs/fs/Makefile --- linux-2.6.25.4-unionfs.orig/fs/Makefile 2008-05-25 11:53:37 +0300 +++ linux-2.6.25.4-unionfs/fs/Makefile 2008-05-25 11:56:08 +0300 @@ -107,6 +107,7 @@ obj-$(CONFIG_AUTOFS4_FS) += autofs4/ obj-$(CONFIG_ADFS_FS) += adfs/ obj-$(CONFIG_FUSE_FS) += fuse/ +obj-$(CONFIG_AUFS) += aufs/ obj-$(CONFIG_UDF_FS) += udf/ obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/ obj-$(CONFIG_JFS_FS) += jfs/ diff -urN linux-2.6.25.4-unionfs.orig/fs/namei.c linux-2.6.25.4-unionfs/fs/namei.c --- linux-2.6.25.4-unionfs.orig/fs/namei.c 2008-05-25 11:53:37 +0300 +++ linux-2.6.25.4-unionfs/fs/namei.c 2008-05-25 11:56:08 +0300 @@ -1292,7 +1292,7 @@ return err; } -static struct dentry *__lookup_hash(struct qstr *name, +struct dentry *__lookup_hash(struct qstr *name, struct dentry *base, struct nameidata *nd) { struct dentry *dentry; @@ -2837,3 +2837,5 @@ EXPORT_SYMBOL(vfs_unlink); EXPORT_SYMBOL(dentry_unhash); EXPORT_SYMBOL(generic_readlink); +EXPORT_SYMBOL(__lookup_hash); +EXPORT_SYMBOL(deny_write_access); diff -urN linux-2.6.25.4-unionfs.orig/fs/splice.c linux-2.6.25.4-unionfs/fs/splice.c --- linux-2.6.25.4-unionfs.orig/fs/splice.c 2008-05-25 11:53:37 +0300 +++ linux-2.6.25.4-unionfs/fs/splice.c 2008-05-25 11:56:08 +0300 @@ -874,7 +874,6 @@ { return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage); } - EXPORT_SYMBOL(generic_splice_sendpage); /* diff -urN linux-2.6.25.4-unionfs.orig/fs/super.c linux-2.6.25.4-unionfs/fs/super.c --- linux-2.6.25.4-unionfs.orig/fs/super.c 2008-04-18 01:20:10 +0300 +++ linux-2.6.25.4-unionfs/fs/super.c 2008-05-25 11:56:08 +0300 @@ -267,6 +267,7 @@ __fsync_super(sb); return sync_blockdev(sb->s_bdev); } +EXPORT_SYMBOL(fsync_super); /** * generic_shutdown_super - common helper for ->kill_sb() diff -urN linux-2.6.25.4-unionfs.orig/include/linux/aufs_type.h linux-2.6.25.4-unionfs/include/linux/aufs_type.h --- linux-2.6.25.4-unionfs.orig/include/linux/aufs_type.h 1970-01-01 03:00:00 +0300 +++ linux-2.6.25.4-unionfs/include/linux/aufs_type.h 2008-05-25 11:56:08 +0300 @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005, 2006, 2007, 2008 Junjiro Okajima + * + * This program, aufs 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* $Id: aufs_type.h,v 1.105 2008/05/04 23:55:14 sfjro Exp $ */ + +#include + +#ifndef __AUFS_TYPE_H__ +#define __AUFS_TYPE_H__ + +#define AUFS_VERSION "20080505" + +/* move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_BRANCH_MAX_127 +/* some environments treat 'char' as 'unsigned char' by default */ +typedef signed char aufs_bindex_t; +#define AUFS_BRANCH_MAX 127 +#else +typedef short aufs_bindex_t; +#ifdef CONFIG_AUFS_BRANCH_MAX_511 +#define AUFS_BRANCH_MAX 511 +#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) +#define AUFS_BRANCH_MAX 1023 +#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) +#define AUFS_BRANCH_MAX 32767 +#else +#error unknown CONFIG_AUFS_BRANCH_MAX value +#endif +#endif + +#define AUFS_NAME "aufs" +#define AUFS_FSTYPE AUFS_NAME + +#define AUFS_ROOT_INO 2 +#define AUFS_FIRST_INO 11 + +#define AUFS_WH_PFX ".wh." +#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) +#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" +#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME +#define AUFS_XINO_TRUNC_INIT 64 /* blocks */ +#define AUFS_XINO_TRUNC_STEP 4 /* blocks */ +#define AUFS_DIRWH_DEF 3 +#define AUFS_RDCACHE_DEF 10 /* seconds */ +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_NWKQ_DEF 4 +#define AUFS_MFS_SECOND_DEF 30 /* seconds */ +#define AUFS_PLINK_WARN 100 /* number of plinks */ + +#ifdef CONFIG_AUFS_COMPAT +#define AUFS_DIROPQ_NAME "__dir_opaque" +#else +#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ +#endif +#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME + +/* will be whiteouted doubly */ +#define AUFS_WH_BASENAME AUFS_WH_PFX AUFS_NAME +#define AUFS_WH_PLINKDIR AUFS_WH_PFX "plink" + +/* ---------------------------------------------------------------------- */ + +/* ioctl */ +enum { + AuCtlErr, + AuCtlErr_Last +}; +enum { + AuCtl_REFRESH, //AuCtl_REFRESHV, + //AuCtl_FLUSH_PLINK, + //AuCtl_CPUP, + AuCtl_CPDOWN, AuCtl_MVDOWN, + //AuCtl_DIROPQ +}; + +struct aufs_ctl_cp { + int bsrc, bdst; + int err; +}; + +#define AuCtlType 'A' +#define AUFS_CTL_REFRESH _IO(AuCtlType, AuCtl_REFRESH) +//#define AUFS_CTL_REFRESHV _IO(AuCtlType, AuCtl_REFRESHV) +//#define AUFS_CTL_FLUSH_PLINK _IOR(AuCtlType, AuCtl_FLUSH_PLINK) +//#define AUFS_CTL_CPUP _IOWR(AuCtlType, AuCtl_CPUP, struct aufs_ctl_cp) +#define AUFS_CTL_CPDOWN \ + _IOWR(AuCtlType, AuCtl_CPDOWN, struct aufs_ctl_cp) +#define AUFS_CTL_MVDOWN \ + _IOWR(AuCtlType, AuCtl_MVDOWN, struct aufs_ctl_cp) +//#define AUFS_CTL_DIROPQ _IO(AuCtlType, AuCtl_DIROPQ) + +#endif /* __AUFS_TYPE_H__ */ diff -urN linux-2.6.25.4-unionfs.orig/include/linux/namei.h linux-2.6.25.4-unionfs/include/linux/namei.h --- linux-2.6.25.4-unionfs.orig/include/linux/namei.h 2008-04-18 01:20:17 +0300 +++ linux-2.6.25.4-unionfs/include/linux/namei.h 2008-05-25 11:56:08 +0300 @@ -71,6 +71,7 @@ extern int path_lookup_open(int dfd, const char *name, unsigned lookup_flags, struct nameidata *, int open_flags); extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, int (*open)(struct inode *, struct file *)); +struct dentry * __lookup_hash(struct qstr *name, struct dentry * base, struct nameidata *nd); extern struct file *nameidata_to_filp(struct nameidata *nd, int flags); extern void release_open_intent(struct nameidata *); diff -urN linux-2.6.25.4-unionfs.orig/include/linux/splice.h linux-2.6.25.4-unionfs/include/linux/splice.h --- linux-2.6.25.4-unionfs.orig/include/linux/splice.h 2008-05-25 11:53:37 +0300 +++ linux-2.6.25.4-unionfs/include/linux/splice.h 2008-05-25 11:56:08 +0300 @@ -75,5 +75,4 @@ extern long vfs_splice_to(struct file *in, loff_t *ppos, struct pipe_inode_info *pipe, size_t len, unsigned int flags); - #endif diff -urN linux-2.6.25.4-unionfs.orig/security/security.c linux-2.6.25.4-unionfs/security/security.c --- linux-2.6.25.4-unionfs.orig/security/security.c 2008-04-18 01:20:20 +0300 +++ linux-2.6.25.4-unionfs/security/security.c 2008-05-25 11:56:08 +0300 @@ -433,6 +433,7 @@ return 0; return security_ops->inode_permission(inode, mask, nd); } +EXPORT_SYMBOL(security_inode_permission); int security_inode_setattr(struct dentry *dentry, struct iattr *attr) {