ALT Linux Bugzilla
– Attachment 2654 Details for
Bug 15838
supermount-ng
New bug
|
Search
|
[?]
|
Help
Register
|
Log In
[x]
|
Forgot Password
Login:
[x]
|
EN
|
RU
[patch]
linux-2.6.25.4-unionfs-aufs-cvs-20080519.patch
linux-2.6.25.4-unionfs-aufs-cvs-20080519.patch (text/plain), 673.97 KB, created by
led
on 2008-05-30 00:15:51 MSD
(
hide
)
Description:
linux-2.6.25.4-unionfs-aufs-cvs-20080519.patch
Filename:
MIME Type:
Creator:
led
Created:
2008-05-30 00:15:51 MSD
Size:
673.97 KB
patch
obsolete
>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 <linux/version.h> >+ >+#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 <linux/iso_fs.h> >+#include <linux/loop.h> >+#include <linux/romfs_fs.h> >+#include <linux/smp_lock.h> >+#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 <linux/fs.h> >+#include <linux/mount.h> >+#include <linux/sysfs.h> >+#include <linux/aufs_type.h> >+#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 <linux/fs_stack.h> >+#include <linux/uaccess.h> >+#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 <linux/fs.h> >+#include <linux/aufs_type.h> >+ >+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 <linux/dcache.h> >+ >+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 <linux/errno.h> >+#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 <linux/fs.h> >+ >+/* 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 <linux/lktr.h> >+#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 <linux/fs.h> >+//#include <linux/namei.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+#include <linux/aufs_type.h> >+#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 <linux/fs_stack.h> >+#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 <linux/fs.h> >+#include <linux/aufs_type.h> >+ >+/* ---------------------------------------------------------------------- */ >+ >+/* 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 <linux/security.h> >+#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 <linux/exportfs.h> >+#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 <linux/fsnotify.h> >+#include <linux/pagemap.h> >+//#include <linux/poll.h> >+//#include <linux/security.h> >+#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 <linux/file.h> >+#include <linux/fs.h> >+#include <linux/mm.h> >+#include <linux/aufs_type.h> >+#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 <linux/fsnotify.h> >+#include <linux/fs_stack.h> >+//#include <linux/pagemap.h> >+#include <linux/poll.h> >+//#include <linux/security.h> >+#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 <linux/fs.h> >+#include <linux/inotify.h> >+#include <linux/aufs_type.h> >+//#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 <linux/uaccess.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+#include <linux/security.h> >+#include <linux/aufs_type.h> >+#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 <linux/fs.h> >+#include <linux/fs_stack.h> >+//#include <linux/namei.h> >+//#include <linux/mm.h> >+#include <linux/uaccess.h> >+#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 <linux/fs.h> >+//#include <linux/namei.h> >+//#include <linux/mm.h> >+//#include <asm/uaccess.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+#include <linux/aufs_type.h> >+ >+/* ---------------------------------------------------------------------- */ >+ >+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 <linux/module.h> >+#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 <sysfs>/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 <linux/slab.h> >+ >+/* ---------------------------------------------------------------------- */ >+ >+/* 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 <linux/types.h> /* a distribution requires */ >+#include <linux/parser.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+#include <linux/aufs_type.h> >+#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 <linux/mnt_namespace.h> >+#include <linux/smp_lock.h> >+#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 <linux/module.h> >+#include <linux/buffer_head.h> >+#include <linux/seq_file.h> >+#include <linux/smp_lock.h> >+#include <linux/statfs.h> >+ >+#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 <linux/fs.h> >+#include <linux/cramfs_fs.h> >+#include <linux/kobject.h> >+#include <linux/magic.h> >+#include <linux/aufs_type.h> >+//#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 <linux/fs.h> >+#include <linux/module.h> >+#include <linux/sysfs.h> >+#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 <linux/fs.h> >+#include <linux/sysfs.h> >+#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 <linux/fs.h> >+#include <linux/module.h> >+#include <linux/seq_file.h> >+#include <linux/sysfs.h> >+#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 <linux/fs.h> >+#include <linux/module.h> >+#include <linux/moduleparam.h> >+#include <linux/sysrq.h> >+#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 <linux/uaccess.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+#include <linux/splice.h> >+#include <linux/inotify.h> >+ >+/* ---------------------------------------------------------------------- */ >+ >+/* 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 <linux/statfs.h> >+#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 <linux/fs.h> >+#include <linux/namei.h> >+//#include <linux/random.h> >+//#include <linux/security.h> >+#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 <linux/fs.h> >+#include <linux/aufs_type.h> >+#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 <linux/module.h> >+#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 <linux/fs.h> >+#include <linux/sched.h> >+#include <linux/workqueue.h> >+#include <linux/aufs_type.h> >+ >+/* ---------------------------------------------------------------------- */ >+ >+/* 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 <linux/fsnotify.h> >+//#include <linux/smp_lock.h> >+#include <linux/uaccess.h> >+#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 <linux/ioctl.h> >+ >+#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) > {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 15838
:
2654
|
2655