[l-u-m] [PATCH] UBUNTU: Add aufs module version 0+20080129
Julian Andres Klode
juliank at ubuntu.com
Tue Jan 29 20:50:56 UTC 2008
Add the aufs module and build it on amd64 and i386 architectures
for generic and server kernels. (-rt not supported yet)
---
debian/config/amd64 | 3 +
debian/config/i386 | 3 +
ubuntu/Makefile | 1 +
ubuntu/fs/aufs/Makefile | 60 ++
ubuntu/fs/aufs/Makefile.upstream | 53 ++
ubuntu/fs/aufs/aufs.h | 128 +++
ubuntu/fs/aufs/branch.c | 903 ++++++++++++++++++
ubuntu/fs/aufs/branch.h | 294 ++++++
ubuntu/fs/aufs/cpup.c | 978 ++++++++++++++++++++
ubuntu/fs/aufs/cpup.h | 86 ++
ubuntu/fs/aufs/dcsub.c | 241 +++++
ubuntu/fs/aufs/dcsub.h | 50 +
ubuntu/fs/aufs/debug.c | 369 ++++++++
ubuntu/fs/aufs/debug.h | 178 ++++
ubuntu/fs/aufs/dentry.c | 1164 +++++++++++++++++++++++
ubuntu/fs/aufs/dentry.h | 250 +++++
ubuntu/fs/aufs/dinfo.c | 624 +++++++++++++
ubuntu/fs/aufs/dir.c | 543 +++++++++++
ubuntu/fs/aufs/dir.h | 126 +++
ubuntu/fs/aufs/export.c | 620 +++++++++++++
ubuntu/fs/aufs/f_op.c | 838 +++++++++++++++++
ubuntu/fs/aufs/file.c | 785 ++++++++++++++++
ubuntu/fs/aufs/file.h | 183 ++++
ubuntu/fs/aufs/finfo.c | 234 +++++
ubuntu/fs/aufs/hinode.h | 186 ++++
ubuntu/fs/aufs/hinotify.c | 920 +++++++++++++++++++
ubuntu/fs/aufs/i_op.c | 769 ++++++++++++++++
ubuntu/fs/aufs/i_op_add.c | 733 +++++++++++++++
ubuntu/fs/aufs/i_op_del.c | 470 ++++++++++
ubuntu/fs/aufs/i_op_ren.c | 738 +++++++++++++++
ubuntu/fs/aufs/iinfo.c | 359 ++++++++
ubuntu/fs/aufs/include/linux/aufs_type.h | 108 +++
ubuntu/fs/aufs/inode.c | 380 ++++++++
ubuntu/fs/aufs/inode.h | 242 +++++
ubuntu/fs/aufs/misc.c | 325 +++++++
ubuntu/fs/aufs/misc.h | 210 +++++
ubuntu/fs/aufs/module.c | 455 +++++++++
ubuntu/fs/aufs/module.h | 77 ++
ubuntu/fs/aufs/opts.c | 1469 ++++++++++++++++++++++++++++++
ubuntu/fs/aufs/opts.h | 184 ++++
ubuntu/fs/aufs/plink.c | 338 +++++++
ubuntu/fs/aufs/sbinfo.c | 204 +++++
ubuntu/fs/aufs/super.c | 994 ++++++++++++++++++++
ubuntu/fs/aufs/super.h | 431 +++++++++
ubuntu/fs/aufs/sysaufs.c | 699 ++++++++++++++
ubuntu/fs/aufs/sysaufs.h | 100 ++
ubuntu/fs/aufs/vdir.c | 821 +++++++++++++++++
ubuntu/fs/aufs/vfsub.c | 1337 +++++++++++++++++++++++++++
ubuntu/fs/aufs/vfsub.h | 903 ++++++++++++++++++
ubuntu/fs/aufs/wbr_policy.c | 732 +++++++++++++++
ubuntu/fs/aufs/whout.c | 1041 +++++++++++++++++++++
ubuntu/fs/aufs/whout.h | 103 +++
ubuntu/fs/aufs/wkq.c | 329 +++++++
ubuntu/fs/aufs/wkq.h | 158 ++++
ubuntu/fs/aufs/xino.c | 1226 +++++++++++++++++++++++++
55 files changed, 25755 insertions(+), 0 deletions(-)
create mode 100644 ubuntu/fs/aufs/Makefile
create mode 100644 ubuntu/fs/aufs/Makefile.upstream
create mode 100644 ubuntu/fs/aufs/aufs.h
create mode 100644 ubuntu/fs/aufs/branch.c
create mode 100644 ubuntu/fs/aufs/branch.h
create mode 100644 ubuntu/fs/aufs/cpup.c
create mode 100644 ubuntu/fs/aufs/cpup.h
create mode 100644 ubuntu/fs/aufs/dcsub.c
create mode 100644 ubuntu/fs/aufs/dcsub.h
create mode 100644 ubuntu/fs/aufs/debug.c
create mode 100644 ubuntu/fs/aufs/debug.h
create mode 100644 ubuntu/fs/aufs/dentry.c
create mode 100644 ubuntu/fs/aufs/dentry.h
create mode 100644 ubuntu/fs/aufs/dinfo.c
create mode 100644 ubuntu/fs/aufs/dir.c
create mode 100644 ubuntu/fs/aufs/dir.h
create mode 100644 ubuntu/fs/aufs/export.c
create mode 100644 ubuntu/fs/aufs/f_op.c
create mode 100644 ubuntu/fs/aufs/file.c
create mode 100644 ubuntu/fs/aufs/file.h
create mode 100644 ubuntu/fs/aufs/finfo.c
create mode 100644 ubuntu/fs/aufs/hinode.h
create mode 100644 ubuntu/fs/aufs/hinotify.c
create mode 100644 ubuntu/fs/aufs/i_op.c
create mode 100644 ubuntu/fs/aufs/i_op_add.c
create mode 100644 ubuntu/fs/aufs/i_op_del.c
create mode 100644 ubuntu/fs/aufs/i_op_ren.c
create mode 100644 ubuntu/fs/aufs/iinfo.c
create mode 100644 ubuntu/fs/aufs/include/linux/aufs_type.h
create mode 100644 ubuntu/fs/aufs/inode.c
create mode 100644 ubuntu/fs/aufs/inode.h
create mode 100644 ubuntu/fs/aufs/misc.c
create mode 100644 ubuntu/fs/aufs/misc.h
create mode 100644 ubuntu/fs/aufs/module.c
create mode 100644 ubuntu/fs/aufs/module.h
create mode 100644 ubuntu/fs/aufs/opts.c
create mode 100644 ubuntu/fs/aufs/opts.h
create mode 100644 ubuntu/fs/aufs/plink.c
create mode 100644 ubuntu/fs/aufs/sbinfo.c
create mode 100644 ubuntu/fs/aufs/super.c
create mode 100644 ubuntu/fs/aufs/super.h
create mode 100644 ubuntu/fs/aufs/sysaufs.c
create mode 100644 ubuntu/fs/aufs/sysaufs.h
create mode 100644 ubuntu/fs/aufs/vdir.c
create mode 100644 ubuntu/fs/aufs/vfsub.c
create mode 100644 ubuntu/fs/aufs/vfsub.h
create mode 100644 ubuntu/fs/aufs/wbr_policy.c
create mode 100644 ubuntu/fs/aufs/whout.c
create mode 100644 ubuntu/fs/aufs/whout.h
create mode 100644 ubuntu/fs/aufs/wkq.c
create mode 100644 ubuntu/fs/aufs/wkq.h
create mode 100644 ubuntu/fs/aufs/xino.c
diff --git a/debian/config/amd64 b/debian/config/amd64
index 294ecc9..ab8f9ff 100644
--- a/debian/config/amd64
+++ b/debian/config/amd64
@@ -81,4 +81,7 @@ CONFIG_ET131X=m
CONFIG_IGB=m
CONFIG_IVTV_FB=m
+ifneq ($(UBUNTU_FLAVOUR),rt)
+CONFIG_AUFS=m
+endif
endif
diff --git a/debian/config/i386 b/debian/config/i386
index d4fcbe9..543713d 100644
--- a/debian/config/i386
+++ b/debian/config/i386
@@ -82,4 +82,7 @@ CONFIG_ET131X=m
CONFIG_IGB=m
CONFIG_IVTV_FB=m
+ifneq ($(UBUNTU_FLAVOUR),rt)
+CONFIG_AUFS=m
+endif
endif
diff --git a/ubuntu/Makefile b/ubuntu/Makefile
index 0636cef..a226c8e 100644
--- a/ubuntu/Makefile
+++ b/ubuntu/Makefile
@@ -21,6 +21,7 @@ snd-bt-sco-objs := misc/btsco.o
CFLAGS_dm-bbr.o += -I$(srctree)/drivers/md
obj-$(CONFIG_ASFS_FS) += fs/asfs/
+obj-$(CONFIG_AUFS) += fs/aufs/
ifneq ($(UBUNTU_FLAVOUR),rt)
obj-$(CONFIG_GFS_FS) += fs/gfs/
endif
diff --git a/ubuntu/fs/aufs/Makefile b/ubuntu/fs/aufs/Makefile
new file mode 100644
index 0000000..8d80085
--- /dev/null
+++ b/ubuntu/fs/aufs/Makefile
@@ -0,0 +1,60 @@
+#!/usr/bin/make -f
+# Makefile for building the aufs module in Debian
+
+# Parts from conf.mk
+# Configuration for aufs in Debian
+export CONFIG_AUFS = m
+export CONFIG_AUFS_BRANCH_MAX_127 = y
+export CONFIG_AUFS_RR_SQUASHFS = y
+export EXTRA_CFLAGS = -I $(src)/include \
+ -DCONFIG_AUFS_BRANCH_MAX_127 -UCONFIG_AUFS \
+ -DCONFIG_AUFS_MODULE \
+ -DCONFIG_AUFS_RR_SQUASHFS
+# Disable sysaufs on ARM, as there is no cmpxchg
+ifneq ($(shell dpkg-architecture -qDEB_BUILD_ARCH),arm)
+export CONFIG_AUFS_SYSAUFS = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_SYSAUFS
+endif
+
+ifneq (,$(KSRC))
+# see for ksize patch
+ifneq (,$(shell grep '^.*[[:space:]]ksize[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_KSIZE_PATCH = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_KSIZE_PATCH
+endif
+# see for lhash patch
+ifneq (,$(shell grep '^.*[[:space:]]__lookup_hash[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_LHASH_PATCH = y
+DISABLE_FAKE_DM=y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_LHASH_PATCH
+endif
+# splice patch
+ifneq (,$(shell grep '^.*[[:space:]]do_splice_from[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_SPLICE_PATCH = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_SPLICE_PATCH
+endif
+# sysfs_get_dentry patch
+ifneq (,$(shell grep '^.*[[:space:]]sysfs_get_dentry[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_SYSFS_GET_DENTRY_PATCH
+endif
+# put_filp patch
+ifneq (,$(shell grep '^.*[[:space:]]put_filp[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_PUT_FLIP_PATCH = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_PUT_FLIP_PATCH
+DISABLE_FAKE_DM=y
+endif
+# Check for sec_perm patch
+ifneq (,$(shell grep '^.*[[:space:]]security_inode_permission[[:space:]]vmlinux[[:space:]]EXPORT_SYMBOL' $(KSRC)/Module.symvers))
+export CONFIG_AUFS_SEC_PERM_PATCH = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_SEC_PERM_PATCH
+endif
+endif
+# If lhash and put_filp are not available, activate FAKE_DM.
+ifndef DISABLE_FAKE_DM
+export CONFIG_AUFS_FAKE_DM = y
+export EXTRA_CFLAGS += -DCONFIG_AUFS_FAKE_DM
+endif
+
+## INCLUDE UPSTREAM MAKEFILE
+include $(src)/Makefile.upstream
diff --git a/ubuntu/fs/aufs/Makefile.upstream b/ubuntu/fs/aufs/Makefile.upstream
new file mode 100644
index 0000000..d88c81e
--- /dev/null
+++ b/ubuntu/fs/aufs/Makefile.upstream
@@ -0,0 +1,53 @@
+# AUFS Makefile for the Linux 2.6.16 and later
+# $Id: Makefile,v 1.39 2007/12/17 03:30:17 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
+# it isn't defined in a header file
+fuse = $(shell grep '\#.*define.*FUSE_SUPER_MAGIC' ${srctree}/fs/fuse/inode.c | \
+ head -n 1 | \
+ awk '{print $$3}')
+EXTRA_CFLAGS += -DFUSE_SUPER_MAGIC=${fuse}
+endif
+
+ifeq ($(strip $(shell test ${SUBLEVEL} -ge 24 && echo t)),t)
+ifdef CONFIG_XFS_FS
+EXTRA_CFLAGS += -DXFS_SB_MAGIC=0x58465342
+endif
+endif
+
+ifdef CONFIG_TMPFS
+EXTRA_CFLAGS += -DTMPFS_MAGIC=0x01021994
+endif
+
+########################################
+
+obj-$(CONFIG_AUFS) += aufs.o
+aufs-y := module.o super.o sbinfo.o xino.o \
+ branch.o cpup.o whout.o plink.o wkq.o dcsub.o vfsub.o wbr_policy.o \
+ opts.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_AUFS_SYSAUFS) += sysaufs.o
+aufs-$(CONFIG_AUFS_HINOTIFY) += hinotify.o
+aufs-$(CONFIG_AUFS_EXPORT) += export.o
+#aufs-$(CONFIG_DEBUGFS) += dbgfs.o
+aufs-$(CONFIG_AUFS_DEBUG) += debug.o
diff --git a/ubuntu/fs/aufs/aufs.h b/ubuntu/fs/aufs/aufs.h
new file mode 100644
index 0000000..1f109e6
--- /dev/null
+++ b/ubuntu/fs/aufs/aufs.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: aufs.h,v 1.40 2007/12/03 01:37:26 sfjro Exp $ */
+
+#ifndef __AUFS_H__
+#define __AUFS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#include <linux/uaccess.h>
+#else
+#include <asm/uaccess.h>
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/* limited support before 2.6.16, curretly 2.6.15 only. */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+#define timespec_to_ns(ts) ({ (long long)(ts)->tv_sec; })
+#define D_CHILD d_child
+#else
+#define D_CHILD d_u.d_child
+#endif
+
+#include <linux/types.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 17)
+typedef unsigned long blkcnt_t;
+#endif
+
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+typedef struct kset au_subsys_t;
+#define au_subsys_to_kset(subsys) (subsys)
+#else
+typedef struct subsystem au_subsys_t;
+#define au_subsys_to_kset(subsys) ((subsys).kset)
+#endif
+
+#include <linux/gfp.h>
+#ifndef GFP_TEMPORARY
+#define GFP_TEMPORARY GFP_KERNEL
+#endif
+
+#include <linux/fs.h>
+#ifndef FMODE_EXEC
+/* introduced linux-2.6.17 */
+#define FMODE_EXEC 0
+#endif
+
+#include <linux/compiler.h>
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+#ifndef __aligned
+#define __aligned(x) __attribute__((aligned(x)))
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#define _AuNoNfsBranchMsg "NFS branch is not supported"
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+#define AuNoNfsBranch
+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && (!defined(CONFIG_AUFS_LHASH_PATCH) \
+ || !(defined(CONFIG_AUFS) \
+ || defined(CONFIG_AUFS_PUT_FILP_PATCH)) \
+ || defined(CONFIG_AUFS_FAKE_DM))
+#define AuNoNfsBranch
+#define AuNoNfsBranchMsg _AuNoNfsBranchMsg \
+ ", try some configurations and patches included in aufs source CVS"
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#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"
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_MODULE
+
+/* call ksize() or not */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) \
+ && !defined(CONFIG_AUFS_KSIZE_PATCH)
+#define ksize(p) (0U)
+#endif
+
+#endif /* CONFIG_AUFS_MODULE */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_H__ */
diff --git a/ubuntu/fs/aufs/branch.c b/ubuntu/fs/aufs/branch.c
new file mode 100644
index 0000000..ed51d07
--- /dev/null
+++ b/ubuntu/fs/aufs/branch.c
@@ -0,0 +1,903 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: branch.c,v 1.70 2007/12/03 01:37:41 sfjro Exp $ */
+
+#include <linux/loop.h>
+#include <linux/smp_lock.h>
+#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE)
+#include <linux/iso_fs.h>
+#endif
+#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE)
+#include <linux/romfs_fs.h>
+#endif
+#include "aufs.h"
+
+static void free_branch(struct aufs_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(br_count(br) || atomic_read(&br->br_wh_running));
+ kfree(br);
+}
+
+/*
+ * frees all branches
+ */
+void free_branches(struct aufs_sbinfo *sbinfo)
+{
+ aufs_bindex_t bmax;
+ struct aufs_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 find_brindex(struct super_block *sb, aufs_bindex_t br_id)
+{
+ aufs_bindex_t bindex, bend;
+
+ AuTraceEnter();
+
+ bend = sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (sbr_id(sb, bindex) == br_id)
+ return bindex;
+ return -1;
+}
+
+/*
+ * test if the @br is readonly or not.
+ */
+int br_rdonly(struct aufs_branch *br)
+{
+ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY)
+ || !br_writable(br->br_perm))
+ ? -EROFS : 0;
+}
+
+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 aufs_branch *br, int new_perm,
+ struct dentry *h_root, struct vfsmount *h_mnt)
+{
+ int err, old_perm;
+ struct inode *dir = sb->s_root->d_inode, *h_dir = h_root->d_inode;
+ const int new = (bindex < 0);
+
+ LKTRTrace("b%d, new_perm %d\n", bindex, new_perm);
+
+ if (new)
+ vfsub_i_lock_nested(h_dir, AuLsc_I_PARENT);
+ else
+ hdir_lock(h_dir, dir, bindex);
+
+ br_wh_write_lock(br);
+ old_perm = br->br_perm;
+ br->br_perm = new_perm;
+ err = init_wh(h_root, br, au_do_nfsmnt(h_mnt), sb);
+ br->br_perm = old_perm;
+ br_wh_write_unlock(br);
+
+ if (new)
+ vfsub_i_unlock(h_dir);
+ else
+ 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 aufs_branch *alloc_addbr(struct super_block *sb, int new_nbranch)
+{
+ struct aufs_branch **branchp, *add_branch;
+ int sz;
+ void *p;
+ struct dentry *root;
+ struct inode *inode;
+ struct aufs_hinode *hinodep;
+ struct aufs_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 = stosi(sb)->si_branch;
+ branchp = au_kzrealloc(p, sz, sizeof(*branchp) * new_nbranch,
+ GFP_KERNEL);
+ //if (LktrCond) branchp = NULL;
+ if (unlikely(!branchp))
+ goto out;
+ stosi(sb)->si_branch = branchp;
+
+ sz = sizeof(*hdentryp) * (new_nbranch - 1);
+ if (unlikely(!sz))
+ sz = sizeof(*hdentryp);
+ p = dtodi(root)->di_hdentry;
+ hdentryp = au_kzrealloc(p, sz, sizeof(*hdentryp) * new_nbranch,
+ GFP_KERNEL);
+ //if (LktrCond) hdentryp = NULL;
+ if (unlikely(!hdentryp))
+ goto out;
+ dtodi(root)->di_hdentry = hdentryp;
+
+ sz = sizeof(*hinodep) * (new_nbranch - 1);
+ if (unlikely(!sz))
+ sz = sizeof(*hinodep);
+ p = itoii(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;
+ itoii(inode)->ii_hinode = hinodep;
+ return add_branch; /* success */
+
+ out:
+ kfree(add_branch);
+ 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(br_writable(brperm) && IS_RDONLY(inode))) {
+ AuErr("write permission for readonly fs or inode, %s\n", path);
+ err = -EINVAL;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * retunrs:
+ * 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, *hidden_inode;
+ aufs_bindex_t bend, bindex;
+
+ LKTRTrace("%s, remo%d\n", add->path, remount);
+
+ root = sb->s_root;
+ bend = sbend(sb);
+ if (unlikely(bend >= 0 && au_find_dbindex(root, add->nd.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.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;
+ }
+
+#ifndef CONFIG_AUFS_ROBR
+ if (unlikely(au_test_aufs(inode->i_sb)
+ || !strcmp(au_sbtype(inode->i_sb), "unionfs"))) {
+ AuErr("nested " AUFS_NAME " %s\n", add->path);
+ goto out;
+ }
+#endif
+
+ if (unlikely(au_test_nfs(inode->i_sb))) {
+#ifdef AuNoNfsBranch
+ AuErr(AuNoNfsBranchMsg ". %s\n", add->path);
+ goto out;
+#endif
+#if 0 //LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+#define Msg "nfs4 brach is supported in linux-2.6.23 and later"
+ if (unlikely(!strcmp(au_sbtype(inode->i_sb), "nfs4"))) {
+ AuErr(Msg ". %s\n", add->path);
+ goto out;
+ }
+#undef Msg
+#endif
+ }
+
+ err = test_br(sb, add->nd.dentry->d_inode, add->perm, add->path);
+ if (unlikely(err))
+ goto out;
+
+ if (unlikely(bend < 0))
+ return 0; /* success */
+
+ hidden_inode = au_h_dptr(root)->d_inode;
+ if (unlikely(AuFlag(stosi(sb), f_warn_perm)
+ && ((hidden_inode->i_mode & S_IALLUGO)
+ != (inode->i_mode & S_IALLUGO)
+ || hidden_inode->i_uid != inode->i_uid
+ || hidden_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),
+ hidden_inode->i_uid, hidden_inode->i_gid,
+ (hidden_inode->i_mode & S_IALLUGO));
+
+ err = -EINVAL;
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (unlikely(test_overlap(sb, add->nd.dentry,
+ au_h_dptr_i(root, bindex)))) {
+ AuErr("%s is overlapped\n", add->path);
+ goto out;
+ }
+ err = 0;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int br_add(struct super_block *sb, struct au_opt_add *add, int remount)
+{
+ int err, sz;
+ aufs_bindex_t bend, add_bindex;
+ struct dentry *root;
+ struct aufs_iinfo *iinfo;
+ struct aufs_sbinfo *sbinfo;
+ struct aufs_dinfo *dinfo;
+ struct inode *root_inode;
+ unsigned long long maxb;
+ struct aufs_branch **branchp, *add_branch;
+ struct aufs_hdentry *hdentryp;
+ struct aufs_hinode *hinodep;
+
+ LKTRTrace("b%d, %s, 0x%x, %.*s\n", add->bindex, add->path,
+ add->perm, AuDLNPair(add->nd.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 (unlikely(err))
+ return 0; /* success */
+
+ bend = sbend(sb);
+ add_branch = alloc_addbr(sb, bend + 2);
+ err = PTR_ERR(add_branch);
+ if (IS_ERR(add_branch))
+ goto out;
+
+ err = 0;
+ rw_init_nolock(&add_branch->br_wh_rwsem);
+ add_branch->br_plink = NULL;
+ add_branch->br_wh = NULL;
+ if (unlikely(br_writable(add->perm))) {
+ err = init_br_wh(sb, /*bindex*/-1, add_branch, add->perm,
+ add->nd.dentry, add->nd.mnt);
+ if (unlikely(err)) {
+ kfree(add_branch);
+ goto out;
+ }
+ }
+ add_branch->br_xino = NULL;
+ add_branch->br_mnt = mntget(add->nd.mnt);
+ atomic_set(&add_branch->br_wh_running, 0);
+ add_branch->br_id = new_br_id(sb);
+ add_branch->br_perm = add->perm;
+ atomic_set(&add_branch->br_count, 0);
+ add_branch->br_bytes = 0;
+ add_branch->br_xino_upper = AUFS_XINO_TRUNC_INIT;
+ atomic_set(&add_branch->br_xino_running, 0);
+ add_branch->br_generation = au_sigen(sb);
+ //smp_mb(); /* atomic_set */
+
+ sbinfo = stosi(sb);
+ dinfo = dtodi(root);
+ iinfo = itoii(root_inode);
+
+ add_bindex = add->bindex;
+ sz = sizeof(*(sbinfo->si_branch)) * (bend + 1 - add_bindex);
+ branchp = sbinfo->si_branch + add_bindex;
+ memmove(branchp + 1, branchp, sz);
+ *branchp = add_branch;
+ sz = sizeof(*hdentryp) * (bend + 1 - add_bindex);
+ hdentryp = dinfo->di_hdentry + add_bindex;
+ memmove(hdentryp + 1, hdentryp, sz);
+ au_h_dentry_init(hdentryp);
+ sz = sizeof(*hinodep) * (bend + 1 - add_bindex);
+ hinodep = iinfo->ii_hinode + add_bindex;
+ memmove(hinodep + 1, hinodep, sz);
+ 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;
+ }
+ set_h_dptr(root, add_bindex, dget(add->nd.dentry));
+ set_h_iptr(root_inode, add_bindex, igrab(add->nd.dentry->d_inode), 0);
+ if (!add_bindex)
+ au_cpup_attr_all(root_inode);
+ else
+ au_add_nlink(root_inode, add->nd.dentry->d_inode);
+ maxb = add->nd.dentry->d_sb->s_maxbytes;
+ if (sb->s_maxbytes < maxb)
+ sb->s_maxbytes = maxb;
+
+ if (AuFlag(sbinfo, f_xino) != AuXino_NONE) {
+ struct file *base_file = stobr(sb, 0)->br_xino;
+ if (!add_bindex)
+ base_file = stobr(sb, 1)->br_xino;
+ err = xino_br(sb, add_bindex, base_file, /*do_test*/1);
+ if (unlikely(err)) {
+ AuDebugOn(add_branch->br_xino);
+ /* bad action? */
+ AuIOErr("err %d, force noxino\n", err);
+ err = -EIO;
+ xino_clr(sb);
+ }
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define Verbose(sb, fmt, args...) do { \
+ if (unlikely(AuFlag(stosi(sb), f_verbose))) \
+ AuInfo(fmt, ##args); \
+ else \
+ LKTRTrace(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;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry *d;
+ aufs_bindex_t bstart, bend;
+
+ 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;
+
+ 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));
+ if (au_digen(d) == 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 = dbstart(d);
+ bend = dbend(d);
+ if (bstart <= bindex
+ && bindex <= bend
+ && au_h_dptr_i(d, bindex)
+ && (!S_ISDIR(d->d_inode->i_mode)
+ || bstart == bend)) {
+ err = -EBUSY;
+ Verbose(root->d_sb, "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;
+ struct inode *i;
+ aufs_bindex_t bstart, bend;
+
+ LKTRTrace("b%d, gen%d\n", bindex, sigen);
+ SiMustWriteLock(sb);
+
+ err = 0;
+ 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 = ibstart(i);
+ bend = ibend(i);
+ if (bstart <= bindex
+ && bindex <= bend
+ && au_h_iptr_i(i, bindex)
+ && (!S_ISDIR(i->i_mode) || bstart == bend)) {
+ err = -EBUSY;
+ Verbose(sb, "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);
+ 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 br_del(struct super_block *sb, struct au_opt_del *del, int remount)
+{
+ int err, do_wh, rerr;
+ struct dentry *root;
+ struct inode *inode, *hidden_dir;
+ aufs_bindex_t bindex, bend, br_id;
+ struct aufs_sbinfo *sbinfo;
+ struct aufs_dinfo *dinfo;
+ struct aufs_iinfo *iinfo;
+ struct aufs_branch *br;
+
+ //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 (unlikely(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;
+ bend = sbend(sb);
+ if (unlikely(!bend)) {
+ Verbose(sb, "no more branches left\n");
+ goto out;
+ }
+ br = stobr(sb, bindex);
+ if (unlikely(br_count(br))) {
+ Verbose(sb, "%d file(s) opened\n", br_count(br));
+ goto out;
+ }
+
+ do_wh = 0;
+ hidden_dir = del->h_root->d_inode;
+ if (unlikely(br->br_wh || br->br_plink)) {
+#if 0 // rfu
+ /* remove whiteout base */
+ err = init_br_wh(sb, bindex, br, AuBr_RO, del->h_root,
+ br->br_mnt);
+ if (unlikely(err))
+ goto out;
+#else
+ dput(br->br_wh);
+ dput(br->br_plink);
+ br->br_plink = NULL;
+ br->br_wh = NULL;
+#endif
+ do_wh = 1;
+ }
+
+ err = test_children_busy(root, bindex);
+ if (unlikely(err)) {
+ if (unlikely(do_wh))
+ goto out_wh;
+ goto out;
+ }
+
+ err = 0;
+ sbinfo = stosi(sb);
+ dinfo = dtodi(root);
+ iinfo = itoii(inode);
+
+ dput(au_h_dptr_i(root, bindex));
+ aufs_hiput(iinfo->ii_hinode + bindex);
+ br_id = br->br_id;
+ free_branch(br);
+
+ //todo: realloc and shrink memeory
+ if (bindex < bend) {
+ const aufs_bindex_t n = bend - bindex;
+ struct aufs_branch **brp;
+ struct aufs_hdentry *hdp;
+ struct aufs_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 (!bindex)
+ au_cpup_attr_all(inode);
+ else
+ au_sub_nlink(inode, del->h_root->d_inode);
+ if (AuFlag(sbinfo, f_plink))
+ au_half_refresh_plink(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 = 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 (br_whable(a) && !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 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 aufs_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 (unlikely(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 = stobr(sb, bindex);
+ if (unlikely(br->br_perm == mod->perm))
+ return 0; /* success */
+
+ if (br_writable(br->br_perm)) {
+#if 1
+ /* 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;
+#else
+ dput(br->br_wh);
+ dput(br->br_plink);
+ br->br_plink = NULL;
+ br->br_wh = NULL;
+#endif
+
+ if (!br_writable(mod->perm)) {
+ /* rw --> ro, file might be mmapped */
+ struct file *file, *hf;
+
+#if 1 // test here
+ 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 (unlikely(!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)
+ || fbstart(file) != bindex) {
+ FiMustNoWaiters(file);
+ fi_read_unlock(file);
+ continue;
+ }
+
+ // todo: already flushed?
+ hf = au_h_fptr(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);
+#endif
+ }
+ }
+
+ *do_update |= need_sigen_inc(br->br_perm, mod->perm);
+ br->br_perm = mod->perm;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
diff --git a/ubuntu/fs/aufs/branch.h b/ubuntu/fs/aufs/branch.h
new file mode 100644
index 0000000..aacb6a9
--- /dev/null
+++ b/ubuntu/fs/aufs/branch.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: branch.h,v 1.48 2007/12/10 01:18:47 sfjro Exp $ */
+
+#ifndef __AUFS_BRANCH_H__
+#define __AUFS_BRANCH_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+#include "super.h"
+
+/* ---------------------------------------------------------------------- */
+
+/* an entry in a xino file */
+struct xino_entry {
+ ino_t ino;
+ //__u32 h_gen;
+} __packed;
+
+//#define AuXino_INVALID_HGEN (-1)
+
+/* a xino file */
+struct 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 aufs_branch {
+ struct file *br_xino;
+ //struct xino_file *br_xino;
+
+ aufs_bindex_t br_id;
+
+ int br_perm;
+ struct vfsmount *br_mnt;
+ atomic_t br_count;
+
+ /* whiteout base */
+ struct aufs_rwsem br_wh_rwsem;
+ struct dentry *br_wh;
+ atomic_t br_wh_running;
+
+ /* pseudo-link dir */
+ struct dentry *br_plink;
+
+#if 1
+ /* xino truncation */
+ blkcnt_t br_xino_upper; /* watermark in block */
+ atomic_t br_xino_running;
+#endif
+
+ /* mfs mode */
+ u64 br_bytes;
+
+ 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 br_writable(int brperm)
+{
+ return (brperm == AuBr_RW
+ || brperm == AuBr_RWNoLinkWH);
+}
+
+static inline int br_whable(int brperm)
+{
+ return (brperm == AuBr_RW
+ || brperm == AuBr_ROWH
+ || brperm == AuBr_RRWH);
+}
+
+static inline int br_linkable_wh(int brperm)
+{
+ return (brperm == AuBr_RW);
+}
+
+static inline int br_hinotifyable(int brperm)
+{
+#ifdef CONFIG_AUFS_HINOTIFY
+ return (brperm != AuBr_RR
+ && brperm != AuBr_RRWH);
+#else
+ return 0;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* branch.c */
+struct aufs_sbinfo;
+void free_branches(struct aufs_sbinfo *sinfo);
+int br_rdonly(struct aufs_branch *br);
+int au_test_def_rr(struct super_block *h_sb);
+int find_brindex(struct super_block *sb, aufs_bindex_t br_id);
+struct au_opt_add;
+int br_add(struct super_block *sb, struct au_opt_add *add, int remount);
+struct au_opt_del;
+int br_del(struct super_block *sb, struct au_opt_del *del, int remount);
+struct au_opt_mod;
+int br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
+ int *do_update);
+
+/* xino.c */
+int xib_trunc(struct super_block *sb);
+
+struct file *xino_create(struct super_block *sb, char *fname, int silent,
+ struct dentry *parent);
+ino_t xino_new_ino(struct super_block *sb);
+int xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t ino);
+int xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct xino_entry *xinoe);
+int xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct xino_entry *xinoe);
+int xino_br(struct super_block *sb, aufs_bindex_t bindex,
+ struct file *base_file, int do_test);
+int xino_trunc(struct super_block *sb, aufs_bindex_t bindex);
+
+struct au_opt_xino;
+int xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount);
+void xino_clr(struct super_block *sb);
+struct file *xino_def(struct super_block *sb);
+
+/* ---------------------------------------------------------------------- */
+
+static inline int br_count(struct aufs_branch *br)
+{
+ return atomic_read(&br->br_count);
+}
+
+static inline int br_get(struct aufs_branch *br)
+{
+ return atomic_inc_return(&br->br_count);
+}
+
+static inline int br_put(struct aufs_branch *br)
+{
+ return atomic_dec_return(&br->br_count);
+}
+
+static inline au_gen_t au_br_gen(struct aufs_branch *br)
+{
+ return br->br_generation;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* Superblock to branch */
+static inline aufs_bindex_t sbr_id(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return stobr(sb, bindex)->br_id;
+}
+
+static inline
+struct vfsmount *sbr_mnt(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return stobr(sb, bindex)->br_mnt;
+}
+
+static inline
+struct super_block *sbr_sb(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return sbr_mnt(sb, bindex)->mnt_sb;
+}
+
+#if 0 // rfu
+static inline int sbr_count(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return br_count(stobr(sb, bindex));
+}
+
+static inline void sbr_get(struct super_block *sb, aufs_bindex_t bindex)
+{
+ br_get(stobr(sb, bindex));
+}
+#endif
+
+static inline void sbr_put(struct super_block *sb, aufs_bindex_t bindex)
+{
+ br_put(stobr(sb, bindex));
+}
+
+static inline int sbr_perm(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return stobr(sb, bindex)->br_perm;
+}
+
+static inline int sbr_whable(struct super_block *sb, aufs_bindex_t bindex)
+{
+ return br_whable(sbr_perm(sb, bindex));
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef AuNoNfsBranch
+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(sbr_mnt(sb, bindex));
+}
+#else
+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;
+}
+#endif /* AuNoNfsBranch */
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * br_wh_read_lock, br_wh_write_lock
+ * br_wh_read_unlock, br_wh_write_unlock, br_wh_downgrade_lock
+ */
+SimpleRwsemFuncs(br_wh, struct aufs_branch *br, br->br_wh_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define BrWhMustReadLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ RwMustReadLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#define BrWhMustWriteLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ RwMustWriteLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#define BrWhMustAnyLock(br) do { \
+ /* SiMustAnyLock(sb); */ \
+ RwMustAnyLock(&(br)->br_wh_rwsem); \
+} while (0)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_BRANCH_H__ */
diff --git a/ubuntu/fs/aufs/cpup.c b/ubuntu/fs/aufs/cpup.c
new file mode 100644
index 0000000..5a4fb3c
--- /dev/null
+++ b/ubuntu/fs/aufs/cpup.c
@@ -0,0 +1,978 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: cpup.c,v 1.60 2008/01/21 04:55:55 sfjro Exp $ */
+
+#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);
+ AuDebugOn(!h_inode);
+ //IMustLock(!h_inode);
+
+ inode->i_atime = h_inode->i_atime;
+ inode->i_mtime = h_inode->i_mtime;
+ inode->i_ctime = h_inode->i_ctime;
+ spin_lock(&inode->i_lock);
+ i_size_write(inode, i_size_read(h_inode));
+ inode->i_blocks = h_inode->i_blocks;
+ 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);
+ 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 = ibend(inode);
+ for (bindex = ibstart(inode) + 1; bindex <= bend; bindex++) {
+ h_inode = au_h_iptr_i(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);
+ 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;
+ itoii(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);
+ 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_attr_blksize(inode, h_inode);
+ 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 aufs_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 aufs_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_need_dlgt(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 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;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ /* 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);
+ }
+#endif
+ 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 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_i(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);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);
+ sbr_put(sb, hidden[DST].bindex);
+ out_src_file:
+ fput(hidden[SRC].file);
+ sbr_put(sb, hidden[SRC].bindex);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* return with hidden dst inode is locked */
+static int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len,
+ struct au_cpup_flags *flags, int dlgt)
+{
+ int err, symlen;
+ struct dentry *h_src, *h_dst, *h_parent, *parent, *gparent;
+ struct inode *h_inode, *h_dir, *dir;
+ struct au_dtime dt;
+ umode_t mode;
+ char *sym;
+ mm_segment_t old_fs;
+ struct super_block *sb;
+ struct vfsub_args vargs;
+ struct aufs_hinode *hgdir;
+ const int do_dt = flags->dtime;
+
+ 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_i(dentry, bsrc);
+ AuDebugOn(!h_src);
+ h_inode = h_src->d_inode;
+ AuDebugOn(!h_inode);
+
+ /* stop refrencing while we are creating */
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ h_dst = au_h_dptr_i(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);
+
+ if (do_dt) {
+ hgdir = NULL;
+ if (unlikely(au_flag_test_udba_inotify(sb)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ hgdir = itohi(gparent->d_inode, bdst);
+ dput(gparent);
+ }
+ au_dtime_store(&dt, parent, h_parent, hgdir);
+ }
+
+ 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) {
+ loff_t 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 (unlikely(err)) {
+ int rerr;
+ 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;
+ }
+ }
+ }
+ break;
+ case S_IFDIR:
+ 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 && ibstart(dir) == bdst)
+ au_cpup_attr_nlink(dir);
+ au_cpup_attr_nlink(dentry->d_inode);
+ }
+ break;
+ case S_IFLNK:
+ err = -ENOMEM;
+ sym = __getname();
+ //if (LktrCond) {__putname(sym); sym = NULL;}
+ if (unlikely(!sym))
+ break;
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ symlen = h_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);
+ 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;
+ }
+
+ 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 trucating 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, struct au_cpup_flags *flags)
+{
+ int err, rerr, isdir, dlgt;
+ struct dentry *h_src, *h_dst, *parent, *h_parent, *gparent;
+ struct inode *dst_inode, *h_dir, *inode, *src_inode, *dir;
+ struct super_block *sb;
+ aufs_bindex_t old_ibstart;
+ struct au_dtime dt;
+ struct vfsub_args vargs;
+ struct aufs_sbinfo *sbinfo;
+ struct aufs_hinode *hgdir;
+
+ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, dtime %u\n",
+ AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+ flags->dtime);
+ sb = dentry->d_sb;
+ AuDebugOn(bsrc <= bdst);
+ h_dst = au_h_dptr_i(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_i(dentry, bsrc);
+ AuDebugOn(!h_src || !h_src->d_inode);
+ inode = dentry->d_inode;
+ IiMustWriteLock(inode);
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+
+ sbinfo = stosi(sb);
+ dlgt = au_need_dlgt(sb);
+ dst_inode = au_h_iptr_i(inode, bdst);
+ if (unlikely(dst_inode)) {
+ if (unlikely(!AuFlag(sbinfo, f_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_lkup_plink(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 = ibstart(inode);
+ err = cpup_entry(dentry, bdst, bsrc, len, flags, dlgt);
+ if (unlikely(err))
+ goto out;
+ dst_inode = h_dst->d_inode;
+ vfsub_i_lock_nested(dst_inode, AuLsc_I_CHILD2);
+
+ //todo: test dlgt
+ err = cpup_iattr(h_dst, h_src, dlgt);
+ //if (LktrCond) err = -1;
+#if 0 // xattr
+ if (0 && !err)
+ err = cpup_xattrs(h_src, h_dst);
+#endif
+ isdir = S_ISDIR(dst_inode->i_mode);
+ if (!err) {
+ if (bdst < old_ibstart)
+ set_ibstart(inode, bdst);
+ set_h_iptr(inode, bdst, igrab(dst_inode),
+ au_hi_flags(inode, isdir));
+ vfsub_i_unlock(dst_inode);
+ src_inode = h_src->d_inode;
+ if (!isdir
+ && src_inode->i_nlink > 1
+ && AuFlag(sbinfo, f_plink))
+ au_append_plink(sb, inode, h_dst, bdst);
+ goto out; /* success */
+ }
+
+ /* revert */
+ vfsub_i_unlock(dst_inode);
+ hgdir = NULL;
+ if (unlikely(au_flag_test_udba_inotify(sb) && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ hgdir = itohi(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;
+ struct au_cpup_flags *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,
+ struct au_cpup_flags *flags)
+{
+ int err, wkq_err;
+ struct dentry *h_dentry;
+ umode_t mode;
+
+ LKTRTrace("%.*s, i%lu, bdst %d, bsrc %d, len %Ld, dtime %u\n",
+ AuDLNPair(dentry), dentry->d_inode->i_ino, bdst, bsrc, len,
+ flags->dtime);
+
+ h_dentry = au_h_dptr_i(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,
+ struct au_cpup_flags *flags)
+{
+ int err;
+ struct inode *inode;
+ aufs_bindex_t bsrc, bend;
+
+ LKTRTrace("%.*s, bdst %d, len %Ld, dtime %u\n",
+ AuDLNPair(dentry), bdst, len, flags->dtime);
+ inode = dentry->d_inode;
+ AuDebugOn(!S_ISDIR(inode->i_mode) && dbstart(dentry) < bdst);
+
+ bend = dbend(dentry);
+ for (bsrc = bdst + 1; bsrc <= bend; bsrc++)
+ if (au_h_dptr_i(dentry, bsrc))
+ break;
+ AuDebugOn(!au_h_dptr_i(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 */
+ set_h_dptr(dentry, bdst, NULL);
+ 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;
+ struct au_cpup_flags *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,
+ struct au_cpup_flags *flags)
+{
+ int err, do_sio, dlgt, wkq_err;
+ struct dentry *parent;
+ struct inode *h_dir, *dir;
+
+ LKTRTrace("%.*s, b%d, len %Ld, dtime %u\n",
+ AuDLNPair(dentry), bdst, len, flags->dtime);
+
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ h_dir = au_h_iptr_i(dir, bdst);
+ dlgt = au_need_dlgt(dir->i_sb);
+ do_sio = au_test_perm(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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * 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;
+ struct dentry *parent, *h_parent, *wh_dentry, *h_dentry_bdst,
+ *h_dentry_bstart, *gparent;
+ struct inode *h_dir;
+ struct super_block *sb;
+ struct au_dtime dt;
+ struct aufs_dinfo *dinfo;
+ aufs_bindex_t bstart;
+ struct vfsub_args vargs;
+ struct aufs_hinode *hgdir;
+ struct aufs_ndx ndx = {
+ .nd = NULL,
+ //.br = NULL
+ };
+ struct au_cpup_flags cflags = {
+ .dtime = 0
+ };
+
+ 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_i(parent, bdst);
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir);
+
+ sb = parent->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bdst);
+ ndx.dlgt = au_need_dlgt(sb);
+ wh_dentry = lkup_whtmp(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_flag_test_udba_inotify(sb) && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ hgdir = itohi(gparent->d_inode, bdst);
+ dput(gparent);
+ }
+ au_dtime_store(&dt, parent, h_parent, hgdir);
+ dinfo = dtodi(dentry);
+ bstart = dinfo->di_bstart;
+ h_dentry_bdst = dinfo->di_hdentry[0 + bdst].hd_dentry;
+ dinfo->di_bstart = bdst;
+ dinfo->di_hdentry[0 + bdst].hd_dentry = wh_dentry;
+ h_dentry_bstart = dinfo->di_hdentry[0 + bstart].hd_dentry;
+ if (file)
+ dinfo->di_hdentry[0 + bstart].hd_dentry
+ = au_h_fptr(file)->f_dentry;
+ err = au_cpup_single(dentry, bdst, bstart, len, &cflags);
+ //if (LktrCond) err = -1;
+ if (!err && file) {
+ err = au_reopen_nondir(file);
+ //err = -1;
+ dinfo->di_hdentry[0 + bstart].hd_dentry = h_dentry_bstart;
+ }
+ dinfo->di_hdentry[0 + bdst].hd_dentry = h_dentry_bdst;
+ dinfo->di_bstart = bstart;
+ 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, ndx.dlgt, 0);
+ err = vfsub_unlink(h_dir, 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);
+ 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_i(dir, bdst);
+
+ if (!au_test_perm(h_dir, MAY_EXEC | MAY_WRITE,
+ au_need_dlgt(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;
+ struct super_block *sb;
+ struct dentry *d, *parent, *h_parent, *gparent, *real_parent;
+ unsigned int udba;
+
+ 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;
+ udba = au_flag_test_udba_inotify(sb);
+ while (1) {
+ dput(parent);
+ parent = dget_parent(dentry);
+ h_parent = au_h_dptr_i(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_i(parent, bdst);
+ di_read_unlock(parent, !AuLock_IR);
+ } else
+ h_parent = au_h_dptr_i(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_i(d, bdst) || !au_h_dptr_i(d, bdst)->d_inode) {
+ struct inode *h_dir = h_parent->d_inode,
+ *dir = parent->d_inode;
+
+ if (au_h_dptr_i(d, bdst))
+ au_update_dbstart(d);
+ //AuDebugOn(dbstart(d) <= bdst);
+ if (parent != locked)
+ di_read_lock_parent3(parent, AuLock_IR);
+ gparent = NULL;
+ if (unlikely(udba && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ if (gparent != locked)
+ ii_read_lock_parent4(gparent->d_inode);
+ else {
+ dput(gparent);
+ gparent = NULL;
+ }
+ }
+ hdir_lock(h_dir, dir, bdst);
+ err = cp(d, bdst, h_parent, arg);
+ //if (LktrCond) err = -1;
+ 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;
+ struct au_cpup_flags cflags = {
+ .dtime = 1
+ };
+
+ err = au_sio_cpup_simple(dentry, bdst, -1, &cflags);
+
+ 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_i(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_i(dir, bdst)))
+ err = au_cpup_dirs(dentry, bdst, locked);
+ di_downgrade_lock(parent, AuLock_IR);
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
diff --git a/ubuntu/fs/aufs/cpup.h b/ubuntu/fs/aufs/cpup.h
new file mode 100644
index 0000000..df887cc
--- /dev/null
+++ b/ubuntu/fs/aufs/cpup.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: cpup.h,v 1.26 2008/01/21 04:56:10 sfjro Exp $ */
+
+#ifndef __AUFS_CPUP_H__
+#define __AUFS_CPUP_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+
+static inline
+void au_cpup_attr_blksize(struct inode *inode, struct inode *h_inode)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+ inode->i_blksize = h_inode->i_blksize;
+#endif
+}
+
+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);
+
+/* ---------------------------------------------------------------------- */
+
+struct au_cpup_flags {
+ unsigned int dtime:1; /* do dtime_store/revert */
+};
+
+int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len, struct au_cpup_flags *flags);
+int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst,
+ aufs_bindex_t bsrc, loff_t len,
+ struct au_cpup_flags *flags);
+int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct au_cpup_flags *flags);
+int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len,
+ struct au_cpup_flags *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 aufs_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 aufs_hinode *hdir);
+void au_dtime_revert(struct au_dtime *dt);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_CPUP_H__ */
diff --git a/ubuntu/fs/aufs/dcsub.c b/ubuntu/fs/aufs/dcsub.c
new file mode 100644
index 0000000..eb2a541
--- /dev/null
+++ b/ubuntu/fs/aufs/dcsub.c
@@ -0,0 +1,241 @@
+/*
+ * 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
+ */
+
+/* $Id: dcsub.c,v 1.9 2007/10/22 02:14:17 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;
+ 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_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_CHILD.next;
+ this_parent = this_parent->d_parent; /* dcache_lock is locked */
+ goto resume;
+ }
+ out:
+ spin_unlock(&dcache_lock);
+#if 0 // debug
+ if (!err) {
+ int i, j;
+ j = 0;
+ for (i = 0; i < dpages->ndpage; i++) {
+ if ((dpages->dpages + i)->ndentry)
+ AuDbg("%d: %d\n",
+ i, (dpages->dpages + i)->ndentry);
+ j += (dpages->dpages + i)->ndentry;
+ }
+ if (j)
+ AuDbg("ndpage %d, %d\n", dpages->ndpage, j);
+ }
+#endif
+ 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 --git a/ubuntu/fs/aufs/dcsub.h b/ubuntu/fs/aufs/dcsub.h
new file mode 100644
index 0000000..707a360
--- /dev/null
+++ b/ubuntu/fs/aufs/dcsub.h
@@ -0,0 +1,50 @@
+/*
+ * 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
+ */
+
+/* $Id: dcsub.h,v 1.5 2007/10/15 01:26:08 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 --git a/ubuntu/fs/aufs/debug.c b/ubuntu/fs/aufs/debug.c
new file mode 100644
index 0000000..a69e8ef
--- /dev/null
+++ b/ubuntu/fs/aufs/debug.c
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: debug.c,v 1.43 2007/11/12 01:43:10 sfjro Exp $ */
+
+#include <linux/errno.h>
+#include <linux/sysrq.h>
+#include "aufs.h"
+
+atomic_t aufs_cond = ATOMIC_INIT(0);
+
+static 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 aufs_nhash *whlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct aufs_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 aufs_vdir *vdir)
+{
+ int i;
+ union aufs_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);
+#if 0 // verbose
+ int j;
+ for (j = 0; j < 8; j++) {
+ dpri("%p(+%d) {%02x %02x %02x %02x %02x %02x %02x %02x "
+ "%02x %02x %02x %02x %02x %02x %02x %02x}\n",
+ p.p, p.p - o,
+ p.p[0], p.p[1], p.p[2], p.p[3],
+ p.p[4], p.p[5], p.p[6], p.p[7],
+ p.p[8], p.p[9], p.p[10], p.p[11],
+ p.p[12], p.p[13], p.p[14], p.p[15]);
+ p.p += 16;
+ }
+#endif
+ }
+}
+
+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;
+ }
+#ifdef CONFIG_INOTIFY
+ ntfy = !list_empty(&inode->inotify_watches);
+#endif
+ 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 aufs_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 = itoii(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 aufs_iinfo *iinfo = itoii(dentry->d_inode);
+ if (iinfo)
+ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;
+ }
+ do_pri_inode(bindex, dentry->d_inode, wh);
+ return 0;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+#define intent(dinfo, bindex) (dinfo)->di_hdentry[0 + (bindex)].hd_intent_list
+#else
+#define intent(dinfo, bindex) NULL
+#endif
+
+void au_dpri_dentry(struct dentry *dentry)
+{
+ struct aufs_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 = dtodi(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,
+ 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)
+ && ftofi(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 aufs_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 = ftofi(file);
+ if (!finfo)
+ return;
+ if (finfo->fi_bstart < 0)
+ return;
+ for (bindex = finfo->fi_bstart; bindex <= finfo->fi_bend; bindex++) {
+ struct aufs_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 aufs_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, 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 aufs_sbinfo *sbinfo;
+ aufs_bindex_t bindex;
+ int err;
+ struct vfsmount mnt = { .mnt_sb = sb };
+ struct aufs_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 = stosi(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);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_MAGIC_SYSRQ
+static void sysrq_sb(struct super_block *sb)
+{
+ char *plevel;
+ struct inode *i;
+
+ plevel = au_plevel;
+ au_plevel = KERN_WARNING;
+ au_debug_on();
+
+ dpri(AUFS_NAME ": superblock\n");
+ au_dpri_sb(sb);
+ dpri(AUFS_NAME ": root dentry\n");
+ au_dpri_dentry(sb->s_root);
+#if 1
+ dpri(AUFS_NAME ": root inode\n");
+ au_dpri_inode(sb->s_root->d_inode);
+#endif
+#if 1
+ dpri(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);
+#endif
+
+ au_plevel = plevel;
+ au_debug_off();
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+static void au_sysrq(int key, struct tty_struct *tty)
+#else
+static void au_sysrq(int key, struct pt_regs *regs, struct tty_struct *tty)
+#endif
+{
+ au_each_sb(sysrq_sb, /*do_lock*/0);
+}
+
+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);
+}
+#endif /* CONFIG_MAGIC_SYSRQ */
diff --git a/ubuntu/fs/aufs/debug.h b/ubuntu/fs/aufs/debug.h
new file mode 100644
index 0000000..4bac966
--- /dev/null
+++ b/ubuntu/fs/aufs/debug.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: debug.h,v 1.41 2007/12/17 03:30:45 sfjro Exp $ */
+
+#ifndef __AUFS_DEBUG_H__
+#define __AUFS_DEBUG_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+
+#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 aufs_cond;
+#define au_debug_on() atomic_inc_return(&aufs_cond)
+#define au_debug_off() atomic_dec_return(&aufs_cond)
+#define au_test_debug() atomic_read(&aufs_cond)
+#else
+#define AuDebugOn(a) do {} while (0)
+#define au_debug_on() do {} while (0)
+#define au_debug_off() do {} while (0)
+#define au_test_debug() 0
+#endif /* CONFIG_AUFS_DEBUG */
+
+#if defined(CONFIG_AUFS_DEBUG) && defined(CONFIG_MAGIC_SYSRQ)
+int __init au_sysrq_init(void);
+void au_sysrq_fin(void);
+#else
+#define au_sysrq_init() 0
+#define au_sysrq_fin() do {} while (0)
+#endif /* CONFIG_AUFS_DEBUG && CONFIG_MAGIC_SYSRQ */
+
+/* ---------------------------------------------------------------------- */
+
+/* debug print */
+#if defined(CONFIG_LKTR) || defined(CONFIG_LKTR_MODULE)
+#include <linux/lktr.h>
+#ifdef CONFIG_AUFS_DEBUG
+#undef LktrCond
+#define LktrCond unlikely(au_test_debug() || (lktr_cond && lktr_cond()))
+#endif
+#else
+#define LktrCond au_test_debug()
+#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 me this application."
+#define AuUnsupport(fmt, args...) do { \
+ AuErr(AuUnsupportMsg "\n" fmt, ##args); \
+ dump_stack(); \
+} while (0)
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_DEBUG
+struct aufs_nhash;
+void au_dpri_whlist(struct aufs_nhash *whlist);
+struct aufs_vdir;
+void au_dpri_vdir(struct aufs_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);
+#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
+#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 */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DEBUG_H__ */
diff --git a/ubuntu/fs/aufs/dentry.c b/ubuntu/fs/aufs/dentry.c
new file mode 100644
index 0000000..046c120
--- /dev/null
+++ b/ubuntu/fs/aufs/dentry.c
@@ -0,0 +1,1164 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: dentry.c,v 1.65 2008/01/21 04:57:48 sfjro Exp $ */
+
+//#include <linux/fs.h>
+//#include <linux/namei.h>
+#include "aufs.h"
+
+#if 0
+/* subset of nameidata */
+struct au_ndsub {
+ struct dentry *dentry;
+ struct vfsmount *mnt;
+ unsigned int flags;
+
+ union {
+ struct open_intent open;
+ } intent;
+};
+
+static void au_ndsub_restore(struct nameidata *nd, struct au_ndsub *save)
+{
+ nd->dentry = save->dentry;
+ nd->mnt = save->mnt;
+ nd->flags = save->flags;
+ nd->intent = save->intent;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+static int au_fake_intent(/* struct au_ndsub *save, */struct nameidata *nd,
+ int perm)
+{
+ int err;
+
+ LKTRTrace("perm %d\n", perm);
+
+ err = 0;
+#if 0
+ save->dentry = nd->dentry;
+ save->mnt = nd->mnt;
+ save->flags = nd->flags;
+ save->intent = nd->intent;
+#endif
+
+ 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 (!br_writable(perm)) {
+ nd->intent.open.flags = au_file_roflags
+ (nd->intent.open.flags) | FMODE_READ;
+ nd->flags &= ~LOOKUP_CREATE;
+ }
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static 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);
+}
+#endif /* CONFIG_AUFS_DLGT */
+
+static struct dentry *au_lkup_hash(const char *name, struct dentry *parent,
+ int len, struct aufs_ndx *ndx)
+{
+ struct dentry *dentry;
+ char *p;
+ unsigned long hash;
+ struct qstr this;
+ unsigned int c;
+ struct nameidata tmp_nd, *ndo;
+ int err;
+
+ 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.dentry = dget(parent);
+ tmp_nd.mnt = mntget(ndx->nfsmnt);
+#ifndef CONFIG_AUFS_DLGT
+ dentry = vfsub__lookup_hash(&this, parent, &tmp_nd);
+#else
+ if (!ndx->dlgt)
+ dentry = vfsub__lookup_hash(&this, parent, &tmp_nd);
+ else {
+ int wkq_err;
+ struct au_lookup_hash_args args = {
+ .errp = &dentry,
+ .name = &this,
+ .base = parent,
+ .nd = &tmp_nd
+ };
+ wkq_err = au_wkq_wait(au_call_lookup_hash, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ }
+#endif /* CONFIG_AUFS_DLGT */
+ 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;
+ //br_get(ndx->br);
+ }
+ }
+ path_release(&tmp_nd);
+
+ out_intent:
+ if (tmp_nd.intent.open.file)
+ put_filp(tmp_nd.intent.open.file);
+ out:
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+#else
+static int au_fake_intent(struct nameidata *nd, int perm)
+{
+ return 0;
+}
+
+static int au_hin_after_reval(struct nameidata *nd, struct dentry *dentry,
+ aufs_bindex_t bindex, struct file *file)
+{
+ return 0;
+}
+
+#if !defined(AuNoNfsBranch) || defined(CONFIG_AUFS_DLGT)
+static struct dentry *au_lkup_hash(const char *name, struct dentry *parent,
+ int len, struct aufs_ndx *ndx)
+{
+ return ERR_PTR(-ENOSYS);
+}
+#endif
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) && !AuNoNfsBranch */
+
+/* ---------------------------------------------------------------------- */
+
+#if !defined(AuNoNfsBranch) || defined(CONFIG_AUFS_DLGT)
+
+#ifdef CONFIG_AUFS_DLGT
+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);
+}
+#endif /* 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 aufs_ndx *ndx)
+{
+ struct dentry *dentry;
+
+ LKTRTrace("%.*s/%.*s, ndx{%d, %d}\n",
+ AuDLNPair(parent), len, name, !!ndx->nfsmnt, ndx->dlgt);
+
+ ndx->nd_file = NULL;
+ if (!ndx->nfsmnt) {
+#ifndef CONFIG_AUFS_DLGT
+ dentry = vfsub_lookup_one_len(name, parent, len);
+#else
+ if (!ndx->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*/1);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ }
+#endif /* CONFIG_AUFS_DLGT */
+
+ } else
+ dentry = au_lkup_hash(name, parent, len, ndx);
+
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+#endif /* !defined(AuNoNfsBranch) || defined(CONFIG_AUFS_DLGT) */
+
+struct au_lkup_one_args {
+ struct dentry **errp;
+ const char *name;
+ struct dentry *parent;
+ int len;
+ struct aufs_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);
+}
+
+struct au_do_lookup_args {
+ unsigned int allow_neg:1;
+ unsigned int dlgt:1;
+ mode_t type;
+ struct nameidata *nd;
+};
+
+/*
+ * returns positive/negative dentry, NULL or an error.
+ * NULL means whiteout-ed or not-found.
+ */
+static
+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;
+ struct inode *h_dir, *h_inode;
+ struct qstr *name;
+ struct super_block *sb;
+ struct nameidata tmp_nd;
+ struct aufs_ndx ndx = {
+ .dlgt = args->dlgt,
+ .nd = args->nd
+ };
+
+ LKTRTrace("%.*s/%.*s, b%d, {allow_neg %d, type 0%o, dlgt %d, nd %d}\n",
+ AuDLNPair(h_parent), AuDLNPair(dentry), bindex,
+ args->allow_neg, args->type, args->dlgt, !!args->nd);
+ AuDebugOn(IS_ROOT(dentry));
+ h_dir = h_parent->d_inode;
+
+ wh_found = 0;
+ sb = dentry->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ LKTRTrace("nfsmnt %p\n", ndx.nfsmnt);
+ ndx.br = stobr(sb, bindex);
+ wh_able = br_whable(ndx.br->br_perm);
+ name = &dentry->d_name;
+ if (unlikely(wh_able)) {
+#ifdef CONFIG_AUFS_ROBR
+ wh_found = -EPERM;
+ if (strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))
+ wh_found = au_test_wh(h_parent, wh_name, /*try_sio*/0,
+ &ndx);
+#else
+ wh_found = au_test_wh(h_parent, wh_name, /*try_sio*/0, &ndx);
+#endif
+ }
+ //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 */
+ //set_dbend(dentry, bindex);
+ set_dbwh(dentry, bindex);
+ if (!args->allow_neg)
+ return NULL; /* success */
+ if (unlikely(ndx.nd
+ && au_test_nfs(h_parent->d_sb)
+ && (ndx.nd->flags & LOOKUP_CREATE))) {
+ tmp_nd = *ndx.nd;
+ tmp_nd.flags &= ~(LOOKUP_OPEN | LOOKUP_CREATE);
+ ndx.nd = &tmp_nd;
+ }
+
+ 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 (!args->allow_neg)
+ goto out_neg;
+ } else if (wh_found
+ || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
+ goto out_neg;
+
+ if (dbend(dentry) <= bindex)
+ set_dbend(dentry, bindex);
+ if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
+ set_dbstart(dentry, bindex);
+ set_h_dptr(dentry, bindex, h_dentry);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+ if (unlikely(ndx.nd_file)) {
+ int err;
+ AuDebugOn(!args->nd);
+ err = au_set_h_intent(dentry, bindex,
+ args->nd->intent.open.file, ndx.nd_file);
+ if (unlikely(err)) {
+ fput(ndx.nd_file);
+ set_h_dptr(dentry, bindex, NULL);
+ //todo: update bstart and bend
+ h_dentry = ERR_PTR(err);
+ goto out;
+ }
+ }
+#endif
+
+ if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able)
+ return h_dentry; /* success */
+
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ opq = au_test_diropq(h_dentry, &ndx);
+ //if (LktrCond) opq = -1;
+ vfsub_i_unlock(h_inode);
+ if (opq > 0)
+ set_dbdiropq(dentry, bindex);
+ else if (unlikely(opq < 0)) {
+ set_h_dptr(dentry, bindex, NULL);
+ h_dentry = ERR_PTR(opq);
+ }
+ goto out;
+
+ out_neg:
+ dput(h_dentry);
+ h_dentry = NULL;
+ out:
+ AuTraceErrPtr(h_dentry);
+ return h_dentry;
+}
+
+/*
+ * returns the number of hidden positive dentries,
+ * otherwise an error.
+ */
+int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type,
+ struct nameidata *nd)
+{
+ int npositive, err;
+ struct dentry *parent;
+ aufs_bindex_t bindex, btail;
+ const struct qstr *name = &dentry->d_name;
+ struct qstr whname;
+ struct super_block *sb;
+ 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);
+
+#ifndef CONFIG_AUFS_ROBR
+ err = -EPERM;
+ if (unlikely(!strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)))
+ goto out;
+#endif
+
+ err = au_alloc_whname(name->name, name->len, &whname);
+ //if (LktrCond) {au_free_whname(&whname); err = -1;}
+ if (unlikely(err))
+ goto out;
+
+ sb = dentry->d_sb;
+ args.dlgt = !!au_need_dlgt(sb);
+ args.allow_neg = !type;
+ npositive = 0;
+ btail = 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_i(dentry, bindex);
+ if (h_dentry) {
+ if (h_dentry->d_inode)
+ npositive++;
+ if (type != S_IFDIR)
+ break;
+ continue;
+ }
+ h_parent = au_h_dptr_i(parent, bindex);
+ if (!h_parent)
+ continue;
+ h_dir = h_parent->d_inode;
+ if (!h_dir || !S_ISDIR(h_dir->i_mode))
+ continue;
+
+ vfsub_i_lock_nested(h_dir, 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);}
+ vfsub_i_unlock(h_dir);
+ err = PTR_ERR(h_dentry);
+ if (IS_ERR(h_dentry))
+ goto out_wh;
+ args.allow_neg = 0;
+
+ if (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 (dbdiropq(dentry) >= 0)
+ break;
+ }
+
+ if (npositive) {
+ LKTRLabel(positive);
+ au_update_dbstart(dentry);
+ }
+ err = npositive;
+
+ out_wh:
+ au_free_whname(&whname);
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+struct dentry *au_sio_lkup_one(const char *name, struct dentry *parent, int len,
+ struct aufs_ndx *ndx)
+{
+ struct dentry *dentry;
+ int wkq_err;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(parent), len, name);
+
+ if (!au_test_perm(parent->d_inode, MAY_EXEC, ndx->dlgt))
+ dentry = au_lkup_one(name, parent, len, ndx);
+ else {
+ // ugly
+ int dlgt = ndx->dlgt;
+ struct au_lkup_one_args args = {
+ .errp = &dentry,
+ .name = name,
+ .parent = parent,
+ .len = len,
+ .ndx = ndx
+ };
+
+ ndx->dlgt = 0;
+ wkq_err = au_wkq_wait(au_call_lkup_one, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ dentry = ERR_PTR(wkq_err);
+ ndx->dlgt = dlgt;
+ }
+
+ 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 aufs_ndx ndx = {
+ .nd = NULL,
+ //.br = NULL
+ };
+ struct super_block *sb;
+
+ 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_i(parent, bindex);
+ AuDebugOn(!h_parent);
+ h_dir = h_parent->d_inode;
+ AuDebugOn(!h_dir || !S_ISDIR(h_dir->i_mode));
+
+ sb = dentry->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ ndx.dlgt = au_need_dlgt(sb);
+ 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 < dbstart(dentry))
+ set_dbstart(dentry, bindex);
+ if (dbend(dentry) < bindex)
+ set_dbend(dentry, bindex);
+ 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 aufs_dinfo *dinfo;
+ struct super_block *sb;
+ struct dentry *parent;
+ aufs_bindex_t bindex, parent_bend, parent_bstart, bwh, bdiropq, bend;
+ struct aufs_hdentry *p;
+
+ LKTRTrace("%.*s, type 0%o\n", AuDLNPair(dentry), type);
+ DiMustWriteLock(dentry);
+ sb = dentry->d_sb;
+ AuDebugOn(IS_ROOT(dentry));
+ parent = dget_parent(dentry);
+ AuDebugOn(au_digen(parent) != au_sigen(sb));
+
+ npositive = -ENOMEM;
+ new_sz = sizeof(*dinfo->di_hdentry) * (sbend(sb) + 1);
+ dinfo = dtodi(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 aufs_hdentry tmp, *q;
+ aufs_bindex_t new_bindex;
+
+ hd = p->hd_dentry;
+ if (!hd)
+ continue;
+ hdp = dget_parent(hd);
+ if (hdp == au_h_dptr_i(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
+ hdput(p);
+ 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 <= sbend(sb) && sbr_whable(sb, bwh)))
+ dinfo->di_bwh = bwh;
+ dinfo->di_bdiropq = -1;
+ if (unlikely(bdiropq >= 0 && bdiropq <= sbend(sb)
+ && sbr_whable(sb, bdiropq)))
+ dinfo->di_bdiropq = bdiropq;
+ parent_bend = 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 = 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 h_d_revalidate(struct dentry *dentry, struct inode *inode,
+ struct nameidata *nd, int do_udba)
+{
+ int err, plus, locked, unhashed, is_root, h_plus, valid;
+ struct nameidata fake_nd, *p;
+ 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;
+ int (*reval)(struct dentry *, struct nameidata *);
+ 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);
+ ibs = ibstart(inode);
+ ibe = ibend(inode);
+ }
+
+ bstart = dbstart(dentry);
+ btail = bstart;
+ if (inode && S_ISDIR(inode->i_mode))
+ btail = dbtaildir(dentry);
+ locked = 0;
+#ifndef CONFIG_AUFS_FAKE_DM
+ if (nd && dentry != nd->dentry) {
+ di_read_lock_parent(nd->dentry, 0);
+ locked = 1;
+ }
+#endif
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr_i(dentry, bindex);
+ if (unlikely(!h_dentry))
+ continue;
+ if (unlikely(do_udba
+ && !is_root
+ && (unhashed != d_unhashed(h_dentry)
+//#if 1
+ || name->len != h_dentry->d_name.len
+ || memcmp(name->name, h_dentry->d_name.name,
+ name->len)
+//#endif
+ ))) {
+ LKTRTrace("unhash 0x%x 0x%x, %.*s %.*s\n",
+ unhashed, d_unhashed(h_dentry),
+ AuDLNPair(dentry), AuDLNPair(h_dentry));
+ goto err;
+ }
+
+ reval = NULL;
+ if (h_dentry->d_op)
+ reval = h_dentry->d_op->d_revalidate;
+ if (unlikely(reval)) {
+ //LKTRLabel(hidden reval);
+ if (nd) {
+ memcpy(&fake_nd, nd, sizeof(*nd));
+ err = au_fake_intent(&fake_nd,
+ sbr_perm(sb, bindex));
+ if (unlikely(err))
+ goto err;
+ }
+ 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) {
+ int e;
+ AuDebugOn(!nd);
+ e = au_hin_after_reval(p, dentry, bindex,
+ nd->intent.open.file);
+ au_update_fuse_h_inode(p->mnt, h_dentry);
+ /*ignore*/
+ if (unlikely(e && !err))
+ err = e;
+ } else
+ au_update_fuse_h_inode(NULL, h_dentry);
+ /*ignore*/
+ au_fake_dm_release(p);
+ if (unlikely(err)) {
+ //AuDbg("here\n");
+ /* 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_i(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;
+ }
+#ifndef CONFIG_AUFS_FAKE_DM
+ if (unlikely(locked))
+ di_read_unlock(nd->dentry, 0);
+#endif
+
+ /*
+ * 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.
+ */
+
+#if 0 // debug
+ if (!err)
+ au_update_from_fuse(inode);
+ if (unlikely(!err && udba && first))
+ au_cpup_attr_all(inode);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+static 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)
+ return 0;
+
+ parent = dget_parent(dentry);
+ di_read_lock_parent(parent, AuLock_IR);
+ AuDebugOn(au_digen(parent) != 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 (!stosi(dentry->d_sb)->si_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) {
+ d = dentry;
+ while (1) {
+ dput(parent);
+ parent = dget_parent(d);
+ if (au_digen(parent) == sgen)
+ break;
+ d = parent;
+ }
+
+ inode = d->d_inode;
+ if (d != dentry) {
+ //vfsub_i_lock(inode);
+ di_write_lock_child(d);
+ }
+
+ /* someone might update our dentry while we were sleeping */
+ if (au_digen(d) != 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);
+ //vfsub_i_unlock(inode);
+ }
+ 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;
+ struct aufs_sbinfo *sbinfo;
+ //todo: no more lower nameidata, only here. re-use it.
+
+ LKTRTrace("dentry %.*s\n", AuDLNPair(dentry));
+ if (nd && nd->dentry)
+ LKTRTrace("nd{%.*s, 0x%x}\n", AuDLNPair(nd->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_read_lock_child(dentry, AuLock_IR);err = -EINVAL; goto out;
+ //vfsub_i_lock(inode);
+ di_write_lock_child(dentry);
+ if (inode)
+ err = au_reval_dpath(dentry, sgen);
+ //err = -1;
+ di_downgrade_lock(dentry, AuLock_IR);
+ //vfsub_i_unlock(inode);
+ if (unlikely(err))
+ goto out;
+ ii_read_unlock(inode);
+ AuDebugOn(au_iigen(inode) != 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_read_lock_child(inode);err = -EINVAL; goto out;
+ 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);
+ }
+ }
+
+#if 0 // fix it
+ /* parent dir i_nlink is not updated in the case of setattr */
+ if (S_ISDIR(inode->i_mode)) {
+ vfsub_i_lock(inode);
+ ii_write_lock(inode);
+ au_cpup_attr_nlink(inode);
+ ii_write_unlock(inode);
+ vfsub_i_unlock(inode);
+ }
+#endif
+
+ err = -EINVAL;
+ sbinfo = stosi(sb);
+ do_udba = (AuFlag(sbinfo, f_udba) != AuUdba_NONE);
+ if (do_udba && inode && ibstart(inode) >= 0
+ && au_test_higen(inode, au_h_iptr(inode)))
+ goto out;
+ ndp = au_dup_nd(sbinfo, &tmp_nd, nd);
+ err = h_d_revalidate(dentry, inode, ndp, do_udba);
+ //err = -1;
+
+ out:
+#if 0
+ AuDbgDentry(dentry);
+ if (nd && (nd->flags & LOOKUP_OPEN)) {
+ LKTRTrace("intent.open.file %p\n", nd->intent.open.file);
+ if (nd->intent.open.file && nd->intent.open.file->f_dentry)
+ AuDbgFile(nd->intent.open.file);
+ }
+#endif
+ 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 aufs_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 aufs_hdentry *p;
+ bend = dinfo->di_bend;
+ AuDebugOn(bend < bindex);
+ p = dinfo->di_hdentry + bindex;
+ while (bindex++ <= bend) {
+ if (p->hd_dentry)
+ hdput(p);
+ 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 --git a/ubuntu/fs/aufs/dentry.h b/ubuntu/fs/aufs/dentry.h
new file mode 100644
index 0000000..42757bb
--- /dev/null
+++ b/ubuntu/fs/aufs/dentry.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: dentry.h,v 1.37 2007/11/05 01:37:02 sfjro Exp $ */
+
+#ifndef __AUFS_DENTRY_H__
+#define __AUFS_DENTRY_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+#include "vfsub.h"
+
+/* nameidata open_intent */
+enum {
+ AuIntent_AUFS,
+ AuIntent_BRANCH,
+ AuIntent_Last
+};
+
+struct aufs_hdintent {
+ struct list_head hdi_list;
+ struct file *hdi_file[AuIntent_Last];
+};
+
+struct aufs_hdentry {
+ struct dentry *hd_dentry;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+ spinlock_t hd_lock; /* intest_list */
+ struct list_head *hd_intent_list;
+#endif
+};
+
+struct aufs_dinfo {
+ atomic_t di_generation;
+
+ struct aufs_rwsem di_rwsem;
+ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq;
+ struct aufs_hdentry *di_hdentry;
+};
+
+/* nameidata extension */
+struct aufs_ndx {
+ struct vfsmount *nfsmnt;
+ int dlgt;
+ struct nameidata *nd;
+ struct aufs_branch *br;
+ struct file *nd_file;
+};
+
+/* ---------------------------------------------------------------------- */
+
+/* dentry.c */
+extern struct dentry_operations aufs_dop;
+#if !defined(AuNoNfsBranch) || defined(CONFIG_AUFS_DLGT)
+struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len,
+ struct aufs_ndx *ndx);
+#else
+static inline
+struct dentry *au_lkup_one(const char *name, struct dentry *parent, int len,
+ struct aufs_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 aufs_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 aufs_dinfo *dtodi(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);
+
+aufs_bindex_t dbstart(struct dentry *dentry);
+aufs_bindex_t dbend(struct dentry *dentry);
+aufs_bindex_t dbwh(struct dentry *dentry);
+aufs_bindex_t dbdiropq(struct dentry *dentry);
+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex);
+struct dentry *au_h_dptr(struct dentry *dentry);
+
+aufs_bindex_t dbtail(struct dentry *dentry);
+aufs_bindex_t dbtaildir(struct dentry *dentry);
+aufs_bindex_t dbtail_generic(struct dentry *dentry);
+
+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex);
+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex);
+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex);
+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex);
+void hdput(struct aufs_hdentry *hdentry);
+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_dentry);
+
+static inline void au_do_h_dentry_init(struct aufs_hdentry *hdentry)
+{
+ hdentry->hd_dentry = NULL;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+static inline void au_h_dentry_init(struct aufs_hdentry *hdentry)
+{
+ au_do_h_dentry_init(hdentry);
+ spin_lock_init(&hdentry->hd_lock);
+}
+
+static inline void au_h_dentry_init_all(struct aufs_hdentry *hdentry, int n)
+{
+ while (n--)
+ spin_lock_init(&hdentry[n].hd_lock);
+}
+
+int au_set_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file, struct file *h_file);
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file);
+#else
+static inline
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file)
+{
+ return ERR_PTR(-ENOSYS);
+}
+
+static inline void au_h_dentry_init(struct aufs_hdentry *hdentry)
+{
+ au_do_h_dentry_init(hdentry);
+}
+
+static inline void au_h_dentry_init_all(struct aufs_hdentry *hdentry, int n)
+{
+ /* nothing */
+}
+#endif
+
+void au_update_digen(struct dentry *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);
+
+/* ---------------------------------------------------------------------- */
+
+static inline au_gen_t au_digen(struct dentry *d)
+{
+ return atomic_read(&dtodi(d)->di_generation);
+}
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline au_gen_t au_digen_dec(struct dentry *d)
+{
+ return atomic_dec_return(&dtodi(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 ReadLockFunc(name, lsc) \
+static inline void di_read_lock_##name(struct dentry *d, int flags) \
+{ di_read_lock(d, flags, AuLsc_DI_##lsc); }
+
+#define WriteLockFunc(name, lsc) \
+static inline void di_write_lock_##name(struct dentry *d) \
+{ di_write_lock(d, AuLsc_DI_##lsc); }
+
+#define RWLockFuncs(name, lsc) \
+ ReadLockFunc(name, lsc) \
+ WriteLockFunc(name, lsc)
+
+RWLockFuncs(child, CHILD);
+RWLockFuncs(child2, CHILD2);
+RWLockFuncs(child3, CHILD3);
+RWLockFuncs(parent, PARENT);
+RWLockFuncs(parent2, PARENT2);
+RWLockFuncs(parent3, PARENT3);
+
+#undef ReadLockFunc
+#undef WriteLockFunc
+#undef RWLockFunc
+
+/* to debug easier, do not make them inlined functions */
+#define DiMustReadLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ RwMustReadLock(&dtodi(d)->di_rwsem); \
+} while (0)
+
+#define DiMustWriteLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ RwMustWriteLock(&dtodi(d)->di_rwsem); \
+} while (0)
+
+#define DiMustAnyLock(d) do { \
+ SiMustAnyLock((d)->d_sb); \
+ RwMustAnyLock(&dtodi(d)->di_rwsem); \
+} while (0)
+
+#define DiMustNoWaiters(d) RwMustNoWaiters(&dtodi(d)->di_rwsem)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DENTRY_H__ */
diff --git a/ubuntu/fs/aufs/dinfo.c b/ubuntu/fs/aufs/dinfo.c
new file mode 100644
index 0000000..f158ce2
--- /dev/null
+++ b/ubuntu/fs/aufs/dinfo.c
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: dinfo.c,v 1.37 2007/11/05 01:37:14 sfjro Exp $ */
+
+#include "aufs.h"
+
+int au_alloc_dinfo(struct dentry *dentry)
+{
+ struct aufs_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 = 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 */
+ 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 aufs_dinfo *dtodi(struct dentry *dentry)
+{
+ struct aufs_dinfo *dinfo = dentry->d_fsdata;
+ AuDebugOn(!dinfo
+ || !dinfo->di_hdentry
+ /* || stosi(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?
+ rw_read_lock_nested(&dtodi(d)->di_rwsem, lsc);
+ if (d->d_inode) {
+ if (flags & AuLock_IW)
+ do_ii_write_lock(d->d_inode, lsc);
+ else if (flags & AuLock_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 (flags & AuLock_IW)
+ ii_write_unlock(d->d_inode);
+ else if (flags & AuLock_IR)
+ ii_read_unlock(d->d_inode);
+ }
+ rw_read_unlock(&dtodi(d)->di_rwsem);
+}
+
+void di_downgrade_lock(struct dentry *d, int flags)
+{
+ SiMustAnyLock(d->d_sb);
+ rw_dgrade_lock(&dtodi(d)->di_rwsem);
+ if (d->d_inode && (flags & AuLock_IR))
+ ii_downgrade_lock(d->d_inode);
+}
+
+void di_write_lock(struct dentry *d, unsigned int lsc)
+{
+ SiMustAnyLock(d->d_sb);
+ // todo: always nested?
+ rw_write_lock_nested(&dtodi(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);
+ rw_write_unlock(&dtodi(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)
+ rw_write_unlock(&dtodi(d2)->di_rwsem);
+ else
+ di_write_unlock(d2);
+}
+
+/* ---------------------------------------------------------------------- */
+
+aufs_bindex_t dbstart(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return dtodi(dentry)->di_bstart;
+}
+
+aufs_bindex_t dbend(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return dtodi(dentry)->di_bend;
+}
+
+aufs_bindex_t dbwh(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ return dtodi(dentry)->di_bwh;
+}
+
+aufs_bindex_t dbdiropq(struct dentry *dentry)
+{
+ DiMustAnyLock(dentry);
+ AuDebugOn(dentry->d_inode
+ && dentry->d_inode->i_mode
+ && !S_ISDIR(dentry->d_inode->i_mode));
+ return dtodi(dentry)->di_bdiropq;
+}
+
+struct dentry *au_h_dptr_i(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ struct dentry *d;
+
+ DiMustAnyLock(dentry);
+ if (dbstart(dentry) < 0 || bindex < dbstart(dentry))
+ return NULL;
+ AuDebugOn(bindex < 0
+ /* || bindex > sbend(dentry->d_sb) */);
+ d = dtodi(dentry)->di_hdentry[0 + bindex].hd_dentry;
+ AuDebugOn(d && (atomic_read(&d->d_count) <= 0));
+ return d;
+}
+
+struct dentry *au_h_dptr(struct dentry *dentry)
+{
+ return au_h_dptr_i(dentry, dbstart(dentry));
+}
+
+aufs_bindex_t dbtail(struct dentry *dentry)
+{
+ aufs_bindex_t bend, bwh;
+
+ bend = dbend(dentry);
+ if (0 <= bend) {
+ bwh = dbwh(dentry);
+ //AuDebugOn(bend < bwh);
+ if (!bwh)
+ return bwh;
+ if (0 < bwh && bwh < bend)
+ return bwh - 1;
+ }
+ return bend;
+}
+
+aufs_bindex_t 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 = dbtail(dentry);
+ if (0 <= bend) {
+ bopq = dbdiropq(dentry);
+ AuDebugOn(bend < bopq);
+ if (0 <= bopq && bopq < bend)
+ bend = bopq;
+ }
+ return bend;
+}
+
+aufs_bindex_t dbtail_generic(struct dentry *dentry)
+{
+ struct inode *inode;
+
+ inode = dentry->d_inode;
+ if (inode && S_ISDIR(inode->i_mode))
+ return dbtaildir(dentry);
+ else
+ return dbtail(dentry);
+}
+
+/* ---------------------------------------------------------------------- */
+
+// hard/soft set
+void set_dbstart(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(sbend(dentry->d_sb) < bindex);
+ /* */
+ dtodi(dentry)->di_bstart = bindex;
+}
+
+void set_dbend(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(sbend(dentry->d_sb) < bindex
+ || bindex < dbstart(dentry));
+ dtodi(dentry)->di_bend = bindex;
+}
+
+void set_dbwh(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(sbend(dentry->d_sb) < bindex);
+ /* dbwh can be outside of bstart - bend range */
+ dtodi(dentry)->di_bwh = bindex;
+}
+
+void set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex)
+{
+ DiMustWriteLock(dentry);
+ AuDebugOn(sbend(dentry->d_sb) < bindex);
+ AuDebugOn((bindex >= 0
+ && (bindex < dbstart(dentry) || dbend(dentry) < bindex))
+ || (dentry->d_inode
+ && dentry->d_inode->i_mode
+ && !S_ISDIR(dentry->d_inode->i_mode)));
+ dtodi(dentry)->di_bdiropq = bindex;
+}
+
+static void hintent_put(struct aufs_hdentry *hd)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+ struct aufs_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);
+ }
+ kfree(hd->hd_intent_list);
+ }
+#endif
+}
+
+void hdput(struct aufs_hdentry *hd)
+{
+ hintent_put(hd);
+ dput(hd->hd_dentry);
+}
+
+void set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex,
+ struct dentry *h_dentry)
+{
+ struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
+ DiMustWriteLock(dentry);
+ AuDebugOn(bindex < dtodi(dentry)->di_bstart
+ || bindex > dtodi(dentry)->di_bend
+ || (h_dentry && atomic_read(&h_dentry->d_count) <= 0)
+ || (h_dentry && hd->hd_dentry)
+ );
+ if (hd->hd_dentry)
+ hdput(hd);
+ hd->hd_dentry = h_dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) \
+ && !defined(AuNoNfsBranch)
+static struct file *au_find_h_intent(struct aufs_hdentry *hd, struct file *file)
+{
+ struct file *h_file, *hf;
+ struct aufs_hdintent *hdi, *tmp;
+
+ LKTRTrace("%.*s\n", AuDLNPair(hd->hd_dentry));
+
+ h_file = 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;
+ list_del(&hdi->hdi_list);
+ kfree(hdi);
+ break;
+ }
+ }
+ spin_unlock(&hd->hd_lock);
+
+ return h_file;
+}
+
+struct file *au_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file)
+{
+ struct file *h_file;
+ struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
+
+ LKTRTrace("%.*s, b%d, f %p\n", AuDLNPair(dentry), bindex, file);
+ DiMustAnyLock(dentry);
+ AuDebugOn(bindex < dtodi(dentry)->di_bstart
+ || bindex > dtodi(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;
+}
+
+int au_set_h_intent(struct dentry *dentry, aufs_bindex_t bindex,
+ struct file *file, struct file *h_file)
+{
+ int err;
+ struct aufs_hdentry *hd = dtodi(dentry)->di_hdentry + bindex;
+ struct aufs_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 < dtodi(dentry)->di_bstart
+ || bindex > dtodi(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;
+#if 0 //def CONFIG_AUFS_DEBUG
+ au_debug_on();
+ DbgDentry(dentry);
+ DbgFile(hf);
+ au_debug_off();
+#endif
+ 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;
+}
+#endif /* !defined(AuNoNfsBranch) */
+
+/* ---------------------------------------------------------------------- */
+
+void au_update_digen(struct dentry *dentry)
+{
+ //DiMustWriteLock(dentry);
+ AuDebugOn(!dentry->d_sb);
+ atomic_set(&dtodi(dentry)->di_generation, au_sigen(dentry->d_sb));
+ //smp_mb(); /* atomic_set */
+}
+
+void au_update_dbrange(struct dentry *dentry, int do_put_zero)
+{
+ struct aufs_dinfo *dinfo;
+ aufs_bindex_t bindex;
+ struct dentry *h_d;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), do_put_zero);
+ DiMustWriteLock(dentry);
+
+ dinfo = dtodi(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)
+ 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 = dbstart(dentry), bend = dbend(dentry);
+ struct dentry *hidden_dentry;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ DiMustWriteLock(dentry);
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ if (hidden_dentry->d_inode) {
+ set_dbstart(dentry, bindex);
+ return;
+ }
+ set_h_dptr(dentry, bindex, NULL);
+ }
+ //set_dbstart(dentry, -1);
+ //set_dbend(dentry, -1);
+}
+
+void au_update_dbend(struct dentry *dentry)
+{
+ aufs_bindex_t bindex, bstart = dbstart(dentry), bend = dbend(dentry);
+ struct dentry *h_dentry;
+
+ DiMustWriteLock(dentry);
+ for (bindex = bend; bindex <= bstart; bindex--) {
+ h_dentry = au_h_dptr_i(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ if (h_dentry->d_inode) {
+ set_dbend(dentry, bindex);
+ return;
+ }
+ set_h_dptr(dentry, bindex, NULL);
+ }
+ //set_dbstart(dentry, -1);
+ //set_dbend(dentry, -1);
+}
+
+int au_find_dbindex(struct dentry *dentry, struct dentry *hidden_dentry)
+{
+ aufs_bindex_t bindex, bend;
+
+ bend = dbend(dentry);
+ for (bindex = dbstart(dentry); bindex <= bend; bindex++)
+ if (au_h_dptr_i(dentry, bindex) == hidden_dentry)
+ return bindex;
+ return -1;
+}
diff --git a/ubuntu/fs/aufs/dir.c b/ubuntu/fs/aufs/dir.c
new file mode 100644
index 0000000..129215f
--- /dev/null
+++ b/ubuntu/fs/aufs/dir.c
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: dir.c,v 1.49 2007/10/29 04:41:28 sfjro Exp $ */
+
+#include "aufs.h"
+
+static int reopen_dir(struct file *file)
+{
+ int err;
+ struct dentry *dentry, *hidden_dentry;
+ aufs_bindex_t bindex, btail, bstart;
+ struct file *hidden_file;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
+
+ /* open all hidden dirs */
+ bstart = dbstart(dentry);
+#if 1
+ for (bindex = fbstart(file); bindex < bstart; bindex++)
+ set_h_fptr(file, bindex, NULL);
+#endif
+ set_fbstart(file, bstart);
+ btail = dbtaildir(dentry);
+#if 1
+ for (bindex = fbend(file); btail < bindex; bindex--)
+ set_h_fptr(file, bindex, NULL);
+#endif
+ set_fbend(file, btail);
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+ hidden_file = au_h_fptr_i(file, bindex);
+ if (hidden_file) {
+ AuDebugOn(hidden_file->f_dentry != hidden_dentry);
+ continue;
+ }
+
+ hidden_file = au_h_open(dentry, bindex, file->f_flags, file);
+ // unavailable
+ //if (LktrCond) {fput(hidden_file);
+ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
+ err = PTR_ERR(hidden_file);
+ if (IS_ERR(hidden_file))
+ goto out; // close all?
+ //cpup_file_flags(hidden_file, file);
+ set_h_fptr(file, bindex, hidden_file);
+ }
+ 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, *hidden_dentry;
+ struct file *hidden_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;
+ set_fvdir_cache(file, NULL);
+ file->f_version = dentry->d_inode->i_version;
+ bindex = dbstart(dentry);
+ set_fbstart(file, bindex);
+ btail = dbtaildir(dentry);
+ set_fbend(file, btail);
+ for (; !err && bindex <= btail; bindex++) {
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (!hidden_dentry)
+ continue;
+
+ hidden_file = au_h_open(dentry, bindex, flags, file);
+ //if (LktrCond) {fput(hidden_file);
+ //br_put(stobr(dentry->d_sb, bindex));hidden_file=ERR_PTR(-1);}
+ if (!IS_ERR(hidden_file)) {
+ set_h_fptr(file, bindex, hidden_file);
+ continue;
+ }
+ err = PTR_ERR(hidden_file);
+ }
+ if (!err)
+ return 0; /* success */
+
+ /* close all */
+ for (bindex = fbstart(file); !err && bindex <= btail; bindex++)
+ set_h_fptr(file, bindex, NULL);
+ set_fbstart(file, -1);
+ 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 aufs_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_read_lock(sb, !AuLock_FLUSH);
+ fi_write_lock(file);
+ vdir_cache = 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 = dbend(dentry);
+ for (bindex = 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_i(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 */
+ //hdir_lock(h_inode, inode, bindex);
+ vfsub_i_lock(h_inode);
+ 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*/
+ //hdir_unlock(h_inode, inode, bindex);
+ vfsub_i_unlock(h_inode);
+ }
+
+ 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_read_lock(sb, !AuLock_FLUSH);
+ 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 = fbend(file);
+ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
+ h_file = au_h_fptr_i(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
+#if 0 // debug
+ AuDebugOn(h_file->f_dentry->d_inode
+ != au_h_iptr_i(inode, bindex));
+ hdir_lock(h_file->f_dentry->d_inode, inode,
+ bindex);
+#else
+ vfsub_i_lock(h_file->f_dentry->d_inode);
+#endif
+ 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*/
+#if 0 // debug
+ hdir_unlock(h_file->f_dentry->d_inode, inode,
+ bindex);
+#else
+ vfsub_i_unlock(h_file->f_dentry->d_inode);
+#endif
+ }
+ }
+ } 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(fvdir_cache(file));// goto out_unlock;
+
+ /* nfsd filldir calls lookup_one_len(). */
+ ii_downgrade_lock(inode);
+ err = au_fill_de(file, dirent, filldir);
+ //DbgVdir(fvdir_cache(file));// goto out_unlock;
+
+ inode->i_atime = au_h_iptr(inode)->i_atime;
+ ii_read_unlock(inode);
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ au_nfsd_lockdep_on();
+#if 0 // debug
+ if (LktrCond)
+ igrab(inode);
+#endif
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct test_empty_arg {
+ struct aufs_nhash *whlist;
+ int whonly;
+ aufs_bindex_t bindex;
+ int err, called;
+};
+
+static int test_empty_cb(void *__arg, const char *__name, int namelen,
+ loff_t offset, au_filldir_ino_t ino,
+ unsigned int d_type)
+{
+ struct test_empty_arg *arg = __arg;
+ char *name = (void *)__name;
+
+ LKTRTrace("%.*s\n", namelen, name);
+
+ arg->err = 0;
+ arg->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 (arg->whonly && !nhash_test_known_wh(arg->whlist, name,
+ namelen))
+ arg->err = -ENOTEMPTY;
+ goto out;
+ }
+
+ name += AUFS_WH_PFX_LEN;
+ namelen -= AUFS_WH_PFX_LEN;
+ if (!nhash_test_known_wh(arg->whlist, name, namelen))
+ arg->err = nhash_append_wh(arg->whlist, name, namelen,
+ arg->bindex);
+
+ 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 *hidden_file;
+
+ LKTRTrace("%.*s, {%p, %d, %d}\n",
+ AuDLNPair(dentry), arg->whlist, arg->whonly, arg->bindex);
+
+ hidden_file = au_h_open(dentry, arg->bindex,
+ O_RDONLY | O_NONBLOCK | O_DIRECTORY
+ | O_LARGEFILE, /*file*/NULL);
+ err = PTR_ERR(hidden_file);
+ if (IS_ERR(hidden_file))
+ goto out;
+
+ dlgt = au_need_dlgt(dentry->d_sb);
+ //hidden_file->f_pos = 0;
+ do {
+ arg->err = 0;
+ arg->called = 0;
+ //smp_mb();
+ err = vfsub_readdir(hidden_file, test_empty_cb, arg, dlgt);
+ if (err >= 0)
+ err = arg->err;
+ } while (!err && arg->called);
+ fput(hidden_file);
+ 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 *hidden_dentry;
+ struct inode *hidden_inode;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ hidden_dentry = au_h_dptr_i(dentry, arg->bindex);
+ AuDebugOn(!hidden_dentry);
+ hidden_inode = hidden_dentry->d_inode;
+ AuDebugOn(!hidden_inode || !S_ISDIR(hidden_inode->i_mode));
+
+ vfsub_i_lock_nested(hidden_inode, AuLsc_I_CHILD);
+ err = au_test_perm(hidden_inode, MAY_EXEC | MAY_READ,
+ au_need_dlgt(dentry->d_sb));
+ vfsub_i_unlock(hidden_inode);
+ if (!err)
+ err = do_test_empty(dentry, arg);
+ else {
+ struct do_test_empty_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .arg = arg
+ };
+ wkq_err = au_wkq_wait(call_do_test_empty, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_empty_lower(struct dentry *dentry)
+{
+ int err;
+ struct inode *inode;
+ struct test_empty_arg arg;
+ struct aufs_nhash *whlist;
+ aufs_bindex_t bindex, bstart, btail;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!inode || !S_ISDIR(inode->i_mode));
+
+ whlist = nhash_new(GFP_TEMPORARY);
+ err = PTR_ERR(whlist);
+ if (IS_ERR(whlist))
+ goto out;
+
+ bstart = dbstart(dentry);
+ arg.whlist = whlist;
+ arg.whonly = 0;
+ arg.bindex = bstart;
+ err = do_test_empty(dentry, &arg);
+ if (unlikely(err))
+ goto out_whlist;
+
+ arg.whonly = 1;
+ btail = dbtaildir(dentry);
+ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) {
+ struct dentry *hidden_dentry;
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (hidden_dentry && hidden_dentry->d_inode) {
+ AuDebugOn(!S_ISDIR(hidden_dentry->d_inode->i_mode));
+ arg.bindex = bindex;
+ err = do_test_empty(dentry, &arg);
+ }
+ }
+
+ out_whlist:
+ nhash_del(whlist);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_empty(struct dentry *dentry, struct aufs_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.whonly = 1;
+ btail = dbtaildir(dentry);
+ for (bindex = dbstart(dentry); !err && bindex <= btail; bindex++) {
+ struct dentry *hidden_dentry;
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (hidden_dentry && hidden_dentry->d_inode) {
+ AuDebugOn(!S_ISDIR(hidden_dentry->d_inode->i_mode));
+ arg.bindex = bindex;
+ err = sio_test_empty(dentry, &arg);
+ }
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+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;
+}
+
+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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+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 --git a/ubuntu/fs/aufs/dir.h b/ubuntu/fs/aufs/dir.h
new file mode 100644
index 0000000..f9021f4
--- /dev/null
+++ b/ubuntu/fs/aufs/dir.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: dir.h,v 1.23 2007/10/29 04:41:44 sfjro Exp $ */
+
+#ifndef __AUFS_DIR_H__
+#define __AUFS_DIR_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+typedef u64 au_filldir_ino_t;
+#else
+typedef ino_t au_filldir_ino_t;
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/* 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 aufs_deblk_t[AuSize_DEBLK];
+
+struct aufs_nhash {
+ struct hlist_head heads[AuSize_NHASH];
+};
+
+struct aufs_destr {
+ unsigned char len;
+ char name[0];
+} __packed;
+
+struct aufs_dehstr {
+ struct hlist_node hash;
+ struct aufs_destr *str;
+};
+
+struct aufs_de {
+ ino_t de_ino;
+ unsigned char de_type;
+ /* caution: packed */
+ struct aufs_destr de_str;
+} __packed;
+
+struct aufs_wh {
+ struct hlist_node wh_hash;
+ aufs_bindex_t wh_bindex;
+ struct aufs_destr wh_str;
+} __packed;
+
+union aufs_deblk_p {
+ unsigned char *p;
+ aufs_deblk_t *deblk;
+ struct aufs_de *de;
+};
+
+struct aufs_vdir {
+ aufs_deblk_t **vd_deblk;
+ int vd_nblk;
+ struct {
+ int i;
+ union aufs_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 aufs_nhash *whlist);
+void au_add_nlink(struct inode *dir, struct inode *h_dir);
+void au_sub_nlink(struct inode *dir, struct inode *h_dir);
+
+/* vdir.c */
+struct aufs_nhash *nhash_new(gfp_t gfp);
+void nhash_del(struct aufs_nhash *nhash);
+void nhash_init(struct aufs_nhash *nhash);
+void nhash_move(struct aufs_nhash *dst, struct aufs_nhash *src);
+void nhash_fin(struct aufs_nhash *nhash);
+int nhash_test_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt,
+ int limit);
+int nhash_test_known_wh(struct aufs_nhash *whlist, char *name, int namelen);
+int nhash_append_wh(struct aufs_nhash *whlist, char *name, int namelen,
+ aufs_bindex_t bindex);
+void au_vdir_free(struct aufs_vdir *vdir);
+int au_vdir_init(struct file *file);
+int au_fill_de(struct file *file, void *dirent, filldir_t filldir);
+
+/* ---------------------------------------------------------------------- */
+
+static inline
+unsigned int au_name_hash(const unsigned char *name, unsigned int len)
+{
+ return (full_name_hash(name, len) % AuSize_NHASH);
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DIR_H__ */
diff --git a/ubuntu/fs/aufs/export.c b/ubuntu/fs/aufs/export.c
new file mode 100644
index 0000000..e0612c4
--- /dev/null
+++ b/ubuntu/fs/aufs/export.c
@@ -0,0 +1,620 @@
+/*
+ * 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
+ */
+
+/* $Id: export.c,v 1.23 2008/01/21 04:57:48 sfjro Exp $ */
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+#include <linux/exportfs.h>
+#endif
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+static struct dentry *
+au_call_decode_fh(struct vfsmount *h_mnt, __u32 *fh, int fh_len, int fh_type,
+ int (*acceptable)(void *, struct dentry *), void *context)
+{
+ /* in linux-2.6.24, it takes struct fid * as file handle */
+ return exportfs_decode_fh(h_mnt, (void *)fh, fh_len, fh_type,
+ acceptable, context);
+}
+
+static int
+au_call_encode_fh(struct dentry *h_dentry, __u32 *fh, int *max_len,
+ int connectable)
+{
+ /* in linux-2.6.24, it takes struct fid * as file handle */
+ return exportfs_encode_fh(h_dentry, (void *)fh, max_len, connectable);
+}
+#else
+extern struct export_operations export_op_default;
+#define CALL(ops, func) \
+ (((ops)->func) ? ((ops)->func) : export_op_default.func)
+
+static struct dentry *
+au_call_decode_fh(struct vfsmount *h_mnt, __u32 *fh, int fh_len, int fh_type,
+ int (*acceptable)(void *, struct dentry *), void *context)
+{
+ return CALL(h_mnt->mnt_sb->s_export_op, decode_fh)
+ (h_mnt->mnt_sb, fh, fh_len, fh_type, acceptable, context);
+}
+
+static int
+au_call_encode_fh(struct dentry *h_dentry, __u32 *fh, int *max_len,
+ int connectable)
+{
+ return CALL(h_dentry->d_sb->s_export_op, encode_fh)
+ (h_dentry, fh, max_len, connectable);
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) */
+
+#define AuTestAnon(d) ((d)->d_flags & DCACHE_DISCONNECTED)
+
+/* ---------------------------------------------------------------------- */
+
+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 {
+ /* support 64bit inode number */
+ /* but untested */
+ Fh_br_id,
+ Fh_sigen,
+ Fh_ino1,
+#if BITS_PER_LONG == 64
+ Fh_ino2,
+#endif
+ Fh_dir_ino1,
+#if BITS_PER_LONG == 64
+ Fh_dir_ino2,
+#endif
+ Fh_h_ino1,
+#if BITS_PER_LONG == 64
+ Fh_h_ino2,
+#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 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)
+ /* dcache_lock is locked */
+ if (!AuTestAnon(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 && !AuTestAnon(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,
+ au_filldir_ino_t 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(AuTestAnon(parent))) {
+ dput(parent);
+ goto out_iput;
+ }
+ } else
+ goto out_iput;
+
+ file = dentry_open(parent, NULL, 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 */
+ vfsub_i_lock(dir);
+ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen);
+ vfsub_i_unlock(dir);
+ 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,
+ au_filldir_ino_t 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 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;
+ struct append_name arg;
+ int len, err;
+ struct file *h_file;
+ struct nameidata nd;
+ struct aufs_branch *br;
+
+ LKTRTrace("b%d\n", bindex);
+ SiMustAnyLock(sb);
+
+ br = stobr(sb, bindex);
+ /* br_get(br); */
+ h_mnt = br->br_mnt;
+ h_sb = h_mnt->mnt_sb;
+ LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb));
+ h_parent = au_call_decode_fh(h_mnt, 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(AuTestAnon(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_i(root, bindex);
+ di_read_unlock(root, !AuLock_IR);
+ arg.h_path = d_path(h_root, h_mnt, path, PATH_MAX);
+ dentry = (void *)arg.h_path;
+ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
+ goto out_putname;
+ len = strlen(arg.h_path);
+ arg.h_path = d_path(h_parent, h_mnt, path, PATH_MAX);
+ dentry = (void *)arg.h_path;
+ if (unlikely(!arg.h_path || IS_ERR(arg.h_path)))
+ goto out_putname;
+ 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(h_parent, NULL, au_dir_roflags);
+ dentry = (void *)h_file;
+ if (IS_ERR(h_file))
+ goto out_putname;
+
+ 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);
+
+ p = d_path(root, stosi(sb)->si_mnt, path, PATH_MAX - arg.len);
+ dentry = (void *)p;
+ if (unlikely(!p || IS_ERR(p)))
+ goto out_fput;
+ p[strlen(p)] = '/';
+ LKTRTrace("%s\n", p);
+
+ err = vfsub_path_lookup(p, LOOKUP_FOLLOW, &nd);
+ dentry = ERR_PTR(err);
+ if (!err) {
+ dentry = dget(nd.dentry);
+ if (unlikely(AuTestAnon(dentry))) {
+ dput(dentry);
+ dentry = ERR_PTR(-ESTALE);
+ }
+ path_release(&nd);
+ }
+
+ out_fput:
+ fput(h_file);
+ out_putname:
+ __putname(path);
+ out:
+ /* br_put(br); */
+ AuTraceErrPtr(dentry);
+ return dentry;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static 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 = find_brindex(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 (ibstart(inode) <= bindex && bindex <= ibend(inode))
+ h_inode = au_h_iptr_i(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;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+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);
+}
+#endif /* KERNEL_VERSION */
+
+/* ---------------------------------------------------------------------- */
+
+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(AuTestAnon(dentry));
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+ parent = dget_parent(dentry);
+ AuDebugOn(AuTestAnon(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(AuFlag(stosi(sb), f_xino) == AuXino_NONE))
+ AuWarn1("NFS-exporting requires xino\n");
+#if 0 // temp
+ if (unlikely(au_flag_test_udba_inotify(sb)))
+ AuWarn1("udba=inotify is not recommended when exporting\n");
+#endif
+#endif
+
+ err = -EPERM;
+ bindex = ibstart(inode);
+ h_sb = 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;
+ }
+
+#ifdef CONFIG_AUFS_ROBR
+ if (unlikely(au_test_aufs(h_sb))) {
+ AuErr1("aufs branch is not supported\n");
+ goto out_unlock;
+ }
+#endif
+
+ /* doesn't support pseudo-link */
+ if (unlikely(bindex < dbstart(dentry)
+ || dbend(dentry) < bindex
+ || !au_h_dptr_i(dentry, bindex))) {
+ AuErr("%.*s/%.*s, b%d, pseudo-link?\n",
+ AuDLNPair(parent), AuDLNPair(dentry), bindex);
+ goto out_unlock;
+ }
+
+ fh[Fh_br_id] = 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_iptr(inode);
+ encode_ino(fh + Fh_h_ino, h_inode->i_ino);
+ fh[Fh_h_igen] = h_inode->i_generation;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+ /* it should be set at exporting time */
+ if (unlikely(!h_sb->s_export_op->find_exported_dentry)) {
+ AuWarn("set default find_exported_dentry for %s\n",
+ au_sbtype(h_sb));
+ h_sb->s_export_op->find_exported_dentry = find_exported_dentry;
+ }
+#endif
+
+ *max_len -= Fh_tail;
+ //LKTRTrace("Fh_tail %d, max_len %d\n", Fh_tail, *max_len);
+ h_parent = au_h_dptr_i(parent, bindex);
+ AuDebugOn(AuTestAnon(h_parent));
+ fh[Fh_h_type] = au_call_encode_fh(h_parent, 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 = {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ .fh_to_dentry = aufs_fh_to_dentry,
+#else
+ .decode_fh = aufs_decode_fh,
+#endif
+ .encode_fh = aufs_encode_fh
+};
diff --git a/ubuntu/fs/aufs/f_op.c b/ubuntu/fs/aufs/f_op.c
new file mode 100644
index 0000000..e0b520e
--- /dev/null
+++ b/ubuntu/fs/aufs/f_op.c
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: f_op.c,v 1.41 2007/10/29 04:41:28 sfjro Exp $ */
+
+#include <linux/fsnotify.h>
+#include <linux/pagemap.h>
+#include <linux/poll.h>
+#include <linux/security.h>
+#include <linux/version.h>
+#include "aufs.h"
+
+/* common function to regular file and dir */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#define FlushArgs h_file, id
+int aufs_flush(struct file *file, fl_owner_t id)
+#else
+#define FlushArgs h_file
+int aufs_flush(struct file *file)
+#endif
+{
+ int err;
+ struct dentry *dentry;
+ aufs_bindex_t bindex, bend;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ // aufs_read_lock_file()
+ si_read_lock(dentry->d_sb, !AuLock_FLUSH);
+ fi_read_lock(file);
+ di_read_lock_child(dentry, AuLock_IW);
+
+ err = 0;
+ bend = fbend(file);
+ for (bindex = fbstart(file); !err && bindex <= bend; bindex++) {
+ struct file *h_file;
+ h_file = au_h_fptr_i(file, bindex);
+ if (h_file && h_file->f_op && h_file->f_op->flush) {
+ err = h_file->f_op->flush(FlushArgs);
+ 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;
+}
+#undef FlushArgs
+
+/* ---------------------------------------------------------------------- */
+
+static int do_open_nondir(struct file *file, int flags)
+{
+ int err;
+ aufs_bindex_t bindex;
+ struct super_block *sb;
+ struct file *hidden_file;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct aufs_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 = ftofi(file);
+ finfo->fi_h_vm_ops = NULL;
+ sb = dentry->d_sb;
+ bindex = dbstart(dentry);
+ AuDebugOn(!au_h_dptr(dentry)->d_inode);
+ /* O_TRUNC is processed already */
+ BUG_ON(au_test_ro(sb, bindex, inode) && (flags & O_TRUNC));
+
+ hidden_file = au_h_open(dentry, bindex, flags, file);
+ //if (LktrCond) {fput(hidden_file); br_put(stobr(dentry->d_sb, bindex));
+ //hidden_file = ERR_PTR(-1);}
+ if (!IS_ERR(hidden_file)) {
+ set_fbstart(file, bindex);
+ set_fbend(file, bindex);
+ set_h_fptr(file, bindex, hidden_file);
+ //AuDbgFile(file);
+ return 0; /* success */
+ }
+ err = PTR_ERR(hidden_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_read_lock(sb, !AuLock_FLUSH);
+ 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 *hidden_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 */
+ hidden_file = au_h_fptr(file);
+ h_inode = hidden_file->f_dentry->d_inode;
+ err = vfsub_read_u(hidden_file, buf, count, ppos, au_need_dlgt(sb));
+ memcpy(&file->f_ra, &hidden_file->f_ra, sizeof(file->f_ra)); //??
+ dentry->d_inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
+
+ fi_read_unlock(file);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+static ssize_t aufs_write(struct file *file, const char __user *__buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t err;
+ struct dentry *dentry, *parent;
+ struct inode *inode, *dir;
+ struct super_block *sb;
+ struct file *h_file;
+ char __user *buf = (char __user *)__buf;
+ struct inode *h_inode;
+ struct aufs_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;
+ vfsub_i_lock(inode);
+ 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 */
+ vfsub_args_init(&vargs, &ign, au_need_dlgt(sb), 0);
+ h_file = au_h_fptr(file);
+ h_inode = h_file->f_dentry->d_inode;
+ if (!au_flag_test_udba_inotify(sb))
+ 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, itohi(dir, 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);
+ vfsub_i_unlock(inode);
+ AuTraceErr(err);
+ return err;
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) \
+ || defined(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;
+}
+#endif
+
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+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);
+ 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_need_dlgt(sb));
+ memcpy(&file->f_ra, &h_file->f_ra, sizeof(file->f_ra)); //??
+ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
+ 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;
+ struct super_block *sb;
+ struct file *h_file;
+ struct inode *h_inode;
+ struct aufs_hin_ignore ign;
+ struct vfsub_args vargs;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, len %lu, pos %Ld\n",
+ AuDLNPair(dentry), (unsigned long)len, *ppos);
+
+ inode = dentry->d_inode;
+ vfsub_i_lock(inode);
+ 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 */
+ vfsub_args_init(&vargs, &ign, au_need_dlgt(sb), 0);
+ h_file = au_h_fptr(file);
+ h_inode = h_file->f_dentry->d_inode;
+ /* current do_splice_from() doesn't fire up the inotify event */
+ if (1 || !au_flag_test_udba_inotify(sb))
+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags, &vargs);
+ else {
+ //struct dentry *parent = dget_parent(dentry);
+ //vfsub_ign_hinode(&vargs, IN_MODIFY,
+ //itohi(parent->d_inode, 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);
+ vfsub_i_unlock(inode);
+ AuTraceErr(err);
+ return err;
+}
+#endif /* CONFIG_AUFS_SPLICE_PATCH */
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_ROBR
+struct lvma {
+ struct list_head list;
+ struct vm_area_struct *vma;
+};
+
+static struct file *safe_file(struct vm_area_struct *vma)
+{
+ struct file *file = vma->vm_file;
+ struct super_block *sb = file->f_dentry->d_sb;
+ struct lvma *lvma, *entry;
+ struct aufs_sbinfo *sbinfo;
+ int found, warn;
+
+ AuTraceEnter();
+ AuDebugOn(!au_test_aufs(sb));
+
+ warn = 0;
+ found = 0;
+ sbinfo = stosi(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;
+}
+
+static void reset_file(struct vm_area_struct *vma, struct file *file)
+{
+ struct super_block *sb = file->f_dentry->d_sb;
+ struct lvma *entry, *found;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ AuDebugOn(!au_test_aufs(sb));
+
+ vma->vm_file = file;
+
+ found = NULL;
+ sbinfo = stosi(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);
+}
+
+#else
+
+static struct file *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 void reset_file(struct vm_area_struct *vma, struct file *file)
+{
+ vma->vm_file = file;
+ smp_mb(); /* flush vm_file */
+}
+#endif /* CONFIG_AUFS_ROBR */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ int err;
+ struct dentry *dentry;
+ struct file *file, *hidden_file;
+ struct inode *inode;
+ static DECLARE_WAIT_QUEUE_HEAD(wq);
+ struct aufs_finfo *finfo;
+
+ AuTraceEnter();
+ AuDebugOn(!vma || !vma->vm_file);
+ wait_event(wq, (file = 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 lock */
+ finfo = ftofi(file);
+ hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
+ AuDebugOn(!hidden_file || !au_test_mmapped(file));
+ vma->vm_file = hidden_file;
+ //smp_mb();
+#if 0 // debug
+ AuDbg("fault %p, filemap_fault %p\n",
+ finfo->fi_h_vm_ops->fault, filemap_fault);
+#endif
+ err = finfo->fi_h_vm_ops->fault(vma, vmf);
+ reset_file(vma, file);
+#if 0 //def CONFIG_SMP
+ //wake_up_nr(&wq, online_cpu - 1);
+ wake_up_all(&wq);
+#else
+ wake_up(&wq);
+#endif
+
+ if (!err) {
+ //page->mapping = file->f_mapping;
+ //get_page(page);
+ //file->f_mapping = hidden_file->f_mapping;
+ //touch_atime(NULL, dentry);
+ //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
+ }
+ AuTraceErr(err);
+ //AuDbg("err %d\n", err);
+ return err;
+}
+#else
+static struct page *aufs_nopage(struct vm_area_struct *vma, unsigned long addr,
+ int *type)
+{
+ struct page *page;
+ struct dentry *dentry;
+ struct file *file, *hidden_file;
+ struct inode *inode;
+ static DECLARE_WAIT_QUEUE_HEAD(wq);
+ struct aufs_finfo *finfo;
+
+ AuTraceEnter();
+ AuDebugOn(!vma || !vma->vm_file);
+ wait_event(wq, (file = safe_file(vma)));
+ AuDebugOn(!au_test_aufs(file->f_dentry->d_sb));
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, addr %lx\n", AuDLNPair(dentry), addr);
+ inode = dentry->d_inode;
+ AuDebugOn(!S_ISREG(inode->i_mode));
+
+ /* do not revalidate, nor lock */
+ finfo = ftofi(file);
+ hidden_file = finfo->fi_hfile[0 + finfo->fi_bstart].hf_file;
+ AuDebugOn(!hidden_file || !au_test_mmapped(file));
+ vma->vm_file = hidden_file;
+ //smp_mb();
+ page = finfo->fi_h_vm_ops->nopage(vma, addr, type);
+ reset_file(vma, file);
+#if 0 //def CONFIG_SMP
+ //wake_up_nr(&wq, online_cpu - 1);
+ wake_up_all(&wq);
+#else
+ wake_up(&wq);
+#endif
+ if (!IS_ERR(page)) {
+ //page->mapping = file->f_mapping;
+ //get_page(page);
+ //file->f_mapping = hidden_file->f_mapping;
+ //touch_atime(NULL, dentry);
+ //inode->i_atime = hidden_file->f_dentry->d_inode->i_atime;
+ }
+ AuTraceErrPtr(page);
+ return page;
+}
+
+static int aufs_populate(struct vm_area_struct *vma, unsigned long addr,
+ unsigned long len, pgprot_t prot, unsigned long pgoff,
+ int nonblock)
+{
+ AuUnsupport();
+ return ftofi(vma->vm_file)->fi_h_vm_ops->populate
+ (vma, addr, len, prot, pgoff, nonblock);
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) */
+
+static struct vm_operations_struct aufs_vm_ops = {
+ //.open = aufs_vmaopen,
+ //.close = aufs_vmaclose,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .fault = aufs_fault,
+#else
+ .nopage = aufs_nopage,
+ .populate = aufs_populate,
+#endif
+#if 0 // rfu
+ unsigned long (*nopfn)(struct vm_area_struct *area,
+ unsigned long address);
+ //page_mkwrite(struct vm_area_struct *vma, struct page *page)
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+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;
+ unsigned long flags;
+
+ 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);
+ wlock = 0;
+ if (file->f_mode & FMODE_WRITE) {
+ flags = VM_SHARED | VM_WRITE;
+ wlock = ((flags & vma->vm_flags) == flags);
+ }
+
+ 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);
+ vm_ops = ftofi(file)->fi_h_vm_ops;
+ if (unlikely(!mmapped)) {
+ 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();
+ }
+ if (unlikely(err))
+ goto out_unlock;
+ vm_ops = vma->vm_ops;
+ AuDebugOn(!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);
+ err = -EIO;
+ goto out_unlock;
+ }
+ }
+ AuDebugOn(!vm_ops);
+
+ err = generic_file_mmap(file, vma);
+ if (!err) {
+ file_accessed(h_file);
+ au_update_fuse_h_inode(h_file->f_vfsmnt, h_file->f_dentry);
+ /*ignore*/
+ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
+ vma->vm_ops = &aufs_vm_ops;
+ if (unlikely(!mmapped))
+ ftofi(file)->fi_h_vm_ops = vm_ops;
+ }
+
+ out_unlock:
+ if (!wlock && mmapped)
+ fi_read_unlock(file);
+ else
+ fi_write_unlock(file);
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 22)
+// todo: try do_sendfile() in fs/read_write.c
+static ssize_t aufs_sendfile(struct file *file, loff_t *ppos,
+ size_t count, read_actor_t actor, void *target)
+{
+ ssize_t err;
+ struct file *h_file;
+ struct dentry *dentry;
+ struct super_block *sb;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld, cnt %lu\n",
+ AuDLNPair(dentry), *ppos, (unsigned long)count);
+
+ 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;
+ h_file = au_h_fptr(file);
+ if (h_file->f_op && h_file->f_op->sendfile) {
+ if (/* unlikely */(au_test_loopback())) {
+ file->f_mapping = h_file->f_mapping;
+ smp_mb(); /* unnecessary? */
+ }
+ if (!au_test_nfs(h_file->f_vfsmnt->mnt_sb))
+ err = h_file->f_op->sendfile(h_file, ppos, count, actor,
+ target);
+ else {
+ lockdep_off();
+ err = h_file->f_op->sendfile(h_file, ppos, count, actor,
+ target);
+ lockdep_on();
+ }
+ if (!err)
+ au_update_fuse_h_inode(h_file->f_vfsmnt,
+ h_file->f_dentry);
+ /*ignore*/
+ dentry->d_inode->i_atime = h_file->f_dentry->d_inode->i_atime;
+ }
+ fi_read_unlock(file);
+
+ out:
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+#endif /* LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 22) */
+
+/* ---------------------------------------------------------------------- */
+
+/* copied from linux/fs/select.h, must match */
+#define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM)
+
+static unsigned int aufs_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask;
+ struct file *hidden_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 happend. */
+ 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;
+ hidden_file = au_h_fptr(file);
+ if (hidden_file->f_op && hidden_file->f_op->poll)
+ mask = hidden_file->f_op->poll(hidden_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, my_lock;
+ struct inode *inode;
+ struct file *h_file;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s, %d\n", AuDLNPair(dentry), datasync);
+ inode = dentry->d_inode;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
+ IMustLock(inode);
+ my_lock = 0;
+#else
+ /* before 2.6.17,
+ * msync(2) calls me without locking i_sem/i_mutex, but fsync(2).
+ */
+ my_lock = !vfsub_i_trylock(inode);
+#endif
+
+ 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);
+ if (h_file->f_op && h_file->f_op->fsync) {
+ // todo: apparmor thread?
+ //file->f_mapping->host->i_mutex
+ ii_write_lock_child(inode);
+ vfsub_i_lock_nested(h_file->f_dentry->d_inode, 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);
+ vfsub_i_unlock(h_file->f_dentry->d_inode);
+ ii_write_unlock(inode);
+ }
+
+ out_unlock:
+ fi_write_unlock(file);
+ out:
+ if (unlikely(my_lock))
+ vfsub_i_unlock(inode);
+ si_read_unlock(sb);
+ AuTraceErr(err);
+ return err;
+}
+
+static int aufs_fasync(int fd, struct file *file, int flag)
+{
+ int err;
+ struct file *hidden_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;
+
+ hidden_file = au_h_fptr(file);
+ if (hidden_file->f_op && hidden_file->f_op->fasync)
+ err = hidden_file->f_op->fasync(fd, hidden_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,
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 22)
+ .sendfile = aufs_sendfile,
+#endif
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+ .splice_write = aufs_splice_write,
+ .splice_read = aufs_splice_read,
+#endif
+};
diff --git a/ubuntu/fs/aufs/file.c b/ubuntu/fs/aufs/file.c
new file mode 100644
index 0000000..084870a
--- /dev/null
+++ b/ubuntu/fs/aufs/file.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: file.c,v 1.64 2008/01/28 05:01:42 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 dentry *h_dentry;
+ struct inode *h_inode;
+ struct super_block *sb;
+ struct vfsmount *h_mnt;
+ struct file *h_file;
+ struct aufs_branch *br;
+ int udba, err;
+
+ LKTRTrace("%.*s, b%d, flags 0%o, f %d\n",
+ AuDLNPair(dentry), bindex, flags, !!file);
+ AuDebugOn(!dentry);
+ h_dentry = au_h_dptr_i(dentry, bindex);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ sb = dentry->d_sb;
+ udba = au_flag_test_udba_inotify(sb);
+#if 0
+ if (unlikely(udba))
+ test here?
+#endif
+
+ br = stobr(sb, bindex);
+ 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);
+ dget(h_dentry);
+ h_mnt = mntget(br->br_mnt);
+ h_file = dentry_open(h_dentry, h_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;
+
+ 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 au_cpup_flags cflags = {
+ .dtime = 1
+ };
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ AuDebugOn(IS_ROOT(dentry));
+ DiMustWriteLock(dentry);
+
+ parent = dget_parent(dentry);
+ di_write_lock_parent(parent);
+ err = AuWbrCopyup(stosi(dentry->d_sb), dentry);
+ bcpup = err;
+ if (unlikely(err < 0)) {
+ err = 0; /* stop copyup, it is not an error */
+ goto out;
+ }
+ err = 0;
+
+ h_parent = au_h_dptr_i(parent, bcpup);
+ if (!h_parent) {
+ err = au_cpup_dirs(dentry, bcpup, NULL);
+ if (unlikely(err))
+ goto out;
+ h_parent = au_h_dptr_i(parent, bcpup);
+ }
+
+ h_dir = h_parent->d_inode;
+ h_dentry = au_h_dptr_i(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_flag_test_udba_inotify(dentry->d_sb)
+ && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ ii_read_lock_parent2(gparent->d_inode);
+ }
+ hdir_lock(h_dir, dir, bcpup);
+ //todo: test parent-gparent relationship
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ AuDebugOn(au_h_dptr_i(dentry, bcpup));
+ err = au_sio_cpup_simple(dentry, bcpup, -1, &cflags);
+ AuTraceErr(err);
+ vfsub_i_unlock(h_inode);
+ 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 (AuFlag(stosi(sb), f_coo)) {
+ case AuCoo_LEAF:
+ coo = !S_ISDIR(inode->i_mode);
+ break;
+ case AuCoo_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 = dbstart(dentry);
+ } else {
+ di_write_lock_child(dentry);
+ bstart = 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 = 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));
+ AuDebugOn(S_ISDIR(dentry->d_inode->i_mode)
+ || !au_h_dptr(dentry)->d_inode);
+
+ h_file_tmp = NULL;
+ bstart = dbstart(dentry);
+ if (fbstart(file) == bstart) {
+ h_file = au_h_fptr(file);
+ if (file->f_mode == h_file->f_mode)
+ return 0; /* success */
+ h_file_tmp = h_file;
+ get_file(h_file_tmp);
+ set_h_fptr(file, bstart, NULL);
+ }
+ AuDebugOn(fbstart(file) < bstart
+ || ftofi(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); br_put(stobr(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);
+ set_fbstart(file, bstart);
+ set_h_fptr(file, bstart, h_file);
+ memcpy(&h_file->f_ra, &file->f_ra, sizeof(file->f_ra)); //??
+
+ /* close lower files */
+ bend = fbend(file);
+ for (bindex = bstart + 1; bindex <= bend; bindex++)
+ set_h_fptr(file, bindex, NULL);
+ 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 aufs_dinfo *dinfo;
+ struct au_cpup_flags cflags;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, len %Ld\n", AuDLNPair(dentry), len);
+ FiMustWriteLock(file);
+
+ sb = dentry->d_sb;
+ bstart = fbstart(file);
+ AuDebugOn(ftobr(file, bstart) != stobr(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)->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(stosi(dentry->d_sb), dentry);
+ bcpup = err;
+ if (unlikely(err < 0))
+ goto out_unlock;
+ err = 0;
+
+ h_parent = au_h_dptr_i(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_i(parent, bcpup);
+ }
+
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ gparent = NULL;
+ if (unlikely(au_flag_test_udba_inotify(sb) && !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)->f_dentry;
+ h_inode = h_dentry->d_inode;
+ dir = parent->d_inode;
+ hdir_lock(h_dir, dir, bcpup);
+ //todo: test parent-gparent relationship
+ vfsub_i_lock_nested(h_inode, 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 = dtodi(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_i(dentry, bcpup)) {
+ cflags.dtime = 1;
+ err = au_sio_cpup_simple(dentry, bcpup, len, &cflags);
+ }
+ //if (LktrCond) err = -1;
+ AuTraceErr(err);
+ if (!err)
+ err = au_reopen_nondir(file);
+ //if (LktrCond) err = -1;
+ AuTraceErr(err);
+ }
+ vfsub_i_unlock(h_inode);
+ 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 aufs_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 aufs_dinfo *dinfo;
+ struct au_cpup_flags cflags;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ FiMustWriteLock(file);
+
+ err = 0;
+ finfo = ftofi(file);
+ inode = dentry->d_inode;
+ sb = dentry->d_sb;
+ again:
+ bstart = 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(stosi(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 != 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 (AuFlag(stosi(sb), f_plink)
+ && au_test_plink(sb, inode)
+ && !d_unhashed(dentry)) {
+ err = au_test_and_cpup_dirs(dentry, bstart, NULL);
+
+ /* always superio. */
+#if 1
+ cflags.dtime = 1;
+ h_dir = au_h_dptr_i(parent, bstart)->d_inode;
+ hdir_lock(h_dir, dir, bstart);
+ err = au_sio_cpup_simple(dentry, bstart, -1, &cflags);
+ hdir_unlock(h_dir, dir, bstart);
+#else
+ if (!au_test_wkq(current)) {
+ int wkq_err;
+ struct cpup_pseudo_link_args args = {
+ .errp = &err,
+ .dentry = dentry,
+ .bdst = bstart,
+ .do_lock = 1
+ };
+ wkq_err = au_wkq_wait(call_cpup_pseudo_link, &args);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ } else
+ err = cpup_pseudo_link(dentry, bstart, /*do_lock*/1);
+#endif
+ } else if (hi_wh) {
+ /* already copied-up after unlink */
+ dinfo = dtodi(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 aufs_hfile *p;
+ struct aufs_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 = ftofi(file);
+ bstart = finfo->fi_bstart;
+ bend = finfo->fi_bstart;
+ new_sz = sizeof(*finfo->fi_hfile) * (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 aufs_hfile tmp, *q;
+ aufs_bindex_t new_bindex;
+
+ if (!p->hf_file)
+ continue;
+ new_bindex = find_bindex(sb, p->hf_br);
+ if (new_bindex == bindex)
+ continue;
+ if (new_bindex < 0) { // test here
+ 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 = sbend(sb);
+ finfo->fi_bstart = s;
+ finfo->fi_bend = e;
+ }
+
+ p = finfo->fi_hfile;
+ if (!au_test_mmapped(file) && !d_unhashed(dentry)) {
+ bend = 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 = find_brindex(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 = 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 = fbend(file);
+ for (bindex = fbstart(file); bindex <= bend; bindex++)
+ 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 = dbstart(dentry);
+ pseudo_link = (bstart != ibstart(dentry->d_inode));
+ di_read_unlock(dentry, AuLock_IR);
+ if (sgen == fgen && !pseudo_link && fbstart(file) == bstart) {
+ if (!wlock)
+ fi_downgrade_lock(file);
+ return 0; /* success */
+ }
+
+ LKTRTrace("sgen %d, fgen %d\n", sgen, fgen);
+ if (sgen != au_digen(dentry)) {
+ /*
+ * 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);
+ }
+
+ di_read_lock_child(dentry, AuLock_IR);
+ err = refresh_file(file, reopen/* , au_flag_test(sb, AuFlag_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_writepage(struct page *page, struct writeback_control *wbc)
+{ AuUnsupport(); return 0; }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
+static void aufs_sync_page(struct page *page)
+{ AuUnsupport(); }
+#else
+static int aufs_sync_page(struct page *page)
+{ AuUnsupport(); return 0; }
+#endif
+
+#if 0 // comment
+static int aufs_writepages(struct address_space *mapping,
+ struct writeback_control *wbc)
+{ AuUnsupport(); return 0; }
+static int aufs_readpages(struct file *filp, struct address_space *mapping,
+ struct list_head *pages, unsigned nr_pages)
+{ AuUnsupport(); return 0; }
+static sector_t aufs_bmap(struct address_space *mapping, sector_t block)
+{ AuUnsupport(); return 0; }
+#endif
+
+static int aufs_set_page_dirty(struct page *page)
+{ AuUnsupport(); return 0; }
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 17)
+static void aufs_invalidatepage(struct page *page, unsigned long offset)
+{ AuUnsupport(); }
+#else
+static int aufs_invalidatepage(struct page *page, unsigned long offset)
+{ AuUnsupport(); return 0; }
+#endif
+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,
+ //.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 --git a/ubuntu/fs/aufs/file.h b/ubuntu/fs/aufs/file.h
new file mode 100644
index 0000000..61a1ade
--- /dev/null
+++ b/ubuntu/fs/aufs/file.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: file.h,v 1.34 2007/11/05 01:39:39 sfjro Exp $ */
+
+#ifndef __AUFS_FILE_H__
+#define __AUFS_FILE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+/* SEEK_xxx are defined in linux/fs.h */
+#else
+enum { SEEK_SET, SEEK_CUR, SEEK_END };
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct aufs_branch;
+struct aufs_hfile {
+ struct file *hf_file;
+ struct aufs_branch *hf_br;
+};
+
+struct aufs_vdir;
+struct aufs_finfo {
+ atomic_t fi_generation;
+
+ struct aufs_rwsem fi_rwsem;
+ struct aufs_hfile *fi_hfile;
+ aufs_bindex_t fi_bstart, fi_bend;
+
+ union {
+ struct vm_operations_struct *fi_h_vm_ops;
+ struct aufs_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;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+int aufs_flush(struct file *file, fl_owner_t id);
+#else
+int aufs_flush(struct file *file);
+#endif
+
+/* finfo.c */
+struct aufs_finfo *ftofi(struct file *file);
+aufs_bindex_t fbstart(struct file *file);
+aufs_bindex_t fbend(struct file *file);
+struct aufs_vdir *fvdir_cache(struct file *file);
+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex);
+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex);
+struct file *au_h_fptr(struct file *file);
+
+void set_fbstart(struct file *file, aufs_bindex_t bindex);
+void set_fbend(struct file *file, aufs_bindex_t bindex);
+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache);
+void au_hfput(struct aufs_hfile *hf);
+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *h_file);
+void au_update_figen(struct file *file);
+
+void au_finfo_fin(struct file *file);
+int au_finfo_init(struct file *file);
+
+/* ---------------------------------------------------------------------- */
+
+static inline au_gen_t au_figen(struct file *f)
+{
+ return atomic_read(&ftofi(f)->fi_generation);
+}
+
+static inline int au_test_mmapped(struct file *f)
+{
+ return !!(ftofi(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
+ */
+SimpleRwsemFuncs(fi, struct file *f, ftofi(f)->fi_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define FiMustReadLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ RwMustReadLock(&ftofi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustWriteLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ RwMustWriteLock(&ftofi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustAnyLock(f) do { \
+ SiMustAnyLock((f)->f_dentry->d_sb); \
+ RwMustAnyLock(&ftofi(f)->fi_rwsem); \
+} while (0)
+
+#define FiMustNoWaiters(f) RwMustNoWaiters(&ftofi(f)->fi_rwsem)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_FILE_H__ */
diff --git a/ubuntu/fs/aufs/finfo.c b/ubuntu/fs/aufs/finfo.c
new file mode 100644
index 0000000..7b265f7
--- /dev/null
+++ b/ubuntu/fs/aufs/finfo.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: finfo.c,v 1.31 2007/10/30 12:52:28 sfjro Exp $ */
+
+#include "aufs.h"
+
+struct aufs_finfo *ftofi(struct file *file)
+{
+ struct aufs_finfo *finfo = file->private_data;
+ AuDebugOn(!finfo
+ || !finfo->fi_hfile
+ || (0 < finfo->fi_bend
+ && (/* stosi(file->f_dentry->d_sb)->si_bend
+ < finfo->fi_bend
+ || */ finfo->fi_bend < finfo->fi_bstart)));
+ return finfo;
+}
+
+// hard/soft set
+aufs_bindex_t fbstart(struct file *file)
+{
+ FiMustAnyLock(file);
+ return ftofi(file)->fi_bstart;
+}
+
+aufs_bindex_t fbend(struct file *file)
+{
+ FiMustAnyLock(file);
+ return ftofi(file)->fi_bend;
+}
+
+struct aufs_vdir *fvdir_cache(struct file *file)
+{
+ FiMustAnyLock(file);
+ return ftofi(file)->fi_vdir_cache;
+}
+
+struct aufs_branch *ftobr(struct file *file, aufs_bindex_t bindex)
+{
+ struct aufs_finfo *finfo = ftofi(file);
+ struct aufs_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 && br_count(hf->hf_br) <= 0);
+ return hf->hf_br;
+}
+
+struct file *au_h_fptr_i(struct file *file, aufs_bindex_t bindex)
+{
+ struct aufs_finfo *finfo = ftofi(file);
+ struct aufs_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
+ && br_count(hf->hf_br) <= 0);
+ return hf->hf_file;
+}
+
+struct file *au_h_fptr(struct file *file)
+{
+ return au_h_fptr_i(file, fbstart(file));
+}
+
+void set_fbstart(struct file *file, aufs_bindex_t bindex)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(sbend(file->f_dentry->d_sb) < bindex);
+ ftofi(file)->fi_bstart = bindex;
+}
+
+void set_fbend(struct file *file, aufs_bindex_t bindex)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(sbend(file->f_dentry->d_sb) < bindex
+ || bindex < fbstart(file));
+ ftofi(file)->fi_bend = bindex;
+}
+
+void set_fvdir_cache(struct file *file, struct aufs_vdir *vdir_cache)
+{
+ FiMustWriteLock(file);
+ AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)
+ || (ftofi(file)->fi_vdir_cache && vdir_cache));
+ ftofi(file)->fi_vdir_cache = vdir_cache;
+}
+
+void au_hfput(struct aufs_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);
+ br_put(hf->hf_br);
+ hf->hf_br = NULL;
+}
+
+void set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val)
+{
+ struct aufs_finfo *finfo = ftofi(file);
+ struct aufs_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 = stobr(file->f_dentry->d_sb, bindex);
+ }
+}
+
+void au_update_figen(struct file *file)
+{
+ atomic_set(&ftofi(file)->fi_generation, au_digen(file->f_dentry));
+ //smp_mb(); /* atomic_set */
+}
+
+void au_finfo_fin(struct file *file)
+{
+ struct aufs_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 = fbend(file);
+ bindex = fbstart(file);
+ if (bindex >= 0)
+ for (; bindex <= bend; bindex++)
+ set_h_fptr(file, bindex, NULL);
+
+ finfo = ftofi(file);
+#ifdef CONFIG_AUFS_DEBUG
+ if (finfo->fi_bstart >= 0) {
+ bend = fbend(file);
+ for (bindex = finfo->fi_bstart; bindex <= bend; bindex++) {
+ struct aufs_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 aufs_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(sbend(dentry->d_sb) + 1,
+ sizeof(*finfo->fi_hfile), GFP_KERNEL);
+ if (finfo->fi_hfile) {
+ 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 --git a/ubuntu/fs/aufs/hinode.h b/ubuntu/fs/aufs/hinode.h
new file mode 100644
index 0000000..b46775f
--- /dev/null
+++ b/ubuntu/fs/aufs/hinode.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: hinode.h,v 1.7 2007/10/22 02:15:35 sfjro Exp $ */
+
+#ifndef __AUFS_HINODE_H__
+#define __AUFS_HINODE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/inotify.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+//#include "branch.h"
+#include "inode.h"
+//#include "vfsub.h"
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#else
+struct inotify_watch {
+ /* empty */
+};
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct aufs_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 aufs_hinode {
+ struct inode *hi_inode;
+ aufs_bindex_t hi_id;
+#ifdef CONFIG_AUFS_HINOTIFY
+ struct aufs_hinotify *hi_notify;
+#endif
+
+ /* reference to the copied-up whiteout with get/put */
+ struct dentry *hi_whdentry;
+};
+
+struct aufs_hin_ignore {
+#ifdef CONFIG_AUFS_HINOTIFY
+ __u32 ign_events;
+ struct aufs_hinode *ign_hinode;
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline
+void au_hin_init(struct aufs_hinode *hinode, struct aufs_hinotify *val)
+{
+ hinode->hi_notify = val;
+}
+
+/* hinotify.c */
+
+int au_hin_alloc(struct aufs_hinode *hinode, struct inode *inode,
+ struct inode *h_inode);
+void au_hin_free(struct aufs_hinode *hinode);
+void au_do_hdir_lock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex, unsigned int lsc);
+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex);
+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir);
+void 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 aufs_hinode *hinode, __u32 events);
+void au_hin_unignore(struct aufs_hinode *hinode, __u32 events);
+
+int __init au_inotify_init(void);
+void au_inotify_fin(void);
+#else
+static inline
+void au_hin_init(struct aufs_hinode *hinode, struct aufs_hinotify *val)
+{
+ /* empty */
+}
+
+static inline
+int au_hin_alloc(struct aufs_hinode *hinode, struct inode *inode,
+ struct inode *h_inode)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline void au_hin_free(struct aufs_hinode *hinode)
+{
+ /* nothing */
+}
+
+static inline void au_do_hdir_lock(struct inode *h_dir, struct inode *dir,
+ aufs_bindex_t bindex, unsigned int lsc)
+{
+ vfsub_i_lock_nested(h_dir, lsc);
+}
+
+static inline
+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
+{
+ vfsub_i_unlock(h_dir);
+}
+
+static inline
+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir)
+{
+ vfsub_lock_rename(h_parents[0], h_parents[1]);
+}
+
+static inline
+void 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 __u32 au_notify_change_events(struct iattr *ia)
+{
+ return 0;
+}
+
+static inline void au_hin_ignore(struct aufs_hinotify *hinotify, __u32 events)
+{
+ /* nothing */
+}
+
+static inline void au_hin_unignore(struct aufs_hinotify *hinotify, __u32 events)
+{
+ /* nothing */
+}
+
+#define au_inotify_init() 0
+#define au_inotify_fin() do {} while (0)
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * hgdir_lock, hdir_lock, hdir2_lock
+ */
+#define LockFunc(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); }
+
+LockFunc(hdir, PARENT);
+LockFunc(hdir2, PARENT2);
+
+#undef LockFunc
+
+/* ---------------------------------------------------------------------- */
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_HINODE_H__ */
diff --git a/ubuntu/fs/aufs/hinotify.c b/ubuntu/fs/aufs/hinotify.c
new file mode 100644
index 0000000..da2904a
--- /dev/null
+++ b/ubuntu/fs/aufs/hinotify.c
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: hinotify.c,v 1.39 2008/01/21 04:57:48 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;
+
+int au_hin_alloc(struct aufs_hinode *hinode, struct inode *inode,
+ struct inode *hidden_inode)
+{
+ int err, i;
+ struct aufs_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 aufs_hinode *hinode)
+{
+ int err;
+ struct aufs_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 (!err) {
+ au_cache_free_hinotify(hin);
+ hinode->hi_notify = NULL;
+ } else
+ AuIOErr1("failed inotify_rm_watch() %d\n", err);
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void ctl_hinotify(struct aufs_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;
+#if 0 // temp
+ {
+ u32 wd;
+ wd = inotify_find_update_watch(in_handle, h_inode, mask);
+ AuTraceErr(wd);
+ /* ignore an err; */
+ }
+#else
+ /* 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);
+#endif
+ 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 aufs_hinode *hinode;
+
+ LKTRTrace("i%lu, b%d, lsc %d\n", dir->i_ino, bindex, lsc);
+ AuDebugOn(!S_ISDIR(dir->i_mode));
+ hinode = itoii(dir)->ii_hinode + bindex;
+ AuDebugOn(h_dir != hinode->hi_inode);
+
+ vfsub_i_lock_nested(h_dir, lsc);
+ suspend_hinotify(hinode);
+}
+
+void hdir_unlock(struct inode *h_dir, struct inode *dir, aufs_bindex_t bindex)
+{
+ struct aufs_hinode *hinode;
+
+ LKTRTrace("i%lu, b%d\n", dir->i_ino, bindex);
+ AuDebugOn(!S_ISDIR(dir->i_mode));
+ hinode = itoii(dir)->ii_hinode + bindex;
+ AuDebugOn(h_dir != hinode->hi_inode);
+
+ resume_hinotify(hinode);
+ vfsub_i_unlock(h_dir);
+}
+
+void hdir_lock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir)
+{
+ struct aufs_hinode *hinode;
+
+ LKTRTrace("%.*s, %.*s\n",
+ AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1]));
+
+ vfsub_lock_rename(h_parents[0], h_parents[1]);
+ hinode = itoii(dirs[0])->ii_hinode + bindex;
+ AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode);
+ suspend_hinotify(hinode);
+ if (issamedir)
+ return;
+ hinode = itoii(dirs[1])->ii_hinode + bindex;
+ AuDebugOn(h_parents[1]->d_inode != hinode->hi_inode);
+ suspend_hinotify(hinode);
+}
+
+void hdir_unlock_rename(struct dentry **h_parents, struct inode **dirs,
+ aufs_bindex_t bindex, int issamedir)
+{
+ struct aufs_hinode *hinode;
+
+ LKTRTrace("%.*s, %.*s\n",
+ AuDLNPair(h_parents[0]), AuDLNPair(h_parents[1]));
+
+ hinode = itoii(dirs[0])->ii_hinode + bindex;
+ AuDebugOn(h_parents[0]->d_inode != hinode->hi_inode);
+ resume_hinotify(hinode);
+ if (!issamedir) {
+ hinode = itoii(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 = ibend(inode);
+ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
+ hi = au_h_iptr_i(inode, bindex);
+ if (hi) {
+ //vfsub_i_lock_nested(hi, AuLsc_I_CHILD);
+ iwhdentry = au_hi_wh(inode, bindex);
+ if (unlikely(iwhdentry))
+ dget(iwhdentry);
+ igrab(hi);
+ set_h_iptr(inode, bindex, NULL, 0);
+ set_h_iptr(inode, bindex, igrab(hi),
+ flags & ~AUFS_HI_XINO);
+ iput(hi);
+ dput(iwhdentry);
+ //vfsub_i_unlock(hi);
+ }
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_hin_ignore(struct aufs_hinode *hinode, __u32 events)
+{
+ int i;
+ atomic_t *ign;
+
+ AuDebugOn(!hinode || !events);
+ LKTRTrace("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events);
+#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 aufs_hinode *hinode, __u32 events)
+{
+ int i;
+ atomic_t *ign;
+
+ AuDebugOn(!hinode || !events);
+ LKTRTrace("hi%lu, 0x%x\n", hinode->hi_inode->i_ino, events);
+#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 xino_entry xinoe;
+ int err;
+
+ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
+ AuDebugOn(AuFlag(stosi(sb), f_xino) == AuXino_NONE);
+
+ inode = NULL;
+ err = 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);
+#if 0 // debug
+ if (au_iigen(inode) == au_sigen(sb))
+ goto out; /* success */
+
+ err = au_refresh_hinode_self(inode);
+ if (!err)
+ goto out; /* success */
+
+ AuIOErr1("err %d ignored, but ino will be broken\n", err);
+ ii_write_unlock(inode);
+ iput(inode);
+ inode = NULL;
+#endif
+
+ out:
+ return inode;
+}
+
+static int hin_xino(struct inode *inode, struct inode *h_inode)
+{
+ int err;
+ aufs_bindex_t bindex, bend, bfound;
+
+ 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 = ibend(inode);
+ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
+ if (au_h_iptr_i(inode, bindex) == h_inode) {
+ bfound = bindex;
+ break;
+ }
+ }
+ if (bfound < 0)
+ goto out;
+
+ err = xino_write0(inode->i_sb, bindex, h_inode->i_ino, 0);
+ /* ignore this error */
+ /* bad action? */
+
+ /* children inode number will be broken */
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int hin_iigen(struct inode *inode)
+{
+ LKTRTrace("i%lu\n", inode->i_ino);
+
+ if (inode->i_ino != AUFS_ROOT_INO)
+ au_iigen_dec(inode);
+ else
+ AuWarn("branch root dir was changed\n");
+ return 0;
+}
+
+static int hin_digen_tree(struct dentry *dentry, int dec_iigen)
+{
+ int err, i, j, ndentry;
+ struct au_dcsub_pages dpages;
+ struct au_dpage *dpage;
+ struct dentry **dentries;
+
+ LKTRTrace("%.*s, iigen %d\n", AuDLNPair(dentry), dec_iigen);
+
+ 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 (dec_iigen && d->d_inode)
+ //reset children xino? cached children only?
+ hin_iigen(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_digen_by_inode(char *name, unsigned int nlen,
+ struct inode *inode, int dec_iigen)
+{
+ int err;
+ struct dentry *d;
+ struct qstr *dname;
+
+ LKTRTrace("%.*s, i%lu, iigen %d\n",
+ nlen, name, inode->i_ino, dec_iigen);
+
+ err = 1;
+ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) {
+ AuWarn("branch root dir was changed\n");
+ err = 0;
+ goto out;
+ }
+
+ if (!S_ISDIR(inode->i_mode)) {
+ AuDebugOn(!name);
+ 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 {
+ d = d_find_alias(inode);
+ if (d) {
+ dname = &d->d_name;
+ if (dname->len == nlen
+ && !memcmp(dname->name, name, nlen))
+ err = hin_digen_tree(d, dec_iigen);
+ dput(d);
+ }
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int hin_digen_by_name(struct dentry *dentry, int dec_iigen)
+{
+ struct inode *inode;
+
+ LKTRTrace("%.*s, iigen %d\n", AuDLNPair(dentry), dec_iigen);
+
+ if (IS_ROOT(dentry)) {
+ AuWarn("branch root dir was changed\n");
+ return 0;
+ }
+
+ inode = dentry->d_inode;
+ if (!inode) {
+ d_drop(dentry);
+ au_digen_dec(dentry);
+ } else if (unlikely(inode->i_ino == AUFS_ROOT_INO))
+ AuWarn("branch root dir was changed\n");
+ else {
+ if (!S_ISDIR(inode->i_mode)) {
+ au_digen_dec(dentry);
+ d_drop(dentry);
+ } else
+ hin_digen_tree(dentry, dec_iigen);
+ }
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+union hin_job {
+ unsigned int flags;
+ struct {
+ unsigned int xino0:1;
+ unsigned int iigen:1;
+ unsigned int digen:1;
+ unsigned int dirent:1;
+ unsigned int attr:1;
+ };
+};
+
+struct hin_job_args {
+ union hin_job *jobs;
+ 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)
+{
+ /* reset xino */
+ if (a->inode && a->jobs->xino0)
+ hin_xino(a->inode, a->h_inode);
+ /* ignore this error */
+
+ /* make the generation obsolete */
+ if (a->jobs->iigen && a->inode)
+ hin_iigen(a->inode);
+ /* ignore this error */
+
+ if (a->jobs->digen) {
+ int err;
+ err = -1;
+ if (a->inode)
+ err = hin_digen_by_inode(a->h_name, a->h_nlen, a->inode,
+ a->jobs->iigen);
+ if (err && a->dentry)
+ hin_digen_by_name(a->dentry, a->jobs->iigen);
+ /* ignore this error */
+ }
+
+ /* make dir entries obsolete */
+ if (a->jobs->dirent) {
+ struct aufs_vdir *vdir;
+ vdir = ivdir(a->inode);
+ if (vdir)
+ vdir->vd_jiffy = 0;
+ IMustLock(a->inode);
+ a->inode->i_version++;
+ }
+
+ /* update the attr */
+ if (a->jobs->attr
+ && a->inode
+ && au_h_iptr(a->inode) == a->h_inode)
+ au_cpup_attr_all(a->inode);
+ return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+
+enum { CHILD, PARENT };
+struct postproc_args {
+ struct inode *h_dir, *dir, *h_child_inode;
+ u32 mask;
+ union hin_job jobs[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 aufs_sbinfo *sbinfo;
+
+ //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;
+ vfsub_i_lock(a->dir);
+ sb = a->dir->i_sb;
+ sbinfo = stosi(sb);
+ si_read_lock(sb, !AuLock_FLUSH);
+
+ ii_read_lock_parent(a->dir);
+ bfound = -1;
+ bend = ibend(a->dir);
+ for (bindex = ibstart(a->dir); bindex <= bend; bindex++)
+ if (au_h_iptr_i(a->dir, bindex) == a->h_dir) {
+ bfound = bindex;
+ break;
+ }
+ ii_read_unlock(a->dir);
+ if (unlikely(bfound < 0))
+ goto out;
+
+ xino = (AuFlag(sbinfo, f_xino) != AuXino_NONE);
+ h_ino = 0;
+ if (a->h_child_inode)
+ h_ino = a->h_child_inode->i_ino;
+
+ if (a->h_child_nlen && a->jobs[CHILD].digen)
+ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen,
+ a->dir);
+ if (dentry)
+ inode = dentry->d_inode;
+ if (xino && !inode && h_ino
+ && (a->jobs[CHILD].xino0
+ || a->jobs[CHILD].iigen
+ || a->jobs[CHILD].digen
+ || a->jobs[CHILD].attr))
+ inode = lookup_wlock_by_ino(sb, bfound, h_ino);
+
+ args.jobs = a->jobs + 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);
+ }
+
+ ii_write_lock_parent(a->dir);
+ args.jobs = a->jobs + 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);
+
+ out:
+ si_read_unlock(sb);
+ vfsub_i_unlock(a->dir);
+ 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 aufs_hinotify *hinotify;
+ struct postproc_args *args;
+ int len, wkq_err, isdir, isroot, wh, idx;
+ char *p;
+ struct inode *dir;
+ union hin_job jobs[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 0 //defined(ForceInotify) || defined(DbgInotify)
+ 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);
+#endif
+ /* 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 aufs_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);
+#ifdef DbgInotify
+#if 0
+ 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);
+#endif
+#if 0
+ if (!h_child_name || strcmp(h_child_name, AUFS_XINO_FNAME))
+ WARN_ON(1);
+#endif
+#endif
+
+ dir = hinotify->hin_aufs_inode;
+ isroot = (dir->i_ino == AUFS_ROOT_INO);
+ len = 0;
+ wh = 0;
+ if (h_child_name) {
+ len = strlen(h_child_name);
+ if (unlikely(!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);
+ jobs[PARENT].flags = 0;
+ jobs[CHILD].flags = 0;
+ switch (mask & IN_ALL_EVENTS) {
+ case IN_MODIFY:
+ /*FALLTHROUGH*/
+ case IN_ATTRIB:
+ if (h_child_inode) {
+ if (!wh)
+ jobs[CHILD].attr = 1;
+ } else
+ jobs[PARENT].attr = 1;
+ 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);
+ jobs[CHILD].iigen = 1;
+ jobs[CHILD].attr = 1;
+ jobs[CHILD].xino0 = 1;//!!isdir;
+ jobs[CHILD].digen = 1;
+ jobs[PARENT].attr = 1;
+ jobs[PARENT].dirent = 1;
+ break;
+
+ case IN_CREATE:
+ AuDebugOn(!h_child_name || !h_child_inode);
+ jobs[PARENT].attr = 1;
+ jobs[PARENT].dirent = 1;
+ jobs[CHILD].digen = 1;
+ jobs[CHILD].iigen = 1;
+ /* hard link */
+ jobs[CHILD].attr = (!isdir && h_child_inode->i_nlink > 1);
+ 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);
+ jobs[PARENT].attr = 1;
+ jobs[PARENT].dirent = 1;
+ jobs[CHILD].iigen = 1;
+ jobs[CHILD].digen = 1;
+ break;
+
+ case IN_DELETE_SELF:
+ jobs[PARENT].iigen = !isroot;
+ /*FALLTHROUGH*/
+
+ case IN_MOVE_SELF:
+ AuDebugOn(h_child_name || h_child_inode);
+ if (unlikely(isroot)) {
+ AuWarn("root branch was moved\n");
+ return;
+ }
+#if 1
+ return;
+#else
+ jobs[PARENT].xino0 = !isroot;
+ jobs[PARENT].iigen = !isroot;
+ jobs[PARENT].digen = !isroot;
+ jobs[PARENT].attr = !isroot;
+ jobs[PARENT].dirent = !isroot;
+ break;
+#endif
+ case IN_ACCESS:
+ default:
+ AuDebugOn(1);
+ }
+
+#if 0 //def DbgInotify
+ WARN_ON(1);
+#endif
+
+ if (wh)
+ h_child_inode = NULL;
+
+ /* iput() and kfree() will be called in postproc() */
+ args = kmalloc(sizeof(*args) + len + 1, GFP_TEMPORARY);
+ if (unlikely(!args)) {
+ AuErr1("no memory\n");
+ return;
+ }
+ memcpy(args->jobs, jobs, sizeof(jobs));
+ args->mask = mask;
+ args->dir = igrab(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(&stosi(sb)->si_nowait);
+ wkq_err = au_wkq_nowait(postproc, args, sb, /*dlgt*/0);
+ if (unlikely(wkq_err)) {
+ AuErr("wkq %d\n", wkq_err);
+ au_nwt_dec(&stosi(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
+};
+
+/* ---------------------------------------------------------------------- */
+
+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 != 12);
+
+ in_handle = inotify_init(&aufs_inotify_ops);
+ if (!IS_ERR(in_handle))
+ return 0;
+ AuTraceErrPtr(in_handle);
+ return PTR_ERR(in_handle);
+}
+
+void au_inotify_fin(void)
+{
+ inotify_destroy(in_handle);
+}
diff --git a/ubuntu/fs/aufs/i_op.c b/ubuntu/fs/aufs/i_op.c
new file mode 100644
index 0000000..3a946b0
--- /dev/null
+++ b/ubuntu/fs/aufs/i_op.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: i_op.c,v 1.53 2008/01/28 05:01:42 sfjro Exp $ */
+
+//#include <linux/fs.h>
+//#include <linux/namei.h>
+#include <linux/security.h>
+#include "aufs.h"
+
+#ifdef CONFIG_AUFS_DLGT
+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);
+}
+#endif /* CONFIG_AUFS_DLGT */
+
+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->mnt
+ && (fake_nd->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 && !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 LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) || defined(CONFIG_AUFS_SEC_PERM_PATCH)
+ if (!err) {
+#ifndef CONFIG_AUFS_DLGT
+ err = security_inode_permission(h_inode, mask, fake_nd);
+#else
+ 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;
+ }
+#endif /* CONFIG_AUFS_DLGT */
+ }
+#endif
+
+ 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);
+
+#ifdef CONFIG_AUFS_FAKE_DM
+ si_read_lock(sb, !AuLock_FLUSH);
+ ii_read_lock_child(inode);
+#else
+ if (!nd || !nd->dentry) {
+ si_read_lock(sb, !AuLock_FLUSH);
+ ii_read_lock_child(inode);
+ } else if (nd->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->dentry, 0);
+ } else {
+ locked = 2;
+ aufs_read_lock(nd->dentry, AuLock_IR);
+ }
+#endif /* CONFIG_AUFS_FAKE_DM */
+ 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);
+
+#ifdef CONFIG_AUFS_FAKE_DM
+ ii_read_unlock(inode);
+ si_read_unlock(sb);
+#else
+ switch (locked) {
+ case 0:
+ ii_read_unlock(inode);
+ si_read_unlock(sb);
+ break;
+ case 1:
+ di_read_unlock(nd->dentry, 0);
+ ii_read_unlock(inode);
+ si_read_unlock(sb);
+ break;
+ case 2:
+ aufs_read_unlock(nd->dentry, AuLock_IR);
+ break;
+ default:
+ BUG();
+ }
+#endif /* CONFIG_AUFS_FAKE_DM */
+}
+
+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;
+ 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->dentry : 0, nd ? !!nd->mnt : 0);
+
+ sb = inode->i_sb;
+ locked = silly_lock(inode, nd);
+ dlgt = au_need_dlgt(sb);
+
+ if (nd)
+ fake_nd = *nd;
+ if (/* unlikely */(nondir || write_mask)) {
+ h_inode = au_h_iptr(inode);
+ AuDebugOn(!h_inode
+ || ((h_inode->i_mode & S_IFMT)
+ != (inode->i_mode & S_IFMT)));
+ err = 0;
+ bindex = 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 {
+ err = h_permission(h_inode, mask, p,
+ 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 (!br_rdonly(stobr(sb, bindex))) {
+ err = 0;
+ break;
+ }
+ }
+ goto out;
+ }
+
+ /* non-write to dir */
+ err = 0;
+ bend = ibend(inode);
+ for (bindex = ibstart(inode); !err && bindex <= bend; bindex++) {
+ h_inode = au_h_iptr_i(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 {
+ err = h_permission(h_inode, mask, p,
+ 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;
+ //todo: no more lower nameidata, only here. re-use it.
+
+ 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(stosi(dir->i_sb), &tmp_nd, nd);
+ npositive = au_lkup_dentry(dentry, dbstart(parent), /*type*/0, ndp);
+ err = npositive;
+ LKTRLabel(here);
+ //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)->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)) {
+#if 1
+ /* d_splice_alias() also supports d_add() */
+ ret = d_splice_alias(inode, dentry);
+ if (unlikely(IS_ERR(ret) && inode))
+ ii_write_unlock(inode);
+#else
+ d_add(dentry, inode);
+#endif
+ //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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+//todo: simplify
+/*
+ * 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 = NULL, *h_parent;
+ struct inode *dir, *src_dir = NULL;
+ struct aufs_sbinfo *sbinfo;
+
+ LKTRTrace("%.*s, src %p, {%d, %d, %d, %d}\n",
+ AuDLNPair(dentry), src_dentry, args->force_btgt,
+ args->add_entry, args->do_lock_srcdir, args->isdir);
+ //AuDbgDentry(dentry);
+
+ sb = dentry->d_sb;
+ sbinfo = stosi(sb);
+ parent = dget_parent(dentry);
+ bstart = dbstart(dentry);
+ bcpup = bstart;
+ if (args->force_btgt < 0) {
+ if (src_dentry) {
+ src_bstart = dbstart(src_dentry);
+ if (src_bstart < bstart)
+ bcpup = src_bstart;
+ } else if (args->add_entry) {
+ err = AuWbrCreate(sbinfo, dentry, args->isdir);
+ bcpup = err;
+ }
+
+ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) {
+ if (args->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);
+ src_dir = src_parent->d_inode;
+ if (args->do_lock_srcdir)
+ di_write_lock_parent2(src_parent);
+ }
+
+ dir = parent->d_inode;
+ if (args->add_entry) {
+ au_update_dbstart(dentry);
+ IMustLock(dir);
+ DiMustWriteLock(parent);
+ IiMustWriteLock(dir);
+ } else
+ di_write_lock_parent(parent);
+
+ err = 0;
+ if (!au_h_dptr_i(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 && args->add_entry) {
+ h_parent = au_h_dptr_i(parent, bcpup);
+ AuDebugOn(!h_parent || !h_parent->d_inode);
+ vfsub_i_lock_nested(h_parent->d_inode, AuLsc_I_PARENT);
+ err = au_lkup_neg(dentry, bcpup);
+ //err = -1;
+ vfsub_i_unlock(h_parent->d_inode);
+ if (bstart < bcpup && dbstart(dentry) < 0) {
+ set_dbstart(dentry, 0);
+ au_update_dbrange(dentry, /*do_put_zero*/0);
+ }
+ }
+
+ if (!args->add_entry)
+ di_write_unlock(parent);
+ if (args->do_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 int aufs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+ int err;
+ aufs_bindex_t bstart, bcpup;
+ struct inode *h_inode, *inode, *dir, *h_dir, *gdir;
+ struct dentry *h_dentry, *parent, *hi_wh, *gparent;
+ unsigned int udba;
+ struct aufs_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct super_block *sb;
+ __u32 events;
+ struct au_cpup_flags cflags;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .add_entry = 0,
+ .do_lock_srcdir = 0,
+ .isdir = S_ISDIR(dentry->d_inode->i_mode)
+ };
+
+ LKTRTrace("%.*s, ia_valid 0x%x\n", AuDLNPair(dentry), ia->ia_valid);
+ inode = dentry->d_inode;
+ IMustLock(inode);
+
+ aufs_read_lock(dentry, AuLock_DW);
+ bstart = dbstart(dentry);
+ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args);
+ bcpup = err;
+ //err = -1;
+ if (unlikely(err < 0))
+ goto out;
+
+ /* crazy udba locks */
+ sb = dentry->d_sb;
+ udba = au_flag_test_udba_inotify(sb);
+ parent = NULL;
+ dir = NULL;
+ if (!IS_ROOT(dentry)) {
+ parent = dget_parent(dentry);
+ dir = parent->d_inode;
+ di_read_lock_parent(parent, AuLock_IR);
+ }
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ gparent = NULL;
+ gdir = NULL;
+ if (unlikely(udba && parent && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ gdir = gparent->d_inode;
+ ii_read_lock_parent2(gdir);
+ }
+
+ h_dentry = au_h_dptr(dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+#define HiLock(bindex) \
+ do { \
+ if (!wr_dir_args.isdir) \
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD); \
+ else \
+ hdir2_lock(h_inode, inode, bindex); \
+ } while (0)
+#define HiUnlock(bindex) \
+ do { \
+ if (!wr_dir_args.isdir) \
+ vfsub_i_unlock(h_inode); \
+ else \
+ hdir_unlock(h_inode, inode, bindex); \
+ } while (0)
+
+ if (bstart != bcpup) {
+ loff_t size = -1;
+
+ if ((ia->ia_valid & ATTR_SIZE)
+ && ia->ia_size < i_size_read(inode)) {
+ size = ia->ia_size;
+ ia->ia_valid &= ~ATTR_SIZE;
+ }
+ hi_wh = NULL;
+ h_dir = au_h_iptr_i(dir, bcpup);
+ hdir_lock(h_dir, dir, bcpup);
+ HiLock(bstart);
+ if (!d_unhashed(dentry)) {
+ cflags.dtime = 1;
+ err = au_sio_cpup_simple(dentry, bcpup, size, &cflags);
+ } else {
+ hi_wh = au_hi_wh(inode, bcpup);
+ if (!hi_wh) {
+ err = au_sio_cpup_wh(dentry, bcpup, size,
+ /*file*/NULL);
+ if (!err)
+ hi_wh = au_hi_wh(inode, bcpup);
+ }
+#if 0
+ revalidate hi_wh
+#endif
+ }
+
+ //err = -1;
+ HiUnlock(bstart);
+ hdir_unlock(h_dir, dir, bcpup);
+ if (unlikely(err || !ia->ia_valid))
+ goto out_unlock;
+
+ if (!hi_wh)
+ h_dentry = au_h_dptr(dentry);
+ else
+ h_dentry = hi_wh; /* do not dget here */
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))
+ ia->ia_valid &= ~ATTR_MODE;
+#endif
+
+ vfsub_args_init(&vargs, &ign, au_need_dlgt(sb), 0);
+ if (unlikely(udba && dir)) {
+ events = vfsub_events_notify_change(ia);
+ if (events)
+ vfsub_ign_hinode(&vargs, events, itohi(dir, bcpup));
+ }
+ HiLock(bcpup);
+ err = vfsub_notify_change(h_dentry, ia, &vargs);
+ //err = -1;
+ if (!err)
+ au_cpup_attr_changeable(inode);
+ HiUnlock(bcpup);
+#undef HiLock
+#undef HiUnlock
+
+ out_unlock:
+ if (parent) {
+ di_read_unlock(parent, AuLock_IR);
+ dput(parent);
+ }
+ if (unlikely(gdir)) {
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+ out:
+ aufs_read_unlock(dentry, AuLock_DW);
+ AuTraceErr(err);
+ return err;
+}
+
+/* currently, for fuse only */
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+static 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);
+ 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;
+}
+#endif /* CONFIG_AUFS_WORKAROUND_FUSE */
+
+/* ---------------------------------------------------------------------- */
+
+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_i(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(sbr_mnt(sb, bindex), h_dentry);
+ au_update_fuse_h_inode(NULL, h_dentry); /*ignore*/
+ dentry->d_inode->i_atime = h_dentry->d_inode->i_atime;
+ }
+ 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, 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->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, 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_release(nd);
+ 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));
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+static void aufs_truncate_range(struct inode *inode, loff_t start, loff_t end)
+{
+ AuUnsupport();
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+static long aufs_fallocate(struct inode *inode, int mode, loff_t offset,
+ loff_t len)
+{
+ AuUnsupport();
+ return -ENOSYS;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+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
+
+#if 0 // xattr
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .fallocate = aufs_fallocate
+#endif
+};
+
+struct inode_operations aufs_iop = {
+ .permission = aufs_permission,
+ .setattr = aufs_setattr,
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ .getattr = aufs_getattr,
+#endif
+
+#if 0 // xattr
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
+#endif
+
+ //void (*truncate) (struct inode *);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ .truncate_range = aufs_truncate_range,
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+ .fallocate = aufs_fallocate
+#endif
+};
diff --git a/ubuntu/fs/aufs/i_op_add.c b/ubuntu/fs/aufs/i_op_add.c
new file mode 100644
index 0000000..aea61b0
--- /dev/null
+++ b/ubuntu/fs/aufs/i_op_add.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: i_op_add.c,v 1.58 2008/01/28 05:02:00 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 aufs_ndx ndx;
+
+ 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 = dbwh(dentry);
+ err = au_unlink_wh_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 (ibstart(dir) == dbstart(dentry))
+ au_cpup_attr_timesizes(dir);
+ dir->i_version++;
+ return 0; /* success */
+ }
+
+ err = PTR_ERR(inode);
+ if (!wh_dentry)
+ goto out;
+
+ /* revert */
+ ndx.dlgt = au_need_dlgt(dentry->d_sb);
+ ndx.nfsmnt = au_nfsmnt(dentry->d_sb, bwh);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ /* dir inode is locked */
+ wh = simple_create_wh(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;
+}
+
+/*
+ * 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 aufs_ndx ndx;
+ struct super_block *sb;
+ struct aufs_hinode *hgdir;
+
+ LKTRTrace("%.*s, src %p\n", AuDLNPair(dentry), src_dentry);
+
+ parent = dentry->d_parent; /* dir inode is locked */
+ IMustLock(parent->d_inode);
+ bstart = 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;
+ //todo: meaningless lock if CONFIG_AUFS_DEBUG is disabled.
+ hgdir = NULL;
+ if (unlikely(dt && au_flag_test_udba_inotify(sb) && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ gdir = gparent->d_inode;
+ ii_read_lock_parent2(gdir);
+ hgdir = itohi(gdir, bcpup);
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+ dir = parent->d_inode;
+ h_parent = au_h_dptr_i(parent, bcpup);
+ h_dir = h_parent->d_inode;
+#ifdef DbgUdbaRace
+ AuDbgSleep(DbgUdbaRace);
+#endif
+ hdir_lock(h_dir, dir, bcpup);
+
+ /*
+ * someone else might change our parent-child relationship directly,
+ * bypassing aufs, while we are handling a systemcall for aufs.
+ */
+ wh_dentry = ERR_PTR(-EIO);
+ if (unlikely(h_parent != au_h_dptr_i(dentry, bcpup)->d_parent))
+ goto out_dir;
+#if 0
+ revalidate h_negative
+#endif
+
+ if (dt)
+ au_dtime_store(dt, parent, h_parent, hgdir);
+ wh_dentry = NULL;
+ if (/* bcpup != bstart || */ bcpup != dbwh(dentry))
+ goto out; /* success */
+
+ ndx.nfsmnt = au_nfsmnt(sb, bcpup);
+ ndx.dlgt = au_need_dlgt(sb);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ wh_dentry = lkup_wh(h_parent, &dentry->d_name, &ndx);
+ //wh_dentry = ERR_PTR(-1);
+
+ out_dir:
+ if (IS_ERR(wh_dentry))
+ 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;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .add_entry = 1,
+ .do_lock_srcdir = 0,
+ .isdir = 0
+ };
+
+ 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;
+
+ h_dentry = au_h_dptr(dentry);
+ 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_need_dlgt(sb);
+
+#if 1 // partial testing
+ 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, dbstart(dentry)));
+ 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();
+ }
+#else
+ err = -1;
+#endif
+ 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);
+ }
+
+ hdir_unlock(h_dir, dir, dbstart(dentry));
+ 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 au_cpup_flags cflags;
+ struct dentry *gparent;
+
+ AuTraceEnter();
+
+ gparent = NULL;
+ gdir = NULL;
+ if (unlikely(au_flag_test_udba_inotify(src_dentry->d_sb)
+ && !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 */
+ 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)) {
+ 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_i(src_dir, a->bdst);
+ hdir_lock(h_dir, src_dir, a->bdst);
+ } else if (unlikely(gparent)) {
+ /* this temporary unlock/lock is safe */
+ hdir_unlock(a->h_dir, dir, a->bdst);
+ ii_read_lock_parent3(gdir);
+ hdir_lock(a->h_dir, dir, a->bdst);
+ }
+ //todo: test parent-gparent relationship
+
+ cflags.dtime = 1;
+ hi = au_h_dptr(src_dentry)->d_inode;
+ vfsub_i_lock_nested(hi, AuLsc_I_CHILD);
+ err = au_sio_cpup_simple(src_dentry, a->bdst, -1, &cflags);
+ //err = -1;
+ vfsub_i_unlock(hi);
+
+ if (unlikely(gparent)) {
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+
+ out:
+ if (h_dir) {
+ hdir_unlock(h_dir, src_dir, a->bdst);
+ 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;
+ struct au_cpup_flags cflags;
+
+ AuTraceEnter();
+
+ sb = src_dentry->d_sb;
+ inode = src_dentry->d_inode;
+ h_dentry = au_h_dptr(src_dentry);
+ h_inode = h_dentry->d_inode;
+ bstart = ibstart(inode);
+ h_dst_inode = NULL;
+ if (bstart <= a->bdst)
+ h_dst_inode = au_h_iptr_i(inode, a->bdst);
+
+ if (!h_dst_inode || !h_dst_inode->i_nlink) {
+ /* copyup src_dentry as the name of dentry. */
+ cflags.dtime = 0;
+ set_dbstart(src_dentry, a->bdst);
+ set_h_dptr(src_dentry, a->bdst, dget(a->h_dentry));
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, -1,
+ &cflags);
+ //err = -1;
+ vfsub_i_unlock(h_inode);
+ set_h_dptr(src_dentry, a->bdst, NULL);
+ 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_append_plink(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;
+ struct vfsub_args vargs;
+ struct au_wr_dir_args wr_dir_args = {
+ //.force_btgt = -1,
+ .add_entry = 1,
+ //.do_lock_srcdir = 0,
+ .isdir = 0
+ };
+
+ 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);
+ wr_dir_args.do_lock_srcdir = !a.issamedir;
+ wr_dir_args.force_btgt = 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.h_dentry = au_h_dptr(dentry);
+ h_parent = a.h_dentry->d_parent; /* dir inode is locked */
+ a.h_dir = h_parent->d_inode;
+ IMustLock(a.h_dir);
+
+ err = 0;
+ a.dlgt = au_need_dlgt(sb);
+
+ //todo: minor optimize, their sb may be same while their bindex differs.
+ a.bsrc = dbstart(src_dentry);
+ a.bdst = dbstart(dentry);
+ h_src_dentry = au_h_dptr(src_dentry);
+ if (unlikely(!AuFlag(stosi(sb), f_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);
+ 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);
+ 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_unlink_wh_dentry(a.h_dir, wh_dentry, dentry,
+ dir, /*dlgt*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out_revert;
+ }
+
+#if 0 // cannot support it
+ /* fuse has different memory inode for the same inode number */
+ if (unlikely(au_test_fuse(a.h_dentry->d_sb))) {
+ LKTRLabel(here);
+ d_drop(a.h_dentry);
+ //d_drop(h_src_dentry);
+ //d_drop(src_dentry);
+ a.inode->i_nlink++;
+ a.inode->i_ctime = dir->i_ctime;
+ }
+#endif
+
+ dir->i_version++;
+ if (ibstart(dir) == 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);
+ a.inode->i_nlink++;
+ a.inode->i_ctime = dir->i_ctime;
+ } else
+ /* nfs case (< 2.6.15) */
+ d_drop(dentry);
+#if 0 // debug
+ //au_debug_on();
+ AuDbgInode(a.inode);
+ //au_debug_off();
+ {
+ aufs_bindex_t i;
+ for (i = ibstart(a.inode); i <= ibend(a.inode); i++) {
+ struct xino_entry xinoe;
+ struct inode *hi;
+ hi = au_h_iptr_i(a.inode, i);
+ if (hi) {
+ xino_read(sb, i, hi->i_ino, &xinoe);
+ AuDbg("hi%lu, i%lu\n", hi->i_ino, xinoe.ino);
+ }
+ }
+ }
+#endif
+ goto out_unlock; /* success */
+
+ out_revert:
+#if 0 // remove
+ if (d_unhashed(a.h_dentry)) {
+ /* hardlink on nfs (< 2.6.15) */
+ struct dentry *d;
+ const struct qstr *name = &a.h_dentry->d_name;
+ AuDebugOn(a.h_dentry->d_parent->d_inode != a.h_dir);
+ /* do not superio. */
+ d = au_lkup_one(name->name, a.h_dentry->d_parent,
+ name->len, au_nfsmnt(sb, a.bdst)??,
+ au_need_dlgt(sb));
+ rerr = PTR_ERR(d);
+ if (IS_ERR(d))
+ goto out_rerr;
+ dput(a.h_dentry);
+ a.h_dentry = d;
+ AuDebugOn(!d->d_inode);
+ }
+#endif
+ 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:
+ 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;
+ struct vfsub_args vargs;
+ struct au_wr_dir_args wr_dir_args = {
+ .force_btgt = -1,
+ .add_entry = 1,
+ .do_lock_srcdir = 0,
+ .isdir = 1
+ };
+
+ 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 = dbstart(dentry);
+ h_dentry = au_h_dptr(dentry);
+ h_parent = h_dentry->d_parent; /* dir inode is locked */
+ h_dir = h_parent->d_inode;
+ IMustLock(h_dir);
+ dlgt = au_need_dlgt(sb);
+
+ 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 (unlikely(wh_dentry || AuFlag(stosi(sb), f_always_diropq))) {
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ opq_dentry = create_diropq(dentry, bindex, /*dlgt*/0);
+ //opq_dentry = ERR_PTR(-1);
+ vfsub_i_unlock(h_inode);
+ 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) {
+ dir->i_nlink++;
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ if (unlikely(diropq)) {
+ LKTRLabel(revert opq);
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ rerr = remove_diropq(dentry, bindex, dlgt);
+ //rerr = -1;
+ vfsub_i_unlock(h_inode);
+ 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:
+ 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 --git a/ubuntu/fs/aufs/i_op_del.c b/ubuntu/fs/aufs/i_op_del.c
new file mode 100644
index 0000000..d133fda
--- /dev/null
+++ b/ubuntu/fs/aufs/i_op_del.c
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: i_op_del.c,v 1.57 2008/01/28 05:02:03 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 = dbstart(dentry);
+ LKTRTrace("bcpup %d, bstart %d\n", *bcpup, bstart);
+ h_dentry = au_h_dptr(dentry);
+ if (*bcpup < 0) {
+ *bcpup = bstart;
+ if (au_test_ro(sb, bstart, dentry->d_inode)) {
+ err = AuWbrCopyup(stosi(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 = dbend(dentry);
+ if (isdir) {
+ bdiropq = dbdiropq(dentry);
+ set_dbdiropq(dentry, -1);
+ }
+ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0,
+ /*nd*/NULL);
+ err = need_wh;
+ //err = -1;
+ if (isdir)
+ set_dbdiropq(dentry, bdiropq);
+ if (unlikely(err < 0))
+ goto out;
+ new_bend = dbend(dentry);
+ if (!need_wh && old_bend != new_bend) {
+ set_h_dptr(dentry, new_bend, NULL);
+ set_dbend(dentry, old_bend);
+#if 0 // remove this?
+ } else if (!au_h_dptr_i(dentry, new_bend)->d_inode) {
+ LKTRTrace("negative\n");
+ set_h_dptr(dentry, new_bend, NULL);
+ set_dbend(dentry, old_bend);
+ need_wh = 0;
+#endif
+ }
+ }
+ LKTRTrace("need_wh %d\n", need_wh);
+ err = need_wh;
+
+ 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 aufs_ndx ndx;
+ struct super_block *sb;
+ struct aufs_hinode *hgdir;
+ aufs_bindex_t bcpup;
+
+ 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;
+ parent = dentry->d_parent; /* dir inode is locked */
+ if (unlikely(au_flag_test_udba_inotify(sb) && !IS_ROOT(parent))) {
+ gparent = dget_parent(parent);
+ gdir = gparent->d_inode;
+ ii_read_lock_parent2(gdir);
+ hgdir = itohi(gdir, bcpup);
+ ii_read_unlock(gdir);
+ dput(gparent);
+ }
+ dir = parent->d_inode;
+ IMustLock(dir);
+ h_parent = au_h_dptr_i(parent, bcpup);
+ h_dir = h_parent->d_inode;
+#ifdef DbgUdbaRace
+ AuDbgSleep(DbgUdbaRace);
+#endif
+ hdir_lock(h_dir, dir, bcpup);
+
+ /*
+ * someone else might change our parent-child relationship directly,
+ * bypassing aufs, while we are handling a systemcall for aufs.
+ */
+ wh_dentry = ERR_PTR(-ENOENT);
+ if (unlikely(dbstart(dentry) == bcpup
+ && h_parent != au_h_dptr_i(dentry, bcpup)->d_parent))
+ goto out_dir;
+#if 0
+ revalidate h_positive
+#endif
+
+ 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.dlgt = au_need_dlgt(sb);
+ ndx.nd = NULL;
+ //ndx.br = NULL;
+ wh_dentry = simple_create_wh(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:
+ 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 aufs_nhash *whlist, struct inode *dir)
+{
+ int rmdir_later, err;
+ struct dentry *hidden_dentry;
+ struct inode *inode, *h_inode;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex);
+
+ inode = NULL;
+ h_inode = NULL;
+ if (unlikely(au_flag_test_udba_inotify(dentry->d_sb))) {
+ inode = dentry->d_inode;
+ h_inode = au_h_iptr_i(inode, bindex);
+ hdir2_lock(h_inode, inode, bindex);
+ }
+ err = rename_whtmp(dir, dentry, bindex, /*noself*/1);
+ if (unlikely(inode))
+ hdir_unlock(h_inode, inode, bindex);
+ //err = -1;
+ if (unlikely(err))
+ goto out;
+
+ hidden_dentry = au_h_dptr_i(dentry, bindex);
+ if (!au_test_nfs(hidden_dentry->d_sb)) {
+ const int dirwh = stosi(dentry->d_sb)->si_dirwh;
+ rmdir_later = (dirwh <= 1);
+ if (!rmdir_later)
+ rmdir_later = nhash_test_longer_wh(whlist, bindex,
+ dirwh);
+ if (rmdir_later)
+ return rmdir_later;
+ }
+
+ err = rmdir_whtmp(hidden_dentry, whlist, bindex, dir, dentry->d_inode,
+ /*noself*/1);
+ //err = -1;
+ if (unlikely(err)) {
+ AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n",
+ AuDLNPair(hidden_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) {
+ set_h_dptr(dentry, dbstart(dentry), NULL);
+ au_update_dbstart(dentry);
+ }
+ if (ibstart(dir) == bindex)
+ au_cpup_attr_timesizes(dir);
+ dir->i_version++;
+}
+
+struct revert_flags {
+ unsigned int dlgt:1;
+};
+
+static int do_revert(int err, struct dentry *wh_dentry, struct dentry *dentry,
+ aufs_bindex_t bwh, struct au_dtime *dt,
+ struct revert_flags *flags)
+{
+ int rerr;
+ struct inode *dir;
+
+ dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */
+ IMustLock(dir);
+ rerr = au_unlink_wh_dentry(dir, wh_dentry, dentry, dir, !!flags->dlgt);
+ //rerr = -1;
+ if (!rerr) {
+ 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 = dbstart(dentry);
+ bwh = 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_need_dlgt(sb);
+ h_dentry = au_h_dptr(dentry);
+ 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_i(parent,
+ bindex));
+ h_dir = wh_dentry->d_parent->d_inode;
+ IMustLock(h_dir);
+ err = 0;
+ }
+
+ if (!err) {
+ inode->i_nlink--;
+#if 0 //todo: update plink
+ if (unlikely(!inode->i_nlink
+ && au_test_plink(sb, inode)
+ /* && atomic_read(&inode->i_count) == 2) */)) {
+ au_debug_on();
+ DbgInode(inode);
+ au_debug_off();
+ }
+#endif
+ epilog(dir, dentry, bindex);
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ if (wh_dentry) {
+ int rerr;
+ struct revert_flags rev_flags = {
+ .dlgt = dlgt
+ };
+ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, &rev_flags);
+ if (rerr)
+ err = rerr;
+ }
+
+ out_unlock:
+ 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 rmdir_whtmp_args *args;
+ struct aufs_nhash *whlist;
+ struct super_block *sb;
+
+ 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 = 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 = dbstart(dentry);
+ bwh = 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;
+
+ h_dentry = au_h_dptr(dentry);
+ 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_i(parent,
+ bindex));
+ h_dir = wh_dentry->d_parent->d_inode;
+ IMustLock(h_dir);
+ err = 0;
+ }
+
+ sb = dentry->d_sb;
+ if (!err) {
+ //aufs_bindex_t bi, bend;
+
+ au_reset_hinotify(inode, /*flags*/0);
+ inode->i_nlink = 0;
+ set_dbdiropq(dentry, -1);
+ epilog(dir, dentry, bindex);
+
+ if (rmdir_later) {
+ kick_rmdir_whtmp(h_dentry, whlist, bstart, dir,
+ inode, /*noself*/1, args);
+ args = NULL;
+ }
+
+ goto out_unlock; /* success */
+ }
+
+ /* revert */
+ LKTRLabel(revert);
+ if (wh_dentry) {
+ int rerr;
+ struct revert_flags rev_flags = {
+ .dlgt = au_need_dlgt(sb)
+ };
+ rerr = do_revert(err, wh_dentry, dentry, bwh, &dt, &rev_flags);
+ if (rerr)
+ err = rerr;
+ }
+
+ out_unlock:
+ 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:
+ nhash_del(whlist);
+ out:
+ AuTraceErr(err);
+ return err;
+}
diff --git a/ubuntu/fs/aufs/i_op_ren.c b/ubuntu/fs/aufs/i_op_ren.c
new file mode 100644
index 0000000..4731670
--- /dev/null
+++ b/ubuntu/fs/aufs/i_op_ren.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: i_op_ren.c,v 1.63 2008/01/28 05:02:03 sfjro Exp $ */
+
+#include "aufs.h"
+
+enum { SRC, DST };
+struct rename_args {
+ struct dentry *h_dentry[2], *parent[2], *h_parent[2];
+ struct aufs_nhash whlist;
+ aufs_bindex_t btgt, bstart[2];
+ struct super_block *sb;
+
+ unsigned int isdir:1;
+ unsigned int issamedir:1;
+ unsigned int whsrc:1;
+ unsigned int whdst:1;
+ unsigned int dlgt:1;
+ unsigned int udba:1;
+} __aligned(sizeof(long));
+
+static 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 rmdir_whtmp_args *thargs;
+ struct dentry *wh_dentry[2], *h_dst;
+ struct inode *h_dir[2];
+ aufs_bindex_t bindex, bend;
+ struct aufs_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct aufs_ndx ndx = {
+ .dlgt = a->dlgt,
+ .nd = NULL,
+ //.br = NULL
+ };
+ struct au_cpup_flags cflags;
+
+ LKTRTrace("%.*s/%.*s, %.*s/%.*s, "
+ "hd{%p, %p}, hp{%p, %p}, wh %p, btgt %d, bstart{%d, %d}, "
+ "flags{%d, %d, %d, %d}\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->isdir, a->issamedir, a->whsrc, a->whdst);
+
+ 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 (a->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);
+ /* create whiteout for src_dentry */
+ if (a->whsrc) {
+ wh_dentry[SRC] = simple_create_wh(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 (a->whdst) {
+ struct dentry *d;
+ d = lkup_wh(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 = rename_whtmp(dir, dentry, a->btgt, /*noself*/0);
+ //err = -1;
+ if (unlikely(err))
+ goto out_whdst;
+ 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_i(dentry, a->btgt);
+ }
+
+ /* cpup src */
+ if (a->h_dentry[DST]->d_inode && a->bstart[SRC] != a->btgt) {
+ cflags.dtime = 0;
+ vfsub_i_lock_nested(a->h_dentry[SRC]->d_inode, AuLsc_I_CHILD);
+ err = au_sio_cpup_simple(src_dentry, a->btgt, -1, &cflags);
+ //err = -1; // untested dir
+ vfsub_i_unlock(a->h_dentry[SRC]->d_inode);
+ if (unlikely(err))
+ goto out_whtmp;
+ }
+
+ /* rename by vfs_rename or cpup */
+ need_diropq = a->isdir
+ && (wh_dentry[DST]
+ || dbdiropq(dentry) == a->btgt
+ /* hide the lower to keep xino */
+ || a->btgt < dbend(dentry)
+ || AuFlag(stosi(a->sb), f_always_diropq));
+ bycpup = 0;
+ if (dbstart(src_dentry) == a->btgt) {
+ if (need_diropq && dbdiropq(src_dentry) == a->btgt)
+ need_diropq = 0;
+ vfsub_args_init(&vargs, &ign, a->dlgt, 0);
+ if (unlikely(a->udba && a->isdir))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ itohi(src_dentry->d_inode, a->btgt));
+ err = vfsub_rename(h_dir[SRC], au_h_dptr(src_dentry),
+ h_dir[DST], a->h_dentry[DST], &vargs);
+ //err = -1;
+ } else {
+ bycpup = 1;
+ cflags.dtime = 0;
+ vfsub_i_lock_nested(a->h_dentry[SRC]->d_inode, AuLsc_I_CHILD);
+ set_dbstart(src_dentry, a->btgt);
+ 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, &cflags);
+ //err = -1; // untested dir
+ if (unlikely(err)) {
+ set_h_dptr(src_dentry, a->btgt, NULL);
+ set_dbstart(src_dentry, a->bstart[SRC]);
+ }
+ vfsub_i_unlock(a->h_dentry[SRC]->d_inode);
+ }
+ if (unlikely(err))
+ goto out_whtmp;
+
+ /* make dir opaque */
+ if (need_diropq) {
+ struct dentry *diropq;
+ struct inode *h_inode;
+
+ h_inode = au_h_dptr_i(src_dentry, a->btgt)->d_inode;
+ hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
+ diropq = create_diropq(src_dentry, a->btgt, a->dlgt);
+ //diropq = ERR_PTR(-1);
+ hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
+ err = PTR_ERR(diropq);
+ if (IS_ERR(diropq))
+ goto out_rename;
+ dput(diropq);
+ }
+
+ /* remove whiteout for dentry */
+ if (wh_dentry[DST]) {
+ err = au_unlink_wh_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)
+ || !nhash_test_longer_wh(&a->whlist, a->btgt,
+ stosi(a->sb)->si_dirwh)) {
+ err = rmdir_whtmp(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 {
+ kick_rmdir_whtmp(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_i(src_dentry, a->btgt)->d_inode;
+ //br_wh_read_lock(stobr(a->sb, a->btgt));
+ /* i_lock simply since inotify is not set to h_inode. */
+ vfsub_i_lock_nested(h_inode, AuLsc_I_PARENT);
+ //hdir_lock(h_inode, src_dentry->d_inode, a->btgt);
+ rerr = remove_diropq(src_dentry, a->btgt, a->dlgt);
+ //rerr = -1;
+ //hdir_unlock(h_inode, src_dentry->d_inode, a->btgt);
+ vfsub_i_unlock(h_inode);
+ //br_wh_read_unlock(stobr(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, a->dlgt, 0);
+ if (unlikely(a->udba && a->isdir))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ itohi(src_dentry->d_inode, a->btgt));
+ rerr = vfsub_rename
+ (h_dir[DST], au_h_dptr_i(src_dentry, a->btgt),
+ h_dir[SRC], d, &vargs);
+ //rerr = -1;
+ d_drop(d);
+ dput(d);
+ //set_h_dptr(src_dentry, a->btgt, NULL);
+ if (rerr)
+ RevertFailure("rename %.*s", AuDLNPair(src_dentry));
+ } else {
+ vfsub_args_init(&vargs, NULL, a->dlgt, 0);
+ rerr = vfsub_unlink(h_dir[DST], a->h_dentry[DST], &vargs);
+ //rerr = -1;
+ set_h_dptr(src_dentry, a->btgt, NULL);
+ 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;
+ LKTRLabel(here);
+ 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, a->dlgt, 0);
+ if (unlikely(0 && a->udba && a->isdir))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ itohi(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;
+ }
+ set_h_dptr(dentry, a->btgt, NULL);
+ 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]) {
+ LKTRLabel(here);
+ rerr = au_unlink_wh_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 = dbend(src_dentry);
+ for (bindex = dbstart(src_dentry); bindex <= bend; bindex++) {
+ struct dentry *hd;
+ hd = au_h_dptr_i(src_dentry, bindex);
+ if (hd)
+ d_drop(hd);
+ }
+ d_drop(dentry);
+ bend = dbend(dentry);
+ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
+ struct dentry *hd;
+ hd = au_h_dptr_i(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 aufs_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 = dbstart(dentry);
+ if (bstart != btgt) {
+ struct aufs_nhash *whlist;
+
+ whlist = nhash_new(GFP_TEMPORARY);
+ err = PTR_ERR(whlist);
+ if (IS_ERR(whlist))
+ goto out;
+ err = au_test_empty(dentry, whlist);
+ nhash_del(whlist);
+ goto out;
+ }
+
+ if (bstart == 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 = dbdiropq(parent);
+ bwh = dbwh(dentry);
+ if (unlikely(br_rdonly(stobr(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 aufs_hinode **hgdir, struct rename_args *a)
+{
+ struct dentry *gparent[2];
+ struct inode *gdir;
+
+ if (!a->udba)
+ 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] = itohi(gdir, a->btgt);
+ ii_read_unlock(gdir);
+ } else
+ hgdir[SRC] = itohi(gdir, a->btgt);
+ dput(gparent[SRC]);
+ }
+
+ if (!a->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] = itohi(gdir, a->btgt);
+ ii_read_unlock(gdir);
+ } else
+ hgdir[DST] = itohi(gdir, a->btgt);
+ dput(gparent[DST]);
+ }
+}
+
+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 aufs_hinode *hgdir[2];
+ 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,
+ .add_entry = 1,
+ .do_lock_srcdir = 0,
+ //.isdir = 0
+ };
+
+ 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;
+ p->a.isdir = !!S_ISDIR(inode[SRC]->i_mode);
+ if (unlikely(p->a.isdir && inode[DST]
+ && !S_ISDIR(inode[DST]->i_mode)))
+ goto out_free;
+
+ flags = 0;
+ if (p->a.isdir)
+ flags = AuLock_DIR;
+ aufs_read_and_write_lock2(dentry, src_dentry, flags);
+ p->a.dlgt = !!au_need_dlgt(p->a.sb);
+ p->a.udba = !!au_flag_test_udba_inotify(p->a.sb);
+ p->a.parent[SRC] = src_dentry->d_parent; /* dir inode is locked */
+ p->a.parent[DST] = dentry->d_parent; /* dir inode is locked */
+ p->a.issamedir = (src_dir == dir);
+ if (p->a.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] = dbstart(src_dentry);
+ p->a.bstart[DST] = dbstart(dentry);
+ wr_dir_args.isdir = p->a.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;
+ nhash_init(&p->a.whlist);
+ if (p->a.isdir && inode[DST]) {
+ set_dbstart(dentry, p->a.bstart[DST]);
+ err = may_rename_dstdir(dentry, p->a.btgt, &p->a.whlist);
+ set_dbstart(dentry, p->a.btgt);
+ }
+ p->a.h_dentry[DST] = au_h_dptr(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);
+ if (p->a.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, p->a.isdir, &p->a.btgt,
+ p->a.issamedir ? NULL : p->a.parent[DST]);
+ if (unlikely(err < 0))
+ goto out_children;
+ p->a.whsrc = !!err;
+ p->a.whdst = (p->a.bstart[DST] == p->a.btgt);
+ if (!p->a.whdst) {
+ err = au_cpup_dirs(dentry, p->a.btgt,
+ p->a.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_i(p->a.parent[SRC], p->a.btgt);
+ p->a.h_parent[DST] = au_h_dptr_i(p->a.parent[DST], p->a.btgt);
+ dirs[0] = src_dir;
+ dirs[1] = dir;
+#ifdef DbgUdbaRace
+ AuDbgSleep(DbgUdbaRace);
+#endif
+ hdir_lock_rename(p->a.h_parent, dirs, p->a.btgt, p->a.issamedir);
+
+ /*
+ * someone else might change our parent-child relationship directly,
+ * bypassing aufs, while we are handling a systemcall for aufs.
+ */
+ err = -ENOENT;
+ if (unlikely(p->a.bstart[SRC] == p->a.btgt
+ && p->a.h_parent[SRC]
+ != au_h_dptr_i(src_dentry, p->a.btgt)->d_parent))
+ goto out_hdir;
+ err = -EIO;
+ if (unlikely(p->a.bstart[DST] == p->a.btgt
+ && p->a.h_parent[DST]
+ != au_h_dptr_i(dentry, p->a.btgt)->d_parent))
+ goto out_hdir;
+#if 0
+ // revalidate h_dentries
+ // test parent-gparent relationship
+#endif
+
+ /* store timestamps to be revertible */
+ au_dtime_store(p->dt[PARENT] + SRC, p->a.parent[SRC],
+ p->a.h_parent[SRC], hgdir[SRC]);
+ if (!p->a.issamedir)
+ au_dtime_store(p->dt[PARENT] + DST, p->a.parent[DST],
+ p->a.h_parent[DST], hgdir[DST]);
+ do_dt_dstdir = 0;
+ if (p->a.isdir) {
+ au_dtime_store
+ (p->dt[CHILD] + SRC, src_dentry, p->a.h_dentry[SRC],
+ itohi(p->a.parent[SRC]->d_inode, p->a.btgt));
+ if (p->a.h_dentry[DST]->d_inode) {
+ do_dt_dstdir = 1;
+ au_dtime_store
+ (p->dt[CHILD] + DST, dentry, p->a.h_dentry[DST],
+ itohi(p->a.parent[DST]->d_inode, p->a.btgt));
+ }
+ }
+
+ err = do_rename(src_dir, src_dentry, dir, dentry, &p->a);
+ if (unlikely(err))
+ goto out_dt;
+ hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt, p->a.issamedir);
+
+ /* update dir attributes */
+ dir->i_version++;
+ if (unlikely(p->a.isdir)) {
+ /* is this updating defined in POSIX? */
+ //vfsub_i_lock(inode[SRC]);
+ au_cpup_attr_timesizes(inode[SRC]);
+ //vfsub_i_unlock(inode[SRC]);
+
+ au_cpup_attr_nlink(dir);
+ if (unlikely(inode[DST])) {
+ inode[DST]->i_nlink--;
+ if (unlikely(p->a.isdir))
+ inode[DST]->i_nlink = 0;
+ au_cpup_attr_timesizes(inode[DST]);
+ }
+ }
+ if (ibstart(dir) == p->a.btgt)
+ au_cpup_attr_timesizes(dir);
+
+ if (!p->a.issamedir) {
+ src_dir->i_version++;
+ if (unlikely(p->a.isdir))
+ au_cpup_attr_nlink(src_dir);
+ if (ibstart(src_dir) == p->a.btgt)
+ au_cpup_attr_timesizes(src_dir);
+ }
+
+#if 0 // todo: test it
+ d_drop(src_dentry);
+#else
+ /* dput/iput all lower dentries */
+ set_dbwh(src_dentry, -1);
+ bend = dbend(src_dentry);
+ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
+ struct dentry *hd;
+ hd = au_h_dptr_i(src_dentry, bindex);
+ if (hd)
+ set_h_dptr(src_dentry, bindex, NULL);
+ }
+ set_dbend(src_dentry, p->a.btgt);
+
+ bend = ibend(inode[SRC]);
+ for (bindex = p->a.btgt + 1; bindex <= bend; bindex++) {
+ struct inode *hi;
+ hi = au_h_iptr_i(inode[SRC], bindex);
+ if (hi) {
+ //AuDbg("hi%lu, i%lu\n", hi->i_ino, 0LU);
+ xino_write0(p->a.sb, bindex, hi->i_ino, 0);
+ /* ignore this error */
+ set_h_iptr(inode[SRC], bindex, NULL, 0);
+ }
+ }
+ set_ibend(inode[SRC], p->a.btgt);
+#endif
+
+#if 0 // remove this
+ if (unlikely(inode[DST])) {
+ struct inode *h_i;
+
+ bend = ibend(inode[DST]);
+ for (bindex = ibstart(inode[DST]); bindex <= bend; bindex++) {
+ h_i = au_h_iptr_i(inode[DST], bindex);
+ if (h_i)
+ xino_write0(p->a.sb, bindex, h_i->i_ino, 0);
+ /* ignore this error */
+ /* bad action? */
+ }
+ }
+#endif
+
+ goto out_children; /* success */
+
+ out_dt:
+ au_dtime_revert(p->dt[PARENT] + SRC);
+ if (!p->a.issamedir)
+ au_dtime_revert(p->dt[PARENT] + DST);
+ if (p->a.isdir && err != -EIO) {
+ struct dentry *hd;
+
+ hd = p->dt[CHILD][SRC].dt_h_dentry;
+ vfsub_i_lock_nested(hd->d_inode, AuLsc_I_CHILD);
+ au_dtime_revert(p->dt[CHILD] + SRC);
+ vfsub_i_unlock(hd->d_inode);
+ if (do_dt_dstdir) {
+ hd = p->dt[CHILD][DST].dt_h_dentry;
+ vfsub_i_lock_nested(hd->d_inode, AuLsc_I_CHILD);
+ au_dtime_revert(p->dt[CHILD] + DST);
+ vfsub_i_unlock(hd->d_inode);
+ }
+ }
+ out_hdir:
+ hdir_unlock_rename(p->a.h_parent, dirs, p->a.btgt, p->a.issamedir);
+ out_children:
+ nhash_fin(&p->a.whlist);
+ out_unlock:
+ //if (unlikely(err /* && p->a.isdir */)) {
+ if (unlikely(err && p->a.isdir)) {
+ au_update_dbstart(dentry);
+ d_drop(dentry);
+ }
+ if (p->a.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 --git a/ubuntu/fs/aufs/iinfo.c b/ubuntu/fs/aufs/iinfo.c
new file mode 100644
index 0000000..b097726
--- /dev/null
+++ b/ubuntu/fs/aufs/iinfo.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: iinfo.c,v 1.45 2008/01/21 04:57:48 sfjro Exp $ */
+
+#include "aufs.h"
+
+struct aufs_iinfo *itoii(struct inode *inode)
+{
+ struct aufs_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
+ /* || stosi(inode->i_sb)->si_bend < iinfo->ii_bend */
+ || iinfo->ii_bend < iinfo->ii_bstart);
+ return iinfo;
+}
+
+aufs_bindex_t ibstart(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ return itoii(inode)->ii_bstart;
+}
+
+aufs_bindex_t ibend(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ return itoii(inode)->ii_bend;
+}
+
+struct aufs_vdir *ivdir(struct inode *inode)
+{
+ IiMustAnyLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode));
+ return itoii(inode)->ii_vdir;
+}
+
+struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct aufs_hinode *hinode;
+
+ IiMustAnyLock(inode);
+
+ hinode = itoii(inode)->ii_hinode + bindex;
+ return hinode->hi_whdentry;
+}
+
+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct inode *hidden_inode;
+
+ IiMustAnyLock(inode);
+ AuDebugOn(bindex < 0 || ibend(inode) < bindex);
+ hidden_inode = itoii(inode)->ii_hinode[0 + bindex].hi_inode;
+ AuDebugOn(hidden_inode && atomic_read(&hidden_inode->i_count) <= 0);
+ return hidden_inode;
+}
+
+struct inode *au_h_iptr(struct inode *inode)
+{
+ return au_h_iptr_i(inode, ibstart(inode));
+}
+
+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex)
+{
+ IiMustAnyLock(inode);
+ AuDebugOn(bindex < 0
+ || ibend(inode) < bindex
+ || !itoii(inode)->ii_hinode[0 + bindex].hi_inode);
+ return itoii(inode)->ii_hinode[0 + bindex].hi_id;
+}
+
+// hard/soft set
+void set_ibstart(struct inode *inode, aufs_bindex_t bindex)
+{
+ struct aufs_iinfo *iinfo = itoii(inode);
+ struct inode *h_inode;
+
+ IiMustWriteLock(inode);
+ AuDebugOn(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);
+}
+
+void set_ibend(struct inode *inode, aufs_bindex_t bindex)
+{
+ IiMustWriteLock(inode);
+ AuDebugOn(sbend(inode->i_sb) < bindex
+ || bindex < ibstart(inode));
+ itoii(inode)->ii_bend = bindex;
+}
+
+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir)
+{
+ IiMustWriteLock(inode);
+ AuDebugOn(!S_ISDIR(inode->i_mode)
+ || (itoii(inode)->ii_vdir && vdir));
+ itoii(inode)->ii_vdir = vdir;
+}
+
+void aufs_hiput(struct aufs_hinode *hinode)
+{
+ au_hin_free(hinode);
+ dput(hinode->hi_whdentry);
+ iput(hinode->hi_inode);
+}
+
+unsigned int au_hi_flags(struct inode *inode, int isdir)
+{
+ unsigned int flags;
+ struct super_block *sb = inode->i_sb;
+ struct aufs_sbinfo *sbinfo = stosi(sb);
+
+ flags = 0;
+ if (AuFlag(sbinfo, f_xino) != AuXino_NONE)
+ flags = AUFS_HI_XINO;
+ if (unlikely(isdir && au_flag_test_udba_inotify(sb)))
+ flags |= AUFS_HI_NOTIFY;
+ return flags;
+}
+
+struct aufs_hinode *itohi(struct inode *inode, aufs_bindex_t bindex)
+{
+ //todo: this lock check causes some unnecessary locks in callers.
+ IiMustAnyLock(inode);
+ return itoii(inode)->ii_hinode + bindex;
+}
+
+void set_h_iptr(struct inode *inode, aufs_bindex_t bindex,
+ struct inode *h_inode, unsigned int flags)
+{
+ struct aufs_hinode *hinode;
+ struct inode *hi;
+ struct aufs_iinfo *iinfo = itoii(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 < ibstart(inode) || ibend(inode) < bindex
+ || (h_inode && atomic_read(&h_inode->i_count) <= 0)
+ || (h_inode && hi));
+
+ if (hi) {
+#if 0 // remove this
+ if (unlikely(!hi->i_nlink && (flags & AUFS_HI_XINO))) {
+ AuDebugOn(sbr_id(sb, bindex) != hinode->hi_id);
+ xino_write0(inode->i_sb, bindex, hi->i_ino, 0);
+ /* ignore this error */
+ /* bad action? */
+ }
+#endif
+ aufs_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 = sbr_id(sb, bindex);
+ if (flags & AUFS_HI_XINO) {
+ struct xino_entry xinoe = {
+ .ino = inode->i_ino,
+ //.h_gen = h_inode->i_generation
+ };
+ err = xino_write(sb, bindex, h_inode->i_ino, &xinoe);
+ if (unlikely(err)) {
+ /* bad action? */
+ AuIOErr1("failed xino_write() %d,"
+ " force noxino\n",
+ err);
+ AuFlagSet(stosi(sb), f_xino, AuXino_NONE);
+ }
+ }
+#ifdef CONFIG_AUFS_HINOTIFY
+ if (unlikely((flags & AUFS_HI_NOTIFY)
+ && br_hinotifyable(sbr_perm(sb, bindex)))) {
+ err = au_hin_alloc(hinode, inode, h_inode);
+ if (unlikely(err))
+ AuIOErr1("au_hin_alloc() %d\n", err);
+ else
+ AuDebugOn(!hinode->hi_notify);
+ }
+#endif
+ }
+}
+
+void set_hi_wh(struct inode *inode, aufs_bindex_t bindex, struct dentry *h_wh)
+{
+ struct aufs_hinode *hinode;
+
+ IiMustWriteLock(inode);
+ hinode = itoii(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(&itoii(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 aufs_iinfo *iinfo;
+
+ LKTRTrace("i%lu, %d\n", inode->i_ino, do_put_zero);
+ IiMustWriteLock(inode);
+
+ iinfo = itoii(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)
+ 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 aufs_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 = 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 */
+ 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 aufs_hinode *hinode,
+ ino_t ino)
+{
+ int err, locked;
+ aufs_bindex_t bindex;
+
+ err = 0;
+ locked = si_read_trylock(sb, !AuLock_FLUSH); // crucio!
+ bindex = find_brindex(sb, hinode->hi_id);
+ if (bindex >= 0)
+ err = 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 aufs_iinfo *iinfo;
+ aufs_bindex_t bend;
+ struct aufs_hinode *hi;
+ struct super_block *sb;
+ int unlinked;
+ ino_t ino;
+
+ iinfo = itoii(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 (unlikely(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 (unlikely(unlinked
+ || !hi->hi_inode->i_nlink)) {
+ au_iinfo_write0(sb, hi, ino);
+ /* ignore this error */
+ ino = 0;
+ }
+ aufs_hiput(hi);
+ }
+ hi++;
+ }
+ //iinfo->ii_bstart = iinfo->ii_bend = -1;
+ }
+
+ kfree(iinfo->ii_hinode);
+ //iinfo->ii_hinode = NULL;
+}
diff --git a/ubuntu/fs/aufs/include/linux/aufs_type.h b/ubuntu/fs/aufs/include/linux/aufs_type.h
new file mode 100644
index 0000000..ca10a22
--- /dev/null
+++ b/ubuntu/fs/aufs/include/linux/aufs_type.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: aufs_type.h,v 1.90 2008/01/28 05:02:33 sfjro Exp $ */
+
+#include <linux/ioctl.h>
+
+#ifndef __AUFS_TYPE_H__
+#define __AUFS_TYPE_H__
+
+#define AUFS_VERSION "20080128"
+
+/* 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 */
+
+#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 --git a/ubuntu/fs/aufs/inode.c b/ubuntu/fs/aufs/inode.c
new file mode 100644
index 0000000..009a461
--- /dev/null
+++ b/ubuntu/fs/aufs/inode.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: inode.c,v 1.37 2007/12/17 03:30:37 sfjro Exp $ */
+
+#include "aufs.h"
+
+int au_refresh_hinode_self(struct inode *inode)
+{
+ int err, new_sz, update;
+ struct inode *first;
+ struct aufs_hinode *p, *q, tmp;
+ struct super_block *sb;
+ struct aufs_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 = sbend(sb);
+ new_sz = sizeof(*iinfo->ii_hinode) * (bend + 1);
+ iinfo = itoii(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 (unlikely(!p->hi_inode))
+ continue;
+
+ new_bindex = find_brindex(sb, p->hi_id);
+ if (new_bindex == bindex)
+ continue;
+ if (new_bindex < 0) {
+ update++;
+ aufs_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))
+ 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 aufs_hinode *p;
+ struct super_block *sb;
+ struct aufs_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 = sbend(sb);
+ iinfo = itoii(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 = dbend(dentry);
+ for (bindex = dbstart(dentry); bindex <= bend; bindex++) {
+ struct inode *hi;
+ struct dentry *hd;
+
+ hd = au_h_dptr_i(dentry, bindex);
+ if (!hd || !hd->d_inode)
+ continue;
+
+ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) {
+ hi = au_h_iptr_i(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;
+ 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))
+ 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 aufs_iinfo *iinfo;
+ unsigned int flags;
+
+ LKTRTrace("i%lu, %.*s\n", inode->i_ino, AuDLNPair(dentry));
+ AuDebugOn(!(inode->i_state & I_NEW));
+ IiMustWriteLock(inode);
+ h_dentry = au_h_dptr(dentry);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ err = 0;
+ isdir = 0;
+ bstart = dbstart(dentry);
+ mode = h_inode->i_mode;
+ switch (mode & S_IFMT) {
+ case S_IFREG:
+ btail = dbtail(dentry);
+ break;
+ case S_IFDIR:
+ isdir = 1;
+ btail = dbtaildir(dentry);
+ inode->i_op = &aufs_dir_iop;
+ inode->i_fop = &aufs_dir_fop;
+ break;
+ case S_IFLNK:
+ btail = dbtail(dentry);
+ inode->i_op = &aufs_symlink_iop;
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ case S_IFIFO:
+ case S_IFSOCK:
+ btail = 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 = itoii(inode);
+ iinfo->ii_bstart = bstart;
+ iinfo->ii_bend = btail;
+ for (bindex = bstart; bindex <= btail; bindex++) {
+ h_dentry = au_h_dptr_i(dentry, bindex);
+ if (!h_dentry)
+ continue;
+ AuDebugOn(!h_dentry->d_inode);
+ 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;
+ //const int udba = !au_flag_test(inode->i_sb, AuFlag_UDBA_NONE);
+
+ 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)->d_inode;
+ vfsub_i_lock_nested(inode, AuLsc_I_CHILD);
+ ii_write_lock_new(inode);
+ bend = ibend(inode);
+ for (bindex = ibstart(inode); bindex <= bend; bindex++) {
+ h_inode = au_h_iptr_i(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;
+ }
+ }
+ vfsub_i_unlock(inode);
+ if (unlikely(err))
+ ii_write_unlock(inode);
+
+ 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 xino_entry xinoe;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+ sb = dentry->d_sb;
+ h_dentry = au_h_dptr(dentry);
+ AuDebugOn(!h_dentry);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(!h_inode);
+
+ bstart = dbstart(dentry);
+ h_ino = h_inode->i_ino;
+ err = 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 = xino_new_ino(sb);
+ if (!xinoe.ino) {
+ inode = ERR_PTR(-EIO);
+ goto out;
+ }
+ }
+
+ LKTRTrace("i%lu\n", xinoe.ino);
+ err = -ENOMEM;
+ inode = iget_locked(sb, xinoe.ino);
+ if (unlikely(!inode))
+ goto out;
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+ err = -ENOMEM;
+ if (unlikely(is_bad_inode(inode)))
+ goto out_iput;
+
+ LKTRTrace("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW));
+ if (inode->i_state & I_NEW) {
+ sb->s_op->read_inode(inode);
+ if (!is_bad_inode(inode)) {
+ ii_write_lock_new(inode);
+ err = set_inode(inode, dentry);
+ //err = -1;
+ }
+ unlock_new_inode(inode);
+ if (!err)
+ goto out; /* success */
+ ii_write_unlock(inode);
+ goto out_iput;
+ } else {
+ 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);
+#if 0 // debug
+ {
+ static unsigned char c;
+ if (!c++) {
+ struct dentry *d = d_find_alias(inode);
+ au_debug_on();
+ DbgDentry(dentry);
+ DbgInode(inode);
+ if (d) {
+ DbgDentry(d);
+ dput(d);
+ }
+ au_debug_off();
+ }
+ }
+#endif
+ xinoe.ino = 0;
+ err = 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;
+}
diff --git a/ubuntu/fs/aufs/inode.h b/ubuntu/fs/aufs/inode.h
new file mode 100644
index 0000000..b7ae504
--- /dev/null
+++ b/ubuntu/fs/aufs/inode.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: inode.h,v 1.47 2007/11/12 01:40:34 sfjro Exp $ */
+
+#ifndef __AUFS_INODE_H__
+#define __AUFS_INODE_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+#include "misc.h"
+
+struct aufs_hinode;
+struct aufs_vdir;
+struct aufs_iinfo {
+ atomic_t ii_generation;
+ struct super_block *ii_hsb1; /* no get/put */
+
+ struct aufs_rwsem ii_rwsem;
+ aufs_bindex_t ii_bstart, ii_bend;
+ struct aufs_hinode *ii_hinode;
+ struct aufs_vdir *ii_vdir;
+};
+
+struct aufs_icntnr {
+ struct aufs_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);
+
+/* i_op.c */
+extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
+struct au_wr_dir_args {
+ aufs_bindex_t force_btgt;
+
+ unsigned int add_entry:1;
+ unsigned int do_lock_srcdir:1;
+ unsigned int isdir:1;
+};
+int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry,
+ struct au_wr_dir_args *args);
+
+/* i_op_add.c */
+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 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);
+
+#if 0 // xattr
+/* xattr.c */
+int aufs_setxattr(struct dentry *dentry, const char *name, const void *value,
+ size_t sz, int flags);
+ssize_t aufs_getxattr(struct dentry *dentry, const char *name, void *value,
+ size_t sz);
+ssize_t aufs_listxattr(struct dentry *dentry, char *list, size_t sz);
+int aufs_removexattr(struct dentry *dentry, const char *name);
+#endif
+
+/* iinfo.c */
+struct aufs_iinfo *itoii(struct inode *inode);
+aufs_bindex_t ibstart(struct inode *inode);
+aufs_bindex_t ibend(struct inode *inode);
+struct aufs_vdir *ivdir(struct inode *inode);
+struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex);
+struct inode *au_h_iptr_i(struct inode *inode, aufs_bindex_t bindex);
+struct inode *au_h_iptr(struct inode *inode);
+aufs_bindex_t itoid_index(struct inode *inode, aufs_bindex_t bindex);
+
+void set_ibstart(struct inode *inode, aufs_bindex_t bindex);
+void set_ibend(struct inode *inode, aufs_bindex_t bindex);
+void set_ivdir(struct inode *inode, struct aufs_vdir *vdir);
+void set_hi_wh(struct inode *inode, aufs_bindex_t bindex, struct dentry *h_wh);
+void aufs_hiput(struct aufs_hinode *hinode);
+#define AUFS_HI_XINO 1
+#define AUFS_HI_NOTIFY 2
+unsigned int au_hi_flags(struct inode *inode, int isdir);
+struct aufs_hinode *itohi(struct inode *inode, aufs_bindex_t bindex);
+void 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_list_plink(struct super_block *sb);
+#else
+static inline void au_list_plink(struct super_block *sb)
+{
+ /* nothing */
+}
+#endif
+int au_test_plink(struct super_block *sb, struct inode *inode);
+struct dentry *au_lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode);
+void au_append_plink(struct super_block *sb, struct inode *inode,
+ struct dentry *h_dentry, aufs_bindex_t bindex);
+void au_put_plink(struct super_block *sb);
+void au_half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id);
+
+/* ---------------------------------------------------------------------- */
+
+/* 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 !(itoii(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(&itoii(inode)->ii_generation);
+}
+
+static inline au_gen_t au_iigen_inc(struct inode *inode)
+{
+ //AuDbg("i%lu\n", inode->i_ino);
+ return atomic_inc_return(&itoii(inode)->ii_generation);
+}
+
+static inline au_gen_t au_iigen_dec(struct inode *inode)
+{
+ //AuDbg("i%lu\n", inode->i_ino);
+ return atomic_dec_return(&itoii(inode)->ii_generation);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* 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 ReadLockFunc(name, lsc) \
+static inline void ii_read_lock_##name(struct inode *i) \
+{ rw_read_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc); }
+
+#define WriteLockFunc(name, lsc) \
+static inline void ii_write_lock_##name(struct inode *i) \
+{ rw_write_lock_nested(&itoii(i)->ii_rwsem, AuLsc_II_##lsc); }
+
+#define RWLockFuncs(name, lsc) \
+ ReadLockFunc(name, lsc) \
+ WriteLockFunc(name, lsc)
+
+RWLockFuncs(child, CHILD);
+RWLockFuncs(child2, CHILD2);
+RWLockFuncs(child3, CHILD3);
+RWLockFuncs(parent, PARENT);
+RWLockFuncs(parent2, PARENT2);
+RWLockFuncs(parent3, PARENT3);
+RWLockFuncs(parent4, PARENT4);
+RWLockFuncs(new, NEW);
+
+#undef ReadLockFunc
+#undef WriteLockFunc
+#undef RWLockFunc
+
+/*
+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
+ */
+SimpleUnlockRwsemFuncs(ii, struct inode *i, itoii(i)->ii_rwsem);
+
+/* to debug easier, do not make them inlined functions */
+#define IiMustReadLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ RwMustReadLock(&itoii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustWriteLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ RwMustWriteLock(&itoii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustAnyLock(i) do { \
+ SiMustAnyLock((i)->i_sb); \
+ RwMustAnyLock(&itoii(i)->ii_rwsem); \
+} while (0)
+
+#define IiMustNoWaiters(i) RwMustNoWaiters(&itoii(i)->ii_rwsem)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_INODE_H__ */
diff --git a/ubuntu/fs/aufs/misc.c b/ubuntu/fs/aufs/misc.c
new file mode 100644
index 0000000..0467362
--- /dev/null
+++ b/ubuntu/fs/aufs/misc.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: misc.c,v 1.47 2007/11/12 01:40:06 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;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+ q = krealloc(p, new_sz, gfp);
+ if (q)
+ memset(q + nused, 0, new_sz - nused);
+ return q;
+#else
+ LKTRTrace("ksize %d\n", ksize(p));
+ if (new_sz <= ksize(p)) {
+ memset(p + nused, 0, new_sz - nused);
+ return p;
+ }
+
+ q = kmalloc(new_sz, gfp);
+ //q = NULL;
+ if (unlikely(!q))
+ return NULL;
+ memcpy(q, p, nused);
+ memset(q + nused, 0, new_sz - nused);
+ //smp_mb();
+ kfree(p);
+ return q;
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct nameidata *au_dup_nd(struct aufs_sbinfo *sbinfo, struct nameidata *dst,
+ struct nameidata *src)
+{
+ LKTRTrace("src %p\n", src);
+
+ if (src) {
+ *dst = *src;
+ dst->flags &= ~LOOKUP_PARENT;
+ if (unlikely(AuFlag(sbinfo, f_wbr_create) != AuWbrCreate_TDP)) {
+ dst->flags &= ~LOOKUP_CREATE;
+ dst->intent.open.flags &= ~O_CREAT;
+ }
+ } else
+ dst = NULL;
+
+ return dst;
+}
+
+// todo: make it inline
+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;
+
+ fake_nd->dentry = NULL;
+ fake_nd->mnt = NULL;
+
+#ifndef CONFIG_AUFS_FAKE_DM
+ DiMustAnyLock(nd->dentry);
+
+ if (bindex <= dbend(nd->dentry))
+ fake_nd->dentry = au_h_dptr_i(nd->dentry, bindex);
+ if (fake_nd->dentry) {
+ dget(fake_nd->dentry);
+ fake_nd->mnt = sbr_mnt(sb, bindex);
+ AuDebugOn(!fake_nd->mnt);
+ mntget(fake_nd->mnt);
+ } else
+ fake_nd = ERR_PTR(-ENOENT);
+#endif
+
+ AuTraceErrPtr(fake_nd);
+ return fake_nd;
+}
+
+void au_fake_dm_release(struct nameidata *fake_nd)
+{
+#ifndef CONFIG_AUFS_FAKE_DM
+ if (fake_nd) {
+ mntput(fake_nd->mnt);
+ dput(fake_nd->dentry);
+ }
+#endif
+}
+
+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 {
+#ifndef CONFIG_AUFS_FAKE_DM
+ struct nameidata fake_nd;
+
+ if (nd)
+ fake_nd = *nd;
+ else
+ memset(&fake_nd, 0, sizeof(fake_nd));
+ fake_nd.dentry = dget(h_dentry);
+ fake_nd.mnt = mntget(nfsmnt);
+ 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_release(&fake_nd);
+#endif
+ }
+
+ 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_need_dlgt(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,
+ vargs.dlgt);
+ err = rbytes;
+ }
+ if (unlikely(err < 0))
+ break;
+
+ all_zero = 0;
+ if (len >= rbytes && rbytes == blksize) {
+ all_zero = 1;
+ p = buf;
+ for (i = 0; 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;
+ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) && defined(CONFIG_SECURITY_APPARMOR)
+ ia->ia_valid = ATTR_SIZE;
+ #else
+ ia->ia_valid = ATTR_SIZE | ATTR_FILE;
+ ia->ia_file = dst;
+ #endif
+ vfsub_i_lock_nested(h_i, AuLsc_I_CHILD2);
+ err = vfsub_notify_change(h_d, ia, &vargs);
+ vfsub_i_unlock(h_i);
+ }
+ }
+
+ kfree(ia);
+ out_buf:
+ kfree(buf);
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode)
+{
+ int err;
+
+ err = br_rdonly(stobr(sb, bindex));
+
+ /* pseudo-link after flushed may out of bounds */
+ if (!err
+ && inode
+ && ibstart(inode) <= bindex
+ && bindex <= ibend(inode)) {
+ /*
+ * permission check is unnecessary since later vfsub routine
+ * does
+ */
+ struct inode *hi = au_h_iptr_i(inode, bindex);
+ if (hi)
+ err = IS_IMMUTABLE(hi) ? -EROFS : 0;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+int au_test_perm(struct inode *hidden_inode, int mask, int dlgt)
+{
+ if (!current->fsuid)
+ return 0;
+ if (unlikely(au_test_nfs(hidden_inode->i_sb)
+ && (mask & MAY_WRITE)
+ && S_ISDIR(hidden_inode->i_mode)))
+ mask |= MAY_READ; /* force permission check */
+ //todo: fake nameidata
+ return vfsub_permission(hidden_inode, mask, NULL, dlgt);
+}
diff --git a/ubuntu/fs/aufs/misc.h b/ubuntu/fs/aufs/misc.h
new file mode 100644
index 0000000..6f19e27
--- /dev/null
+++ b/ubuntu/fs/aufs/misc.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: misc.h,v 1.39 2008/01/21 04:57:48 sfjro Exp $ */
+
+#ifndef __AUFS_MISC_H__
+#define __AUFS_MISC_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#define I_MUTEX_QUOTA 0
+#define lockdep_off() do {} while (0)
+#define lockdep_on() do {} while (0)
+#define mutex_lock_nested(mtx, lsc) mutex_lock(mtx)
+#define down_write_nested(rw, lsc) down_write(rw)
+#define down_read_nested(rw, lsc) down_read(rw)
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+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 aufs_rwsem {
+ struct rw_semaphore rwsem;
+#ifdef CONFIG_AUFS_DEBUG
+ atomic_t rcnt;
+#endif
+};
+
+#ifdef CONFIG_AUFS_DEBUG
+#define DbgRcntInit(rw) do { \
+ atomic_set(&(rw)->rcnt, 0); \
+ smp_mb(); \
+} while (0)
+
+#define DbgRcntInc(rw) atomic_inc_return(&(rw)->rcnt)
+#define DbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0)
+#else
+#define DbgRcntInit(rw) do {} while (0)
+#define DbgRcntInc(rw) do {} while (0)
+#define DbgRcntDec(rw) do {} while (0)
+#endif /* CONFIG_AUFS_DEBUG */
+
+static inline void rw_init_nolock(struct aufs_rwsem *rw)
+{
+ DbgRcntInit(rw);
+ init_rwsem(&rw->rwsem);
+}
+
+static inline void rw_init_wlock(struct aufs_rwsem *rw)
+{
+ rw_init_nolock(rw);
+ down_write(&rw->rwsem);
+}
+
+static inline void rw_init_wlock_nested(struct aufs_rwsem *rw, unsigned int lsc)
+{
+ rw_init_nolock(rw);
+ down_write_nested(&rw->rwsem, lsc);
+}
+
+static inline void rw_read_lock(struct aufs_rwsem *rw)
+{
+ down_read(&rw->rwsem);
+ DbgRcntInc(rw);
+}
+
+static inline void rw_read_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
+{
+ down_read_nested(&rw->rwsem, lsc);
+ DbgRcntInc(rw);
+}
+
+static inline void rw_read_unlock(struct aufs_rwsem *rw)
+{
+ DbgRcntDec(rw);
+ up_read(&rw->rwsem);
+}
+
+static inline void rw_dgrade_lock(struct aufs_rwsem *rw)
+{
+ DbgRcntInc(rw);
+ downgrade_write(&rw->rwsem);
+}
+
+static inline void rw_write_lock(struct aufs_rwsem *rw)
+{
+ down_write(&rw->rwsem);
+}
+
+static inline void rw_write_lock_nested(struct aufs_rwsem *rw, unsigned int lsc)
+{
+ down_write_nested(&rw->rwsem, lsc);
+}
+
+static inline void rw_write_unlock(struct aufs_rwsem *rw)
+{
+ up_write(&rw->rwsem);
+}
+
+/* why is not _nested version defined */
+static inline int rw_read_trylock(struct aufs_rwsem *rw)
+{
+ int ret = down_read_trylock(&rw->rwsem);
+ if (ret)
+ DbgRcntInc(rw);
+ return ret;
+}
+
+static inline int rw_write_trylock(struct aufs_rwsem *rw)
+{
+ return down_write_trylock(&rw->rwsem);
+}
+
+#undef DbgRcntInit
+#undef DbgRcntInc
+#undef DbgRcntDec
+
+/* to debug easier, do not make them inlined functions */
+#define RwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list))
+#define RwMustAnyLock(rw) AuDebugOn(down_write_trylock(&(rw)->rwsem))
+#ifdef CONFIG_AUFS_DEBUG
+#define RwMustReadLock(rw) do { \
+ RwMustAnyLock(rw); \
+ AuDebugOn(!atomic_read(&(rw)->rcnt)); \
+} while (0)
+
+#define RwMustWriteLock(rw) do { \
+ RwMustAnyLock(rw); \
+ AuDebugOn(atomic_read(&(rw)->rcnt)); \
+} while (0)
+#else
+#define RwMustReadLock(rw) RwMustAnyLock(rw)
+#define RwMustWriteLock(rw) RwMustAnyLock(rw)
+#endif /* CONFIG_AUFS_DEBUG */
+
+#define SimpleLockRwsemFuncs(prefix, param, rwsem) \
+static inline void prefix##_read_lock(param) \
+{ rw_read_lock(&(rwsem)); } \
+static inline void prefix##_write_lock(param) \
+{ rw_write_lock(&(rwsem)); } \
+static inline int prefix##_read_trylock(param) \
+{ return rw_read_trylock(&(rwsem)); } \
+static inline int prefix##_write_trylock(param) \
+{ return rw_write_trylock(&(rwsem)); }
+//static inline void prefix##_read_trylock_nested(param, lsc)
+//{rw_read_trylock_nested(&(rwsem, lsc));}
+//static inline void prefix##_write_trylock_nestd(param, lsc)
+//{rw_write_trylock_nested(&(rwsem), nested);}
+
+#define SimpleUnlockRwsemFuncs(prefix, param, rwsem) \
+static inline void prefix##_read_unlock(param) \
+{ rw_read_unlock(&(rwsem)); } \
+static inline void prefix##_write_unlock(param) \
+{ rw_write_unlock(&(rwsem)); } \
+static inline void prefix##_downgrade_lock(param) \
+{ rw_dgrade_lock(&(rwsem)); }
+
+#define SimpleRwsemFuncs(prefix, param, rwsem) \
+ SimpleLockRwsemFuncs(prefix, param, rwsem) \
+ SimpleUnlockRwsemFuncs(prefix, param, rwsem)
+
+/* ---------------------------------------------------------------------- */
+
+void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp);
+
+struct aufs_sbinfo;
+struct nameidata *au_dup_nd(struct aufs_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);
+int au_test_ro(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode);
+int au_test_perm(struct inode *h_inode, int mask, int dlgt);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_MISC_H__ */
diff --git a/ubuntu/fs/aufs/module.c b/ubuntu/fs/aufs/module.c
new file mode 100644
index 0000000..f9cee2d
--- /dev/null
+++ b/ubuntu/fs/aufs/module.c
@@ -0,0 +1,455 @@
+/*
+ * 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
+ */
+
+/* $Id: module.c,v 1.27 2007/11/26 01:32:50 sfjro Exp $ */
+
+//#include <linux/init.h>
+#include <linux/module.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * aufs caches
+ */
+struct kmem_cache *aufs_cachep[AuCache_Last];
+static int __init create_cache(void)
+{
+ void *p;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+#define Args(type, sz) (type), (sz), 0, SLAB_RECLAIM_ACCOUNT, NULL
+#else
+#define Args(type, sz) (type), (sz), 0, SLAB_RECLAIM_ACCOUNT, NULL, NULL
+#endif
+#define CacheX(type, extra) \
+ kmem_cache_create(Args(#type, sizeof(struct type) + extra))
+#define Cache(type) CacheX(type, 0)
+
+ p = NULL;
+ aufs_cachep[AuCache_DINFO] = Cache(aufs_dinfo);
+ if (aufs_cachep[AuCache_DINFO])
+ aufs_cachep[AuCache_ICNTNR] = Cache(aufs_icntnr);
+ if (aufs_cachep[AuCache_ICNTNR])
+ aufs_cachep[AuCache_FINFO] = Cache(aufs_finfo);
+ //aufs_cachep[AuCache_FINFO] = NULL;
+ if (aufs_cachep[AuCache_FINFO])
+ aufs_cachep[AuCache_VDIR] = Cache(aufs_vdir);
+ if (aufs_cachep[AuCache_VDIR]) {
+ aufs_cachep[AuCache_DEHSTR] = Cache(aufs_dehstr);
+ p = aufs_cachep[AuCache_DEHSTR];
+ }
+
+#ifdef CONFIG_AUFS_HINOTIFY
+ AuDebugOn(!au_hin_nignore);
+ if (p) {
+ aufs_cachep[AuCache_HINOTIFY]
+ = CacheX(aufs_hinotify,
+ sizeof(atomic_t) * au_hin_nignore);
+ p = aufs_cachep[AuCache_HINOTIFY];
+ }
+#endif
+
+ if (p)
+ return 0;
+ return -ENOMEM;
+
+#undef CacheX
+#undef Cache
+#undef Args
+}
+
+static void destroy_cache(void)
+{
+ int i;
+ for (i = 0; i < AuCache_Last; i++)
+ if (aufs_cachep[i])
+ kmem_cache_destroy(aufs_cachep[i]);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_HINOTIFY
+/* the size of an array for ignore counter */
+int au_hin_nignore;
+#endif
+
+char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */
+int au_dir_roflags;
+
+#ifdef DbgDlgt
+#include <linux/security.h>
+#include "dbg_dlgt.c"
+#else
+#define dbg_dlgt_init() 0
+#define dbg_dlgt_fin() do {} while (0)
+#endif
+
+/*
+ * 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/brs");
+module_param_named(brs, sysaufs_brs, int, S_IRUGO);
+
+char *aufs_sysrq_key = "a";
+#ifdef CONFIG_MAGIC_SYSRQ
+MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME);
+module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO);
+#endif
+
+static int __init aufs_init(void)
+{
+ int err, i;
+ char *p;
+
+#ifdef CONFIG_AUFS_DEBUG
+ {
+ aufs_bindex_t bindex = -1;
+ AuDebugOn(bindex >= 0);
+ }
+ {
+ struct aufs_destr destr;
+ destr.len = -1;
+ AuDebugOn(destr.len < NAME_MAX);
+ }
+
+#ifdef CONFIG_4KSTACKS
+ AuWarn("CONFIG_4KSTACKS is defined.\n");
+#endif
+#if 0 // verbose debug
+ {
+ union {
+ struct aufs_branch *br;
+ struct aufs_dinfo *di;
+ struct aufs_finfo *fi;
+ struct aufs_iinfo *ii;
+ struct aufs_hinode *hi;
+ struct aufs_sbinfo *si;
+ struct aufs_destr *destr;
+ struct aufs_de *de;
+ struct aufs_wh *wh;
+ struct aufs_vdir *vd;
+ } u;
+
+ pr_info("br{"
+ "xino %d, "
+ "id %d, perm %d, mnt %d, count %d, "
+ "wh_sem %d, wh %d, run %d, plink %d, gen %d} %d\n",
+ offsetof(typeof(*u.br), br_xino),
+ offsetof(typeof(*u.br), br_id),
+ offsetof(typeof(*u.br), br_perm),
+ offsetof(typeof(*u.br), br_mnt),
+ offsetof(typeof(*u.br), br_count),
+ offsetof(typeof(*u.br), br_wh_rwsem),
+ offsetof(typeof(*u.br), br_wh),
+ offsetof(typeof(*u.br), br_wh_running),
+ offsetof(typeof(*u.br), br_plink),
+ offsetof(typeof(*u.br), br_generation),
+ sizeof(*u.br));
+ pr_info("di{gen %d, rwsem %d, bstart %d, bend %d, bwh %d, "
+ "bdiropq %d, hdentry %d} %d\n",
+ offsetof(typeof(*u.di), di_generation),
+ offsetof(typeof(*u.di), di_rwsem),
+ offsetof(typeof(*u.di), di_bstart),
+ offsetof(typeof(*u.di), di_bend),
+ offsetof(typeof(*u.di), di_bwh),
+ offsetof(typeof(*u.di), di_bdiropq),
+ offsetof(typeof(*u.di), di_hdentry),
+ sizeof(*u.di));
+ pr_info("fi{gen %d, rwsem %d, hfile %d, bstart %d, bend %d, "
+ "h_vm_ops %d, vdir_cach %d} %d\n",
+ offsetof(typeof(*u.fi), fi_generation),
+ offsetof(typeof(*u.fi), fi_rwsem),
+ offsetof(typeof(*u.fi), fi_hfile),
+ offsetof(typeof(*u.fi), fi_bstart),
+ offsetof(typeof(*u.fi), fi_bend),
+ offsetof(typeof(*u.fi), fi_h_vm_ops),
+ offsetof(typeof(*u.fi), fi_vdir_cache),
+ sizeof(*u.fi));
+ pr_info("ii{gen %d, hsb %d, "
+ "rwsem %d, bstart %d, bend %d, hinode %d, vdir %d} "
+ "%d\n",
+ offsetof(typeof(*u.ii), ii_generation),
+ offsetof(typeof(*u.ii), ii_hsb1),
+ offsetof(typeof(*u.ii), ii_rwsem),
+ offsetof(typeof(*u.ii), ii_bstart),
+ offsetof(typeof(*u.ii), ii_bend),
+ offsetof(typeof(*u.ii), ii_hinode),
+ offsetof(typeof(*u.ii), ii_vdir),
+ sizeof(*u.ii));
+ pr_info("hi{inode %d, id %d, notify %d, wh %d} %d\n",
+ offsetof(typeof(*u.hi), hi_inode),
+ offsetof(typeof(*u.hi), hi_id),
+ offsetof(typeof(*u.hi), hi_notify),
+ offsetof(typeof(*u.hi), hi_whdentry),
+ sizeof(*u.hi));
+ pr_info("si{nwt %d, rwsem %d, gen %d, "
+ "bend %d, last id %d, br %d, "
+ "flags %d, "
+ "xread %d, xwrite %d, xib %d, xmtx %d, buf %d, "
+ "xlast %d, xnext %d, "
+ "rdcache %d, "
+ "dirwh %d, "
+ "pl_lock %d, pl %d, "
+ "l %d, mnt %d, "
+ "sys %d, "
+ "} %d\n",
+ offsetof(typeof(*u.si), si_nowait),
+ offsetof(typeof(*u.si), si_rwsem),
+ offsetof(typeof(*u.si), si_generation),
+ offsetof(typeof(*u.si), si_bend),
+ offsetof(typeof(*u.si), si_last_br_id),
+ offsetof(typeof(*u.si), si_branch),
+ offsetof(typeof(*u.si), au_si_flags),
+ offsetof(typeof(*u.si), si_xread),
+ offsetof(typeof(*u.si), si_xwrite),
+ offsetof(typeof(*u.si), si_xib),
+ offsetof(typeof(*u.si), si_xib_mtx),
+ offsetof(typeof(*u.si), si_xib_buf),
+ offsetof(typeof(*u.si), si_xib_last_pindex),
+ offsetof(typeof(*u.si), si_xib_next_bit),
+ offsetof(typeof(*u.si), si_rdcache),
+ offsetof(typeof(*u.si), si_dirwh),
+ offsetof(typeof(*u.si), si_plink_lock),
+ offsetof(typeof(*u.si), si_plink),
+ offsetof(typeof(*u.si), si_list),
+ offsetof(typeof(*u.si), si_mnt),
+ offsetof(typeof(*u.si), si_sysaufs),
+ sizeof(*u.si));
+ pr_info("destr{len %d, name %d} %d\n",
+ offsetof(typeof(*u.destr), len),
+ offsetof(typeof(*u.destr), name),
+ sizeof(*u.destr));
+ pr_info("de{ino %d, type %d, str %d} %d\n",
+ offsetof(typeof(*u.de), de_ino),
+ offsetof(typeof(*u.de), de_type),
+ offsetof(typeof(*u.de), de_str),
+ sizeof(*u.de));
+ pr_info("wh{hash %d, bindex %d, str %d} %d\n",
+ offsetof(typeof(*u.wh), wh_hash),
+ offsetof(typeof(*u.wh), wh_bindex),
+ offsetof(typeof(*u.wh), wh_str),
+ sizeof(*u.wh));
+ pr_info("vd{deblk %d, nblk %d, last %d, ver %d, jiffy %d} %d\n",
+ offsetof(typeof(*u.vd), vd_deblk),
+ offsetof(typeof(*u.vd), vd_nblk),
+ offsetof(typeof(*u.vd), vd_last),
+ offsetof(typeof(*u.vd), vd_version),
+ offsetof(typeof(*u.vd), vd_jiffy),
+ sizeof(*u.vd));
+ }
+#endif
+#endif /* CONFIG_AUFS_DEBUG */
+
+ 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);
+#ifndef CONFIG_AUFS_SYSAUFS
+ sysaufs_brs = 0;
+#endif
+
+ 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 = dbg_dlgt_init();
+ if (unlikely(err))
+ goto out_cache;
+
+ err = register_filesystem(&aufs_fs_type);
+ if (unlikely(err))
+ goto out_dlgt;
+ pr_info(AUFS_NAME " " AUFS_VERSION "\n");
+ return 0; /* success */
+
+ out_dlgt:
+ dbg_dlgt_fin();
+ 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);
+ dbg_dlgt_fin();
+ 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
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#error CONFIG_AUFS_HINOTIFY is supported in linux-2.6.18 and later.
+#endif
+#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_SYSAUFS
+#ifndef CONFIG_SYSFS
+#error CONFIG_AUFS_SYSAUFS requires CONFIG_SYSFS.
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#error CONFIG_AUFS_SYSAUFS requires linux-2.6.18 and later.
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23) \
+ && defined(CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH)
+#warning CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH is supported linux-2.6.23 and later.
+#endif
+#elif defined(CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH)
+#warning CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH requires CONFIG_AUFS_SYSAUFS.
+#endif /* CONFIG_AUFS_SYSAUFS */
+
+#ifdef CONFIG_AUFS_EXPORT
+#if !defined(CONFIG_EXPORTFS) && !defined(CONFIG_EXPORTFS_MODULE)
+#error CONFIG_AUFS_EXPORT requires CONFIG_EXPORTFS
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#error CONFIG_AUFS_EXPORT requires linux-2.6.18 and later.
+#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
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
+#warning CONFIG_AUFS_SEC_PERM_PATCH does not support before linux-2.6.24.
+#endif
+#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_PUT_FILP_PATCH
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#warning CONFIG_AUFS_PUT_FILP_PATCH does not support before linux-2.6.19.
+#endif
+#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
+#ifdef CONFIG_AUFS_FAKE_DM
+#error CONFIG_AUFS_FAKE_DM must be disabled for CONFIG_AUFS_PUT_FILP_PATCH.
+#endif
+#endif /* CONFIG_AUFS_PUT_FILP_PATCH */
+
+#ifdef CONFIG_AUFS_LHASH_PATCH
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
+#error CONFIG_AUFS_LHASH_PATCH does not support before linux-2.6.19.
+#endif
+#if !defined(CONFIG_NFS_FS) && !defined(CONFIG_NFS_FS_MODULE)
+#warning CONFIG_AUFS_LHASH_PATCH is unnecessary since CONFIG_NFS_FS is disabled.
+#endif
+#ifdef CONFIG_AUFS_FAKE_DM
+#error CONFIG_AUFS_FAKE_DM must be disabled for CONFIG_AUFS_LHASH_PATCH.
+#endif
+#endif
+
+#if defined(CONFIG_AUFS_KSIZE_PATCH) \
+ && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+#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
+
+#if defined(CONFIG_AUFS_SPLICE_PATCH) \
+ && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+#error CONFIG_AUFS_SPLICE_PATCH is supported linux-2.6.23 and later.
+#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 --git a/ubuntu/fs/aufs/module.h b/ubuntu/fs/aufs/module.h
new file mode 100644
index 0000000..2aeea51
--- /dev/null
+++ b/ubuntu/fs/aufs/module.h
@@ -0,0 +1,77 @@
+/*
+ * 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
+ */
+
+/* $Id: module.h,v 1.14 2007/10/08 03:58:19 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 *aufs_sysrq_key;
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_HINOTIFY
+extern int au_hin_nignore;
+#endif
+
+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 *aufs_cachep[];
+
+#define CacheFuncs(name, index) \
+static inline void *au_cache_alloc_##name(void) \
+{ return kmem_cache_alloc(aufs_cachep[index], GFP_KERNEL); } \
+static inline void au_cache_free_##name(void *p) \
+{ kmem_cache_free(aufs_cachep[index], p); }
+
+CacheFuncs(dinfo, AuCache_DINFO);
+CacheFuncs(icntnr, AuCache_ICNTNR);
+CacheFuncs(finfo, AuCache_FINFO);
+CacheFuncs(vdir, AuCache_VDIR);
+CacheFuncs(dehstr, AuCache_DEHSTR);
+#ifdef CONFIG_AUFS_HINOTIFY
+CacheFuncs(hinotify, AuCache_HINOTIFY);
+#endif
+
+#undef CacheFuncs
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_MODULE_H__ */
diff --git a/ubuntu/fs/aufs/opts.c b/ubuntu/fs/aufs/opts.c
new file mode 100644
index 0000000..334001b
--- /dev/null
+++ b/ubuntu/fs/aufs/opts.c
@@ -0,0 +1,1469 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: opts.c,v 1.55 2007/12/10 01:19:25 sfjro Exp $ */
+
+#include <linux/types.h> /* a distribution requires */
+#include <linux/parser.h>
+#include "aufs.h"
+
+void au_opts_flags_def(struct au_opts_flags *flags)
+{
+ // self-check at build time.
+ flags->f_wbr_create = AuWbrCreate_Last - 1;
+
+ flags->f_xino = AuXino_XINO;
+ flags->f_trunc_xino = 0;
+ flags->f_udba = AuUdba_REVAL;
+ flags->f_dlgt = 0;
+ flags->f_plink = 1;
+ flags->f_warn_perm = 1;
+ flags->f_coo = AuCoo_NONE;
+ flags->f_always_diropq = 0;
+ flags->f_refrof = 0;
+ flags->f_verbose = 0;
+ flags->f_wbr_copyup = AuWbrCopyup_TDP;
+ flags->f_wbr_create = AuWbrCreate_TDP;
+
+#ifdef CONFIG_AUFS_COMPAT
+ flags->f_always_diropq = 1;
+#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+ flags->f_plink = 0;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+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_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_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"},
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
+ {Opt_plink, "plink"},
+ {Opt_noplink, "noplink"},
+#ifdef CONFIG_AUFS_DEBUG
+ {Opt_list_plink, "list_plink"},
+#endif
+ {Opt_clean_plink, "clean_plink"},
+#endif
+
+ {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"},
+#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"},
+
+#if 0 // rfu
+ {Opt_deblk, "deblk=%d"},
+ {Opt_deblk, "deblk:%d"},
+ {Opt_nhash, "nhash=%d"},
+ {Opt_nhash, "nhash:%d"},
+#endif
+
+ {Opt_br, "dirs=%s"},
+ {Opt_ignore, "debug=%d"},
+ {Opt_ignore, "delete=whiteout"},
+ {Opt_ignore, "delete=all"},
+ {Opt_ignore, "imap=%s"},
+
+ {Opt_err, NULL}
+};
+
+/* ---------------------------------------------------------------------- */
+
+static au_parser_pattern_t 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 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;
+}
+
+au_parser_pattern_t au_optstr_br_perm(int brperm)
+{
+ return au_parser_pattern(brperm, brperms);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static match_table_t udbalevel = {
+ {AuUdba_REVAL, "reval"},
+#ifdef CONFIG_AUFS_HINOTIFY
+ {AuUdba_INOTIFY, "inotify"},
+#endif
+ {AuUdba_NONE, "none"},
+ {-1, NULL}
+};
+
+static int udba_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, udbalevel, args);
+}
+
+au_parser_pattern_t au_optstr_udba(int udba)
+{
+ return au_parser_pattern(udba, udbalevel);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static match_table_t coolevel = {
+ {AuCoo_LEAF, "leaf"},
+ {AuCoo_ALL, "all"},
+ {AuCoo_NONE, "none"},
+ {-1, NULL}
+};
+
+static int coo_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, coolevel, args);
+}
+
+au_parser_pattern_t 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 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;
+}
+
+au_parser_pattern_t 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 int au_wbr_copyup_val(char *str)
+{
+ substring_t args[MAX_OPT_ARGS];
+ return match_token(str, au_wbr_copyup_policy, args);
+}
+
+au_parser_pattern_t 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 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.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.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.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_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_release(&opt->add.nd);
+ 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.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, 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.dentry);
+ path_release(&nd);
+ opt->type = token;
+ break;
+#if 0 // rfu
+ case Opt_idel:
+ u.del = &opt->del;
+ u.del->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 || sbend(sb) < bindex) {
+ AuErr("out of bounds, %d\n", bindex);
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+ }
+ err = 0;
+ u.del->h_root = dget(au_h_dptr_i(root, bindex));
+ opt->type = token;
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+#endif
+ 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.dentry);
+ path_release(&nd);
+ 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 || 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_i(root, bindex));
+ opt->type = token;
+ aufs_read_unlock(root, !AuLock_IR);
+ break;
+#endif
+ case Opt_xino:
+ u.xino = &opt->xino;
+ file = 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 = sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ if (au_h_dptr_i(root, bindex) == nd.dentry) {
+ u.xino_itrunc->bindex = bindex;
+ break;
+ }
+ }
+ aufs_read_unlock(root, !AuLock_IR);
+ path_release(&nd);
+ 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 || 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_trunc_xino:
+ case Opt_notrunc_xino:
+ case Opt_noxino:
+ case Opt_trunc_xib:
+ case Opt_notrunc_xib:
+ 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
+ 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 aufs_sbinfo *sbinfo;
+ struct au_opts_flags *given;
+ struct au_opt_wbr_create *create;
+
+ AuTraceEnter();
+
+ err = 1; /* handled */
+ sbinfo = stosi(sb);
+ given = &opts->given;
+ switch (opt->type) {
+ case Opt_udba:
+ AuFlagSet(sbinfo, f_udba, opt->udba);
+ given->f_udba = 1;
+ break;
+
+ case Opt_plink:
+ AuFlagSet(sbinfo, f_plink, 1);
+ given->f_plink = 1;
+ break;
+ case Opt_noplink:
+ if (AuFlag(sbinfo, f_plink))
+ au_put_plink(sb);
+ AuFlagSet(sbinfo, f_plink, 0);
+ given->f_plink = 1;
+ break;
+ case Opt_list_plink:
+ if (AuFlag(sbinfo, f_plink))
+ au_list_plink(sb);
+ break;
+ case Opt_clean_plink:
+ if (AuFlag(sbinfo, f_plink))
+ au_put_plink(sb);
+ break;
+
+ case Opt_diropq_a:
+ AuFlagSet(sbinfo, f_always_diropq, 1);
+ given->f_always_diropq = 1;
+ break;
+ case Opt_diropq_w:
+ AuFlagSet(sbinfo, f_always_diropq, 0);
+ given->f_always_diropq = 1;
+ break;
+
+ case Opt_dlgt:
+ AuFlagSet(sbinfo, f_dlgt, 1);
+ given->f_dlgt = 1;
+ break;
+ case Opt_nodlgt:
+ AuFlagSet(sbinfo, f_dlgt, 0);
+ given->f_dlgt = 1;
+ break;
+
+ case Opt_warn_perm:
+ AuFlagSet(sbinfo, f_warn_perm, 1);
+ given->f_warn_perm = 1;
+ break;
+ case Opt_nowarn_perm:
+ AuFlagSet(sbinfo, f_warn_perm, 0);
+ given->f_warn_perm = 1;
+ break;
+
+ case Opt_refrof:
+ AuFlagSet(sbinfo, f_refrof, 1);
+ given->f_refrof = 1;
+ break;
+ case Opt_norefrof:
+ //coo_set(sb, AuFlag_COO_LEAF);
+ AuFlagSet(sbinfo, f_refrof, 0);
+ given->f_refrof = 1;
+ break;
+
+ case Opt_verbose:
+ AuFlagSet(sbinfo, f_verbose, 1);
+ given->f_verbose = 1;
+ break;
+ case Opt_noverbose:
+ AuFlagSet(sbinfo, f_verbose, 0);
+ given->f_verbose = 1;
+ 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;
+ }
+ AuFlagSet(sbinfo, f_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 */
+ given->f_wbr_create = 1;
+ break;
+ case Opt_wbr_copyup:
+ AuFlagSet(sbinfo, f_wbr_copyup, opt->wbr_copyup);
+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup;
+ given->f_wbr_copyup = 1;
+ break;
+
+ case Opt_coo:
+ AuFlagSet(sbinfo, f_coo, opt->coo);
+ given->f_coo = 1;
+ break;
+
+ case Opt_dirwh:
+ sbinfo->si_dirwh = opt->dirwh;
+ break;
+
+ case Opt_rdcache:
+ sbinfo->si_rdcache = opt->rdcache * HZ;
+ break;
+
+ case Opt_trunc_xino:
+ AuFlagSet(sbinfo, f_trunc_xino, 1);
+ given->f_trunc_xino = 1;
+ break;
+ case Opt_notrunc_xino:
+ AuFlagSet(sbinfo, f_trunc_xino, 0);
+ given->f_trunc_xino = 1;
+ break;
+
+ case Opt_trunc_xino_path:
+ case Opt_itrunc_xino:
+ err = xino_trunc(sb, opt->xino_itrunc.bindex);
+ if (!err)
+ err = 1;
+ break;
+
+ case Opt_trunc_xib:
+ opts->trunc_xib = 1;
+ break;
+ case Opt_notrunc_xib:
+ opts->trunc_xib = 0;
+ 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 = 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 = br_add(sb, &opt->add, opts->remount);
+ if (!err) {
+ err = 1;
+ opts->refresh_dir = 1;
+ if (unlikely(br_whable(opt->add.perm)))
+ opts->refresh_nondir = 1;
+ }
+ break;
+
+ case Opt_del:
+ case Opt_idel:
+ err = br_del(sb, &opt->del, opts->remount);
+ if (!err) {
+ err = 1;
+ opts->trunc_xib = 1;
+ opts->refresh_dir = 1;
+ opts->refresh_nondir = 1;
+ }
+ break;
+
+ case Opt_mod:
+ case Opt_imod:
+ err = br_mod(sb, &opt->mod, opts->remount, &do_refresh);
+ if (!err) {
+ err = 1;
+ if (unlikely(do_refresh)) {
+ opts->refresh_dir = 1;
+ opts->refresh_nondir = 1;
+ }
+ }
+ 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 = xino_set(sb, &opt->xino, opts->remount);
+ if (!err)
+ *opt_xino = &opt->xino;
+ break;
+ case Opt_noxino:
+ xino_clr(sb);
+ *opt_xino = (void *)-1;
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int verify_opts(struct super_block *sb, struct au_opts_flags *pending,
+ int remount)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct aufs_branch *br;
+ struct dentry *root;
+ struct inode *dir;
+ unsigned int do_plink;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ if (unlikely(!(sb->s_flags & MS_RDONLY)
+ && !br_writable(sbr_perm(sb, 0))))
+ AuWarn("first branch should be rw\n");
+
+ sbinfo = stosi(sb);
+ if (unlikely((au_flag_test_udba_inotify(sb)
+ || pending->f_udba == AuUdba_INOTIFY)
+ && AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ AuWarn("udba=inotify requires xino\n");
+
+ err = 0;
+ root = sb->s_root;
+ dir = sb->s_root->d_inode;
+ do_plink = AuFlag(sbinfo, f_plink);
+ bend = sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ struct inode *h_dir;
+ int skip;
+
+ skip = 0;
+ h_dir = au_h_iptr_i(dir, bindex);
+ br = stobr(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;
+
+ hdir_lock(h_dir, dir, bindex);
+ br_wh_write_lock(br);
+ err = init_wh(au_h_dptr_i(root, bindex), br,
+ au_nfsmnt(sb, bindex), sb);
+ br_wh_write_unlock(br);
+ 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 aufs_sbinfo *sbinfo;
+ struct au_opts_flags 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 = stosi(sb);
+ tmp = sbinfo->au_si_flags;
+ AuFlagSet(sbinfo, f_xino, AuXino_NONE);
+ AuFlagSet(sbinfo, f_dlgt, 0);
+ AuFlagSet(sbinfo, f_udba, AuUdba_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 = sbend(sb);
+ if (unlikely(bend < 0)) {
+ err = -EINVAL;
+ AuErr("no branches\n");
+ goto out;
+ }
+
+ AuFlagSet(sbinfo, f_xino, tmp.f_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 (tmp.f_xino != AuXino_NONE && !opt_xino) {
+ struct au_opt_xino xino;
+
+ xino.file = xino_def(sb);
+ err = PTR_ERR(xino.file);
+ if (IS_ERR(xino.file))
+ goto out;
+
+ err = xino_set(sb, &xino, /*remount*/0);
+ fput(xino.file);
+ if (unlikely(err))
+ goto out;
+ }
+
+ /* restore hinotify */
+ AuFlagSet(sbinfo, f_udba, tmp.f_udba);
+ if (tmp.f_udba == AuUdba_INOTIFY)
+ au_reset_hinotify(dir, au_hi_flags(dir, 1) & ~AUFS_HI_XINO);
+
+ /* restore dlgt */
+ AuFlagSet(sbinfo, f_dlgt, tmp.f_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 aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ DiMustWriteLock(sb->s_root);
+ dir = sb->s_root->d_inode;
+ IiMustWriteLock(dir);
+ //AuDebugOn(au_flag_test_udba_inotify(sb));
+
+ err = 0;
+ sbinfo = stosi(sb);
+ dlgt = au_need_dlgt(sb);
+ 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_need_dlgt(sb);
+ AuFlagSet(sbinfo, f_dlgt, 0);
+
+ if (!err)
+ err = au_opt_br(sb, opt, opts);
+ if (!err)
+ err = au_opt_xino(sb, opt, &opt_xino, opts);
+
+ /* restore it */
+ AuFlagSet(sbinfo, f_dlgt, dlgt);
+ opt++;
+ }
+ if (err > 0)
+ err = 0;
+ AuTraceErr(err);
+
+ /* go on even err */
+
+ //todo: test this error case.
+ AuFlagSet(sbinfo, f_dlgt, 0);
+ rerr = verify_opts(sb, &sbinfo->au_si_flags, /*remount*/1);
+ AuFlagSet(sbinfo, f_dlgt, dlgt);
+ if (unlikely(rerr && !err))
+ err = rerr;
+
+ if (unlikely(opts->trunc_xib)) {
+ rerr = xib_trunc(sb);
+ if (unlikely(rerr && !err))
+ err = rerr;
+ }
+
+ /* they are handled by the caller */
+ if (!opts->refresh_dir)
+ opts->refresh_dir
+ = !!(opts->given.f_udba
+ || AuFlag(sbinfo, f_xino) != AuXino_NONE);
+
+ LKTRTrace("status {%d, %d}\n",
+ opts->refresh_dir, opts->refresh_nondir);
+ AuTraceErr(err);
+ return err;
+}
diff --git a/ubuntu/fs/aufs/opts.h b/ubuntu/fs/aufs/opts.h
new file mode 100644
index 0000000..1c5c827
--- /dev/null
+++ b/ubuntu/fs/aufs/opts.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: opts.h,v 1.25 2007/12/10 01:19:34 sfjro Exp $ */
+
+#ifndef __AUFS_OPTS_H__
+#define __AUFS_OPTS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/namei.h>
+#include <linux/version.h>
+#include <linux/aufs_type.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
+typedef const char *au_parser_pattern_t;
+#else
+typedef char *au_parser_pattern_t;
+#endif
+
+/* ---------------------------------------------------------------------- */
+/* mount flags */
+
+/* external inode number bitmap and translation table */
+enum {
+ AuXino_NONE,
+ AuXino_XINO,
+ AuXino_XIB
+};
+
+/* users direct branch access */
+enum {
+ AuUdba_NONE,
+ AuUdba_REVAL,
+ AuUdba_INOTIFY
+};
+
+/* copyup on open */
+enum {
+ AuCoo_NONE,
+ AuCoo_LEAF,
+ AuCoo_ALL
+};
+
+/* 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_Last
+};
+
+enum {
+ AuWbrCopyup_TDP, /* top down parent */
+ AuWbrCopyup_BUP, /* bottom up parent */
+ AuWbrCopyup_BU /* bottom up */
+};
+
+/* revert it to bit-shift? */
+struct au_opts_flags {
+ unsigned int f_xino:2;
+ unsigned int f_trunc_xino:1;
+ unsigned int f_udba:2;
+ unsigned int f_dlgt:1;
+ unsigned int f_plink:1;
+ unsigned int f_warn_perm:1;
+ unsigned int f_coo:2;
+ unsigned int f_always_diropq:1;
+ unsigned int f_refrof:1;
+ unsigned int f_verbose:1;
+ unsigned int f_wbr_copyup:2;
+ unsigned int f_wbr_create:3;
+};
+
+/* ---------------------------------------------------------------------- */
+
+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;
+ };
+};
+
+struct au_opts {
+ struct au_opt *opt;
+ int max_opt;
+
+ struct au_opts_flags given;
+ struct {
+ unsigned int remount:1;
+
+ unsigned int refresh_dir:1;
+ unsigned int refresh_nondir:1;
+ unsigned int trunc_xib:1;
+ };
+};
+
+/* ---------------------------------------------------------------------- */
+
+void au_opts_flags_def(struct au_opts_flags *flags);
+au_parser_pattern_t au_optstr_br_perm(int brperm);
+au_parser_pattern_t au_optstr_udba(int udba);
+au_parser_pattern_t au_optstr_coo(int coo);
+au_parser_pattern_t au_optstr_wbr_copyup(int wbr_copyup);
+au_parser_pattern_t au_optstr_wbr_create(int wbr_create);
+
+void au_opts_free(struct au_opts *opts);
+int au_opts_parse(struct super_block *sb, 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);
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_OPTS_H__ */
diff --git a/ubuntu/fs/aufs/plink.c b/ubuntu/fs/aufs/plink.c
new file mode 100644
index 0000000..78f7356
--- /dev/null
+++ b/ubuntu/fs/aufs/plink.c
@@ -0,0 +1,338 @@
+/*
+ * 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
+ */
+
+/* $Id: plink.c,v 1.16 2007/10/29 04:41:10 sfjro Exp $ */
+
+#include "aufs.h"
+
+struct pseudo_link {
+ struct list_head list;
+ struct inode *inode;
+};
+
+#ifdef CONFIG_AUFS_DEBUG
+void au_list_plink(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink;
+
+ AuTraceEnter();
+ SiMustAnyLock(sb);
+ sbinfo = stosi(sb);
+ AuDebugOn(!AuFlag(sbinfo, f_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_test_plink(struct super_block *sb, struct inode *inode)
+{
+ int found;
+ struct aufs_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink;
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+ SiMustAnyLock(sb);
+ sbinfo = stosi(sb);
+ AuDebugOn(!AuFlag(sbinfo, f_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_i(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_lkup_plink(struct super_block *sb, aufs_bindex_t bindex,
+ struct inode *inode)
+{
+ struct dentry *h_dentry, *h_parent;
+ struct aufs_branch *br;
+ struct inode *h_dir;
+ char tgtname[PLINK_NAME_LEN];
+ int len;
+ struct aufs_ndx ndx = {
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("b%d, i%lu\n", bindex, inode->i_ino);
+ br = stobr(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);
+ ndx.dlgt = au_need_dlgt(sb);
+ vfsub_i_lock_nested(h_dir, AuLsc_I_CHILD2);
+ h_dentry = au_sio_lkup_one(tgtname, h_parent, len, &ndx);
+ vfsub_i_unlock(h_dir);
+ 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;
+ struct dentry *h_tgt;
+ struct inode *h_dir;
+ struct vfsub_args vargs;
+ struct aufs_ndx ndx = {
+ .nfsmnt = nfsmnt,
+ .dlgt = au_need_dlgt(sb),
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ 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, ndx.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, ndx.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 aufs_branch *br;
+ struct dentry *h_parent;
+ struct inode *h_dir;
+ char tgtname[PLINK_NAME_LEN];
+
+ LKTRTrace("%.*s\n", AuDLNPair(h_dentry));
+ br = stobr(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. */
+ vfsub_i_lock_nested(h_dir, 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);
+ vfsub_i_unlock(h_dir);
+
+ AuTraceErr(err);
+ return err;
+}
+
+void au_append_plink(struct super_block *sb, struct inode *inode,
+ struct dentry *h_dentry, aufs_bindex_t bindex)
+{
+ struct aufs_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 = stosi(sb);
+ AuDebugOn(!AuFlag(sbinfo, f_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 > 100))
+ 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_put_plink(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+ struct list_head *plink_list;
+ struct pseudo_link *plink, *tmp;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+ sbinfo = stosi(sb);
+ AuDebugOn(!AuFlag(sbinfo, f_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_half_refresh_plink(struct super_block *sb, aufs_bindex_t br_id)
+{
+ struct aufs_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 = stosi(sb);
+ AuDebugOn(!AuFlag(sbinfo, f_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 = ibstart(inode);
+ bend = ibend(inode);
+ if (bstart >= 0) {
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (!au_h_iptr_i(inode, bindex)
+ || itoid_index(inode, bindex) != br_id)
+ continue;
+ 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_i(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 --git a/ubuntu/fs/aufs/sbinfo.c b/ubuntu/fs/aufs/sbinfo.c
new file mode 100644
index 0000000..1d141b3
--- /dev/null
+++ b/ubuntu/fs/aufs/sbinfo.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: sbinfo.c,v 1.44 2007/11/12 01:43:10 sfjro Exp $ */
+
+#include "aufs.h"
+
+struct aufs_sbinfo *stosi(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+ sbinfo = sb->s_fs_info;
+ //AuDebugOn(sbinfo->si_bend < 0);
+ return sbinfo;
+}
+
+aufs_bindex_t sbend(struct super_block *sb)
+{
+ SiMustAnyLock(sb);
+ return stosi(sb)->si_bend;
+}
+
+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex)
+{
+ struct aufs_branch *br;
+
+ SiMustAnyLock(sb);
+ AuDebugOn(bindex < 0 || sbend(sb) < bindex);
+ br = stosi(sb)->si_branch[0 + bindex];
+ AuDebugOn(!br);
+ return br;
+}
+
+au_gen_t au_sigen(struct super_block *sb)
+{
+ SiMustAnyLock(sb);
+ return stosi(sb)->si_generation;
+}
+
+au_gen_t au_sigen_inc(struct super_block *sb)
+{
+ au_gen_t gen;
+
+ SiMustWriteLock(sb);
+ gen = ++stosi(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 find_bindex(struct super_block *sb, struct aufs_branch *br)
+{
+ aufs_bindex_t bindex, bend;
+
+ bend = sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (stobr(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 (flags & AuLock_DW)
+ di_write_lock_child(dentry);
+ else
+ di_read_lock_child(dentry, flags);
+}
+
+void aufs_read_unlock(struct dentry *dentry, int flags)
+{
+ if (flags & AuLock_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, flags);
+}
+
+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 new_br_id(struct super_block *sb)
+{
+ aufs_bindex_t br_id;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ sbinfo = stosi(sb);
+ while (1) {
+ br_id = ++sbinfo->si_last_br_id;
+ if (br_id && find_brindex(sb, br_id) < 0)
+ return br_id;
+ }
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_SYSAUFS
+static int make_xino(struct seq_file *seq, struct sysaufs_args *args,
+ int *do_size)
+{
+ int err, dlgt;
+ struct super_block *sb = args->sb;
+ aufs_bindex_t bindex, bend;
+ struct file *xf;
+ struct kstat st;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ AuDebugOn(args->index != SysaufsSb_XINO);
+ SiMustReadLock(sb);
+
+ err = 0;
+ *do_size = 0;
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE)) {
+#ifdef CONFIG_AUFS_DEBUG
+ AuDebugOn(sbinfo->si_xib);
+ bend = sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++)
+ AuDebugOn(stobr(sb, bindex)->br_xino);
+#endif
+ return err;
+ }
+
+ dlgt = au_need_dlgt(sb);
+ xf = sbinfo->si_xib;
+ err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st, dlgt);
+ if (!err)
+ err = seq_printf(seq, "%Lux%lu %Ld\n",
+ st.blocks, st.blksize, (long long)st.size);
+ else
+ err = seq_printf(seq, "err %d\n", err);
+
+ bend = sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ xf = stobr(sb, bindex)->br_xino;
+ if (!xf)
+ continue;
+ err = seq_printf(seq, "%d: ", bindex);
+ if (!err)
+ err = vfsub_getattr(xf->f_vfsmnt, xf->f_dentry, &st,
+ dlgt);
+ if (!err)
+ err = seq_printf(seq, "%d, %Lux%lu %Ld\n",
+ file_count(xf), st.blocks, st.blksize,
+ (long long)st.size);
+ else
+ err = seq_printf(seq, "err %d\n", err);
+ }
+ AuTraceErr(err);
+ return err;
+}
+
+sysaufs_op au_si_ops[] = {
+ [SysaufsSb_XINO] = make_xino
+};
+#endif /* CONFIG_AUFS_SYSAUFS */
diff --git a/ubuntu/fs/aufs/super.c b/ubuntu/fs/aufs/super.c
new file mode 100644
index 0000000..c3edb2f
--- /dev/null
+++ b/ubuntu/fs/aufs/super.c
@@ -0,0 +1,994 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: super.c,v 1.73 2008/01/28 05:02:15 sfjro Exp $ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/smp_lock.h>
+#include <linux/statfs.h>
+
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#include <linux/mnt_namespace.h>
+typedef struct mnt_namespace au_mnt_ns_t;
+#define au_nsproxy(tsk) (tsk)->nsproxy
+#define au_mnt_ns(tsk) (tsk)->nsproxy->mnt_ns
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#include <linux/namespace.h>
+typedef struct namespace au_mnt_ns_t;
+#define au_nsproxy(tsk) (tsk)->nsproxy
+#define au_mnt_ns(tsk) (tsk)->nsproxy->namespace
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#include <linux/namespace.h>
+typedef struct namespace au_mnt_ns_t;
+#define au_nsproxy(tsk) (tsk)->namespace
+#define au_mnt_ns(tsk) (tsk)->namespace
+#endif
+
+#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));
+}
+
+//todo: how about merge with alloc_inode()?
+static void aufs_read_inode(struct inode *inode)
+{
+ int err;
+#if 0
+ static struct backing_dev_info bdi = {
+ .ra_pages = 0, /* No readahead */
+ .capabilities = BDI_CAP_NO_ACCT_DIRTY | BDI_CAP_NO_WRITEBACK
+ };
+#endif
+
+ LKTRTrace("i%lu\n", inode->i_ino);
+
+ 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;
+ return; /* success */
+ }
+
+ LKTRTrace("intializing inode info failed(%d)\n", err);
+ make_bad_inode(inode);
+}
+
+int au_show_brs(struct seq_file *seq, struct super_block *sb)
+{
+ int err;
+ aufs_bindex_t bindex, bend;
+ struct dentry *root;
+
+ AuTraceEnter();
+ SiMustAnyLock(sb);
+ root = sb->s_root;
+ DiMustAnyLock(root);
+
+ err = 0;
+ bend = sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ err = seq_path(seq, sbr_mnt(sb, bindex),
+ au_h_dptr_i(root, bindex), au_esc_chars);
+ if (err > 0)
+ err = seq_printf
+ (seq, "=%s",
+ au_optstr_br_perm(sbr_perm(sb, bindex)));
+ if (!err && bindex != bend)
+ err = seq_putc(seq, ':');
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ int err, n;
+ struct super_block *sb;
+ struct aufs_sbinfo *sbinfo;
+ struct dentry *root;
+ struct file *xino;
+ struct au_opts_flags def;
+
+ AuTraceEnter();
+
+ sb = mnt->mnt_sb;
+ root = sb->s_root;
+ aufs_read_lock(root, !AuLock_IR);
+ sbinfo = stosi(sb);
+ if (AuFlag(sbinfo, f_xino) == AuXino_XINO) {
+ err = seq_puts(m, ",xino=");
+ if (unlikely(err))
+ goto out;
+ xino = sbinfo->si_xib;
+ err = seq_path(m, xino->f_vfsmnt, xino->f_dentry, 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
+ err = seq_puts(m, ",noxino");
+
+ au_opts_flags_def(&def);
+
+#define Print(name) do { \
+ n = AuFlag(sbinfo, f_##name); \
+ if (unlikely(!err && n != def.f_##name)) \
+ err = seq_printf(m, ",%s"#name, n ? "" : "no"); \
+} while (0)
+
+#define PrintStr(name, str) do { \
+ n = AuFlag(sbinfo, f_##name); \
+ if (unlikely(!err && n != def.f_##name)) \
+ err = seq_printf(m, "," str "=%s", au_optstr_##name(n)); \
+} while (0)
+
+ Print(trunc_xino);
+ Print(plink);
+ PrintStr(udba, "udba");
+ switch (AuFlag(sbinfo, f_wbr_create)) {
+ case AuWbrCreate_TDP:
+ case AuWbrCreate_RR:
+ case AuWbrCreate_MFS:
+ case AuWbrCreate_PMFS:
+ PrintStr(wbr_create, "create");
+ break;
+ case AuWbrCreate_MFSV:
+ err = seq_printf(m, ",create=mfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_PMFSV:
+ err = seq_printf(m, ",create=pmfs:%lu",
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ case AuWbrCreate_MFSRR:
+ err = seq_printf(m, ",create=mfsrr:%Lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark);
+ break;
+ case AuWbrCreate_MFSRRV:
+ err = seq_printf(m, ",create=mfsrr:%Lu:%lu",
+ sbinfo->si_wbr_mfs.mfsrr_watermark,
+ sbinfo->si_wbr_mfs.mfs_expire / HZ);
+ break;
+ default:
+ AuDebugOn(1);
+ }
+
+ PrintStr(wbr_copyup, "cpup");
+ n = AuFlag(sbinfo, f_always_diropq);
+ if (unlikely(!err && n != def.f_always_diropq))
+ err = seq_printf(m, ",diropq=%c", n ? 'a' : 'w');
+ Print(refrof);
+ Print(dlgt);
+ Print(warn_perm);
+ Print(verbose);
+
+ n = sbinfo->si_dirwh;
+ if (unlikely(!err && n != AUFS_DIRWH_DEF))
+ err = seq_printf(m, ",dirwh=%d", n);
+ n = sbinfo->si_rdcache / HZ;
+ if (unlikely(!err && n != AUFS_RDCACHE_DEF))
+ err = seq_printf(m, ",rdcache=%d", n);
+
+ PrintStr(coo, "coo");
+
+#undef Print
+#undef PrintStr
+
+ if (!err && !sysaufs_brs) {
+#ifdef CONFIG_AUFS_COMPAT
+ err = seq_puts(m, ",dirs=");
+#else
+ err = seq_puts(m, ",br:");
+#endif
+ if (!err)
+ err = au_show_brs(m, sb);
+ }
+
+ out:
+ aufs_read_unlock(root, !AuLock_IR);
+ AuTraceErr(err);
+ if (err)
+ err = -E2BIG;
+ AuTraceErr(err);
+ return err;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+#define StatfsLock(d) aufs_read_lock((d)->d_sb->s_root, 0)
+#define StatfsUnlock(d) aufs_read_unlock((d)->d_sb->s_root, 0)
+#define StatfsArg(d) au_h_dptr((d)->d_sb->s_root)
+#define StatfsHInode(d) (StatfsArg(d)->d_inode)
+#define StatfsSb(d) ((d)->d_sb)
+static int aufs_statfs(struct dentry *arg, struct kstatfs *buf)
+#else
+#define StatfsLock(s) si_read_lock(s, !AuLock_FLUSH)
+#define StatfsUnlock(s) si_read_unlock(s)
+#define StatfsArg(s) sbr_sb(s, 0)
+#define StatfsHInode(s) (StatfsArg(s)->s_root->d_inode)
+#define StatfsSb(s) (s)
+static int aufs_statfs(struct super_block *arg, struct kstatfs *buf)
+#endif
+{
+ int err;
+
+ AuTraceEnter();
+
+ StatfsLock(arg);
+ err = vfsub_statfs(StatfsArg(arg), buf, au_need_dlgt(StatfsSb(arg)));
+ //if (LktrCond) err = -1;
+ StatfsUnlock(arg);
+ if (!err) {
+ buf->f_type = AUFS_SUPER_MAGIC;
+ buf->f_type = 0;
+ 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_update_mnt(struct vfsmount *mnt, int flags)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ struct vfsmount *pos;
+ struct super_block *sb = mnt->mnt_sb;
+ struct dentry *root = sb->s_root;
+ struct aufs_sbinfo *sbinfo = stosi(sb);
+ au_mnt_ns_t *ns;
+
+ AuTraceEnter();
+ AuDebugOn(!kernel_locked());
+
+ if (sbinfo->si_mnt != mnt
+ || atomic_read(&sb->s_active) == 1
+ || !au_nsproxy(current))
+ return;
+
+ /* no get/put */
+ ns = au_mnt_ns(current);
+ AuDebugOn(!ns);
+ sbinfo->si_mnt = NULL;
+ list_for_each_entry(pos, &ns->list, mnt_list)
+ if (pos != mnt && pos->mnt_sb->s_root == root) {
+ sbinfo->si_mnt = pos;
+ break;
+ }
+ AuDebugOn(!(flags & MNT_DETACH) && !sbinfo->si_mnt);
+#endif
+}
+
+#define UmountBeginHasMnt (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18))
+
+#if UmountBeginHasMnt
+#define UmountBeginSb(mnt) (mnt)->mnt_sb
+#define UmountBeginMnt(mnt) (mnt)
+#define UmountBeginFlags _flags
+static void aufs_umount_begin(struct vfsmount *arg, int _flags)
+#else
+#define UmountBeginSb(sb) sb
+#define UmountBeginMnt(sb) NULL
+#define UmountBeginFlags 0
+static void aufs_umount_begin(struct super_block *arg)
+#endif
+{
+ struct super_block *sb = UmountBeginSb(arg);
+ struct vfsmount *mnt = UmountBeginMnt(arg);
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ sbinfo = stosi(sb);
+ if (unlikely(!sbinfo))
+ return;
+
+ si_write_lock(sb);
+ if (AuFlag(sbinfo, f_plink))
+ au_put_plink(sb);
+#if 0 // remove
+ if (unlikely(au_flag_test(sb, AuFlag_UDBA_INOTIFY)))
+ shrink_dcache_sb(sb);
+#endif
+ au_update_mnt(mnt, UmountBeginFlags);
+#if 0
+ if (sbinfo->si_wbr_create_ops->fin)
+ sbinfo->si_wbr_create_ops->fin(sb);
+#endif
+ si_write_unlock(sb);
+}
+
+static void free_sbinfo(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ sbinfo = stosi(sb);
+ AuDebugOn(!sbinfo || !list_empty(&sbinfo->si_plink));
+
+ si_write_lock(sb);
+ xino_clr(sb);
+ free_branches(sbinfo);
+ kfree(sbinfo->si_branch);
+ si_write_unlock(sb);
+ kfree(sbinfo);
+}
+
+/* final actions when unmounting a file system */
+static void aufs_put_super(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ sbinfo = stosi(sb);
+ if (unlikely(!sbinfo))
+ return;
+
+ sysaufs_del(sbinfo);
+
+#if !UmountBeginHasMnt
+ /* umount_begin() may not be called. */
+ aufs_umount_begin(sb);
+#endif
+ free_sbinfo(sb);
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * 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 (unlikely(!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);
+}
+
+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);
+ AuDebugOn(au_digen(root) != sgen
+ || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (unlikely(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;
+ }
+
+ e = 0;
+ 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);
+ AuDebugOn(au_digen(root) != sgen
+ || !kernel_locked());
+
+ err = 0;
+ list_for_each_entry(inode, &root->d_sb->s_inodes, i_sb_list)
+ if (unlikely(!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 (unlikely(!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 aufs_sbinfo *sbinfo;
+
+ //au_debug_on();
+ LKTRTrace("flags 0x%x, data %s, len %lu\n",
+ *flags, data ? data : "NULL",
+ (unsigned long)(data ? strlen(data) : 0));
+
+ err = 0;
+ if (unlikely(!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.remount = 1;
+
+ //todo: writeback everything on branches.
+
+ /* parse it before aufs lock */
+ err = au_opts_parse(sb, data, &opts);
+ //if (LktrCond) {au_free_opts(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_opts;
+
+ sbinfo = stosi(sb);
+ root = sb->s_root;
+ inode = root->d_inode;
+ vfsub_i_lock(inode);
+ 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 (opts.refresh_dir || opts.refresh_nondir) {
+ int rerr;
+ au_gen_t sigen;
+
+ dlgt = au_need_dlgt(sb);
+ AuFlagSet(sbinfo, f_dlgt, 0);
+ au_sigen_inc(sb);
+ au_reset_hinotify(inode, au_hi_flags(inode, /*isdir*/1));
+ sigen = au_sigen(sb);
+ sbinfo->si_failed_refresh_dirs = 0;
+
+ DiMustNoWaiters(root);
+ IiMustNoWaiters(root->d_inode);
+ di_write_unlock(root);
+
+ rerr = refresh_dir(root, sigen);
+ if (unlikely(rerr)) {
+ sbinfo->si_failed_refresh_dirs = 1;
+ AuWarn("Refreshing directories failed, ignores (%d)\n",
+ rerr);
+ }
+
+ if (unlikely(opts.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);
+ AuFlagSet(sbinfo, f_dlgt, dlgt);
+ }
+
+ aufs_write_unlock(root);
+ vfsub_i_unlock(inode);
+ if (opts.refresh_dir)
+ sysaufs_notify_remount();
+
+ 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,
+ .read_inode = aufs_read_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
+};
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * at first mount time.
+ */
+
+static int alloc_sbinfo(struct super_block *sb)
+{
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+
+ 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;
+
+ rw_init_wlock(&sbinfo->si_rwsem);
+ sbinfo->si_generation = 0;
+ //sbinfo->si_generation = INT_MAX - 2;
+ sbinfo->si_failed_refresh_dirs = 0;
+ sbinfo->si_bend = -1;
+ sbinfo->si_last_br_id = 0;
+
+ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + AuWbrCopyup_TDP;
+ sbinfo->si_wbr_create_ops = au_wbr_create_ops + AuWbrCreate_TDP;
+
+ au_opts_flags_def(&sbinfo->au_si_flags);
+
+ 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);
+
+ /* leave syaufs members, si_list, si_mnt and si_sysaufs. */
+
+ init_lvma(sbinfo);
+ sb->s_fs_info = sbinfo;
+
+#ifdef ForceInotify
+ AuFlagSet(sbinfo, f_udba, AuUdba_INOTIFY);
+#endif
+#ifdef ForceDlgt
+ AuFlagSet(sbinfo, f_dlgt, 1);
+#endif
+#ifdef ForceNoPlink
+ AuFlagSet(sbinfo, f_plink, 0);
+#endif
+#ifdef ForceNoXino
+ AuFlagSet(sbinfo, f_xino, AuXino_NONE);
+#endif
+#ifdef ForceNoRefrof
+ AuFlagSet(sbinfo, f_refrof, 0);
+#endif
+ return 0; /* success */
+
+// out_branch:
+ kfree(sbinfo->si_branch);
+ out_sbinfo:
+ kfree(sbinfo);
+ out:
+ AuTraceErr(-ENOMEM);
+ return -ENOMEM;
+}
+
+static int alloc_root(struct super_block *sb)
+{
+ int err;
+ struct inode *inode;
+ struct dentry *root;
+
+ AuTraceEnter();
+
+ err = -ENOMEM;
+ inode = iget(sb, AUFS_ROOT_INO);
+ //if (LktrCond) {iput(inode); inode = NULL;}
+ if (unlikely(!inode))
+ goto out;
+ err = PTR_ERR(inode);
+ if (IS_ERR(inode))
+ goto out;
+ err = -ENOMEM;
+ if (unlikely(is_bad_inode(inode)))
+ goto out_iput;
+
+ 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){rw_write_unlock(&dtodi(root)->di_rwsem);err=-1;}
+ if (!err) {
+ sb->s_root = root;
+ return 0; /* success */
+ }
+ dput(root);
+ goto out; /* do not iput */
+
+ out_iput:
+ 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 = alloc_sbinfo(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) {rw_write_unlock(&dtodi(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, arg, &opts);
+ //if (LktrCond) {au_opts_free(&opts); err = -1;}
+ if (unlikely(err))
+ goto out_root;
+
+ /* lock vfs_inode first, then aufs. */
+ vfsub_i_lock(inode);
+ 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);
+ vfsub_i_unlock(inode);
+ //AuDbgSb(sb);
+ goto out_opts; /* success */
+
+ out_unlock:
+ aufs_write_unlock(root);
+ vfsub_i_unlock(inode);
+ out_root:
+ dput(root);
+ sb->s_root = NULL;
+ out_info:
+ free_sbinfo(sb);
+ 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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+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 aufs_sbinfo *sbinfo = stosi(mnt->mnt_sb);
+ sbinfo->si_mnt = mnt;
+ sysaufs_add(sbinfo);
+ }
+ return err;
+}
+#else
+static struct super_block *aufs_get_sb(struct file_system_type *fs_type,
+ int flags, const char *dev_name,
+ void *raw_data)
+{
+ return get_sb_nodev(fs_type, flags, raw_data, aufs_fill_super);
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) */
+
+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,
+ /* no need to __module_get() and module_put(). */
+ .owner = THIS_MODULE,
+};
diff --git a/ubuntu/fs/aufs/super.h b/ubuntu/fs/aufs/super.h
new file mode 100644
index 0000000..09b5883
--- /dev/null
+++ b/ubuntu/fs/aufs/super.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: super.h,v 1.67 2007/12/17 03:30:59 sfjro Exp $ */
+
+#ifndef __AUFS_SUPER_H__
+#define __AUFS_SUPER_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#include <linux/cramfs_fs.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
+#include <linux/magic.h>
+#else
+#include <linux/nfs_fs.h>
+#endif
+#include <linux/aufs_type.h>
+//#include "hinode.h"
+#include "misc.h"
+#include "opts.h"
+#include "sysaufs.h"
+#include "wkq.h"
+
+typedef ssize_t (*readf_t)(struct file *, char __user *, size_t, loff_t *);
+typedef ssize_t (*writef_t)(struct file *, const char __user *, size_t,
+ loff_t *);
+
+#ifdef CONFIG_AUFS_SYSAUFS
+/* entries under sysfs per mount-point */
+enum {
+ SysaufsSb_XINO,
+#if 0
+ SysaufsSb_PLINK,
+ SysaufsSb_files,
+#endif
+ SysaufsSb_Last
+};
+
+struct sysaufs_sbinfo {
+ au_subsys_t subsys;
+ struct sysaufs_entry array[SysaufsSb_Last];
+};
+
+extern sysaufs_op au_si_ops[];
+
+#else
+struct sysaufs_sbinfo {};
+#endif /* CONFIG_AUFS_SYSAUFS */
+
+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;
+ unsigned long mfs_jiffy;
+ unsigned long mfs_expire;
+ aufs_bindex_t mfs_bindex;
+
+ u64 mfsrr_bytes;
+ u64 mfsrr_watermark;
+};
+
+struct aufs_branch;
+struct aufs_sbinfo {
+ /* nowait tasks in the system-wide workqueue */
+ struct au_nowait_tasks si_nowait;
+
+ struct aufs_rwsem si_rwsem;
+
+ /* branch management */
+ au_gen_t si_generation;
+
+ /*
+ * set true when refresh_dirs() at remount time failed.
+ * then try refreshing dirs at access time again.
+ * if it is false, refreshing dirs at access time is unnecesary
+ */
+ unsigned int si_failed_refresh_dirs:1;
+
+ aufs_bindex_t si_bend;
+ aufs_bindex_t si_last_br_id;
+ struct aufs_branch **si_branch;
+
+ /* policy to select a writable branch */
+ 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 */
+ struct au_opts_flags au_si_flags;
+
+ /* external inode number (bitmap and translation table) */
+ readf_t si_xread;
+ 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 rename_whtmp 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;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+ /* super_blocks list is not exported */
+ struct list_head si_list;
+ struct vfsmount *si_mnt; /* no get/put */
+#endif
+
+ /* sysfs */
+ struct sysaufs_sbinfo si_sysaufs;
+
+#ifdef CONFIG_AUFS_ROBR
+ /* locked vma list for mmap() */ // very dirty
+ spinlock_t si_lvma_lock;
+ struct list_head si_lvma;
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+/* mount flags */
+#define AuFlag(sbinfo, name) ({ (sbinfo)->au_si_flags.name; })
+#define AuFlagSet(sbinfo, name, val) { (sbinfo)->au_si_flags.name = (val); }
+
+/* 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
+#define AuLock_IR (1 << 1)
+#define AuLock_IW (1 << 2)
+#define AuLock_FLUSH (1 << 3)
+#define AuLock_DIR (1 << 4)
+
+/* ---------------------------------------------------------------------- */
+
+/* super.c */
+int au_show_brs(struct seq_file *seq, struct super_block *sb);
+extern struct file_system_type aufs_fs_type;
+
+/* sbinfo.c */
+struct aufs_sbinfo *stosi(struct super_block *sb);
+aufs_bindex_t sbend(struct super_block *sb);
+struct aufs_branch *stobr(struct super_block *sb, aufs_bindex_t bindex);
+au_gen_t au_sigen(struct super_block *sb);
+au_gen_t au_sigen_inc(struct super_block *sb);
+int find_bindex(struct super_block *sb, struct aufs_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 new_br_id(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 const char *au_sbtype(struct super_block *sb)
+{
+ return sb->s_type->name;
+}
+
+static inline int au_test_aufs(struct super_block *sb)
+{
+#ifdef AUFS_SUPER_MAGIC
+ return (sb->s_magic == AUFS_SUPER_MAGIC);
+#else
+ return !strcmp(au_sbtype(sb), AUFS_FSTYPE);
+#endif
+}
+
+static inline int au_test_fuse(struct super_block *sb)
+{
+ int ret = 0;
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+#ifdef FUSE_SUPER_MAGIC
+ BUILD_BUG_ON(FUSE_SUPER_MAGIC != 0x65735546);
+ ret = (sb->s_magic == FUSE_SUPER_MAGIC);
+#else
+ ret = !strcmp(au_sbtype(sb), "fuse");
+#endif
+#endif
+ return ret;
+}
+
+static inline int au_test_xfs(struct super_block *sb)
+{
+ int ret = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) \
+ && (defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE))
+#ifdef XFS_SB_MAGIC
+ BUILD_BUG_ON(XFS_SB_MAGIC != 0x58465342);
+ ret = (sb->s_magic == XFS_SB_MAGIC);
+#else
+ ret = !strcmp(au_sbtype(sb), "xfs");
+#endif
+#endif
+ return ret;
+}
+
+static inline int au_test_nfs(struct super_block *sb)
+{
+#if defined(CONFIG_NFS_FS) || defined(CONFIG_NFS_FS_MODULE)
+ return (sb->s_magic == NFS_SUPER_MAGIC);
+#else
+ return 0;
+#endif
+}
+
+static inline int au_test_trunc_xino(struct super_block *sb)
+{
+ int ret = 0;
+#ifdef CONFIG_TMPFS
+#ifdef TMPFS_MAGIC
+ BUILD_BUG_ON(TMPFS_MAGIC != 0x01021994);
+ ret = (sb->s_magic == TMPFS_MAGIC);
+#else
+ ret = !strcmp(au_sbtype(sb), "tmpfs");
+#endif
+#endif
+ return ret;
+}
+
+/* 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_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 */
+
+static inline void init_lvma(struct aufs_sbinfo *sbinfo)
+{
+#ifdef CONFIG_AUFS_ROBR
+ spin_lock_init(&sbinfo->si_lvma_lock);
+ INIT_LIST_HEAD(&sbinfo->si_lvma);
+#else
+ /* nothing */
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* limited support before 2.6.18 */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+static inline void au_mntget(struct super_block *sb)
+{
+ mntget(stosi(sb)->si_mnt);
+}
+
+static inline void au_mntput(struct super_block *sb)
+{
+ mntput(stosi(sb)->si_mnt);
+}
+#else
+static inline void au_mntget(struct super_block *sb)
+{
+ /* empty */
+}
+
+static inline void au_mntput(struct super_block *sb)
+{
+ /* empty */
+}
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) */
+
+/* ---------------------------------------------------------------------- */
+
+static inline unsigned int au_flag_test_udba_inotify(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_HINOTIFY
+ return (AuFlag(stosi(sb), f_udba) == AuUdba_INOTIFY);
+#else
+ return 0;
+#endif
+}
+
+static inline int au_need_dlgt(struct super_block *sb)
+{
+#ifdef CONFIG_AUFS_DLGT
+ return (AuFlag(stosi(sb), f_dlgt) && !au_test_wkq(current));
+#else
+ return 0;
+#endif
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* lock superblock. mainly for entry point functions */
+/*
+ * si_noflush_read_lock, si_noflush_write_lock,
+ * si_read_unlock, si_write_unlock, si_downgrade_lock
+ */
+SimpleLockRwsemFuncs(si_noflush, struct super_block *sb, stosi(sb)->si_rwsem);
+SimpleUnlockRwsemFuncs(si, struct super_block *sb, stosi(sb)->si_rwsem);
+
+static inline void si_read_lock(struct super_block *sb, int flags)
+{
+ if (unlikely(flags & AuLock_FLUSH))
+ au_nwt_flush(&stosi(sb)->si_nowait);
+ si_noflush_read_lock(sb);
+}
+
+static inline void si_write_lock(struct super_block *sb)
+{
+ au_nwt_flush(&stosi(sb)->si_nowait);
+ si_noflush_write_lock(sb);
+}
+
+static inline int si_read_trylock(struct super_block *sb, int flags)
+{
+ if (unlikely(flags & AuLock_FLUSH))
+ au_nwt_flush(&stosi(sb)->si_nowait);
+ return si_noflush_read_trylock(sb);
+}
+
+static inline int si_write_trylock(struct super_block *sb)
+{
+ au_nwt_flush(&stosi(sb)->si_nowait);
+ return si_noflush_write_trylock(sb);
+}
+
+/* to debug easier, do not make them inlined functions */
+#define SiMustReadLock(sb) RwMustReadLock(&stosi(sb)->si_rwsem)
+#define SiMustWriteLock(sb) RwMustWriteLock(&stosi(sb)->si_rwsem)
+#define SiMustAnyLock(sb) RwMustAnyLock(&stosi(sb)->si_rwsem)
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_SUPER_H__ */
diff --git a/ubuntu/fs/aufs/sysaufs.c b/ubuntu/fs/aufs/sysaufs.c
new file mode 100644
index 0000000..3878cbe
--- /dev/null
+++ b/ubuntu/fs/aufs/sysaufs.c
@@ -0,0 +1,699 @@
+/*
+ * 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
+ */
+
+/* $Id: sysaufs.c,v 1.24 2008/01/21 04:56:54 sfjro Exp $ */
+
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/sysfs.h>
+#include <linux/version.h>
+#ifdef CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH
+#include <linux/fs.h>
+#include <../fs/sysfs/sysfs.h>
+#endif
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+/* super_blocks list is not exported */
+static DEFINE_MUTEX(aufs_sbilist_mtx);
+static LIST_HEAD(aufs_sbilist);
+
+/* ---------------------------------------------------------------------- */
+
+typedef ssize_t (*rwfunc_t)(struct kobject *kobj, char *buf, loff_t offset,
+ size_t sz, struct sysaufs_args *args);
+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
+ size_t sz, struct sysaufs_args *args);
+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf, loff_t
+ offset, size_t sz, struct sysaufs_args *args);
+
+#define GFuncBody(_index, func) \
+{ \
+ struct sysaufs_args args = { \
+ .index = (_index), \
+ .mtx = &aufs_sbilist_mtx, \
+ .sb = NULL \
+ }; \
+ return func(kobj, buf, offset, sz, &args); \
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+#define GFunc(name, _index, func) \
+static ssize_t name(struct kobject *kobj, struct bin_attribute *attr, \
+ char *buf, loff_t offset, size_t sz) \
+GFuncBody(_index, func)
+#else
+#define GFunc(name, _index, func) \
+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
+GFuncBody(_index, func)
+#endif
+
+#define GFuncs(name, _index) \
+ GFunc(read_##name, _index, sysaufs_read) \
+ GFunc(write_##name, _index, sysaufs_free_write)
+
+static struct super_block *find_sb_lock(struct kobject *kobj)
+{
+ struct super_block *sb;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ MtxMustLock(&aufs_sbilist_mtx);
+
+ sb = NULL;
+ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
+ if (&au_subsys_to_kset(sbinfo->si_sysaufs.subsys).kobj != kobj)
+ continue;
+ sb = sbinfo->si_mnt->mnt_sb;
+ si_read_lock(sb, !AuLock_FLUSH);
+ break;
+ }
+ return sb;
+}
+
+static ssize_t sb_func(struct kobject *kobj, char *buf, loff_t offset,
+ size_t sz, struct sysaufs_args *args, rwfunc_t func)
+{
+ ssize_t err;
+
+ err = -ENOENT;
+ mutex_lock(&aufs_sbilist_mtx);
+ args->sb = find_sb_lock(kobj);
+ if (args->sb) {
+ err = func(kobj, buf, offset, sz, args);
+ si_read_unlock(args->sb);
+ }
+ mutex_unlock(&aufs_sbilist_mtx);
+ return err;
+}
+
+#define SbFuncBody(_index, func) \
+{ \
+ struct sysaufs_args args = { \
+ .index = (_index), \
+ .mtx = NULL \
+ }; \
+ return sb_func(kobj, buf, offset, sz, &args, func); \
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+#define SbFunc(name, _index, func) \
+static ssize_t name(struct kobject *kobj, struct bin_attribute *attr, \
+ char *buf, loff_t offset, size_t sz) \
+SbFuncBody(_index, func)
+#else
+#define SbFunc(name, _index, func) \
+static ssize_t name(struct kobject *kobj, char *buf, loff_t offset, size_t sz) \
+SbFuncBody(_index, func)
+#endif
+
+#define SbFuncs(name, index) \
+ SbFunc(read_##name, index, sysaufs_read) \
+ SbFunc(write_##name, index, sysaufs_free_write)
+
+static decl_subsys(aufs, NULL, NULL);
+enum { Brs, Stat, Config, _Last };
+static struct sysaufs_entry g_array[_Last];
+/*
+ * read_brs, write_brs,
+ * read_stat, write_stat,
+ * read_config, write_config
+ */
+GFuncs(brs, Brs);
+GFuncs(stat, Stat);
+GFuncs(config, Config);
+
+/*
+ * read_xino, write_xino
+ */
+SbFuncs(xino, SysaufsSb_XINO);
+
+#define SetEntry(e, _name, init_size, _ops) \
+ do { \
+ (e)->attr.attr.name = #_name; \
+ (e)->attr.attr.owner = THIS_MODULE; \
+ (e)->attr.attr.mode = S_IRUGO | S_IWUSR; \
+ (e)->attr.read = read_##_name; \
+ (e)->attr.write = write_##_name; \
+ (e)->allocated = init_size; \
+ (e)->err = -1; \
+ (e)->ops = _ops; \
+ } while (0)
+
+#define Priv(e) (e)->attr.private
+#define Allocated(e) (e)->allocated
+#define Len(e) (e)->attr.size
+#define Name(e) attr_name((e)->attr)
+
+/* ---------------------------------------------------------------------- */
+
+static void free_entry(struct sysaufs_entry *e)
+{
+ MtxMustLock(&aufs_sbilist_mtx);
+ AuDebugOn(!Priv(e));
+
+ if (Allocated(e) > 0)
+ kfree(Priv(e));
+ else
+ free_pages((unsigned long)Priv(e), -Allocated(e));
+ Priv(e) = NULL;
+ Len(e) = 0;
+}
+
+static void free_entries(void)
+{
+ static int a[] = { Brs, -1 };
+ int *p = a;
+
+ MtxMustLock(&aufs_sbilist_mtx);
+
+ while (*p >= 0) {
+ if (Priv(g_array + *p))
+ free_entry(g_array + *p);
+ p++;
+ }
+}
+
+static int alloc_entry(struct sysaufs_entry *e)
+{
+ MtxMustLock(&aufs_sbilist_mtx);
+ AuDebugOn(Priv(e));
+
+ if (Allocated(e) > 0)
+ Priv(e) = kmalloc(Allocated(e), GFP_TEMPORARY);
+ else
+ Priv(e) = (void *)__get_free_pages(GFP_TEMPORARY,
+ -Allocated(e));
+ if (Priv(e))
+ return 0;
+ return -ENOMEM;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void unreg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
+ au_subsys_t *parent)
+{
+ int i;
+
+ AuTraceEnter();
+
+ for (i = 0; i < n; i++, a++)
+ if (!a->err) {
+ sysfs_remove_bin_file
+ (&au_subsys_to_kset(*subsys).kobj, &a->attr);
+ if (Priv(a))
+ free_entry(a);
+ }
+
+ subsystem_unregister(subsys);
+ subsys_put(parent);
+}
+
+static int reg(au_subsys_t *subsys, struct sysaufs_entry *a, int n,
+ au_subsys_t *parent)
+{
+ int err, i;
+
+ AuTraceEnter();
+
+ subsys_get(parent);
+ kobj_set_kset_s(&au_subsys_to_kset(*subsys), *parent);
+ err = subsystem_register(subsys);
+ if (unlikely(err))
+ goto out;
+
+ for (i = 0; !err && i < n; i++) {
+ a[i].err = sysfs_create_bin_file
+ (&au_subsys_to_kset(*subsys).kobj, &a[i].attr);
+ err = a[i].err;
+ }
+ if (unlikely(err))
+ unreg(subsys, a, n, parent);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#define SbSetEntry(index, name, init_size) \
+ SetEntry(sa->array + index, name, init_size, au_si_ops);
+
+void sysaufs_add(struct aufs_sbinfo *sbinfo)
+{
+ int err;
+ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
+
+ AuTraceEnter();
+
+ mutex_lock(&aufs_sbilist_mtx);
+ list_add_tail(&sbinfo->si_list, &aufs_sbilist);
+ free_entries();
+
+ memset(sa, 0, sizeof(*sa));
+ SbSetEntry(SysaufsSb_XINO, xino, 128);
+ err = kobject_set_name(&au_subsys_to_kset(sa->subsys).kobj, "%p",
+ sbinfo->si_mnt->mnt_sb);
+ if (!err)
+ err = reg(&sa->subsys, sa->array, SysaufsSb_Last, &aufs_subsys);
+ if (unlikely(err))
+ AuWarn("failed adding sysfs (%d)\n", err);
+
+ mutex_unlock(&aufs_sbilist_mtx);
+}
+
+void sysaufs_del(struct aufs_sbinfo *sbinfo)
+{
+ struct sysaufs_sbinfo *sa = &sbinfo->si_sysaufs;
+
+ AuTraceEnter();
+
+ mutex_lock(&aufs_sbilist_mtx);
+ unreg(&sa->subsys, sa->array, SysaufsSb_Last, &aufs_subsys);
+ list_del(&sbinfo->si_list);
+ free_entries();
+ mutex_unlock(&aufs_sbilist_mtx);
+}
+
+void sysaufs_notify_remount(void)
+{
+ mutex_lock(&aufs_sbilist_mtx);
+ free_entries();
+ mutex_unlock(&aufs_sbilist_mtx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int make_brs(struct seq_file *seq, struct sysaufs_args *args,
+ int *do_size)
+{
+ int err;
+ struct aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ MtxMustLock(&aufs_sbilist_mtx);
+ AuDebugOn(args->index != Brs);
+
+ err = 0;
+ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
+ struct super_block *sb;
+ struct dentry *root;
+ struct vfsmount *mnt;
+
+ sb = sbinfo->si_mnt->mnt_sb;
+ root = sb->s_root;
+ aufs_read_lock(root, !AuLock_IR);
+ mnt = sbinfo->si_mnt;
+ err = seq_escape
+ (seq, mnt->mnt_devname ? mnt->mnt_devname : "none",
+ au_esc_chars);
+ if (!err)
+ err = seq_putc(seq, ' ');
+ if (!err)
+ err = seq_path(seq, mnt, root, au_esc_chars);
+ if (err > 0)
+ err = seq_printf(seq, " %p br:", sb);
+ if (!err)
+ err = au_show_brs(seq, sb);
+ aufs_read_unlock(root, !AuLock_IR);
+ if (!err)
+ err = seq_putc(seq, '\n');
+ else
+ break;
+ }
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int make_config(struct seq_file *seq, struct sysaufs_args *args,
+ int *do_size)
+{
+ int err;
+
+ AuTraceEnter();
+ AuDebugOn(args->index != Config);
+
+#ifdef CONFIG_AUFS
+ err = seq_puts(seq, "CONFIG_AUFS=y\n");
+#else
+ err = seq_puts(seq, "CONFIG_AUFS=m\n");
+#endif
+
+#define puts(m, v) do { \
+ if (!err) \
+ err = seq_puts(seq, "CONFIG_AUFS_" #m "=" #v "\n"); \
+} while (0)
+#define puts_bool(m) puts(m, y)
+#define puts_mod(m) puts(m, m)
+
+#ifdef CONFIG_AUFS_FAKE_DM
+ puts_bool(FAKE_DM);
+#endif
+#ifdef CONFIG_AUFS_BRANCH_MAX_127
+ puts_bool(BRANCH_MAX_127);
+#elif defined(CONFIG_AUFS_BRANCH_MAX_511)
+ puts_bool(BRANCH_MAX_511);
+#elif defined(CONFIG_AUFS_BRANCH_MAX_1023)
+ puts_bool(BRANCH_MAX_1023);
+#elif defined(CONFIG_AUFS_BRANCH_MAX_32767)
+ puts_bool(BRANCH_MAX_32767);
+#endif
+ puts_bool(SYSAUFS);
+#ifdef CONFIG_AUFS_HINOTIFY
+ puts_bool(HINOTIFY);
+#endif
+#ifdef CONFIG_AUFS_EXPORT
+ puts_bool(EXPORT);
+#endif
+#ifdef CONFIG_AUFS_ROBR
+ puts_bool(ROBR);
+#endif
+#ifdef CONFIG_AUFS_DLGT
+ puts_bool(DLGT);
+#endif
+#ifdef CONFIG_AUFS_RR_SQUASHFS
+ puts_bool(RR_SQUASHFS);
+#endif
+#ifdef CONFIG_AUFS_SEC_PERM_PATCH
+ puts_bool(SEC_PERM_PATCH);
+#endif
+#ifdef CONFIG_AUFS_SPLICE_PATCH
+ puts_bool(SPLICE_PATCH);
+#endif
+#ifdef CONFIG_AUFS_PUT_FILP_PATCH
+ puts_bool(PUT_FILP_PATCH);
+#endif
+#ifdef CONFIG_AUFS_LHASH_PATCH
+ puts_bool(LHASH_PATCH);
+#endif
+#ifdef CONFIG_AUFS_DENY_WRITE_ACCESS_PATCH
+ puts_bool(DENY_WRITE_ACCESS_PATCH);
+#endif
+#ifdef CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH
+ puts_bool(SYSFS_GET_DENTRY_PATCH);
+#endif
+#ifdef CONFIG_AUFS_KSIZE_PATCH
+ puts_bool(KSIZE_PATCH);
+#endif
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+ puts_bool(WORKAROUND_FUSE);
+#endif
+#ifdef CONFIG_AUFS_DEBUG
+ puts_bool(DEBUG);
+#endif
+#ifdef CONFIG_AUFS_COMPAT
+ puts_bool(COMPAT);
+#endif
+#ifdef DbgUdbaRace
+ if (!err)
+ err = seq_printf(seq, "DbgUdbaRace=%d\n", DbgUdbaRace);
+#endif
+
+#undef puts_bool
+#undef puts
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int make_stat(struct seq_file *seq, struct sysaufs_args *args,
+ int *do_size)
+{
+ int err, i;
+
+ AuTraceEnter();
+ AuDebugOn(args->index != Stat);
+
+ *do_size = 0;
+ err = seq_puts(seq, "wkq max_busy:");
+ for (i = 0; !err && i < aufs_nwkq; i++)
+ err = seq_printf(seq, " %u", au_wkq[i].max_busy);
+ if (!err)
+ err = seq_printf(seq, ", %u(generic)\n",
+ au_wkq[aufs_nwkq].max_busy);
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static int make(struct sysaufs_entry *e, struct sysaufs_args *args,
+ int *do_size)
+{
+ int err;
+ struct seq_file *seq;
+
+ AuTraceEnter();
+ AuDebugOn(Priv(e));
+ MtxMustLock(&aufs_sbilist_mtx);
+
+ err = -ENOMEM;
+ seq = kzalloc(sizeof(*seq), GFP_TEMPORARY);
+ if (unlikely(!seq))
+ goto out;
+
+ Len(e) = 0;
+ while (1) {
+ err = alloc_entry(e);
+ if (unlikely(err))
+ break;
+
+ //mutex_init(&seq.lock);
+ seq->buf = Priv(e);
+ seq->count = 0;
+ seq->size = Allocated(e);
+ if (unlikely(Allocated(e) <= 0))
+ seq->size = PAGE_SIZE << -Allocated(e);
+
+ err = e->ops[args->index](seq, args, do_size);
+ if (!err) {
+ Len(e) = seq->count;
+ break; /* success */
+ }
+
+ free_entry(e);
+ if (Allocated(e) > 0) {
+ Allocated(e) <<= 1;
+ if (unlikely(Allocated(e) >= (int)PAGE_SIZE))
+ Allocated(e) = 0;
+ } else
+ Allocated(e)--;
+ }
+ kfree(seq);
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+/* why does sysfs pass my parent kobject? */
+static struct dentry *find_me(struct kobject *kobj, struct sysaufs_entry *e)
+{
+#if 1
+ struct dentry *dentry, *parent;
+ const char *name = Name(e);
+ const unsigned int len = strlen(name);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
+ parent = kobj->dentry;
+#elif defined(CONFIG_AUFS_SYSFS_GET_DENTRY_PATCH)
+ parent = sysfs_get_dentry(kobj->sd);
+#else
+ return NULL;
+#endif
+
+ spin_lock(&dcache_lock);
+ list_for_each_entry(dentry, &parent->d_subdirs, D_CHILD) {
+ if (len == dentry->d_name.len
+ && !strcmp(dentry->d_name.name, name)) {
+ spin_unlock(&dcache_lock);
+ return dentry;
+ }
+ }
+ spin_unlock(&dcache_lock);
+#endif
+ return NULL;
+}
+
+static ssize_t sysaufs_read(struct kobject *kobj, char *buf, loff_t offset,
+ size_t sz, struct sysaufs_args *args)
+{
+ ssize_t err;
+ loff_t len;
+ struct dentry *d;
+ struct sysaufs_entry *e;
+ int do_size;
+
+ LKTRTrace("{%d, %p}, offset %Ld, sz %lu\n",
+ args->index, args->sb, offset, (unsigned long)sz);
+
+ if (unlikely(!sz))
+ return 0;
+
+ err = 0;
+ d = NULL;
+ e = g_array + args->index;
+ if (args->sb)
+ e = stosi(args->sb)->si_sysaufs.array + args->index;
+
+ do_size = 1;
+ if (args->mtx)
+ mutex_lock(args->mtx);
+ if (unlikely(!Priv(e))) {
+ err = make(e, args, &do_size);
+ AuDebugOn(Len(e) > INT_MAX);
+ if (do_size) {
+ d = find_me(kobj, e);
+ if (d)
+ i_size_write(d->d_inode, Len(e));
+ }
+ }
+
+ if (!err) {
+ len = Len(e) - offset;
+ err = len;
+ LKTRTrace("%Ld\n", len);
+ if (len > 0) {
+ if (len > sz)
+ err = sz;
+ memcpy(buf, Priv(e) + offset, err);
+ }
+
+ if (!do_size)
+ free_entry(e);
+ }
+ if (args->mtx)
+ mutex_unlock(args->mtx);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static ssize_t sysaufs_free_write(struct kobject *kobj, char *buf,
+ loff_t offset, size_t sz,
+ struct sysaufs_args *args)
+{
+ struct dentry *d;
+ int allocated, len;
+ struct sysaufs_entry *e;
+
+ LKTRTrace("{%d, %p}\n", args->index, args->sb);
+
+ e = g_array + args->index;
+ if (args->sb)
+ e = stosi(args->sb)->si_sysaufs.array + args->index;
+
+ if (args->mtx)
+ mutex_lock(args->mtx);
+ if (Priv(e)) {
+ allocated = Allocated(e);
+ if (unlikely(allocated <= 0))
+ allocated = PAGE_SIZE << -allocated;
+ allocated >>= 1;
+ len = Len(e);
+
+ free_entry(e);
+ if (unlikely(len <= allocated)) {
+ if (Allocated(e) >= 0)
+ Allocated(e) = allocated;
+ else
+ Allocated(e)++;
+ }
+
+ d = find_me(kobj, e);
+ if (d && i_size_read(d->d_inode))
+ i_size_write(d->d_inode, 0);
+ }
+ if (args->mtx)
+ mutex_unlock(args->mtx);
+
+ return sz;
+}
+
+static sysaufs_op g_ops[] = {
+ [Brs] = make_brs,
+ [Stat] = make_stat,
+ [Config] = make_config
+};
+
+/* ---------------------------------------------------------------------- */
+
+#define GSetEntry(index, name, init_size) \
+ SetEntry(g_array + index, name, init_size, g_ops)
+
+int __init sysaufs_init(void)
+{
+ int err;
+
+ GSetEntry(Brs, brs, 128);
+ GSetEntry(Stat, stat, 32);
+ GSetEntry(Config, config, 256);
+ err = reg(&aufs_subsys, g_array, _Last, &fs_subsys);
+ AuTraceErr(err);
+ return err;
+}
+
+void sysaufs_fin(void)
+{
+ mutex_lock(&aufs_sbilist_mtx);
+ unreg(&aufs_subsys, g_array, _Last, &fs_subsys);
+ mutex_unlock(&aufs_sbilist_mtx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+void au_each_sb(each_sb_cb_t cb, int do_lock)
+{
+ struct aufs_sbinfo *sbinfo;
+
+ if (do_lock)
+ mutex_lock(&aufs_sbilist_mtx);
+ list_for_each_entry(sbinfo, &aufs_sbilist, si_list)
+ cb(sbinfo->si_mnt->mnt_sb);
+ if (do_lock)
+ mutex_unlock(&aufs_sbilist_mtx);
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef DbgDlgt
+int au_test_branch(struct super_block *h_sb)
+{
+ int found = 0;
+ struct aufs_sbinfo *sbinfo;
+
+ mutex_lock(&aufs_sbilist_mtx);
+ list_for_each_entry(sbinfo, &aufs_sbilist, si_list) {
+ aufs_bindex_t bindex, bend;
+ struct super_block *sb;
+
+ sb = sbinfo->si_mnt->mnt_sb;
+ si_read_lock(sb);
+ bend = sbend(sb);
+ for (bindex = 0; !found && bindex <= bend; bindex++)
+ found = (h_sb == sbr_sb(sb, bindex));
+ si_read_unlock(sb);
+ }
+ mutex_unlock(&aufs_sbilist_mtx);
+ return found;
+}
+#endif
diff --git a/ubuntu/fs/aufs/sysaufs.h b/ubuntu/fs/aufs/sysaufs.h
new file mode 100644
index 0000000..ea14983
--- /dev/null
+++ b/ubuntu/fs/aufs/sysaufs.h
@@ -0,0 +1,100 @@
+/*
+ * 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
+ */
+
+/* $Id: sysaufs.h,v 1.12 2007/11/26 01:34:33 sfjro Exp $ */
+
+#ifndef __SYSAUFS_H__
+#define __SYSAUFS_H__
+
+#ifdef __KERNEL__
+
+#include <linux/seq_file.h>
+#include <linux/version.h>
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+static inline struct kset *subsys_get(struct kset *s)
+{
+ return kset_get(s);
+}
+
+static inline void subsys_put(struct kset *s)
+{
+ kset_put(s);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/* arguments for an entry under sysfs */
+struct sysaufs_args {
+ int index;
+ struct mutex *mtx; /* template for sysaufs_read */
+ struct super_block *sb;
+};
+
+typedef int (*sysaufs_op)(struct seq_file *seq, struct sysaufs_args *args,
+ int *do_size);
+
+/* an entry under sysfs */
+struct sysaufs_entry {
+ struct bin_attribute attr;
+ int allocated; /* zero and minus means pages, otherwise bytes */
+ int err;
+ sysaufs_op *ops;
+};
+
+/* ---------------------------------------------------------------------- */
+
+struct aufs_sbinfo;
+typedef void (*each_sb_cb_t)(struct super_block *sb);
+
+#ifdef CONFIG_AUFS_SYSAUFS
+void sysaufs_add(struct aufs_sbinfo *sbinfo);
+void sysaufs_del(struct aufs_sbinfo *sbinfo);
+int __init sysaufs_init(void);
+void sysaufs_fin(void);
+void sysaufs_notify_remount(void);
+
+void au_each_sb(each_sb_cb_t cb, int do_lock);
+
+#else
+
+static inline void sysaufs_add(struct aufs_sbinfo *sbinfo)
+{
+ /* nothing */
+}
+
+static inline void sysaufs_del(struct aufs_sbinfo *sbinfo)
+{
+ /* nothing */
+}
+
+#define sysaufs_init() 0
+#define sysaufs_fin() do {} while (0)
+#define sysaufs_notify_remount() do {} while (0)
+
+static inline void au_each_sb(each_sb_cb_t cb, int do_lock)
+{
+ /* empty */
+}
+#endif /* CONFIG_AUFS_SYSAUFS */
+
+#endif /* __KERNEL__ */
+#endif /* __SYSAUFS_H__ */
diff --git a/ubuntu/fs/aufs/vdir.c b/ubuntu/fs/aufs/vdir.c
new file mode 100644
index 0000000..14e3a7a
--- /dev/null
+++ b/ubuntu/fs/aufs/vdir.c
@@ -0,0 +1,821 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: vdir.c,v 1.31 2007/12/10 01:19:07 sfjro Exp $ */
+
+#include "aufs.h"
+
+static int calc_size(int namelen)
+{
+ int sz;
+
+ sz = sizeof(struct aufs_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 {
+#if 0 // remove
+ BUG();
+ /* this block will be discarded by optimizer. */
+ int m;
+ m = sz % sizeof(ino_t);
+ if (m)
+ sz += sizeof(ino_t) - m;
+#endif
+ }
+
+ AuDebugOn(sz % sizeof(ino_t));
+ return sz;
+}
+
+static int set_deblk_end(union aufs_deblk_p *p, union aufs_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 aufs_deblk_p *p, union aufs_deblk_p *deblk_end)
+{
+ if (calc_size(0) <= deblk_end->p - p->p)
+ return !p->de->de_str.len;
+ return 1;
+}
+
+static aufs_deblk_t *last_deblk(struct aufs_vdir *vdir)
+{
+ return vdir->vd_deblk[vdir->vd_nblk - 1];
+}
+
+void nhash_init(struct aufs_nhash *nhash)
+{
+ int i;
+ for (i = 0; i < AuSize_NHASH; i++)
+ INIT_HLIST_HEAD(nhash->heads + i);
+}
+
+struct aufs_nhash *nhash_new(gfp_t gfp)
+{
+ struct aufs_nhash *nhash;
+
+ nhash = kmalloc(sizeof(*nhash), gfp);
+ if (nhash) {
+ nhash_init(nhash);
+ return nhash;
+ }
+ return ERR_PTR(-ENOMEM);
+}
+
+void nhash_del(struct aufs_nhash *nhash)
+{
+ nhash_fin(nhash);
+ kfree(nhash);
+}
+
+void nhash_move(struct aufs_nhash *dst, struct aufs_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 nhash_fin(struct aufs_nhash *whlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct aufs_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 nhash_test_longer_wh(struct aufs_nhash *whlist, aufs_bindex_t btgt,
+ int limit)
+{
+ int n, i;
+ struct hlist_head *head;
+ struct aufs_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;
+}
+
+/* returns found(true) or not */
+int nhash_test_known_wh(struct aufs_nhash *whlist, char *name, int namelen)
+{
+ struct hlist_head *head;
+ struct aufs_wh *tpos;
+ struct hlist_node *pos;
+ struct aufs_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 nhash_append_wh(struct aufs_nhash *whlist, char *name, int namelen,
+ aufs_bindex_t bindex)
+{
+ int err;
+ struct aufs_destr *str;
+ struct aufs_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;
+ 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 aufs_vdir *vdir)
+{
+ aufs_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 aufs_vdir *vdir)
+{
+ int err, sz, i;
+ aufs_deblk_t **o;
+ union aufs_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 aufs_vdir *alloc_vdir(void)
+{
+ struct aufs_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 aufs_vdir *vdir)
+{
+ int err;
+ union aufs_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 aufs_nhash *dehlist)
+{
+ int i;
+ struct hlist_head *head;
+ struct aufs_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 aufs_nhash *delist, char *name, int namelen)
+{
+ struct hlist_head *head;
+ struct aufs_dehstr *tpos;
+ struct hlist_node *pos;
+ struct aufs_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 aufs_vdir *vdir, char *name, int namelen, ino_t ino,
+ unsigned int d_type, struct aufs_nhash *delist)
+{
+ int err, sz;
+ union aufs_deblk_p p, *room, deblk_end;
+ struct aufs_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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+struct fillvdir_arg {
+ struct file *file;
+ struct aufs_vdir *vdir;
+ struct aufs_nhash *delist;
+ struct aufs_nhash *whlist;
+ aufs_bindex_t bindex;
+ int err;
+ int called;
+};
+
+static int fillvdir(void *__arg, const char *__name, int namelen, loff_t offset,
+ au_filldir_ino_t h_ino, unsigned int d_type)
+{
+ struct fillvdir_arg *arg = __arg;
+ char *name = (void *)__name;
+ aufs_bindex_t bindex, bend;
+ struct xino_entry xinoe;
+ struct super_block *sb;
+
+ 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;
+ arg->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)
+ || nhash_test_known_wh(arg->whlist + bindex, name,
+ namelen))
+ goto out; /* already exists or whiteouted */
+
+ arg->err = xino_read(sb, bend, h_ino, &xinoe);
+ if (!arg->err && !xinoe.ino) {
+ //struct inode *h_inode;
+ xinoe.ino = xino_new_ino(sb);
+ if (unlikely(!xinoe.ino))
+ arg->err = -EIO;
+#if 0 // rfu
+ //xinoe.h_gen = AuXino_INVALID_HGEN;
+ h_inode = ilookup(sbr_sb(sb, bend), h_ino);
+ if (h_inode) {
+ if (!is_bad_inode(h_inode)) {
+ xinoe.h_gen = h_inode->i_generation;
+ WARN_ON(xinoe.h_gen
+ == AuXino_INVALID_HGEN);
+ }
+ iput(h_inode);
+ }
+#endif
+ arg->err = xino_write(sb, bend, h_ino, &xinoe);
+ }
+ if (!arg->err)
+ arg->err = append_de
+ (arg->vdir, name, namelen, xinoe.ino, d_type,
+ arg->delist + bend);
+ } else {
+ name += AUFS_WH_PFX_LEN;
+ namelen -= AUFS_WH_PFX_LEN;
+ for (bindex = 0; bindex < bend; bindex++)
+ if (nhash_test_known_wh(arg->whlist + bend, name,
+ namelen))
+ goto out; /* already whiteouted */
+ arg->err = nhash_append_wh(arg->whlist + bend, name, namelen,
+ bend);
+ }
+
+ out:
+ if (!arg->err)
+ arg->vdir->vd_jiffy = jiffies;
+ //smp_mb();
+ AuTraceErr(arg->err);
+ return arg->err;
+}
+
+static int read_vdir(struct file *file, int may_read)
+{
+ int err, do_read, dlgt;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct aufs_vdir *vdir, *allocated;
+ unsigned long expire;
+ struct fillvdir_arg arg;
+ aufs_bindex_t bindex, bend, bstart;
+ 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 = stosi(sb)->si_rdcache;
+ vdir = ivdir(inode);
+#if 0 // debug
+ AuDbg("vdir %p, fvdir %p, i_ver %lu, vd_ver %lu, jiffy %lu,"
+ " vd_jiffy %lu, expire %lu\n",
+ vdir, fvdir_cache(file), inode->i_version,
+ vdir ? vdir->vd_version : 0LU, jiffies,
+ vdir ? vdir->vd_jiffy + expire : 0LU, expire);
+#endif
+ if (!vdir) {
+ AuDebugOn(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 0 // debug
+ AuDbg("do_read %d\n", do_read);
+#endif
+ if (!do_read)
+ return 0; /* success */
+
+ err = -ENOMEM;
+ bend = fbend(file);
+ arg.delist = kmalloc(sizeof(*arg.delist) * (bend + 1), GFP_TEMPORARY);
+ if (unlikely(!arg.delist))
+ goto out_vdir;
+ 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++) {
+ nhash_init(arg.delist + bindex);
+ nhash_init(arg.whlist + bindex);
+ }
+
+ dlgt = au_need_dlgt(sb);
+ arg.file = file;
+ arg.vdir = vdir;
+ bstart = fbstart(file);
+ for (bindex = bstart; !err && bindex <= bend; bindex++) {
+ struct file *hf;
+ loff_t offset;
+
+ hf = au_h_fptr_i(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;
+ arg.called = 0;
+ //smp_mb();
+ err = vfsub_readdir(hf, fillvdir, &arg, dlgt);
+ if (err >= 0)
+ err = arg.err;
+ } while (!err && arg.called);
+ }
+
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ free_dehlist(arg.delist + bindex);
+ nhash_fin(arg.whlist + bindex);
+ }
+ kfree(arg.whlist);
+
+ out_delist:
+ kfree(arg.delist);
+ out_vdir:
+ 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)
+ set_ivdir(inode, allocated);
+ } else if (allocated)
+ au_vdir_free(allocated);
+ //AuDbgVdir(vdir); goto out;
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+static int copy_vdir(struct aufs_vdir *tgt, struct aufs_vdir *src)
+{
+ int err, i, rerr, n;
+
+ AuTraceEnter();
+ AuDebugOn(tgt->vd_nblk != 1);
+ //AuDbgVdir(tgt);
+
+ err = -ENOMEM;
+ if (tgt->vd_nblk < src->vd_nblk) {
+ aufs_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 aufs_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(ivdir(inode)); goto out;
+
+ allocated = NULL;
+ vdir_cache = 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, ivdir(inode));
+ if (!err) {
+ file->f_version = inode->i_version;
+ if (allocated)
+ 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 aufs_vdir *vdir)
+{
+ loff_t offset;
+ union aufs_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 aufs_vdir *vdir_cache;
+ loff_t offset;
+ union aufs_deblk_p p, deblk_end;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ vdir_cache = 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_fill_de(struct file *file, void *dirent, filldir_t filldir)
+{
+ int err, l;
+ struct dentry *dentry;
+ struct aufs_vdir *vdir_cache;
+ struct aufs_de *de;
+ union aufs_deblk_p deblk_end;
+
+ dentry = file->f_dentry;
+ LKTRTrace("%.*s, pos %Ld\n", AuDLNPair(dentry), file->f_pos);
+ vdir_cache = 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 --git a/ubuntu/fs/aufs/vfsub.c b/ubuntu/fs/aufs/vfsub.c
new file mode 100644
index 0000000..e1bd099
--- /dev/null
+++ b/ubuntu/fs/aufs/vfsub.c
@@ -0,0 +1,1337 @@
+/*
+ * 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
+ */
+
+/* $Id: vfsub.c,v 1.26 2008/01/28 05:01:42 sfjro Exp $ */
+// I'm going to slightly mad
+
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_HINOTIFY
+/* 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 aufs_hinode *hinode)
+{
+ struct aufs_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 aufs_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 aufs_hin_ignore *ign;
+
+ n = vargs->nignore;
+ ign = vargs->ignore;
+ while (n-- > 0) {
+ au_hin_unignore(ign->ign_hinode, ign->ign_events);
+ ign++;
+ }
+}
+#endif /* CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+int do_vfsub_create(struct inode *dir, struct dentry *dentry, int mode,
+ struct nameidata *nd)
+{
+ int err;
+ struct dentry *parent;
+ struct vfsmount *mnt;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ err = vfs_create(dir, dentry, mode, nd);
+ }
+ if (!err) {
+ mnt = NULL;
+ if (nd)
+ mnt = nd->mnt;
+ au_update_fuse_h_inode(mnt, parent); /*ignore*/
+ au_update_fuse_h_inode(mnt, dentry); /*ignore*/
+ }
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_symlink(struct inode *dir, struct dentry *dentry,
+ const char *symname, int mode)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("i%lu, %.*s, %s, 0x%x\n",
+ dir->i_ino, AuDLNPair(dentry), symname, mode);
+
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ err = vfs_symlink(dir, dentry, symname, mode);
+ }
+ if (!err) {
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_mknod(struct inode *dir, struct dentry *dentry, int mode,
+ dev_t dev)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ err = vfs_mknod(dir, dentry, mode, dev);
+ }
+ if (!err) {
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("i%lu, %.*s, 0x%x\n", dir->i_ino, AuDLNPair(dentry), mode);
+
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ err = vfs_mkdir(dir, dentry, mode);
+ }
+ if (!err) {
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_link(struct dentry *src_dentry, struct inode *dir,
+ struct dentry *dentry)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("%.*s, i%lu, %.*s\n",
+ AuDLNPair(src_dentry), dir->i_ino, AuDLNPair(dentry));
+
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ 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*/
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, dentry); /*ignore*/
+ }
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_rename(struct inode *src_dir, struct dentry *src_dentry,
+ struct inode *dir, struct dentry *dentry)
+{
+ int err;
+ struct dentry *parent, *src_parent;
+
+ LKTRTrace("i%lu, %.*s, i%lu, %.*s\n",
+ src_dir->i_ino, AuDLNPair(src_dentry),
+ dir->i_ino, AuDLNPair(dentry));
+
+ err = -ENOENT;
+ parent = NULL;
+ src_parent = dget_parent(src_dentry);
+ if (src_parent->d_inode == src_dir) {
+ err = -EIO;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir)
+ err = 0;
+ }
+ if (!err) {
+ IMustLock(dir);
+ IMustLock(src_dir);
+ lockdep_off();
+ err = vfs_rename(src_dir, src_dentry, dir, dentry);
+ lockdep_on();
+ AuTraceErr(err);
+ }
+ if (!err) {
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, src_parent); /*ignore*/
+ au_update_fuse_h_inode(NULL, src_dentry); /*ignore*/
+ }
+ dput(parent);
+ dput(src_parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_rmdir(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+
+ err = -ENOENT;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ lockdep_off();
+ err = vfs_rmdir(dir, dentry);
+ lockdep_on();
+ }
+ if (!err)
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int do_vfsub_unlink(struct inode *dir, struct dentry *dentry)
+{
+ int err;
+ struct dentry *parent;
+
+ LKTRTrace("i%lu, %.*s\n", dir->i_ino, AuDLNPair(dentry));
+
+ err = -ENOENT;
+ parent = dget_parent(dentry);
+ if (parent->d_inode == dir) {
+ IMustLock(dir);
+ /* vfs_unlink() locks inode */
+ lockdep_off();
+ err = vfs_unlink(dir, dentry);
+ lockdep_on();
+ }
+ if (!err)
+ au_update_fuse_h_inode(NULL, parent); /*ignore*/
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#if defined(CONFIG_AUFS_DLGT) || defined(CONFIG_AUFS_HINOTIFY)
+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 (!vargs->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 (!vargs->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 (unlikely(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 (!vargs->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 (!vargs->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 (!vargs->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;
+ }
+}
+#endif /* CONFIG_AUFS_DLGT || CONFIG_AUFS_HINOTIFY */
+
+/* ---------------------------------------------------------------------- */
+
+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_perm(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_perm(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();
+ #ifdef CONFIG_SECURITY_APPARMOR
+ *a->errp = notify_change(a->h_dentry, NULL, a->ia);
+ #else
+ *a->errp = notify_change(a->h_dentry, a->ia);
+ #endif
+ lockdep_on();
+ if (!*a->errp)
+ au_update_fuse_h_inode(NULL, a->h_dentry); /*ignore*/
+ else
+ vfsub_unignore(a->vargs);
+ }
+ AuTraceErr(*a->errp);
+}
+
+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
+ };
+
+#ifndef CONFIG_AUFS_DLGT
+ call_notify_change(&args);
+#else
+ if (!vargs->dlgt)
+ call_notify_change(&args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_notify_change, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+#endif
+
+ 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);
+#if 0 // partial testing
+ {
+ struct qstr *name = &a->dentry->d_name;
+ if (name->len == sizeof(AUFS_XINO_FNAME) - 1
+ && !strncmp(name->name, AUFS_XINO_FNAME, name->len))
+ *a->errp = do_vfsub_unlink(a->dir, a->dentry);
+ else
+ err = -1;
+ }
+#else
+ *a->errp = do_vfsub_unlink(a->dir, a->dentry);
+#endif
+
+ 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 (!vargs->dlgt && !vargs->force_unlink)
+ call_unlink(&args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_unlink, &args, vargs->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);
+}
+
+int vfsub_statfs(void *arg, struct kstatfs *buf, int dlgt)
+{
+ int err;
+ struct statfs_args args = {
+ .errp = &err,
+ .arg = arg,
+ .buf = buf
+ };
+
+#ifndef CONFIG_AUFS_DLGT
+ call_statfs(&args);
+#else
+ if (!dlgt)
+ call_statfs(&args);
+ else {
+ int wkq_err;
+ wkq_err = au_wkq_wait(call_statfs, &args, /*dlgt*/1);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ }
+#endif
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#ifdef CONFIG_AUFS_WORKAROUND_FUSE
+/* 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;
+}
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) \
+ && (defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE))
+/* 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;
+}
+#endif /* xfs rdev */
+
+#if 0 // temp
+/*
+ * This function was born after a discussion with the FUSE developer.
+ * The inode attributes on a filesystem who defines i_op->getattr()
+ * is unreliable since such fs may not maintain the attributes at lookup.
+ * This function doesn't want the result of stat, instead wants the side-effect
+ * which refreshes the attributes.
+ * Hmm, there seems to be no such filesystem except fuse.
+ */
+int vfsub_i_attr(struct vfsmount *mnt, struct dentry *dentry, int dlgt)
+{
+ int err;
+ struct inode *inode;
+ struct inode_operations *op;
+ struct kstat st;
+
+ inode = dentry->d_inode;
+ AuDebugOn(!inode);
+
+ err = 0;
+ op = inode->i_op;
+ if (unlikely(op && op->getattr && !au_test_aufs(dentry->d_sb))) {
+ err = security_inode_getattr(mnt, dentry);
+ if (!err)
+ err = op->getattr(mnt, dentry, &st);
+ }
+ AuTraceErr(err);
+ return err;
+}
+#endif
diff --git a/ubuntu/fs/aufs/vfsub.h b/ubuntu/fs/aufs/vfsub.h
new file mode 100644
index 0000000..77cac6c
--- /dev/null
+++ b/ubuntu/fs/aufs/vfsub.h
@@ -0,0 +1,903 @@
+/*
+ * 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
+ */
+
+/* $Id: vfsub.h,v 1.28 2008/01/28 05:01:42 sfjro Exp $ */
+
+#ifndef __AUFS_VFSUB_H__
+#define __AUFS_VFSUB_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/version.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23) \
+ && defined(CONFIG_AUFS_SPLICE_PATCH)
+#include <linux/splice.h>
+#endif
+#ifdef CONFIG_AUFS_HINOTIFY
+#include <linux/inotify.h>
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+struct aufs_hin_ignore;
+struct vfsub_args {
+#ifdef CONFIG_AUFS_HINOTIFY
+ /* inotify events to be ignored */
+ int nignore;
+ struct aufs_hin_ignore *ignore;
+#endif
+
+ /* operation with delegation */
+ unsigned int dlgt:1;
+
+ /* force unlinking */
+ unsigned int force_unlink:1;
+};
+
+struct aufs_hinode;
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline
+void do_vfsub_args_reinit(struct vfsub_args *vargs, struct aufs_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 aufs_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 aufs_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 aufs_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 */
+
+static inline
+void vfsub_args_init(struct vfsub_args *vargs, struct aufs_hin_ignore *ign,
+ int dlgt, int force_unlink)
+{
+ do_vfsub_args_reinit(vargs, ign);
+ vargs->dlgt = !!dlgt;
+ vargs->force_unlink = !!force_unlink;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/* lock subclass for hidden inode */
+/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */
+// todo: reduce it
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#define I_MUTEX_QUOTA 0
+#endif
+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
+};
+
+/* simple abstraction */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
+static inline void vfsub_i_lock(struct inode *i)
+{
+ down(&i->i_sem);
+}
+
+static inline void vfsub_i_lock_nested(struct inode *i, unsigned lsc)
+{
+ vfsub_i_lock(i);
+}
+
+static inline void vfsub_i_unlock(struct inode *i)
+{
+ up(&i->i_sem);
+}
+
+static inline int vfsub_i_trylock(struct inode *i)
+{
+ return down_trylock(&i->i_sem);
+}
+
+#define IMustLock(i) AuDebugOn(!down_trylock(&(i)->i_sem))
+#else
+static inline void vfsub_i_lock(struct inode *i)
+{
+ mutex_lock(&i->i_mutex);
+}
+
+static inline void vfsub_i_lock_nested(struct inode *i, unsigned lsc)
+{
+ mutex_lock_nested(&i->i_mutex, lsc);
+}
+
+static inline void vfsub_i_unlock(struct inode *i)
+{
+ mutex_unlock(&i->i_mutex);
+}
+
+static inline int vfsub_i_trylock(struct inode *i)
+{
+ return mutex_trylock(&i->i_mutex);
+}
+
+#define IMustLock(i) MtxMustLock(&(i)->i_mutex)
+#endif /* LINUX_VERSION_CODE */
+
+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
+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
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) \
+ && (defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE))
+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);
+}
+
+static inline
+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;
+}
+
+static inline
+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->mnt, nd->dentry); /*ignore*/
+ return err;
+}
+
+static inline
+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
+static inline
+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);
+ 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
+
+/* because of the internal lock, never use vfs_path_lookup() */
+#if 0 //LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
+static inline
+int vfsub_vfs_path_lookup(struct dentry *parent, struct vfsmount *mnt,
+ char *name, unsigned int flags, struct nameidata *nd)
+{
+ int err;
+
+ LKTRTrace("%.*s/%s, 0x%x, nd{}\n", AuDLNPair(parent), name, flags);
+
+ /* vfs_path_lookup() will lock the parent inode */
+ lockdep_off();
+ err = vfs_path_lookup(parent, mnt, name, flags, nd);
+ lockdep_on();
+ if (!err)
+ au_update_fuse_h_inode(mnt, nd->dentry); /*ignore*/
+
+ AuTraceErr(err);
+ return err;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+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);
+#else
+static inline
+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->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;
+}
+
+static inline
+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);
+
+#ifdef CONFIG_VSERVER
+ err = vfs_symlink(dir, dentry, symname, mode, NULL);
+#elif defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_symlink(dir, dentry, NULL, symname, mode);
+#else
+ err = vfs_symlink(dir, dentry, symname, mode);
+#endif
+ 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;
+}
+
+static inline
+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);
+
+#ifdef CONFIG_VSERVER
+ err = vfs_mknod(dir, dentry, mode, dev, NULL);
+#elif defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_mknod(dir, dentry, NULL, mode, dev);
+#else
+ err = vfs_mknod(dir, dentry, mode, dev);
+#endif
+ 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;
+}
+
+static inline
+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();
+#ifdef CONFIG_VSERVER
+ err = vfs_link(src_dentry, dir, dentry, NULL);
+#elif defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_link(src_dentry, NULL, dir, dentry, NULL);
+#else
+ err = vfs_link(src_dentry, dir, dentry);
+#endif
+ 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;
+}
+
+static inline
+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();
+ #ifdef CONFIG_SECURITY_APPARMOR
+ err = vfs_rename(src_dir, src_dentry, NULL, dir, dentry, NULL);
+ #else
+ err = vfs_rename(src_dir, src_dentry, dir, dentry);
+ #endif
+ 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;
+}
+
+static inline
+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);
+
+#ifdef CONFIG_VSERVER
+ err = vfs_mkdir(dir, dentry, mode, NULL);
+#elif defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_mkdir(dir, dentry, NULL, mode);
+#else
+ err = vfs_mkdir(dir, dentry, mode);
+#endif
+ 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;
+}
+
+static inline 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();
+#if defined(CONFIG_VSERVER) || defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_rmdir(dir, dentry, NULL);
+#else
+ err = vfs_rmdir(dir, dentry);
+#endif
+ lockdep_on();
+ /* dir inode is locked */
+ if (!err)
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ return err;
+}
+
+static inline 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();
+#if defined(CONFIG_VSERVER) || defined(CONFIG_SECURITY_APPARMOR)
+ err = vfs_unlink(dir, dentry, NULL);
+#else
+ err = vfs_unlink(dir, dentry);
+#endif
+ lockdep_on();
+ /* dir inode is locked */
+ if (!err)
+ au_update_fuse_h_inode(NULL, dentry->d_parent); /*ignore*/
+ return err;
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+static inline
+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() ??
+static inline
+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;
+}
+
+static inline
+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;
+}
+
+static inline
+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;
+}
+
+static inline
+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
+static inline
+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 = do_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;
+}
+
+static inline
+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 = do_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;
+}
+#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_DLGT) || defined(CONFIG_AUFS_HINOTIFY)
+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 --git a/ubuntu/fs/aufs/wbr_policy.c b/ubuntu/fs/aufs/wbr_policy.c
new file mode 100644
index 0000000..7c495fc
--- /dev/null
+++ b/ubuntu/fs/aufs/wbr_policy.c
@@ -0,0 +1,732 @@
+/*
+ * 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
+ */
+
+/* $Id: wbr_policy.c,v 1.5 2007/11/26 01:34:50 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);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
+ /* 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);
+ }
+#endif
+ 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:1;
+};
+
+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;
+ struct dentry *h_dentry, *opq_dentry, *wh_dentry;
+ struct inode *h_dir, *h_inode, *inode;
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bdst);
+ AuDebugOn(dbstart(dentry) <= bdst
+ && bdst <= dbend(dentry)
+ && au_h_dptr_i(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_i(dentry, bdst);
+ dlgt = au_need_dlgt(dentry->d_sb);
+ err = vfsub_sio_mkdir(h_dir, h_dentry, 0755, dlgt);
+ if (unlikely(err))
+ goto out_put;
+
+ made_dir = 1;
+ bend = dbend(dentry);
+ bopq = dbdiropq(dentry);
+ whed = (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;
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ if (whed || (parent_opq && do_opq)) {
+ opq_dentry = create_diropq(dentry, bdst, dlgt);
+ err = PTR_ERR(opq_dentry);
+ if (IS_ERR(opq_dentry)) {
+ vfsub_i_unlock(h_inode);
+ goto out_dir;
+ }
+ dput(opq_dentry);
+ diropq = 1;
+ }
+
+ err = au_cpdown_attr(h_dentry, au_h_dptr(dentry), dlgt);
+ vfsub_i_unlock(h_inode);
+ if (unlikely(err))
+ goto out_opq;
+
+ wh_dentry = NULL;
+ if (whed) {
+ wh_dentry = lkup_wh(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_unlink_wh_dentry(h_dir, wh_dentry, dentry,
+ NULL, dlgt);
+ dput(wh_dentry);
+ if (unlikely(err))
+ goto out_opq;
+ }
+
+ inode = dentry->d_inode;
+ if (ibend(inode) < bdst)
+ set_ibend(inode, bdst);
+ set_h_iptr(inode, bdst, igrab(h_inode), au_hi_flags(inode, 1));
+ goto out; /* success */
+
+ /* revert */
+ out_opq:
+ if (diropq) {
+ vfsub_i_lock_nested(h_inode, AuLsc_I_CHILD);
+ rerr = remove_diropq(dentry, bdst, dlgt);
+ vfsub_i_unlock(h_inode);
+ 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:
+ set_h_dptr(dentry, bdst, NULL);
+ if (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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if 0
+/*
+ * returns writable branch index, otherwise an error.
+ * todo: customizable writable-branch-policy
+ */
+static int find_rw_parent(struct dentry *dentry, aufs_bindex_t bend)
+{
+ int err;
+ aufs_bindex_t bindex, candidate;
+ struct super_block *sb;
+ struct dentry *parent, *hidden_parent;
+
+ err = bend;
+ sb = dentry->d_sb;
+ parent = dget_parent(dentry);
+#if 1 // branch policy
+ hidden_parent = au_h_dptr_i(parent, bend);
+ if (hidden_parent && !br_rdonly(stobr(sb, bend)))
+ goto out; /* success */
+#endif
+
+ candidate = -1;
+ for (bindex = dbstart(parent); bindex <= bend; bindex++) {
+ hidden_parent = au_h_dptr_i(parent, bindex);
+ if (hidden_parent && !br_rdonly(stobr(sb, bindex))) {
+#if 0 // branch policy
+ if (candidate == -1)
+ candidate = bindex;
+ if (!au_test_perm(hidden_parent->d_inode, MAY_WRITE))
+ return bindex;
+#endif
+ err = bindex;
+ goto out; /* success */
+ }
+ }
+#if 0 // branch policy
+ err = candidate;
+ if (candidate != -1)
+ goto out; /* success */
+#endif
+ err = -EROFS;
+
+ out:
+ dput(parent);
+ return err;
+}
+
+int find_rw_br(struct super_block *sb, aufs_bindex_t bend)
+{
+ aufs_bindex_t bindex;
+
+ for (bindex = bend; bindex >= 0; bindex--)
+ if (!br_rdonly(stobr(sb, bindex)))
+ return bindex;
+ return -EROFS;
+}
+
+int find_rw_parent_br(struct dentry *dentry, aufs_bindex_t bend)
+{
+ int err;
+
+ err = find_rw_parent(dentry, bend);
+ if (err >= 0)
+ return err;
+ return find_rw_br(dentry->d_sb, bend);
+}
+
+#if 0 // branch policy
+/*
+ * dir_cpdown/nodir_cpdown(def)
+ * wr_br_policy=dir | branch
+ */
+int au_rw(struct dentry *dentry, aufs_bindex_t bend)
+{
+ int err;
+ struct super_block *sb;
+
+ sb = dentry->d_sb;
+ SiMustAnyLock(sb);
+
+ if (!au_flag_test(sb, AuFlag_DIR_CPDOWN)) {
+ dpages;
+ }
+}
+#endif
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/* policies for create */
+
+static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex)
+{
+ for (; bindex >= 0; bindex--)
+ if (!br_rdonly(stobr(sb, bindex)))
+ return bindex;
+ return -EROFS;
+}
+
+/* top down parent */
+static int au_wbr_create_tdp(struct dentry *dentry, int isdir)
+{
+ int err;
+ struct super_block *sb;
+ aufs_bindex_t bstart, bindex;
+ struct dentry *parent, *h_parent;
+
+ LKTRTrace("%.*s, dir %d\n", AuDLNPair(dentry), isdir);
+
+ sb = dentry->d_sb;
+ bstart = dbstart(dentry);
+ err = bstart;
+ if (!br_rdonly(stobr(sb, bstart)))
+ goto out;
+
+ err = -EROFS;
+ parent = dget_parent(dentry);
+ for (bindex = dbstart(parent); bindex < bstart; bindex++) {
+ h_parent = au_h_dptr_i(parent, bindex);
+ if (h_parent && !br_rdonly(stobr(sb, bindex))) {
+ 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 = dbwh(dentry);
+ parent = dget_parent(dentry);
+ bdiropq = 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 && br_rdonly(stobr(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, sbend(sb));
+ atomic_set(&stosi(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 = &stosi(sb)->si_wbr_rr_next;
+ bend = 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 (!br_rdonly(stobr(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 aufs_branch *br, struct super_block *sb,
+ aufs_bindex_t bindex)
+{
+ struct super_block *h_sb;
+
+ h_sb = br->br_mnt->mnt_sb;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+ return h_sb;
+#else
+ if (!au_test_nfs(h_sb))
+ return h_sb->s_root;
+
+ /* sigh,,, why nfs s_root has wrong inode? */
+ return dtodi(sb->s_root)->di_hdentry[0 + bindex].hd_dentry;
+#endif
+}
+
+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 aufs_branch *br;
+ struct au_wbr_mfs *mfs;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ bavail = 0;
+ sb = dentry->d_sb;
+ mfs = &stosi(sb)->si_wbr_mfs;
+ mfs->mfs_bindex = -EROFS;
+ mfs->mfsrr_bytes = 0;
+ dlgt = au_need_dlgt(sb);
+ bend = sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = stobr(sb, bindex);
+ if (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 = &stosi(sb)->si_wbr_mfs;
+ mutex_lock(&mfs->mfs_lock);
+ if (unlikely(time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire)
+ || mfs->mfs_bindex < 0
+ || br_rdonly(stobr(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 = &stosi(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(&stosi(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 = &stosi(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;
+ struct super_block *sb;
+ struct dentry *parent;
+ aufs_bindex_t bindex, bstart, bend;
+ struct aufs_branch *br;
+ u64 b;
+
+ //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 = dbstart(parent);
+ bend = dbtaildir(parent);
+ if (bstart == bend)
+ goto out_parent; /* success */
+
+ e2 = au_wbr_create_mfs(dentry, isdir);
+ if (unlikely(e2 < 0))
+ goto out_parent; /* success */
+
+ /* when the available size is equal, select upper one */
+ sb = dentry->d_sb;
+ br = stobr(sb, err);
+ b = br->br_bytes;
+ LKTRTrace("b%d, %Lu\n", err, b);
+ for (bindex = bstart; bindex <= bend; bindex++) {
+ if (!au_h_dptr_i(parent, bindex))
+ continue;
+ br = stobr(sb, bindex);
+ if (!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;
+ struct dentry *parent, *h_parent;
+ aufs_bindex_t bindex, bstart;
+ struct super_block *sb;
+
+ LKTRTrace("%.*s\n", AuDLNPair(dentry));
+
+ err = -EROFS;
+ sb = dentry->d_sb;
+ parent = dget_parent(dentry);
+ bstart = dbstart(parent);
+ for (bindex = dbstart(dentry); bindex >= bstart; bindex--) {
+ h_parent = au_h_dptr_i(parent, bindex);
+ if (h_parent && !br_rdonly(stobr(sb, bindex))) {
+ 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, 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 --git a/ubuntu/fs/aufs/whout.c b/ubuntu/fs/aufs/whout.c
new file mode 100644
index 0000000..78aa21e
--- /dev/null
+++ b/ubuntu/fs/aufs/whout.c
@@ -0,0 +1,1041 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: whout.c,v 1.30 2008/01/28 05:01:42 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_free_whname().
+ */
+int au_alloc_whname(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_free_whname(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_test_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
+ struct aufs_ndx *ndx)
+{
+ int err;
+ struct dentry *wh_dentry;
+ struct inode *h_dir;
+ unsigned int flags;
+
+ LKTRTrace("%.*s/%.*s, ndx{%p, %d}\n", AuDLNPair(h_parent),
+ wh_name->len, wh_name->name, ndx->nfsmnt, ndx->dlgt);
+ 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_test_diropq(struct dentry *h_dentry, struct aufs_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_perm(h_dir, MAY_EXEC, ndx->dlgt);
+ err = au_test_wh(h_dentry, &diropq_name, try_sio, ndx);
+ AuTraceErr(err);
+ return err;
+}
+
+/*
+ * returns a negative dentry whose name is unique and temporary.
+ */
+struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
+ struct aufs_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 rename_whtmp(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex,
+ int noself)
+{
+ int err;
+ struct inode *h_dir;
+ struct dentry *h_dentry, *h_parent, *tmp_dentry;
+ struct super_block *sb;
+ struct aufs_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct aufs_ndx ndx = {
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s, b%d\n", AuDLNPair(dentry), bindex);
+ h_dentry = au_h_dptr_i(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;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ ndx.dlgt = au_need_dlgt(sb);
+ tmp_dentry = lkup_whtmp(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, ndx.dlgt, 0);
+ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
+ if (unlikely(au_flag_test_udba_inotify(sb)
+ && !noself))
+ vfsub_ign_hinode(&vargs, IN_MOVE_SELF,
+ itohi(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_unlink_wh_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 && 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)
+ set_dbwh(dentry, -1);
+
+ AuTraceErr(err);
+ return err;
+}
+
+static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,
+ struct inode *dir, struct aufs_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_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, ndx->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 init_wh(struct dentry *h_root, struct aufs_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 aufs_ndx ndx = {
+ .nfsmnt = nfsmnt,
+ .dlgt = 0, /* always no dlgt */
+ .nd = NULL,
+ //.br = NULL
+ };
+ const int do_plink = AuFlag(stosi(sb), f_plink);
+
+ LKTRTrace("nfsmnt %p\n", nfsmnt);
+ BrWhMustWriteLock(br);
+ SiMustWriteLock(sb);
+ h_dir = h_root->d_inode;
+
+ /* doubly whiteouted */
+ wh = lkup_wh(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 = lkup_wh(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 aufs_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;
+ /* aufs big lock */
+ si_write_lock(a->sb);
+ if (unlikely(!br_writable(a->br->br_perm)))
+ goto out;
+ bindex = find_brindex(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);
+ hdir_lock(h_dir, dir, bindex);
+#if 0
+ revalidate h_wh
+#endif
+ 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 = init_wh(h_root, a->br, au_do_nfsmnt(a->br->br_mnt),
+ a->sb);
+ br_wh_write_unlock(a->br);
+ hdir_unlock(h_dir, dir, bindex);
+ dput(h_root);
+
+ out:
+ atomic_dec_return(&a->br->br_wh_running);
+ 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 aufs_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 br_put()
+ * in reinit function
+ */
+ arg->sb = sb;
+ arg->br = br;
+ 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);
+ br_put(br);
+ kfree(arg);
+ }
+ do_dec = 0;
+ }
+
+ out:
+ if (do_dec)
+ atomic_dec_return(&br->br_wh_running);
+}
+
+/*
+ * create the whiteoute @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 aufs_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_need_dlgt(sb);
+ br = stobr(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,
+ struct au_diropq_flags *flags)
+{
+ struct dentry *opq_dentry, *h_dentry;
+ struct inode *h_dir;
+ int err;
+ struct super_block *sb;
+ struct aufs_ndx ndx = {
+ .nd = NULL,
+ //.br = NULL
+ };
+
+ LKTRTrace("%.*s, bindex %d, do_create %d\n", AuDLNPair(dentry),
+ bindex, flags->create);
+ h_dentry = au_h_dptr_i(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_perm(). */
+ sb = dentry->d_sb;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ ndx.dlgt = 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 (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) {
+ 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,
+ ndx.dlgt);
+ //if (LktrCond) err = -1;
+ if (!err)
+ 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;
+ struct au_diropq_flags *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 *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
+ struct au_diropq_flags *flags)
+{
+ struct dentry *diropq, *h_dentry;
+
+ LKTRTrace("%.*s, bindex %d, do_create %d\n",
+ AuDLNPair(dentry), bindex, flags->create);
+
+ h_dentry = au_h_dptr_i(dentry, bindex);
+ if (!au_test_perm(h_dentry->d_inode, MAY_EXEC | MAY_WRITE, 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 *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
+ struct aufs_ndx *ndx)
+{
+ int err;
+ struct qstr wh_name;
+ struct dentry *wh_dentry;
+
+ LKTRTrace("%.*s/%.*s\n", AuDLNPair(h_parent), AuLNPair(base_name));
+
+ err = au_alloc_whname(base_name->name, base_name->len, &wh_name);
+ //if (LktrCond) {au_free_whname(&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_free_whname(&wh_name);
+ }
+ AuTraceErrPtr(wh_dentry);
+ return wh_dentry;
+}
+
+/*
+ * link/create a whiteout for @dentry on @bindex.
+ */
+struct dentry *simple_create_wh(struct inode *dir, struct dentry *dentry,
+ aufs_bindex_t bindex, struct dentry *h_parent,
+ struct aufs_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 = lkup_wh(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)
+ 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 aufs_nhash *whlist, struct dentry *h_parent,
+ aufs_bindex_t bindex, struct inode *inode,
+ struct aufs_ndx *ndx)
+{
+ int err, i;
+ struct qstr wh_name;
+ char *p;
+ struct inode *h_dir;
+ struct hlist_head *head;
+ struct aufs_wh *tpos;
+ struct hlist_node *pos;
+ struct aufs_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_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 aufs_nhash *whlist;
+ struct dentry *h_parent;
+ aufs_bindex_t bindex;
+ struct inode *inode;
+ struct aufs_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 rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir, struct inode *inode,
+ int noself)
+{
+ int err;
+ struct inode *h_inode, *h_dir;
+ struct super_block *sb;
+ struct aufs_hin_ignore ign;
+ struct vfsub_args vargs;
+ struct aufs_ndx ndx = {
+ .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;
+ ndx.nfsmnt = au_nfsmnt(sb, bindex);
+ ndx.dlgt = au_need_dlgt(sb);
+ h_inode = h_dentry->d_inode;
+ AuDebugOn(h_inode != au_h_iptr_i(inode, bindex));
+ 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_perm(h_inode, MAY_EXEC | MAY_WRITE, ndx.dlgt))
+ err = del_wh_children(whlist, h_dentry, bindex, inode, &ndx);
+ else {
+ int wkq_err;
+ /* ugly */
+ int dlgt = ndx.dlgt;
+ struct del_wh_children_args args = {
+ .errp = &err,
+ .whlist = whlist,
+ .h_parent = h_dentry,
+ .bindex = bindex,
+ .inode = inode,
+ .ndx = &ndx
+ };
+
+ ndx.dlgt = 0;
+ wkq_err = au_wkq_wait(call_del_wh_children, &args, /*dlgt*/0);
+ if (unlikely(wkq_err))
+ err = wkq_err;
+ ndx.dlgt = dlgt;
+ }
+ hdir_unlock(h_inode, inode, bindex);
+
+ if (!err) {
+ vfsub_args_init(&vargs, &ign, ndx.dlgt, 0);
+ if (unlikely(au_flag_test_udba_inotify(sb)
+ && !noself))
+ vfsub_ign_hinode(&vargs, IN_DELETE_SELF,
+ itohi(inode, bindex));
+ err = vfsub_rmdir(h_dir, h_dentry, &vargs);
+ //d_drop(h_dentry);
+ //if (LktrCond) err = -1;
+ }
+
+ if (!err) {
+ if (ibstart(dir) == bindex) {
+ au_cpup_attr_timesizes(dir);
+ //au_cpup_attr_nlink(dir);
+ dir->i_nlink--;
+ }
+ return 0; /* success */
+ }
+
+ AuWarn("failed removing %.*s(%d), ignored\n", AuDLNPair(h_dentry), err);
+ return err;
+}
+
+static void rmdir_whtmp_free_args(struct rmdir_whtmp_args *args)
+{
+ dput(args->h_dentry);
+ nhash_fin(&args->whlist);
+ iput(args->inode);
+ vfsub_i_unlock(args->dir);
+ iput(args->dir);
+ kfree(args);
+}
+
+static void do_rmdir_whtmp(void *args)
+{
+ int err;
+ struct rmdir_whtmp_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);
+
+ vfsub_i_lock(a->dir);
+ sb = a->dir->i_sb;
+ //AuDbgSleep(3);
+ si_read_lock(sb, !AuLock_FLUSH);
+ 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);
+ hdir_lock(h_dir, a->dir, a->bindex);
+#if 0
+ revalidate h_dentry
+#endif
+ err = rmdir_whtmp(a->h_dentry, &a->whlist, a->bindex,
+ a->dir, a->inode, a->noself);
+ 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);
+ rmdir_whtmp_free_args(a);
+ if (unlikely(err))
+ AuIOErr("err %d\n", err);
+}
+
+void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir,
+ struct inode *inode, int noself,
+ struct rmdir_whtmp_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);
+ nhash_init(&args->whlist);
+ 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);
+ rmdir_whtmp_free_args(args);
+ }
+}
diff --git a/ubuntu/fs/aufs/whout.h b/ubuntu/fs/aufs/whout.h
new file mode 100644
index 0000000..55af667
--- /dev/null
+++ b/ubuntu/fs/aufs/whout.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: whout.h,v 1.13 2007/10/01 04:48:51 sfjro Exp $ */
+
+#ifndef __AUFS_WHOUT_H__
+#define __AUFS_WHOUT_H__
+
+#ifdef __KERNEL__
+
+#include <linux/fs.h>
+#include <linux/aufs_type.h>
+
+int au_alloc_whname(const char *name, int len, struct qstr *wh);
+void au_free_whname(struct qstr *wh);
+
+struct aufs_ndx;
+int au_test_wh(struct dentry *h_parent, struct qstr *wh_name, int try_sio,
+ struct aufs_ndx *ndx);
+int au_test_diropq(struct dentry *h_dentry, struct aufs_ndx *ndx);
+
+struct dentry *lkup_whtmp(struct dentry *h_parent, struct qstr *prefix,
+ struct aufs_ndx *ndx);
+int rename_whtmp(struct inode *dir, struct dentry *dentry, aufs_bindex_t bindex,
+ int noself);
+int au_unlink_wh_dentry(struct inode *h_dir, struct dentry *wh_dentry,
+ struct dentry *dentry, struct inode *dir, int dlgt);
+
+struct aufs_branch;
+int init_wh(struct dentry *h_parent, struct aufs_branch *br,
+ struct vfsmount *nfsmnt, struct super_block *sb);
+
+struct au_diropq_flags {
+ unsigned int create:1;
+ unsigned int dlgt:1;
+};
+struct dentry *sio_diropq(struct dentry *dentry, aufs_bindex_t bindex,
+ struct au_diropq_flags *flags);
+
+struct dentry *lkup_wh(struct dentry *h_parent, struct qstr *base_name,
+ struct aufs_ndx *ndx);
+struct dentry *simple_create_wh(struct inode *dir, struct dentry *dentry,
+ aufs_bindex_t bindex, struct dentry *h_parent,
+ struct aufs_ndx *ndx);
+
+/* real rmdir the whiteout-ed dir */
+struct rmdir_whtmp_args {
+ struct dentry *h_dentry;
+ struct aufs_nhash whlist;
+ aufs_bindex_t bindex;
+ struct inode *dir, *inode;
+ int noself;
+};
+
+struct aufs_nhash;
+int rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir, struct inode *inode,
+ int noself);
+void kick_rmdir_whtmp(struct dentry *h_dentry, struct aufs_nhash *whlist,
+ aufs_bindex_t bindex, struct inode *dir,
+ struct inode *inode, int noself,
+ struct rmdir_whtmp_args *args);
+
+/* ---------------------------------------------------------------------- */
+
+static inline
+struct dentry *create_diropq(struct dentry *dentry, aufs_bindex_t bindex,
+ int dlgt)
+{
+ struct au_diropq_flags flags = {
+ .create = 1,
+ .dlgt = !!dlgt
+ };
+ return sio_diropq(dentry, bindex, &flags);
+}
+
+static inline
+int remove_diropq(struct dentry *dentry, aufs_bindex_t bindex, int dlgt)
+{
+ struct au_diropq_flags flags = {
+ .create = 0,
+ .dlgt = !!dlgt
+ };
+ return PTR_ERR(sio_diropq(dentry, bindex, &flags));
+}
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_WHOUT_H__ */
diff --git a/ubuntu/fs/aufs/wkq.c b/ubuntu/fs/aufs/wkq.c
new file mode 100644
index 0000000..1da6d48
--- /dev/null
+++ b/ubuntu/fs/aufs/wkq.c
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: wkq.c,v 1.25 2007/10/22 02:14:17 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 super_block *sb;
+
+ unsigned int wait:1;
+ unsigned int dlgt:1;
+ 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);
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+static void update_busy(struct au_wkq *wkq, struct au_wkinfo *wkinfo)
+{
+#ifdef CONFIG_AUFS_SYSAUFS
+ 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 (wkinfo->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 (wkinfo->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 AuWkqFunc(wkq_func, wk)
+{
+ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk);
+ struct aufs_sbinfo *sbinfo;
+
+ LKTRTrace("wkinfo{%u, %u, %p, %p, %p}\n",
+ wkinfo->wait, wkinfo->dlgt, wkinfo->func, wkinfo->busyp,
+ wkinfo->comp);
+#ifdef CONFIG_AUFS_DLGT
+ if (!wkinfo->dlgt)
+ wkinfo->func(wkinfo->args);
+ else {
+ struct au_cred cred;
+ cred_switch(&cred, &wkinfo->cred);
+ wkinfo->func(wkinfo->args);
+ cred_revert(&cred);
+ }
+#else
+ wkinfo->func(wkinfo->args);
+#endif
+ atomic_dec_return(wkinfo->busyp);
+ if (wkinfo->wait)
+ complete(wkinfo->comp);
+ else {
+ sbinfo = stosi(wkinfo->sb);
+#if 0 // rfu
+ if (atomic_dec_and_test(&sbinfo->si_wkq_nowait))
+ wake_up_all(&sbinfo->si_wkq_nowait_wq);
+#endif
+ au_mntput(wkinfo->sb);
+ module_put(THIS_MODULE);
+ kfree(wkinfo);
+ }
+}
+
+int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb,
+ struct au_wkq_flags *flags)
+{
+ int err;
+ //int queued;
+ //struct workqueue_struct *wkq;
+ struct aufs_sbinfo *sbinfo;
+ DECLARE_COMPLETION_ONSTACK(comp);
+ struct au_wkinfo _wkinfo = {
+ //.sb = sb,
+ .wait = 1,
+ .dlgt = flags->dlgt,
+ .func = func,
+ .args = args,
+ .comp = &comp
+ }, *wkinfo = &_wkinfo;
+
+ LKTRTrace("dlgt %d, do_wait %d\n", flags->dlgt, flags->wait);
+ AuDebugOn(au_test_wkq(current));
+
+ err = 0;
+ sbinfo = NULL;
+ if (unlikely(!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;
+ wkinfo->sb = sb;
+ wkinfo->wait = 0;
+ wkinfo->dlgt = flags->dlgt;
+ wkinfo->func = func;
+ wkinfo->args = args;
+ wkinfo->comp = NULL;
+ /* prohibit umount */
+ __module_get(THIS_MODULE);
+ sbinfo = stosi(sb);
+ au_mntget(sb);
+ }
+
+ AuInitWkq(&wkinfo->wk, wkq_func);
+#ifdef CONFIG_AUFS_DLGT
+ if (flags->dlgt)
+ cred_store(&wkinfo->cred);
+#endif
+
+ do_wkq(wkinfo);
+#if 0 // remove
+ /* never fail */
+ queued = 0;
+ wkq = sbinfo->si_wkq;
+ while (1) {
+ if (flags->wait)
+ queued = queue_work(wkq, &wkinfo->wk);
+ else
+ queued = schedule_work(&wkinfo->wk);
+ if (queued)
+ break;
+
+ /* impossible? */
+ Warn1("failed to queuing\n");
+ yield();
+ }
+#endif
+
+ if (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 */
+
+#if 0 // test accouting
+ if (!err) {
+ static void f(void *args)
+ {
+ DbgSleep(1);
+ }
+ int i;
+ //au_debug_on();
+ LKTRTrace("f %p\n", f);
+ for (i = 0; i < 10; i++)
+ au_wkq_nowait(f, NULL, 0);
+ for (i = 0; i < aufs_nwkq; i++)
+ au_wkq_wait(f, NULL, 0);
+ DbgSleep(11);
+ //au_debug_off();
+ }
+#endif
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
diff --git a/ubuntu/fs/aufs/wkq.h b/ubuntu/fs/aufs/wkq.h
new file mode 100644
index 0000000..a9e0693
--- /dev/null
+++ b/ubuntu/fs/aufs/wkq.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: wkq.h,v 1.16 2007/10/15 01:29:36 sfjro Exp $ */
+
+#ifndef __AUFS_WKQ_H__
+#define __AUFS_WKQ_H__
+
+#ifdef __KERNEL__
+
+#include <linux/sched.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 18)
+#define DECLARE_COMPLETION_ONSTACK(work) DECLARE_COMPLETION(work)
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+#define AuInitWkq(wk, func) INIT_WORK(wk, func)
+#define AuWkqFunc(name, arg) void name(struct work_struct *arg)
+#else
+#define AuInitWkq(wk, func) INIT_WORK(wk, func, wk)
+#define AuWkqFunc(name, arg) void name(void *arg)
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+/* internal workqueue named AUFS_WKQ_NAME */
+struct au_wkq {
+ struct workqueue_struct *q;
+
+ /* accounting */
+ atomic_t busy;
+ unsigned int max_busy;
+};
+
+/* wait for 'nowait' tasks in system-wide workqueue */
+struct au_nowait_tasks {
+#ifdef CONFIG_AUFS_HINOTIFY
+//currently, the 'nowait' task which should be waited for is only hinotify.
+ atomic_t nw_len;
+ struct completion nw_comp;
+#endif
+};
+
+/* ---------------------------------------------------------------------- */
+
+extern struct au_wkq *au_wkq;
+typedef void (*au_wkq_func_t)(void *args);
+struct au_wkq_flags {
+ unsigned int dlgt:1;
+ unsigned int wait:1;
+};
+int au_wkq_run(au_wkq_func_t func, void *args, struct super_block *sb,
+ struct au_wkq_flags *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(current->comm, AUFS_WKQ_NAME));
+#if 0 // multithread workqueue
+ return (!tsk->mm
+ && !memcmp(current->comm, AUFS_WKQ_NAME "/",
+ sizeof(AUFS_WKQ_NAME)));
+#endif
+}
+
+static inline int au_wkq_wait(au_wkq_func_t func, void *args, int dlgt)
+{
+ struct au_wkq_flags flags = {
+ .dlgt = !!dlgt,
+ .wait = 1
+ };
+ 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)
+{
+ struct au_wkq_flags flags = {
+ .dlgt = !!dlgt,
+ .wait = 0
+ };
+ return au_wkq_run(func, args, sb, &flags);
+}
+
+#ifdef CONFIG_AUFS_HINOTIFY
+static inline void au_nwt_init(struct au_nowait_tasks *nwt)
+{
+ atomic_set(&nwt->nw_len, 0);
+ smp_mb(); /* atomic_set */
+ init_completion(&nwt->nw_comp);
+}
+
+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)
+ complete_all(&nwt->nw_comp);
+ return ret;
+}
+
+static inline int au_nwt_flush(struct au_nowait_tasks *nwt)
+{
+ if (unlikely(atomic_read(&nwt->nw_len))) {
+ //return wait_for_completion_interupptible(&nwt->nw_comp);
+ wait_for_completion(&nwt->nw_comp);
+ }
+ 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 --git a/ubuntu/fs/aufs/xino.c b/ubuntu/fs/aufs/xino.c
new file mode 100644
index 0000000..95b53bb
--- /dev/null
+++ b/ubuntu/fs/aufs/xino.c
@@ -0,0 +1,1226 @@
+/*
+ * Copyright (C) 2005, 2006, 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
+ */
+
+/* $Id: xino.c,v 1.47 2007/12/10 01:19:54 sfjro Exp $ */
+
+#include <linux/fsnotify.h>
+#include <linux/smp_lock.h>
+#include "aufs.h"
+
+/* ---------------------------------------------------------------------- */
+
+static ssize_t xino_fread(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);
+
+#if 0 // rfu
+ if (err > 0)
+ fsnotify_access(file->f_dentry);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static ssize_t do_xino_fwrite(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*/
+
+#if 0 // rfu
+ if (err > 0)
+ fsnotify_modify(file->f_dentry);
+#endif
+
+ AuTraceErr(err);
+ return err;
+}
+
+struct do_xino_fwrite_args {
+ ssize_t *errp;
+ 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(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;
+}
+
+/* ---------------------------------------------------------------------- */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
+struct xino_do_trunc_args {
+ struct vfsmount *mnt;
+ struct aufs_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 = find_brindex(sb, args->br->br_id);
+ AuDebugOn(bindex < 0);
+ err = 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(&stosi(sb)->si_nowait);
+ atomic_dec_return(&args->br->br_xino_running);
+ br_put(args->br);
+ mntput(args->mnt);
+ kfree(args);
+}
+
+static void xino_try_trunc(struct super_block *sb, struct aufs_branch *br)
+{
+ struct xino_do_trunc_args *args;
+ struct aufs_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) {
+ sbinfo = stosi(sb);
+ args->mnt = mntget(sbinfo->si_mnt);
+ args->br = br;
+ br_get(br);
+ 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(sbinfo->si_mnt);
+ br_put(br);
+ kfree(args);
+ } else
+ AuErr1("no memory\n");
+ out:
+ atomic_dec_return(&br->br_xino_running);
+}
+#else
+static void xino_try_trunc(struct super_block *sb, struct aufs_branch *br)
+{
+ /* nothing */
+}
+#endif
+
+/* ---------------------------------------------------------------------- */
+
+#ifndef LLONG_MAX /* before linux-2.6.18 */
+#define LLONG_MAX ((long long)(~0ULL >> 1))
+#endif
+#define Au_LOFF_MAX ((loff_t)LLONG_MAX)
+
+/*
+ * 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 xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct xino_entry *xinoe)
+{
+ loff_t pos;
+ ssize_t sz;
+ struct file *file;
+ struct aufs_sbinfo *sbinfo;
+ struct aufs_branch *br;
+
+ 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);
+
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ return 0;
+
+ 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);
+ br = stobr(sb, bindex);
+ file = br->br_xino;
+ AuDebugOn(!file);
+ sz = xino_fwrite(sbinfo->si_xwrite, file, xinoe, sizeof(*xinoe), &pos);
+ //if (LktrCond) sz = 1;
+ if (sz == sizeof(*xinoe)) {
+ if (unlikely(AuFlag(sbinfo, f_trunc_xino)
+ && au_test_trunc_xino(br->br_mnt->mnt_sb)))
+ xino_try_trunc(sb, br);
+ return 0; /* success */
+ }
+
+ AuIOErr("write failed (%ld)\n", (long)sz);
+ 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 aufs_sbinfo *sbinfo;
+ loff_t pos;
+ ssize_t sz;
+ struct file *xib;
+ unsigned long *p;
+
+ LKTRTrace("pindex %lu\n", pindex);
+ sbinfo = stosi(sb);
+ MtxMustLock(&sbinfo->si_xib_mtx);
+ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE
+ || AuFlag(sbinfo, f_xino) == AuXino_NONE);
+
+ 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 xino_write0(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ ino_t ino)
+{
+ int err, bit;
+ unsigned long pindex;
+ struct aufs_sbinfo *sbinfo;
+ struct xino_entry xinoe = {
+ .ino = 0
+ };
+
+ LKTRTrace("b%d, hi%lu, i%lu\n", bindex, h_ino, ino);
+
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ 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 = xino_write(sb, bindex, h_ino, &xinoe);
+ return err;
+}
+
+ino_t xino_new_ino(struct super_block *sb)
+{
+ ino_t ino;
+ struct aufs_sbinfo *sbinfo;
+ int free_bit, err;
+ unsigned long *p, pindex, ul, pend;
+ struct file *file;
+
+ //au_debug_on();
+ AuTraceEnter();
+
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ 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)) {
+ LKTRLabel(here);
+ goto out; /* success */
+ }
+ free_bit = find_first_zero_bit(p, page_bits);
+ if (free_bit < page_bits) {
+ LKTRLabel(here);
+ 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) {
+ LKTRLabel(here);
+ 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) {
+ LKTRLabel(here);
+ 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 xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
+ struct xino_entry *xinoe)
+{
+ int err;
+ struct file *file;
+ loff_t pos;
+ ssize_t sz;
+ struct aufs_sbinfo *sbinfo;
+
+ LKTRTrace("b%d, hi%lu\n", bindex, h_ino);
+
+ err = 0;
+ xinoe->ino = 0;
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ 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 = stobr(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 *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 udba = au_flag_test(sb, AuFlag_UDBA_INOTIFY);
+
+ LKTRTrace("%s\n", fname);
+ //AuDebugOn(!au_flag_test(sb, AuFlag_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);
+ vfsub_i_lock_nested(h_dir, AuLsc_I_PARENT);
+ err = vfsub_unlink(h_dir, file->f_dentry, &vargs);
+ vfsub_i_unlock(h_dir);
+ 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 = sbr_sb(sb, btgt);
+
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (unlikely(btgt != bindex && tgt_sb == sbr_sb(sb, bindex)))
+ return bindex;
+ return -1;
+}
+
+/*
+ * create a new xinofile at the same place/path as @base_file.
+ */
+static struct file *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 aufs_ndx ndx = {
+ .nfsmnt = NULL,
+ .dlgt = 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 (unlikely(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 xino_br(struct super_block *sb, aufs_bindex_t bindex,
+ struct file *base_file, int do_test)
+{
+ int err, do_create;
+ struct aufs_branch *br, *shared_br;
+ aufs_bindex_t bshared, bend;
+ struct inode *inode, *h_inode, *dir;
+ struct xino_entry xinoe;
+ struct dentry *parent;
+ struct file *file;
+
+ LKTRTrace("b%d, base_file %p, do_test %d\n",
+ bindex, base_file, do_test);
+ SiMustWriteLock(sb);
+ AuDebugOn(AuFlag(stosi(sb), f_xino) == AuXino_NONE);
+ br = stobr(sb, bindex);
+ AuDebugOn(br->br_xino);
+
+ do_create = 1;
+ bshared = -1;
+ shared_br = NULL;
+ bend = sbend(sb);
+ if (do_test)
+ bshared = is_sb_shared(sb, bindex, bend);
+ if (unlikely(bshared >= 0)) {
+ shared_br = stobr(sb, bshared);
+ do_create = !shared_br->br_xino;
+ }
+
+ if (do_create) {
+ parent = dget_parent(base_file->f_dentry);
+ dir = parent->d_inode;
+
+ vfsub_i_lock_nested(dir, AuLsc_I_PARENT);
+ file = xino_create2(sb, base_file, NULL);
+ err = PTR_ERR(file);
+ vfsub_i_unlock(dir);
+ 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);
+ }
+
+ inode = sb->s_root->d_inode;
+ h_inode = au_h_iptr_i(inode, bindex);
+ xinoe.ino = inode->i_ino;
+ //xinoe.h_gen = h_inode->i_generation;
+ //WARN_ON(xinoe.h_gen == AuXino_INVALID_HGEN);
+ err = xino_write(sb, bindex, h_inode->i_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 aufs_sbinfo *sbinfo;
+ readf_t func;
+ loff_t pos, pend;
+ ssize_t sz;
+ struct xino_entry *xinoe;
+ unsigned long pindex;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ err = 0;
+ sbinfo = stosi(sb);
+ func = sbinfo->si_xread;
+ pend = i_size_read(file->f_dentry->d_inode);
+#ifdef CONFIG_AUFS_DEBUG
+ if (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 = sbend(sb);
+ for (bindex = 0; !err && bindex <= bend; bindex++)
+ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0)
+ err = do_xib_restore(sb, stobr(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 xib_trunc(struct super_block *sb)
+{
+ int err;
+ struct aufs_sbinfo *sbinfo;
+ unsigned long *p;
+ //ino_t ino;
+ loff_t pos;
+ ssize_t sz;
+ struct dentry *parent;
+ struct inode *dir;
+ struct file *file;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ sbinfo = stosi(sb);
+ if (unlikely(AuFlag(sbinfo, f_xino) == AuXino_NONE))
+ return 0;
+
+ //aufs_debug_on();
+ parent = dget_parent(sbinfo->si_xib->f_dentry);
+ dir = parent->d_inode;
+ vfsub_i_lock_nested(dir, AuLsc_I_PARENT);
+ file = xino_create2(sb, sbinfo->si_xib, NULL);
+ vfsub_i_unlock(dir);
+ 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 (AuFlag(sbinfo, f_xino) != AuXino_NONE) {
+ mutex_lock(&sbinfo->si_xib_mtx);
+ err = xib_restore(sb);
+ mutex_unlock(&sbinfo->si_xib_mtx);
+#if 0 // rfu
+ } else {
+ /* is it really safe? */
+ AuDebugOn(!kernel_locked());
+ ino = AUFS_FIRST_INO;
+ list_for_each_entry(inode, &sb->s_inodes, i_sb_list)
+ if (ino < inode->i_ino)
+ ino = inode->i_ino;
+
+ /* make iunique to return larger than active max inode number */
+ iunique(sb, ino);
+ err = 0;
+#endif
+ }
+
+out:
+ //aufs_debug_off();
+ AuTraceErr(err);
+ return err;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * xino mount option handlers
+ */
+static 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 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 aufs_sbinfo *sbinfo;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ sbinfo = stosi(sb);
+ sbinfo->si_xread = NULL;
+ sbinfo->si_xwrite = NULL;
+ if (unlikely(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 xino_set_xib(struct super_block *sb, struct file *base)
+{
+ int err;
+ struct aufs_sbinfo *sbinfo;
+ struct file *file;
+ loff_t pos;
+
+ LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry));
+ SiMustWriteLock(sb);
+
+ sbinfo = stosi(sb);
+ file = xino_create2(sb, base, sbinfo->si_xib);
+ err = PTR_ERR(file);
+ if (IS_ERR(file))
+ goto out;
+ if (unlikely(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 aufs_branch *br;
+
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ bend = sbend(sb);
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = stobr(sb, bindex);
+ if (unlikely(!br || !br->br_xino))
+ continue;
+
+ fput(br->br_xino);
+ br->br_xino = NULL;
+ }
+}
+
+static int xino_set_br(struct super_block *sb, struct file *base)
+{
+ int err;
+ aufs_bindex_t bindex, bend, bshared;
+ struct aufs_branch *br, *shared;
+ struct xino_entry xinoe;
+ struct file *file;
+ struct inode *inode;
+
+ LKTRTrace("%.*s\n", AuDLNPair(base->f_dentry));
+ SiMustWriteLock(sb);
+
+ bend = sbend(sb);
+ for (bindex = bend; bindex >= 0; bindex--) {
+ br = stobr(sb, bindex);
+ if (!br->br_xino || file_count(br->br_xino) == 1)
+ continue;
+
+ fput(br->br_xino);
+ br->br_xino = NULL;
+ }
+
+ for (bindex = 0; bindex <= bend; bindex++) {
+ br = stobr(sb, bindex);
+ if (!br->br_xino)
+ continue;
+ AuDebugOn(file_count(br->br_xino) != 1);
+ file = xino_create2(sb, base, br->br_xino);
+ if (IS_ERR(file)) {
+ //todo: restore
+ err = PTR_ERR(file);
+ goto out;
+ }
+ fput(br->br_xino);
+ br->br_xino = file;
+ }
+
+ err = 0;
+ inode = sb->s_root->d_inode;
+ xinoe.ino = AUFS_ROOT_INO;
+ for (bindex = 0; !err && bindex <= bend; bindex++) {
+ br = stobr(sb, bindex);
+ if (br->br_xino)
+ continue;
+ bshared = is_sb_shared(sb, bindex, bindex);
+ if (bshared < 0) {
+ file = xino_create2(sb, base, NULL);
+ if (IS_ERR(file)) {
+ //todo: restore
+ err = PTR_ERR(file);
+ goto out;
+ }
+ br->br_xino = file;
+ } else {
+ shared = stobr(sb, bshared);
+ AuDebugOn(!shared->br_xino);
+ br->br_xino = shared->br_xino;
+ get_file(br->br_xino);
+ }
+ err = xino_write(sb, bindex, au_h_iptr_i(inode, bindex)->i_ino,
+ &xinoe);
+ }
+
+ out:
+ AuTraceErr(err);
+ return err;
+}
+
+void xino_clr(struct super_block *sb)
+{
+ AuTraceEnter();
+ SiMustWriteLock(sb);
+
+ xino_clear_xib(sb);
+ xino_clear_br(sb);
+ AuFlagSet(stosi(sb), f_xino, AuXino_NONE);
+}
+
+int 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 aufs_sbinfo *sbinfo;
+
+ LKTRTrace("%s\n", xino->path);
+ SiMustWriteLock(sb);
+
+ err = 0;
+ sbinfo = stosi(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;
+ }
+
+ AuFlagSet(sbinfo, f_xino, AuXino_XINO);
+ dir = parent->d_inode;
+ vfsub_i_lock_nested(dir, AuLsc_I_PARENT);
+ err = xino_set_xib(sb, xino->file);
+ if (!err)
+ err = xino_set_br(sb, xino->file);
+ vfsub_i_unlock(dir);
+ if (!err)
+ goto out; /* success */
+
+ /* reset all */
+ AuIOErr("failed creating xino, forcing noxino (%d).\n", err);
+ err = -EIO;
+ xino_clr(sb);
+
+ out:
+ dput(parent);
+ AuTraceErr(err);
+ return err;
+}
+
+int xino_trunc(struct super_block *sb, aufs_bindex_t bindex)
+{
+ int err;
+ struct aufs_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 = sbend(sb);
+ if (unlikely(bindex < 0 || bend < bindex))
+ goto out;
+ br = stobr(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);
+ vfsub_i_lock_nested(dir, AuLsc_I_PARENT);
+ new_xino = xino_create2(sb, br->br_xino, br->br_xino);
+ vfsub_i_unlock(dir);
+ //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 = stobr(sb, bi);
+ if (unlikely(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 *xino_def(struct super_block *sb)
+{
+ struct file *file;
+ aufs_bindex_t bend, bindex, bwr;
+ char *page, *p;
+
+ AuTraceEnter();
+
+ bend = sbend(sb);
+ bwr = -1;
+ for (bindex = 0; bindex <= bend; bindex++)
+ if (br_writable(sbr_perm(sb, bindex))
+ && !au_test_nfs(au_h_dptr_i(sb->s_root, bindex)->d_sb)) {
+ bwr = bindex;
+ break;
+ }
+
+ if (bwr >= 0) {
+ // todo: rewrite with au_lkup_one()
+ file = ERR_PTR(-ENOMEM);
+ page = __getname();
+ //if (LktrCond) {__putname(page); page = NULL;}
+ if (unlikely(!page))
+ goto out;
+ p = d_path(au_h_dptr_i(sb->s_root, bwr), sbr_mnt(sb, bwr), 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 = xino_create(sb, p, /*silent*/0, sb->s_root);
+ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
+ }
+ __putname(page);
+ } else {
+ file = xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0,
+ /*parent*/NULL);
+ //if (LktrCond) {fput(file); file = ERR_PTR(-1);}
+ }
+
+ out:
+ AuTraceErrPtr(file);
+ return file;
+}
--
1.5.2.5
More information about the kernel-team
mailing list