From a154a18cf9fbaf3b50d21445700ff28d9f03d555 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:11:14 +0100 Subject: add aufs2 kernel patches, update aufs2-util to match --- package/aufs2-util/Makefile | 13 +- package/aufs2-util/patches/patch-Makefile | 47 + target/linux/patches/2.6.36/aufs2.patch | 33532 +++++++++++++++++++ .../kernel-headers/patches/2.6.36/aufs2.patch | 240 + 4 files changed, 33825 insertions(+), 7 deletions(-) create mode 100644 package/aufs2-util/patches/patch-Makefile create mode 100644 target/linux/patches/2.6.36/aufs2.patch create mode 100644 toolchain/kernel-headers/patches/2.6.36/aufs2.patch diff --git a/package/aufs2-util/Makefile b/package/aufs2-util/Makefile index 14ceeb4c7..f42767dbe 100644 --- a/package/aufs2-util/Makefile +++ b/package/aufs2-util/Makefile @@ -4,16 +4,16 @@ include $(TOPDIR)/rules.mk PKG_NAME:= aufs2-util -PKG_VERSION:= 2-301210 +PKG_VERSION:= 2-100111 PKG_RELEASE:= 1 -PKG_MD5SUM:= 83b07d6a6d43362dfc234ecff11367f3 +PKG_MD5SUM:= 1854f5ab560dd375b22f6e2b747cb412 PKG_DESCR:= aufs2 utilities PKG_SECTION:= fs PKG_URL:= http://aufs.sf.net/ # created from git via: -# $ git archive --format tar origin/aufs2 | \ -# gzip -9 -c >aufs2-util-2-$(date +%d%m%g).tar.gz +# $ git archive --format tar aufs2.1 | \ +# gzip -9 -c >../aufs2-util-2-$(date +%d%m%g).tar.gz PKG_SITES:= http://nwl.cc/~n0-1/ WRKDIST= ${WRKDIR} @@ -29,8 +29,7 @@ $(eval $(call PKG_template,AUFS2_UTIL,${PKG_NAME},${PKG_VERSION}-${PKG_RELEASE}, $(eval $(call PKG_template,LIBAU,libau,${PKG_VERSION}-${PKG_RELEASE},${PKG_DEPENDS},${PKGSD_LIBAU},${PKG_SECTION})) CONFIG_STYLE:= manual -TCFLAGS+= -I${LINUX_DIR}/include -XAKE_FLAGS+= Install=install +XAKE_FLAGS+= Install=install HOSTCPPFLAGS="-I${LINUX_HEADER_DIR}/include" post-install: $(INSTALL_DIR) $(IDIR_AUFS2_UTIL)/sbin @@ -40,7 +39,7 @@ post-install: $(INSTALL_BIN) ${WRKINST}/usr/bin/au{brsync,chk} \ $(IDIR_AUFS2_UTIL)/usr/bin $(INSTALL_DIR) $(IDIR_AUFS2_UTIL)/etc/default - $(INSTALL_BIN) ${WRKINST}/etc/default/aufs \ + $(INSTALL_DATA) ${WRKINST}/etc/default/aufs \ $(IDIR_AUFS2_UTIL)/etc/default $(INSTALL_DIR) $(IDIR_LIBAU)/usr/lib ${CP} ${WRKINST}/usr/lib/libau* ${IDIR_LIBAU}/usr/lib diff --git a/package/aufs2-util/patches/patch-Makefile b/package/aufs2-util/patches/patch-Makefile new file mode 100644 index 000000000..d77ea81ec --- /dev/null +++ b/package/aufs2-util/patches/patch-Makefile @@ -0,0 +1,47 @@ +--- w-aufs2-util-2-100111-1.orig/Makefile 2011-01-07 05:00:54.000000000 +0100 ++++ w-aufs2-util-2-100111-1/Makefile 2011-01-10 23:43:23.000000000 +0100 +@@ -15,6 +15,11 @@ + # along with this program; if not, write to the Free Software + # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ++c2sh c2tmac: CC = ${HOSTCC} ++c2sh c2tmac: CFLAGS= ${HOSTCFLAGS} ++c2sh c2tmac: LDFLAGS= ${HOSTLDFLAGS} ++c2sh c2tmac: CPPFLAGS= ${HOSTCPPFLAGS} -I./libau -I${LINUX_HEADER_DIR}/include ++ + CFLAGS += -I./libau + CFLAGS += -O -Wall + +@@ -28,13 +33,10 @@ LibUtilObj = proc_mnt.o br.o plink.o mta + LibUtilHdr = au_util.h + export + +-all: ver_test ${Man} ${Bin} ${Etc} ++all: ${Man} ${Bin} ${Etc} + ${MAKE} -C libau $@ + ln -sf ./libau/libau*.so . + +-ver_test: ver +- ./ver +- + ${Bin}: LDFLAGS += -static -s + ${Bin}: LDLIBS = -L. -lautil + ${BinObj}: %.o: %.c ${LibUtilHdr} ${LibUtil} +@@ -63,7 +65,7 @@ aufs.5: aufs.in.5 c2tmac + }' aufs.in.5 >> $@ + chmod a-w $@ + +-.INTERMEDIATE: c2sh c2tmac ver ++.INTERMEDIATE: c2sh c2tmac + + Install = install -o root -g root -p + install_sbin: File = mount.aufs umount.aufs auplink +@@ -77,7 +79,7 @@ install_etc: File = etc_default_aufs + install_etc: Tgt = ${DESTDIR}/etc/default/aufs + install_etc: ${File} + install -d $(dir ${Tgt}) +- ${Install} -m 644 -T ${File} ${Tgt} ++ ${Install} -m 644 ${File} ${Tgt} + install_man: File = aufs.5 + install_man: Tgt = ${DESTDIR}/usr/share/man/man5 + install_man: ${File} diff --git a/target/linux/patches/2.6.36/aufs2.patch b/target/linux/patches/2.6.36/aufs2.patch new file mode 100644 index 000000000..e9e5fc277 --- /dev/null +++ b/target/linux/patches/2.6.36/aufs2.patch @@ -0,0 +1,33532 @@ +diff -Nur linux-2.6.36.orig/fs/Kconfig linux-2.6.36/fs/Kconfig +--- linux-2.6.36.orig/fs/Kconfig 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/Kconfig 2011-01-10 19:52:38.000000000 +0100 +@@ -189,6 +189,7 @@ + source "fs/sysv/Kconfig" + source "fs/ufs/Kconfig" + source "fs/exofs/Kconfig" ++source "fs/aufs/Kconfig" + + endif # MISC_FILESYSTEMS + +diff -Nur linux-2.6.36.orig/fs/Makefile linux-2.6.36/fs/Makefile +--- linux-2.6.36.orig/fs/Makefile 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/Makefile 2011-01-10 19:52:38.000000000 +0100 +@@ -126,3 +126,4 @@ + obj-$(CONFIG_GFS2_FS) += gfs2/ + obj-$(CONFIG_EXOFS_FS) += exofs/ + obj-$(CONFIG_CEPH_FS) += ceph/ ++obj-$(CONFIG_AUFS_FS) += aufs/ +diff -Nur linux-2.6.36.orig/fs/aufs/Kconfig linux-2.6.36/fs/aufs/Kconfig +--- linux-2.6.36.orig/fs/aufs/Kconfig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/Kconfig 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,180 @@ ++config AUFS_FS ++ tristate "Aufs (Advanced multi layered unification filesystem) support" ++ depends on EXPERIMENTAL ++ help ++ Aufs is a stackable unification filesystem such as Unionfs, ++ which unifies several directories and provides a merged single ++ directory. ++ In the early days, aufs was entirely re-designed and ++ re-implemented Unionfs Version 1.x series. Introducing many ++ original ideas, approaches and improvements, it becomes totally ++ different from Unionfs while keeping the basic features. ++ ++if AUFS_FS ++choice ++ prompt "Maximum number of branches" ++ default AUFS_BRANCH_MAX_127 ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_127 ++ bool "127" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_511 ++ bool "511" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_1023 ++ bool "1023" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++config AUFS_BRANCH_MAX_32767 ++ bool "32767" ++ help ++ Specifies the maximum number of branches (or member directories) ++ in a single aufs. The larger value consumes more system ++ resources and has a minor impact to performance. ++endchoice ++ ++config AUFS_SBILIST ++ bool ++ depends on AUFS_MAGIC_SYSRQ || PROC_FS ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq or /proc, enabled automatically. ++ ++config AUFS_HNOTIFY ++ bool "Detect direct branch access (bypassing aufs)" ++ help ++ If you want to modify files on branches directly, eg. bypassing aufs, ++ and want aufs to detect the changes of them fully, then enable this ++ option and use 'udba=notify' mount option. ++ Currently there is only one available configuration, "fsnotify". ++ It will have a negative impact to the performance. ++ See detail in aufs.5. ++ ++choice ++ prompt "method" if AUFS_HNOTIFY ++ default AUFS_HFSNOTIFY ++config AUFS_HFSNOTIFY ++ bool "fsnotify" ++ select FSNOTIFY ++endchoice ++ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on (AUFS_FS = y && EXPORTFS = y) || (AUFS_FS = m && EXPORTFS) ++ help ++ If you want to export your mounted aufs via NFS, then enable this ++ option. There are several requirements for this configuration. ++ See detail in aufs.5. ++ ++config AUFS_INO_T_64 ++ bool ++ depends on AUFS_EXPORT ++ depends on 64BIT && !(ALPHA || S390) ++ default y ++ help ++ Automatic configuration for internal use. ++ /* typedef unsigned long/int __kernel_ino_t */ ++ /* alpha and s390x are int */ ++ ++config AUFS_RDU ++ bool "Readdir in userspace" ++ help ++ Aufs has two methods to provide a merged view for a directory, ++ by a user-space library and by kernel-space natively. The latter ++ is always enabled but sometimes large and slow. ++ If you enable this option, install the library in aufs2-util ++ package, and set some environment variables for your readdir(3), ++ then the work will be handled in user-space which generally ++ shows better performance in most cases. ++ See detail in aufs.5. ++ ++config AUFS_SP_IATTR ++ bool "Respect the attributes (mtime/ctime mainly) of special files" ++ help ++ When you write something to a special file, some attributes of it ++ (mtime/ctime mainly) may be updated. Generally such updates are ++ less important (actually some device drivers and NFS ignore ++ it). But some applications (such like test program) requires ++ such updates. If you need these updates, then enable this ++ configuration which introduces some overhead. ++ Currently this configuration handles FIFO only. ++ ++config AUFS_SHWH ++ bool "Show whiteouts" ++ help ++ If you want to make the whiteouts in aufs visible, then enable ++ this option and specify 'shwh' mount option. Although it may ++ sounds like philosophy or something, but in technically it ++ simply shows the name of whiteout with keeping its behaviour. ++ ++config AUFS_BR_RAMFS ++ bool "Ramfs (initramfs/rootfs) as an aufs branch" ++ help ++ If you want to use ramfs as an aufs branch fs, then enable this ++ option. Generally tmpfs is recommended. ++ Aufs prohibited them to be a branch fs by default, because ++ initramfs becomes unusable after switch_root or something ++ generally. If you sets initramfs as an aufs branch and boot your ++ system by switch_root, you will meet a problem easily since the ++ files in initramfs may be inaccessible. ++ Unless you are going to use ramfs as an aufs branch fs without ++ switch_root or something, leave it N. ++ ++config AUFS_BR_FUSE ++ bool "Fuse fs as an aufs branch" ++ depends on FUSE_FS ++ select AUFS_POLL ++ help ++ If you want to use fuse-based userspace filesystem as an aufs ++ branch fs, then enable this option. ++ It implements the internal poll(2) operation which is ++ implemented by fuse only (curretnly). ++ ++config AUFS_POLL ++ bool ++ help ++ Automatic configuration for internal use. ++ ++config AUFS_BR_HFSPLUS ++ bool "Hfsplus as an aufs branch" ++ depends on HFSPLUS_FS ++ default y ++ help ++ If you want to use hfsplus fs as an aufs branch fs, then enable ++ this option. This option introduces a small overhead at ++ copying-up a file on hfsplus. ++ ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. ++ ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. ++ ++config AUFS_MAGIC_SYSRQ ++ bool ++ depends on AUFS_DEBUG && MAGIC_SYSRQ ++ default y ++ help ++ Automatic configuration for internal use. ++ When aufs supports Magic SysRq, enabled automatically. ++endif +diff -Nur linux-2.6.36.orig/fs/aufs/Makefile linux-2.6.36/fs/aufs/Makefile +--- linux-2.6.36.orig/fs/aufs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/Makefile 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,38 @@ ++ ++include ${src}/magic.mk ++ifeq (${CONFIG_AUFS_FS},m) ++include ${src}/conf.mk ++endif ++-include ${src}/priv_def.mk ++ ++# cf. include/linux/kernel.h ++# enable pr_debug ++ccflags-y += -DDEBUG ++# sparse doesn't allow spaces ++ccflags-y += -D'pr_fmt(fmt)=AUFS_NAME"\040%s:%d:%s[%d]:\040"fmt,__func__,__LINE__,current->comm,current->pid' ++ ++obj-$(CONFIG_AUFS_FS) += aufs.o ++aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ++ wkq.o vfsub.o dcsub.o \ ++ cpup.o whout.o wbr_policy.o \ ++ dinfo.o dentry.o \ ++ dynop.o \ ++ finfo.o file.o f_op.o \ ++ dir.o vdir.o \ ++ iinfo.o inode.o i_op.o i_op_add.o i_op_del.o i_op_ren.o \ ++ ioctl.o ++ ++# all are boolean ++aufs-$(CONFIG_PROC_FS) += procfs.o plink.o ++aufs-$(CONFIG_SYSFS) += sysfs.o ++aufs-$(CONFIG_DEBUG_FS) += dbgaufs.o ++aufs-$(CONFIG_AUFS_BDEV_LOOP) += loop.o ++aufs-$(CONFIG_AUFS_HNOTIFY) += hnotify.o ++aufs-$(CONFIG_AUFS_HFSNOTIFY) += hfsnotify.o ++aufs-$(CONFIG_AUFS_EXPORT) += export.o ++aufs-$(CONFIG_AUFS_POLL) += poll.o ++aufs-$(CONFIG_AUFS_RDU) += rdu.o ++aufs-$(CONFIG_AUFS_SP_IATTR) += f_op_sp.o ++aufs-$(CONFIG_AUFS_BR_HFSPLUS) += hfsplus.o ++aufs-$(CONFIG_AUFS_DEBUG) += debug.o ++aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o +diff -Nur linux-2.6.36.orig/fs/aufs/aufs.h linux-2.6.36/fs/aufs/aufs.h +--- linux-2.6.36.orig/fs/aufs/aufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/aufs.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,61 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * all header files ++ */ ++ ++#ifndef __AUFS_H__ ++#define __AUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#define AuStub(type, name, body, ...) \ ++ static inline type name(__VA_ARGS__) { body; } ++ ++#define AuStubVoid(name, ...) \ ++ AuStub(void, name, , __VA_ARGS__) ++#define AuStubInt0(name, ...) \ ++ AuStub(int, name, return 0, __VA_ARGS__) ++ ++#include "debug.h" ++ ++#include "branch.h" ++#include "cpup.h" ++#include "dcsub.h" ++#include "dbgaufs.h" ++#include "dentry.h" ++#include "dir.h" ++#include "dynop.h" ++#include "file.h" ++#include "fstype.h" ++#include "inode.h" ++#include "loop.h" ++#include "module.h" ++/* never include ./mtx.h */ ++#include "opts.h" ++#include "rwsem.h" ++#include "spl.h" ++#include "super.h" ++#include "sysaufs.h" ++#include "vfsub.h" ++#include "whout.h" ++#include "wkq.h" ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/branch.c linux-2.6.36/fs/aufs/branch.c +--- linux-2.6.36.orig/fs/aufs/branch.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/branch.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1071 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * branch management ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * free a single branch ++ */ ++static void au_br_do_free(struct au_branch *br) ++{ ++ int i; ++ struct au_wbr *wbr; ++ struct au_dykey **key; ++ ++ au_hnotify_fin_br(br); ++ ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ mutex_destroy(&br->br_xino.xi_nondir_mtx); ++ ++ AuDebugOn(atomic_read(&br->br_count)); ++ ++ wbr = br->br_wbr; ++ if (wbr) { ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(wbr->wbr_wh[i]); ++ AuDebugOn(atomic_read(&wbr->wbr_wh_running)); ++ AuRwDestroy(&wbr->wbr_wh_rwsem); ++ } ++ ++ key = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++, key++) ++ if (*key) ++ au_dy_put(*key); ++ else ++ break; ++ ++ mntput(br->br_mnt); ++ kfree(wbr); ++ kfree(br); ++} ++ ++/* ++ * frees all branches ++ */ ++void au_br_free(struct au_sbinfo *sbinfo) ++{ ++ aufs_bindex_t bmax; ++ struct au_branch **br; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ bmax = sbinfo->si_bend + 1; ++ br = sbinfo->si_branch; ++ while (bmax--) ++ au_br_do_free(*br++); ++} ++ ++/* ++ * find the index of a branch which is specified by @br_id. ++ */ ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (au_sbr_id(sb, bindex) == br_id) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * add a branch ++ */ ++ ++static int test_overlap(struct super_block *sb, struct dentry *h_adding, ++ struct dentry *h_root) ++{ ++ if (unlikely(h_adding == h_root ++ || au_test_loopback_overlap(sb, h_adding))) ++ return 1; ++ if (h_adding->d_sb != h_root->d_sb) ++ return 0; ++ return au_test_subdir(h_adding, h_root) ++ || au_test_subdir(h_root, h_adding); ++} ++ ++/* ++ * returns a newly allocated branch. @new_nbranch is a number of branches ++ * after adding a branch. ++ */ ++static struct au_branch *au_br_alloc(struct super_block *sb, int new_nbranch, ++ int perm) ++{ ++ struct au_branch *add_branch; ++ struct dentry *root; ++ int err; ++ ++ err = -ENOMEM; ++ root = sb->s_root; ++ add_branch = kmalloc(sizeof(*add_branch), GFP_NOFS); ++ if (unlikely(!add_branch)) ++ goto out; ++ ++ err = au_hnotify_init_br(add_branch, perm); ++ if (unlikely(err)) ++ goto out_br; ++ ++ add_branch->br_wbr = NULL; ++ if (au_br_writable(perm)) { ++ /* may be freed separately at changing the branch permission */ ++ add_branch->br_wbr = kmalloc(sizeof(*add_branch->br_wbr), ++ GFP_NOFS); ++ if (unlikely(!add_branch->br_wbr)) ++ goto out_hnotify; ++ } ++ ++ err = au_sbr_realloc(au_sbi(sb), new_nbranch); ++ if (!err) ++ err = au_di_realloc(au_di(root), new_nbranch); ++ if (!err) ++ err = au_ii_realloc(au_ii(root->d_inode), new_nbranch); ++ if (!err) ++ return add_branch; /* success */ ++ ++ kfree(add_branch->br_wbr); ++ ++out_hnotify: ++ au_hnotify_fin_br(add_branch); ++out_br: ++ kfree(add_branch); ++out: ++ return ERR_PTR(err); ++} ++ ++/* ++ * test if the branch permission is legal or not. ++ */ ++static int test_br(struct inode *inode, int brperm, char *path) ++{ ++ int err; ++ ++ err = (au_br_writable(brperm) && IS_RDONLY(inode)); ++ if (!err) ++ goto out; ++ ++ err = -EINVAL; ++ pr_err("write permission for readonly mount or inode, %s\n", path); ++ ++out: ++ return err; ++} ++ ++/* ++ * returns: ++ * 0: success, the caller will add it ++ * plus: success, it is already unified, the caller should ignore it ++ * minus: error ++ */ ++static int test_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root; ++ struct inode *inode, *h_inode; ++ ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ if (unlikely(bend >= 0 ++ && au_find_dbindex(root, add->path.dentry) >= 0)) { ++ err = 1; ++ if (!remount) { ++ err = -EINVAL; ++ pr_err("%s duplicated\n", add->pathname); ++ } ++ goto out; ++ } ++ ++ err = -ENOSPC; /* -E2BIG; */ ++ if (unlikely(AUFS_BRANCH_MAX <= add->bindex ++ || AUFS_BRANCH_MAX - 1 <= bend)) { ++ pr_err("number of branches exceeded %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EDOM; ++ if (unlikely(add->bindex < 0 || bend + 1 < add->bindex)) { ++ pr_err("bad index %d\n", add->bindex); ++ goto out; ++ } ++ ++ inode = add->path.dentry->d_inode; ++ err = -ENOENT; ++ if (unlikely(!inode->i_nlink)) { ++ pr_err("no existence %s\n", add->pathname); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ if (unlikely(inode->i_sb == sb)) { ++ pr_err("%s must be outside\n", add->pathname); ++ goto out; ++ } ++ ++ if (unlikely(au_test_fs_unsuppoted(inode->i_sb))) { ++ pr_err("unsupported filesystem, %s (%s)\n", ++ add->pathname, au_sbtype(inode->i_sb)); ++ goto out; ++ } ++ ++ err = test_br(add->path.dentry->d_inode, add->perm, add->pathname); ++ if (unlikely(err)) ++ goto out; ++ ++ if (bend < 0) ++ return 0; /* success */ ++ ++ err = -EINVAL; ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (unlikely(test_overlap(sb, add->path.dentry, ++ au_h_dptr(root, bindex)))) { ++ pr_err("%s is overlapped\n", add->pathname); ++ goto out; ++ } ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), WARN_PERM)) { ++ h_inode = au_h_dptr(root, 0)->d_inode; ++ if ((h_inode->i_mode & S_IALLUGO) != (inode->i_mode & S_IALLUGO) ++ || h_inode->i_uid != inode->i_uid ++ || h_inode->i_gid != inode->i_gid) ++ pr_warning("uid/gid/perm %s %u/%u/0%o, %u/%u/0%o\n", ++ add->pathname, ++ inode->i_uid, inode->i_gid, ++ (inode->i_mode & S_IALLUGO), ++ h_inode->i_uid, h_inode->i_gid, ++ (h_inode->i_mode & S_IALLUGO)); ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize or clean the whiteouts for an adding branch ++ */ ++static int au_br_init_wh(struct super_block *sb, struct au_branch *br, ++ int new_perm, struct dentry *h_root) ++{ ++ int err, old_perm; ++ aufs_bindex_t bindex; ++ struct mutex *h_mtx; ++ struct au_wbr *wbr; ++ struct au_hinode *hdir; ++ ++ wbr = br->br_wbr; ++ old_perm = br->br_perm; ++ br->br_perm = new_perm; ++ hdir = NULL; ++ h_mtx = NULL; ++ bindex = au_br_index(sb, br->br_id); ++ if (0 <= bindex) { ++ hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ } else { ++ h_mtx = &h_root->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_PARENT); ++ } ++ if (!wbr) ++ err = au_wh_init(h_root, br, sb); ++ else { ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(h_root, br, sb); ++ wbr_wh_write_unlock(wbr); ++ } ++ if (hdir) ++ au_hn_imtx_unlock(hdir); ++ else ++ mutex_unlock(h_mtx); ++ br->br_perm = old_perm; ++ ++ if (!err && wbr && !au_br_writable(new_perm)) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ ++ return err; ++} ++ ++static int au_wbr_init(struct au_branch *br, struct super_block *sb, ++ int perm, struct path *path) ++{ ++ int err; ++ struct kstatfs kst; ++ struct au_wbr *wbr; ++ struct dentry *h_dentry; ++ ++ wbr = br->br_wbr; ++ au_rw_init(&wbr->wbr_wh_rwsem); ++ memset(wbr->wbr_wh, 0, sizeof(wbr->wbr_wh)); ++ atomic_set(&wbr->wbr_wh_running, 0); ++ wbr->wbr_bytes = 0; ++ ++ /* ++ * a limit for rmdir/rename a dir ++ * cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h ++ */ ++ err = vfs_statfs(path, &kst); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ h_dentry = path->dentry; ++ if (kst.f_namelen >= NAME_MAX) ++ err = au_br_init_wh(sb, br, perm, h_dentry); ++ else ++ pr_err("%.*s(%s), unsupported namelen %ld\n", ++ AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb), ++ kst.f_namelen); ++ ++out: ++ return err; ++} ++ ++/* intialize a new branch */ ++static int au_br_init(struct au_branch *br, struct super_block *sb, ++ struct au_opt_add *add) ++{ ++ int err; ++ ++ err = 0; ++ memset(&br->br_xino, 0, sizeof(br->br_xino)); ++ mutex_init(&br->br_xino.xi_nondir_mtx); ++ br->br_perm = add->perm; ++ br->br_mnt = add->path.mnt; /* set first, mntget() later */ ++ spin_lock_init(&br->br_dykey_lock); ++ memset(br->br_dykey, 0, sizeof(br->br_dykey)); ++ atomic_set(&br->br_count, 0); ++ br->br_xino_upper = AUFS_XINO_TRUNC_INIT; ++ atomic_set(&br->br_xino_running, 0); ++ br->br_id = au_new_br_id(sb); ++ AuDebugOn(br->br_id < 0); ++ ++ if (au_br_writable(add->perm)) { ++ err = au_wbr_init(br, sb, add->perm, &add->path); ++ if (unlikely(err)) ++ goto out_err; ++ } ++ ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_br(sb, br, add->path.dentry->d_inode->i_ino, ++ au_sbr(sb, 0)->br_xino.xi_file, /*do_test*/1); ++ if (unlikely(err)) { ++ AuDebugOn(br->br_xino.xi_file); ++ goto out_err; ++ } ++ } ++ ++ sysaufs_br_init(br); ++ mntget(add->path.mnt); ++ goto out; /* success */ ++ ++out_err: ++ br->br_mnt = NULL; ++out: ++ return err; ++} ++ ++static void au_br_do_add_brp(struct au_sbinfo *sbinfo, aufs_bindex_t bindex, ++ struct au_branch *br, aufs_bindex_t bend, ++ aufs_bindex_t amount) ++{ ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ memmove(brp + 1, brp, sizeof(*brp) * amount); ++ *brp = br; ++ sbinfo->si_bend++; ++ if (unlikely(bend < 0)) ++ sbinfo->si_bend = 0; ++} ++ ++static void au_br_do_add_hdp(struct au_dinfo *dinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry + bindex; ++ memmove(hdp + 1, hdp, sizeof(*hdp) * amount); ++ au_h_dentry_init(hdp); ++ dinfo->di_bend++; ++ if (unlikely(bend < 0)) ++ dinfo->di_bstart = 0; ++} ++ ++static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, ++ aufs_bindex_t bend, aufs_bindex_t amount) ++{ ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ memmove(hip + 1, hip, sizeof(*hip) * amount); ++ hip->hi_inode = NULL; ++ au_hn_init(hip); ++ iinfo->ii_bend++; ++ if (unlikely(bend < 0)) ++ iinfo->ii_bstart = 0; ++} ++ ++static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry, ++ struct au_branch *br, aufs_bindex_t bindex) ++{ ++ struct dentry *root; ++ struct inode *root_inode; ++ aufs_bindex_t bend, amount; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ bend = au_sbend(sb); ++ amount = bend + 1 - bindex; ++ au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); ++ au_br_do_add_hdp(au_di(root), bindex, bend, amount); ++ au_br_do_add_hip(au_ii(root_inode), bindex, bend, amount); ++ au_set_h_dptr(root, bindex, dget(h_dentry)); ++ au_set_h_iptr(root_inode, bindex, au_igrab(h_dentry->d_inode), ++ /*flags*/0); ++} ++ ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) ++{ ++ int err; ++ aufs_bindex_t bend, add_bindex; ++ struct dentry *root, *h_dentry; ++ struct inode *root_inode; ++ struct au_branch *add_branch; ++ ++ root = sb->s_root; ++ root_inode = root->d_inode; ++ IMustLock(root_inode); ++ err = test_add(sb, add, remount); ++ if (unlikely(err < 0)) ++ goto out; ++ if (err) { ++ err = 0; ++ goto out; /* success */ ++ } ++ ++ bend = au_sbend(sb); ++ add_branch = au_br_alloc(sb, bend + 2, add->perm); ++ err = PTR_ERR(add_branch); ++ if (IS_ERR(add_branch)) ++ goto out; ++ ++ err = au_br_init(add_branch, sb, add); ++ if (unlikely(err)) { ++ au_br_do_free(add_branch); ++ goto out; ++ } ++ ++ add_bindex = add->bindex; ++ h_dentry = add->path.dentry; ++ if (!remount) ++ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ else { ++ sysaufs_brs_del(sb, add_bindex); ++ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ sysaufs_brs_add(sb, add_bindex); ++ } ++ ++ if (!add_bindex) { ++ au_cpup_attr_all(root_inode, /*force*/1); ++ sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; ++ } else ++ au_add_nlink(root_inode, h_dentry->d_inode); ++ ++ /* ++ * this test/set prevents aufs from handling unnecesary notify events ++ * of xino files, in a case of re-adding a writable branch which was ++ * once detached from aufs. ++ */ ++ if (au_xino_brid(sb) < 0 ++ && au_br_writable(add_branch->br_perm) ++ && !au_test_fs_bad_xino(h_dentry->d_sb) ++ && add_branch->br_xino.xi_file ++ && add_branch->br_xino.xi_file->f_dentry->d_parent == h_dentry) ++ au_xino_brid_set(sb, add_branch->br_id); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * delete a branch ++ */ ++ ++/* to show the line number, do not make it inlined function */ ++#define AuVerbose(do_info, fmt, ...) do { \ ++ if (do_info) \ ++ pr_info(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++/* ++ * test if the branch is deletable or not. ++ */ ++static int test_dentry_busy(struct dentry *root, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bstart, bend; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *d; ++ struct inode *inode; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ 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_test(d, sigen)) { ++ di_read_lock_child(d, AuLock_IR); ++ if (unlikely(au_dbrange_test(d))) { ++ di_read_unlock(d, AuLock_IR); ++ continue; ++ } ++ } else { ++ di_write_lock_child(d); ++ if (unlikely(au_dbrange_test(d))) { ++ di_write_unlock(d); ++ continue; ++ } ++ err = au_reval_dpath(d, sigen); ++ if (!err) ++ di_downgrade_lock(d, AuLock_IR); ++ else { ++ di_write_unlock(d); ++ break; ++ } ++ } ++ ++ /* AuDbgDentry(d); */ ++ inode = d->d_inode; ++ bstart = au_dbstart(d); ++ bend = au_dbend(d); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_dptr(d, bindex) ++ && ((inode && !S_ISDIR(inode->i_mode)) ++ || bstart == bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy %.*s\n", AuDLNPair(d)); ++ AuDbgDentry(d); ++ } ++ di_read_unlock(d, AuLock_IR); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, ++ unsigned int sigen, const unsigned int verbose) ++{ ++ int err; ++ unsigned long long max, ull; ++ struct inode *i, **array; ++ aufs_bindex_t bstart, bend; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ AuDbg("b%d\n", bindex); ++ for (ull = 0; !err && ull < max; ull++) { ++ i = array[ull]; ++ if (i->i_ino == AUFS_ROOT_INO) ++ continue; ++ ++ /* AuDbgInode(i); */ ++ if (au_iigen(i) == sigen) ++ ii_read_lock_child(i); ++ else { ++ ii_write_lock_child(i); ++ err = au_refresh_hinode_self(i); ++ au_iigen_dec(i); ++ if (!err) ++ ii_downgrade_lock(i); ++ else { ++ ii_write_unlock(i); ++ break; ++ } ++ } ++ ++ bstart = au_ibstart(i); ++ bend = au_ibend(i); ++ if (bstart <= bindex ++ && bindex <= bend ++ && au_h_iptr(i, bindex) ++ && (!S_ISDIR(i->i_mode) || bstart == bend)) { ++ err = -EBUSY; ++ AuVerbose(verbose, "busy i%lu\n", i->i_ino); ++ AuDbgInode(i); ++ } ++ ii_read_unlock(i); ++ } ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static int test_children_busy(struct dentry *root, aufs_bindex_t bindex, ++ const unsigned int verbose) ++{ ++ int err; ++ unsigned int sigen; ++ ++ sigen = au_sigen(root->d_sb); ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = test_dentry_busy(root, bindex, sigen, verbose); ++ if (!err) ++ err = test_inode_busy(root->d_sb, bindex, sigen, verbose); ++ di_write_lock_child(root); /* aufs_write_lock() calls ..._child() */ ++ ++ return err; ++} ++ ++static void au_br_do_del_brp(struct au_sbinfo *sbinfo, ++ const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_branch **brp, **p; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ brp = sbinfo->si_branch + bindex; ++ if (bindex < bend) ++ memmove(brp, brp + 1, sizeof(*brp) * (bend - bindex)); ++ sbinfo->si_branch[0 + bend] = NULL; ++ sbinfo->si_bend--; ++ ++ p = krealloc(sbinfo->si_branch, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ sbinfo->si_branch = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hdp(struct au_dinfo *dinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hdentry *hdp, *p; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ hdp = dinfo->di_hdentry; ++ if (bindex < bend) ++ memmove(hdp + bindex, hdp + bindex + 1, ++ sizeof(*hdp) * (bend - bindex)); ++ hdp[0 + bend].hd_dentry = NULL; ++ dinfo->di_bend--; ++ ++ p = krealloc(hdp, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ dinfo->di_hdentry = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del_hip(struct au_iinfo *iinfo, const aufs_bindex_t bindex, ++ const aufs_bindex_t bend) ++{ ++ struct au_hinode *hip, *p; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ hip = iinfo->ii_hinode + bindex; ++ if (bindex < bend) ++ memmove(hip, hip + 1, sizeof(*hip) * (bend - bindex)); ++ iinfo->ii_hinode[0 + bend].hi_inode = NULL; ++ au_hn_init(iinfo->ii_hinode + bend); ++ iinfo->ii_bend--; ++ ++ p = krealloc(iinfo->ii_hinode, sizeof(*p) * bend, GFP_NOFS); ++ if (p) ++ iinfo->ii_hinode = p; ++ /* harmless error */ ++} ++ ++static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_branch *br) ++{ ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root; ++ struct inode *inode; ++ ++ SiMustWriteLock(sb); ++ ++ root = sb->s_root; ++ inode = root->d_inode; ++ sbinfo = au_sbi(sb); ++ bend = sbinfo->si_bend; ++ ++ dput(au_h_dptr(root, bindex)); ++ au_hiput(au_hi(inode, bindex)); ++ au_br_do_free(br); ++ ++ au_br_do_del_brp(sbinfo, bindex, bend); ++ au_br_do_del_hdp(au_di(root), bindex, bend); ++ au_br_do_del_hip(au_ii(inode), bindex, bend); ++} ++ ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) ++{ ++ int err, rerr, i; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, br_id; ++ unsigned char do_wh, verbose; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ ++ err = 0; ++ bindex = au_find_dbindex(sb->s_root, del->h_path.dentry); ++ if (bindex < 0) { ++ if (remount) ++ goto out; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", del->pathname); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = -EBUSY; ++ mnt_flags = au_mntflags(sb); ++ verbose = !!au_opt_test(mnt_flags, VERBOSE); ++ bend = au_sbend(sb); ++ if (unlikely(!bend)) { ++ AuVerbose(verbose, "no more branches left\n"); ++ goto out; ++ } ++ br = au_sbr(sb, bindex); ++ i = atomic_read(&br->br_count); ++ if (unlikely(i)) { ++ AuVerbose(verbose, "%d file(s) opened\n", i); ++ goto out; ++ } ++ ++ wbr = br->br_wbr; ++ do_wh = wbr && (wbr->wbr_whbase || wbr->wbr_plink || wbr->wbr_orph); ++ if (do_wh) { ++ /* instead of WbrWhMustWriteLock(wbr) */ ++ SiMustWriteLock(sb); ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ } ++ ++ err = test_children_busy(sb->s_root, bindex, verbose); ++ if (unlikely(err)) { ++ if (do_wh) ++ goto out_wh; ++ goto out; ++ } ++ ++ err = 0; ++ br_id = br->br_id; ++ if (!remount) ++ au_br_do_del(sb, bindex, br); ++ else { ++ sysaufs_brs_del(sb, bindex); ++ au_br_do_del(sb, bindex, br); ++ sysaufs_brs_add(sb, bindex); ++ } ++ ++ if (!bindex) { ++ au_cpup_attr_all(sb->s_root->d_inode, /*force*/1); ++ sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes; ++ } else ++ au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode); ++ if (au_opt_test(mnt_flags, PLINK)) ++ au_plink_half_refresh(sb, br_id); ++ ++ if (au_xino_brid(sb) == br_id) ++ au_xino_brid_set(sb, -1); ++ goto out; /* success */ ++ ++out_wh: ++ /* revert */ ++ rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry); ++ if (rerr) ++ pr_warning("failed re-creating base whiteout, %s. (%d)\n", ++ del->pathname, rerr); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * change a branch permission ++ */ ++ ++static void au_warn_ima(void) ++{ ++#ifdef CONFIG_IMA ++ /* since it doesn't support mark_files_ro() */ ++ AuWarn1("RW -> RO makes IMA to produce wrong message\n"); ++#endif ++} ++ ++static int do_need_sigen_inc(int a, int b) ++{ ++ return au_br_whable(a) && !au_br_whable(b); ++} ++ ++static int need_sigen_inc(int old, int new) ++{ ++ return do_need_sigen_inc(old, new) ++ || do_need_sigen_inc(new, old); ++} ++ ++static unsigned long long au_farray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct file **p, *f; ++ struct super_block *sb = arg; ++ ++ n = 0; ++ p = a; ++ lg_global_lock(files_lglock); ++ do_file_list_for_each_entry(sb, f) { ++ if (au_fi(f) ++ && !special_file(f->f_dentry->d_inode->i_mode)) { ++ get_file(f); ++ *p++ = f; ++ n++; ++ AuDebugOn(n > max); ++ } ++ } while_file_list_for_each_entry; ++ lg_global_unlock(files_lglock); ++ ++ return n; ++} ++ ++static struct file **au_farray_alloc(struct super_block *sb, ++ unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_nfiles); ++ return au_array_alloc(max, au_farray_cb, sb); ++} ++ ++static void au_farray_free(struct file **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ if (a[ull]) ++ fput(a[ull]); ++ au_array_free(a); ++} ++ ++static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err, do_warn; ++ unsigned long long ull, max; ++ aufs_bindex_t br_id; ++ struct file *file, *hf, **array; ++ struct inode *inode; ++ struct au_hfile *hfile; ++ ++ array = au_farray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ do_warn = 0; ++ br_id = au_sbr_id(sb, bindex); ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ ++ /* AuDbg("%.*s\n", AuDLNPair(file->f_dentry)); */ ++ fi_read_lock(file); ++ if (unlikely(au_test_mmapped(file))) { ++ err = -EBUSY; ++ AuDbgFile(file); ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ goto out_array; ++ } ++ ++ inode = file->f_dentry->d_inode; ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ if (!S_ISREG(inode->i_mode) ++ || !(file->f_mode & FMODE_WRITE) ++ || hfile->hf_br->br_id != br_id ++ || !(hf->f_mode & FMODE_WRITE)) ++ array[ull] = NULL; ++ else { ++ do_warn = 1; ++ get_file(file); ++ } ++ ++ FiMustNoWaiters(file); ++ fi_read_unlock(file); ++ fput(file); ++ } ++ ++ err = 0; ++ if (do_warn) ++ au_warn_ima(); ++ ++ for (ull = 0; ull < max; ull++) { ++ file = array[ull]; ++ if (!file) ++ continue; ++ ++ /* todo: already flushed? */ ++ /* cf. fs/super.c:mark_files_ro() */ ++ /* fi_read_lock(file); */ ++ hfile = &au_fi(file)->fi_htop; ++ hf = hfile->hf_file; ++ /* fi_read_unlock(file); */ ++ spin_lock(&hf->f_lock); ++ hf->f_mode &= ~FMODE_WRITE; ++ spin_unlock(&hf->f_lock); ++ if (!file_check_writeable(hf)) { ++ file_release_write(hf); ++ mnt_drop_write(hf->f_vfsmnt); ++ } ++ } ++ ++out_array: ++ au_farray_free(array, max); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ struct path path; ++ struct dentry *root; ++ struct au_branch *br; ++ ++ root = sb->s_root; ++ bindex = au_find_dbindex(root, mod->h_root); ++ if (bindex < 0) { ++ if (remount) ++ return 0; /* success */ ++ err = -ENOENT; ++ pr_err("%s no such branch\n", mod->path); ++ goto out; ++ } ++ AuDbg("bindex b%d\n", bindex); ++ ++ err = test_br(mod->h_root->d_inode, mod->perm, mod->path); ++ if (unlikely(err)) ++ goto out; ++ ++ br = au_sbr(sb, bindex); ++ if (br->br_perm == mod->perm) ++ return 0; /* success */ ++ ++ if (au_br_writable(br->br_perm)) { ++ /* remove whiteout base */ ++ err = au_br_init_wh(sb, br, mod->perm, mod->h_root); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!au_br_writable(mod->perm)) { ++ /* rw --> ro, file might be mmapped */ ++ DiMustNoWaiters(root); ++ IiMustNoWaiters(root->d_inode); ++ di_write_unlock(root); ++ err = au_br_mod_files_ro(sb, bindex); ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ if (unlikely(err)) { ++ rerr = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), ++ GFP_NOFS); ++ if (br->br_wbr) { ++ path.mnt = br->br_mnt; ++ path.dentry = mod->h_root; ++ rerr = au_wbr_init(br, sb, br->br_perm, ++ &path); ++ } ++ if (unlikely(rerr)) { ++ AuIOErr("nested error %d (%d)\n", ++ rerr, err); ++ br->br_perm = mod->perm; ++ } ++ } ++ } ++ } else if (au_br_writable(mod->perm)) { ++ /* ro --> rw */ ++ err = -ENOMEM; ++ br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); ++ if (br->br_wbr) { ++ path.mnt = br->br_mnt; ++ path.dentry = mod->h_root; ++ err = au_wbr_init(br, sb, mod->perm, &path); ++ if (unlikely(err)) { ++ kfree(br->br_wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ } ++ ++ if (!err) { ++ *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); ++ br->br_perm = mod->perm; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/branch.h linux-2.6.36/fs/aufs/branch.h +--- linux-2.6.36.orig/fs/aufs/branch.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/branch.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,229 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * branch filesystems and xino for them ++ */ ++ ++#ifndef __AUFS_BRANCH_H__ ++#define __AUFS_BRANCH_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "dynop.h" ++#include "rwsem.h" ++#include "super.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* a xino file */ ++struct au_xino_file { ++ struct file *xi_file; ++ struct mutex xi_nondir_mtx; ++ ++ /* todo: make xino files an array to support huge inode number */ ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *xi_dbgaufs; ++#endif ++}; ++ ++/* members for writable branch only */ ++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last}; ++struct au_wbr { ++ struct au_rwsem wbr_wh_rwsem; ++ struct dentry *wbr_wh[AuBrWh_Last]; ++ atomic_t wbr_wh_running; ++#define wbr_whbase wbr_wh[AuBrWh_BASE] /* whiteout base */ ++#define wbr_plink wbr_wh[AuBrWh_PLINK] /* pseudo-link dir */ ++#define wbr_orph wbr_wh[AuBrWh_ORPH] /* dir for orphans */ ++ ++ /* mfs mode */ ++ unsigned long long wbr_bytes; ++}; ++ ++/* ext2 has 3 types of operations at least, ext3 has 4 */ ++#define AuBrDynOp (AuDyLast * 4) ++ ++/* protected by superblock rwsem */ ++struct au_branch { ++ struct au_xino_file br_xino; ++ ++ aufs_bindex_t br_id; ++ ++ int br_perm; ++ struct vfsmount *br_mnt; ++ spinlock_t br_dykey_lock; ++ struct au_dykey *br_dykey[AuBrDynOp]; ++ atomic_t br_count; ++ ++ struct au_wbr *br_wbr; ++ ++ /* xino truncation */ ++ blkcnt_t br_xino_upper; /* watermark in blocks */ ++ atomic_t br_xino_running; ++ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ struct fsnotify_group *br_hfsn_group; ++ struct fsnotify_ops br_hfsn_ops; ++#endif ++ ++#ifdef CONFIG_SYSFS ++ /* an entry under sysfs per mount-point */ ++ char br_name[8]; ++ struct attribute br_attr; ++#endif ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch permission and attribute */ ++enum { ++ AuBrPerm_RW, /* writable, linkable wh */ ++ AuBrPerm_RO, /* readonly, no wh */ ++ AuBrPerm_RR, /* natively readonly, no wh */ ++ ++ AuBrPerm_RWNoLinkWH, /* un-linkable whiteouts */ ++ ++ AuBrPerm_ROWH, /* whiteout-able */ ++ AuBrPerm_RRWH, /* whiteout-able */ ++ ++ AuBrPerm_Last ++}; ++ ++static inline int au_br_writable(int brperm) ++{ ++ return brperm == AuBrPerm_RW || brperm == AuBrPerm_RWNoLinkWH; ++} ++ ++static inline int au_br_whable(int brperm) ++{ ++ return brperm == AuBrPerm_RW ++ || brperm == AuBrPerm_ROWH ++ || brperm == AuBrPerm_RRWH; ++} ++ ++static inline int au_br_rdonly(struct au_branch *br) ++{ ++ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) ++ || !au_br_writable(br->br_perm)) ++ ? -EROFS : 0; ++} ++ ++static inline int au_br_hnotifyable(int brperm __maybe_unused) ++{ ++#ifdef CONFIG_AUFS_HNOTIFY ++ return brperm != AuBrPerm_RR && brperm != AuBrPerm_RRWH; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* branch.c */ ++struct au_sbinfo; ++void au_br_free(struct au_sbinfo *sinfo); ++int au_br_index(struct super_block *sb, aufs_bindex_t br_id); ++struct au_opt_add; ++int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount); ++struct au_opt_del; ++int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount); ++struct au_opt_mod; ++int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, ++ int *do_refresh); ++ ++/* xino.c */ ++static const loff_t au_loff_max = LLONG_MAX; ++ ++int au_xib_trunc(struct super_block *sb); ++ssize_t xino_fread(au_readf_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos); ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src); ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent); ++ino_t au_xino_new_ino(struct super_block *sb); ++void au_xino_delete_inode(struct inode *inode, const int unlinked); ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino); ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino); ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t hino, ++ struct file *base_file, int do_test); ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex); ++ ++struct au_opt_xino; ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount); ++void au_xino_clr(struct super_block *sb); ++struct file *au_xino_def(struct super_block *sb); ++int au_xino_path(struct seq_file *seq, struct file *file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Superblock to branch */ ++static inline ++aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_id; ++} ++ ++static inline ++struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_mnt; ++} ++ ++static inline ++struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr_mnt(sb, bindex)->mnt_sb; ++} ++ ++static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ atomic_dec(&au_sbr(sb, bindex)->br_count); ++} ++ ++static inline int au_sbr_perm(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_sbr(sb, bindex)->br_perm; ++} ++ ++static inline int au_sbr_whable(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ return au_br_whable(au_sbr_perm(sb, bindex)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * wbr_wh_read_lock, wbr_wh_write_lock ++ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem); ++ ++#define WbrWhMustNoWaiters(wbr) AuRwMustNoWaiters(&wbr->wbr_wh_rwsem) ++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem) ++#define WbrWhMustWriteLock(wbr) AuRwMustWriteLock(&wbr->wbr_wh_rwsem) ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_BRANCH_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/conf.mk linux-2.6.36/fs/aufs/conf.mk +--- linux-2.6.36.orig/fs/aufs/conf.mk 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/conf.mk 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,37 @@ ++ ++AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} ++ ++define AuConf ++ifdef ${1} ++AuConfStr += ${1}=${${1}} ++endif ++endef ++ ++AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ ++ SBILIST \ ++ HNOTIFY HFSNOTIFY \ ++ EXPORT INO_T_64 \ ++ RDU \ ++ SP_IATTR \ ++ SHWH \ ++ BR_RAMFS \ ++ BR_FUSE POLL \ ++ BR_HFSPLUS \ ++ BDEV_LOOP \ ++ DEBUG MAGIC_SYSRQ ++$(foreach i, ${AuConfAll}, \ ++ $(eval $(call AuConf,CONFIG_AUFS_${i}))) ++ ++AuConfName = ${obj}/conf.str ++${AuConfName}.tmp: FORCE ++ @echo ${AuConfStr} | tr ' ' '\n' | sed -e 's/^/"/' -e 's/$$/\\n"/' > $@ ++${AuConfName}: ${AuConfName}.tmp ++ @diff -q $< $@ > /dev/null 2>&1 || { \ ++ echo ' GEN ' $@; \ ++ cp -p $< $@; \ ++ } ++FORCE: ++clean-files += ${AuConfName} ${AuConfName}.tmp ++${obj}/sysfs.o: ${AuConfName} ++ ++-include ${srctree}/${src}/conf_priv.mk +diff -Nur linux-2.6.36.orig/fs/aufs/cpup.c linux-2.6.36/fs/aufs/cpup.c +--- linux-2.6.36.orig/fs/aufs/cpup.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/cpup.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1063 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * copy-up functions, see wbr_policy.c for copy-down ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++void au_cpup_attr_flags(struct inode *dst, struct inode *src) ++{ ++ const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE ++ | S_NOATIME | S_NOCMTIME; ++ ++ dst->i_flags |= src->i_flags & ~mask; ++ if (au_test_fs_notime(dst->i_sb)) ++ dst->i_flags |= S_NOATIME | S_NOCMTIME; ++} ++ ++void au_cpup_attr_timesizes(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ fsstack_copy_attr_times(inode, h_inode); ++ fsstack_copy_inode_size(inode, h_inode); ++} ++ ++void au_cpup_attr_nlink(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ ++ sb = inode->i_sb; ++ bindex = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bindex); ++ if (!force ++ && !S_ISDIR(h_inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode)) ++ return; ++ ++ inode->i_nlink = h_inode->i_nlink; ++ ++ /* ++ * fewer nlink makes find(1) noisy, but larger nlink doesn't. ++ * it may includes whplink directory. ++ */ ++ if (S_ISDIR(h_inode->i_mode)) { ++ bend = au_ibend(inode); ++ for (bindex++; bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) ++ au_add_nlink(inode, h_inode); ++ } ++ } ++} ++ ++void au_cpup_attr_changeable(struct inode *inode) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(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); ++ au_cpup_attr_flags(inode, h_inode); ++} ++ ++void au_cpup_igen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ iinfo->ii_higen = h_inode->i_generation; ++ iinfo->ii_hsb1 = h_inode->i_sb; ++} ++ ++void au_cpup_attr_all(struct inode *inode, int force) ++{ ++ struct inode *h_inode; ++ ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ au_cpup_attr_changeable(inode); ++ if (inode->i_nlink > 0) ++ au_cpup_attr_nlink(inode, force); ++ inode->i_rdev = h_inode->i_rdev; ++ inode->i_blkbits = h_inode->i_blkbits; ++ au_cpup_igen(inode, h_inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Note: dt_dentry and dt_h_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 path *h_path) ++{ ++ struct inode *h_inode; ++ ++ dt->dt_dentry = dentry; ++ dt->dt_h_path = *h_path; ++ h_inode = h_path->dentry->d_inode; ++ dt->dt_atime = h_inode->i_atime; ++ dt->dt_mtime = h_inode->i_mtime; ++ /* smp_mb(); */ ++} ++ ++void au_dtime_revert(struct au_dtime *dt) ++{ ++ struct iattr attr; ++ int err; ++ ++ 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; ++ ++ err = vfsub_notify_change(&dt->dt_h_path, &attr); ++ if (unlikely(err)) ++ pr_warning("restoring timestamps failed(%d). ignored\n", err); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static noinline_for_stack ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct path h_path; ++ struct inode *h_isrc, *h_idst; ++ ++ h_path.dentry = au_h_dptr(dst, bindex); ++ h_idst = h_path.dentry->d_inode; ++ h_path.mnt = au_sbr_mnt(dst->d_sb, bindex); ++ h_isrc = h_src->d_inode; ++ ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID ++ | ATTR_ATIME | ATTR_MTIME ++ | ATTR_ATIME_SET | ATTR_MTIME_SET; ++ 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; ++ if (h_idst->i_mode != h_isrc->i_mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ } ++ sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_isrc); ++ err = vfsub_notify_change(&h_path, &ia); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path.dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_notify_change(&h_path, &ia); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, ++ char *buf, unsigned long blksize) ++{ ++ int err; ++ size_t sz, rbytes, wbytes; ++ unsigned char all_zero; ++ char *p, *zp; ++ struct mutex *h_mtx; ++ /* reduce stack usage */ ++ struct iattr *ia; ++ ++ zp = page_address(ZERO_PAGE(0)); ++ if (unlikely(!zp)) ++ return -ENOMEM; /* possible? */ ++ ++ err = 0; ++ all_zero = 0; ++ while (len) { ++ AuDbg("len %lld\n", len); ++ sz = blksize; ++ if (len < blksize) ++ sz = len; ++ ++ rbytes = 0; ++ /* todo: signal_pending? */ ++ while (!rbytes || err == -EAGAIN || err == -EINTR) { ++ rbytes = vfsub_read_k(src, buf, sz, &src->f_pos); ++ err = rbytes; ++ } ++ if (unlikely(err < 0)) ++ break; ++ ++ all_zero = 0; ++ if (len >= rbytes && rbytes == blksize) ++ all_zero = !memcmp(buf, zp, rbytes); ++ if (!all_zero) { ++ wbytes = rbytes; ++ p = buf; ++ while (wbytes) { ++ size_t b; ++ ++ b = vfsub_write_k(dst, p, wbytes, &dst->f_pos); ++ err = b; ++ /* todo: signal_pending? */ ++ if (unlikely(err == -EAGAIN || err == -EINTR)) ++ continue; ++ if (unlikely(err < 0)) ++ break; ++ wbytes -= b; ++ p += b; ++ } ++ } else { ++ loff_t res; ++ ++ AuLabel(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 (!err && all_zero) { ++ AuLabel(last hole); ++ ++ err = 1; ++ if (au_test_nfs(dst->f_dentry->d_sb)) { ++ /* nfs requires this step to make last hole */ ++ /* is this only nfs? */ ++ do { ++ /* todo: signal_pending? */ ++ err = vfsub_write_k(dst, "\0", 1, &dst->f_pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ if (err == 1) ++ dst->f_pos--; ++ } ++ ++ if (err == 1) { ++ ia = (void *)buf; ++ ia->ia_size = dst->f_pos; ++ ia->ia_valid = ATTR_SIZE | ATTR_FILE; ++ ia->ia_file = dst; ++ h_mtx = &dst->f_dentry->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ err = vfsub_notify_change(&dst->f_path, ia); ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++ return err; ++} ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len) ++{ ++ int err; ++ unsigned long blksize; ++ unsigned char do_kfree; ++ char *buf; ++ ++ err = -ENOMEM; ++ blksize = dst->f_dentry->d_sb->s_blocksize; ++ if (!blksize || PAGE_SIZE < blksize) ++ blksize = PAGE_SIZE; ++ AuDbg("blksize %lu\n", blksize); ++ do_kfree = (blksize != PAGE_SIZE && blksize >= sizeof(struct iattr *)); ++ if (do_kfree) ++ buf = kmalloc(blksize, GFP_NOFS); ++ else ++ buf = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!buf)) ++ goto out; ++ ++ if (len > (1 << 22)) ++ AuDbg("copying a large file %lld\n", (long long)len); ++ ++ src->f_pos = 0; ++ dst->f_pos = 0; ++ err = au_do_copy_file(dst, src, len, buf, blksize); ++ if (do_kfree) ++ kfree(buf); ++ else ++ free_page((unsigned long)buf); ++ ++out: ++ return err; ++} ++ ++/* ++ * to support a sparse file which is opened with O_APPEND, ++ * we need to close the file. ++ */ ++static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len) ++{ ++ int err, i; ++ enum { SRC, DST }; ++ struct { ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ struct dentry *dentry; ++ struct file *file; ++ void *label, *label_file; ++ } *f, file[] = { ++ { ++ .bindex = bsrc, ++ .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, ++ .file = NULL, ++ .label = &&out, ++ .label_file = &&out_src ++ }, ++ { ++ .bindex = bdst, ++ .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, ++ .file = NULL, ++ .label = &&out_src, ++ .label_file = &&out_dst ++ } ++ }; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ sb = dentry->d_sb; ++ f = file; ++ for (i = 0; i < 2; i++, f++) { ++ f->dentry = au_h_dptr(dentry, f->bindex); ++ f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL); ++ err = PTR_ERR(f->file); ++ if (IS_ERR(f->file)) ++ goto *f->label; ++ err = -EINVAL; ++ if (unlikely(!f->file->f_op)) ++ goto *f->label_file; ++ } ++ ++ /* try stopping to update while we copyup */ ++ IMustLock(file[SRC].dentry->d_inode); ++ err = au_copy_file(file[DST].file, file[SRC].file, len); ++ ++out_dst: ++ fput(file[DST].file); ++ au_sbr_put(sb, file[DST].bindex); ++out_src: ++ fput(file[SRC].file); ++ au_sbr_put(sb, file[SRC].bindex); ++out: ++ return err; ++} ++ ++static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, ++ struct inode *h_dir, struct path *h_path) ++{ ++ int err, rerr; ++ loff_t l; ++ ++ err = 0; ++ l = i_size_read(au_h_iptr(dentry->d_inode, bsrc)); ++ if (len == -1 || l < len) ++ len = l; ++ if (len) ++ err = au_cp_regular(dentry, bdst, bsrc, len); ++ if (!err) ++ goto out; /* success */ ++ ++ rerr = vfsub_unlink(h_dir, h_path, /*force*/0); ++ if (rerr) { ++ AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n", ++ AuDLNPair(h_path->dentry), err, rerr); ++ err = -EIO; ++ } ++ ++out: ++ return err; ++} ++ ++static int au_do_cpup_symlink(struct path *h_path, struct dentry *h_src, ++ struct inode *h_dir) ++{ ++ int err, symlen; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } sym; ++ ++ err = -ENOSYS; ++ if (unlikely(!h_src->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = -ENOMEM; ++ sym.k = __getname_gfp(GFP_NOFS); ++ if (unlikely(!sym.k)) ++ goto out; ++ ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ symlen = h_src->d_inode->i_op->readlink(h_src, sym.u, PATH_MAX); ++ err = symlen; ++ set_fs(old_fs); ++ ++ if (symlen > 0) { ++ sym.k[symlen] = 0; ++ err = vfsub_symlink(h_dir, h_path, sym.k); ++ } ++ __putname(sym.k); ++ ++out: ++ return err; ++} ++ ++/* return with the lower dst inode is locked */ ++static noinline_for_stack ++int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err; ++ umode_t mode; ++ unsigned int mnt_flags; ++ unsigned char isdir; ++ const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME); ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *h_inode, *h_dir; ++ struct super_block *sb; ++ ++ /* bsrc branch can be ro/rw. */ ++ h_src = au_h_dptr(dentry, bsrc); ++ h_inode = h_src->d_inode; ++ AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc)); ++ ++ /* try stopping to be referenced while we are creating */ ++ h_dst = au_h_dptr(dentry, bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDebugOn(h_parent != h_dst->d_parent); ++ ++ sb = dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, bdst); ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ ++ isdir = 0; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ /* try stopping to update while we are referencing */ ++ IMustLock(h_inode); ++ err = vfsub_create(h_dir, &h_path, mode | S_IWUSR); ++ if (!err) ++ err = au_do_cpup_regular ++ (dentry, bdst, bsrc, len, ++ au_h_iptr(dst_parent->d_inode, bdst), &h_path); ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ err = vfsub_mkdir(h_dir, &h_path, mode); ++ if (!err) { ++ /* ++ * strange behaviour from the users view, ++ * particularry setattr case ++ */ ++ if (au_ibstart(dst_parent->d_inode) == bdst) ++ au_cpup_attr_nlink(dst_parent->d_inode, ++ /*force*/1); ++ au_cpup_attr_nlink(dentry->d_inode, /*force*/1); ++ } ++ break; ++ case S_IFLNK: ++ err = au_do_cpup_symlink(&h_path, h_src, h_dir); ++ break; ++ case S_IFCHR: ++ case S_IFBLK: ++ AuDebugOn(!capable(CAP_MKNOD)); ++ /*FALLTHROUGH*/ ++ case S_IFIFO: ++ case S_IFSOCK: ++ err = vfsub_mknod(h_dir, &h_path, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown inode type 0%o\n", mode); ++ err = -EIO; ++ } ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, UDBA_NONE) ++ && !isdir ++ && au_opt_test(mnt_flags, XINO) ++ && h_inode->i_nlink == 1 ++ /* todo: unnecessary? */ ++ /* && dentry->d_inode->i_nlink == 1 */ ++ && bdst < bsrc ++ && !au_ftest_cpup(flags, KEEPLINO)) ++ au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0); ++ /* ignore this error */ ++ ++ if (do_dt) ++ au_dtime_revert(&dt); ++ return err; ++} ++ ++/* ++ * copyup the @dentry from @bsrc to @bdst. ++ * the caller must set the both of lower dentries. ++ * @len is for truncating when it is -1 copyup the entire file. ++ * in link/rename cases, @dst_parent may be different from the real one. ++ */ ++static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err, rerr; ++ aufs_bindex_t old_ibstart; ++ unsigned char isdir, plink; ++ struct au_dtime dt; ++ struct path h_path; ++ struct dentry *h_src, *h_dst, *h_parent; ++ struct inode *dst_inode, *h_dir, *inode; ++ struct super_block *sb; ++ ++ AuDebugOn(bsrc <= bdst); ++ ++ sb = dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, bdst); ++ h_dst = au_h_dptr(dentry, bdst); ++ h_parent = h_dst->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_src = au_h_dptr(dentry, bsrc); ++ inode = dentry->d_inode; ++ ++ if (!dst_parent) ++ dst_parent = dget_parent(dentry); ++ else ++ dget(dst_parent); ++ ++ plink = !!au_opt_test(au_mntflags(sb), PLINK); ++ dst_inode = au_h_iptr(inode, bdst); ++ if (dst_inode) { ++ if (unlikely(!plink)) { ++ err = -EIO; ++ AuIOErr("hi%lu(i%lu) exists on b%d " ++ "but plink is disabled\n", ++ dst_inode->i_ino, inode->i_ino, bdst); ++ goto out; ++ } ++ ++ if (dst_inode->i_nlink) { ++ const int do_dt = au_ftest_cpup(flags, DTIME); ++ ++ h_src = au_plink_lkup(inode, bdst); ++ err = PTR_ERR(h_src); ++ if (IS_ERR(h_src)) ++ goto out; ++ if (unlikely(!h_src->d_inode)) { ++ err = -EIO; ++ AuIOErr("i%lu exists on a upper branch " ++ "but not pseudo-linked\n", ++ inode->i_ino); ++ dput(h_src); ++ goto out; ++ } ++ ++ if (do_dt) { ++ h_path.dentry = h_parent; ++ au_dtime_store(&dt, dst_parent, &h_path); ++ } ++ h_path.dentry = h_dst; ++ err = vfsub_link(h_src, h_dir, &h_path); ++ if (do_dt) ++ au_dtime_revert(&dt); ++ dput(h_src); ++ goto out; ++ } else ++ /* todo: cpup_wh_file? */ ++ /* udba work */ ++ au_update_ibrange(inode, /*do_put_zero*/1); ++ } ++ ++ old_ibstart = au_ibstart(inode); ++ err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent); ++ if (unlikely(err)) ++ goto out; ++ dst_inode = h_dst->d_inode; ++ mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ ++ err = cpup_iattr(dentry, bdst, h_src); ++ isdir = S_ISDIR(dst_inode->i_mode); ++ if (!err) { ++ if (bdst < old_ibstart) { ++ if (S_ISREG(inode->i_mode)) { ++ err = au_dy_iaop(inode, bdst, dst_inode); ++ if (unlikely(err)) ++ goto out_rev; ++ } ++ au_set_ibstart(inode, bdst); ++ } ++ au_set_h_iptr(inode, bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ mutex_unlock(&dst_inode->i_mutex); ++ if (!isdir ++ && h_src->d_inode->i_nlink > 1 ++ && plink) ++ au_plink_append(inode, bdst, h_dst); ++ goto out; /* success */ ++ } ++ ++ /* revert */ ++out_rev: ++ h_path.dentry = h_parent; ++ mutex_unlock(&dst_inode->i_mutex); ++ au_dtime_store(&dt, dst_parent, &h_path); ++ h_path.dentry = h_dst; ++ if (!isdir) ++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ else ++ rerr = vfsub_rmdir(h_dir, &h_path); ++ au_dtime_revert(&dt); ++ if (rerr) { ++ AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); ++ err = -EIO; ++ } ++ ++out: ++ dput(dst_parent); ++ return err; ++} ++ ++struct au_cpup_single_args { ++ int *errp; ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++ unsigned int flags; ++ struct dentry *dst_parent; ++}; ++ ++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, a->dst_parent); ++} ++ ++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent) ++{ ++ int err, wkq_err; ++ umode_t mode; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bsrc); ++ mode = h_dentry->d_inode->i_mode & S_IFMT; ++ if ((mode != S_IFCHR && mode != S_IFBLK) ++ || capable(CAP_MKNOD)) ++ err = au_cpup_single(dentry, bdst, bsrc, len, flags, ++ dst_parent); ++ else { ++ struct au_cpup_single_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len, ++ .flags = flags, ++ .dst_parent = dst_parent ++ }; ++ wkq_err = au_wkq_wait(au_call_cpup_single, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ++ * copyup the @dentry from the first active lower branch to @bdst, ++ * using au_cpup_single(). ++ */ ++static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags) ++{ ++ int err; ++ aufs_bindex_t bsrc, bend; ++ ++ bend = au_dbend(dentry); ++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) ++ if (au_h_dptr(dentry, bsrc)) ++ break; ++ ++ err = au_lkup_neg(dentry, bdst); ++ if (!err) { ++ err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL); ++ if (!err) ++ return 0; /* success */ ++ ++ /* revert */ ++ au_set_h_dptr(dentry, bdst, NULL); ++ au_set_dbstart(dentry, bsrc); ++ } ++ ++ return err; ++} ++ ++struct au_cpup_simple_args { ++ int *errp; ++ struct dentry *dentry; ++ aufs_bindex_t bdst; ++ loff_t len; ++ unsigned int flags; ++}; ++ ++static void au_call_cpup_simple(void *args) ++{ ++ struct au_cpup_simple_args *a = args; ++ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags); ++} ++ ++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags) ++{ ++ int err, wkq_err; ++ unsigned char do_sio; ++ struct dentry *parent; ++ struct inode *h_dir; ++ ++ parent = dget_parent(dentry); ++ h_dir = au_h_iptr(parent->d_inode, bdst); ++ do_sio = !!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE); ++ 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); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * copyup the deleted file for writing. ++ */ ++static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *wh_dentry, struct file *file, ++ loff_t len) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_d_dst, *h_d_start; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bstart = dinfo->di_bstart; ++ hdp = dinfo->di_hdentry; ++ h_d_dst = hdp[0 + bdst].hd_dentry; ++ dinfo->di_bstart = bdst; ++ hdp[0 + bdst].hd_dentry = wh_dentry; ++ if (file) { ++ h_d_start = hdp[0 + bstart].hd_dentry; ++ hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry; ++ } ++ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, ++ /*h_parent*/NULL); ++ if (file) { ++ if (!err) ++ err = au_reopen_nondir(file); ++ hdp[0 + bstart].hd_dentry = h_d_start; ++ } ++ hdp[0 + bdst].hd_dentry = h_d_dst; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ struct file *file) ++{ ++ int err; ++ struct au_dtime dt; ++ struct dentry *parent, *h_parent, *wh_dentry; ++ struct au_branch *br; ++ struct path h_path; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ wh_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out; ++ ++ h_path.dentry = h_parent; ++ h_path.mnt = br->br_mnt; ++ au_dtime_store(&dt, parent, &h_path); ++ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); ++ if (unlikely(err)) ++ goto out_wh; ++ ++ dget(wh_dentry); ++ h_path.dentry = wh_dentry; ++ if (!S_ISDIR(wh_dentry->d_inode->i_mode)) ++ err = vfsub_unlink(h_parent->d_inode, &h_path, /*force*/0); ++ else ++ err = vfsub_rmdir(h_parent->d_inode, &h_path); ++ if (unlikely(err)) { ++ AuIOErr("failed remove copied-up tmp file %.*s(%d)\n", ++ AuDLNPair(wh_dentry), err); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ au_set_hi_wh(dentry->d_inode, bdst, wh_dentry); ++ ++out_wh: ++ dput(wh_dentry); ++out: ++ dput(parent); ++ 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, *h_orph, *h_parent, *h_dentry; ++ struct inode *dir, *h_dir, *h_tmpdir, *h_inode; ++ struct au_wbr *wbr; ++ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_orph = NULL; ++ h_parent = NULL; ++ h_dir = au_igrab(au_h_iptr(dir, bdst)); ++ h_tmpdir = h_dir; ++ if (!h_dir->i_nlink) { ++ wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; ++ h_orph = wbr->wbr_orph; ++ ++ h_parent = dget(au_h_dptr(parent, bdst)); ++ au_set_h_dptr(parent, bdst, dget(h_orph)); ++ h_tmpdir = h_orph->d_inode; ++ au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); ++ ++ /* this temporary unlock is safe */ ++ if (file) ++ h_dentry = au_hf_top(file)->f_dentry; ++ else ++ h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); ++ h_inode = h_dentry->d_inode; ++ IMustLock(h_inode); ++ mutex_unlock(&h_inode->i_mutex); ++ mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ /* todo: au_h_open_pre()? */ ++ } ++ ++ if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE)) ++ 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); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ if (h_orph) { ++ mutex_unlock(&h_tmpdir->i_mutex); ++ /* todo: au_h_open_post()? */ ++ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); ++ au_set_h_dptr(parent, bdst, h_parent); ++ } ++ iput(h_dir); ++ dput(parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generic routine for both of copy-up and copy-down. ++ */ ++/* cf. revalidate function in file.c */ ++int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, ++ int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent, void *arg), ++ void *arg) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *d, *parent, *h_parent, *real_parent; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (IS_ROOT(parent)) ++ goto out; ++ ++ au_pin_init(&pin, dentry, bdst, AuLsc_DI_PARENT2, AuLsc_I_PARENT2, ++ au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); ++ ++ /* do not use au_dpage */ ++ real_parent = parent; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bdst); ++ if (h_parent) ++ goto out; /* success */ ++ ++ /* find top dir which is necessary to cpup */ ++ do { ++ d = parent; ++ dput(parent); ++ parent = dget_parent(d); ++ di_read_lock_parent3(parent, !AuLock_IR); ++ h_parent = au_h_dptr(parent, bdst); ++ di_read_unlock(parent, !AuLock_IR); ++ } while (!h_parent); ++ ++ if (d != real_parent) ++ di_write_lock_child3(d); ++ ++ /* somebody else might create while we were sleeping */ ++ if (!au_h_dptr(d, bdst) || !au_h_dptr(d, bdst)->d_inode) { ++ if (au_h_dptr(d, bdst)) ++ au_update_dbstart(d); ++ ++ au_pin_set_dentry(&pin, d); ++ err = au_do_pin(&pin); ++ if (!err) { ++ err = cp(d, bdst, h_parent, arg); ++ au_unpin(&pin); ++ } ++ } ++ ++ if (d != real_parent) ++ di_write_unlock(d); ++ if (unlikely(err)) ++ break; ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent __maybe_unused , ++ void *arg __maybe_unused) ++{ ++ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); ++} ++ ++int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ return au_cp_dirs(dentry, bdst, au_cpup_dir, NULL); ++} ++ ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *dir; ++ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ err = 0; ++ if (au_h_iptr(dir, bdst)) ++ goto out; ++ ++ di_read_unlock(parent, AuLock_IR); ++ di_write_lock_parent(parent); ++ /* someone else might change our inode while we were sleeping */ ++ if (!au_h_iptr(dir, bdst)) ++ err = au_cpup_dirs(dentry, bdst); ++ di_downgrade_lock(parent, AuLock_IR); ++ ++out: ++ dput(parent); ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/cpup.h linux-2.6.36/fs/aufs/cpup.h +--- linux-2.6.36.orig/fs/aufs/cpup.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/cpup.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,83 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * copy-up/down functions ++ */ ++ ++#ifndef __AUFS_CPUP_H__ ++#define __AUFS_CPUP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct inode; ++struct file; ++ ++void au_cpup_attr_flags(struct inode *dst, struct inode *src); ++void au_cpup_attr_timesizes(struct inode *inode); ++void au_cpup_attr_nlink(struct inode *inode, int force); ++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, int force); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cpup flags */ ++#define AuCpup_DTIME 1 /* do dtime_store/revert */ ++#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, ++ for link(2) */ ++#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) ++#define au_fset_cpup(flags, name) \ ++ do { (flags) |= AuCpup_##name; } while (0) ++#define au_fclr_cpup(flags, name) \ ++ do { (flags) &= ~AuCpup_##name; } while (0) ++ ++int au_copy_file(struct file *dst, struct file *src, loff_t len); ++int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, ++ aufs_bindex_t bsrc, loff_t len, unsigned int flags, ++ struct dentry *dst_parent); ++int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, ++ unsigned int flags); ++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, ++ 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); ++int au_test_and_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* keep timestamps when copyup */ ++struct au_dtime { ++ struct dentry *dt_dentry; ++ struct path dt_h_path; ++ struct timespec dt_atime, dt_mtime; ++}; ++void au_dtime_store(struct au_dtime *dt, struct dentry *dentry, ++ struct path *h_path); ++void au_dtime_revert(struct au_dtime *dt); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_CPUP_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/dbgaufs.c linux-2.6.36/fs/aufs/dbgaufs.c +--- linux-2.6.36.orig/fs/aufs/dbgaufs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dbgaufs.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,334 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#include ++#include "aufs.h" ++ ++#ifndef CONFIG_SYSFS ++#error DEBUG_FS depends upon SYSFS ++#endif ++ ++static struct dentry *dbgaufs; ++static const mode_t dbgaufs_mode = S_IRUSR | S_IRGRP | S_IROTH; ++ ++/* 20 is max digits length of ulong 64 */ ++struct dbgaufs_arg { ++ int n; ++ char a[20 * 4]; ++}; ++ ++/* ++ * common function for all XINO files ++ */ ++static int dbgaufs_xi_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ kfree(file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_xi_open(struct file *xf, struct file *file, int do_fcnt) ++{ ++ int err; ++ struct kstat st; ++ struct dbgaufs_arg *p; ++ ++ err = -ENOMEM; ++ p = kmalloc(sizeof(*p), GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ p->n = 0; ++ file->private_data = p; ++ if (!xf) ++ goto out; ++ ++ err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st); ++ if (!err) { ++ if (do_fcnt) ++ p->n = snprintf ++ (p->a, sizeof(p->a), "%ld, %llux%lu %lld\n", ++ (long)file_count(xf), st.blocks, st.blksize, ++ (long long)st.size); ++ else ++ p->n = snprintf(p->a, sizeof(p->a), "%llux%lu %lld\n", ++ st.blocks, st.blksize, ++ (long long)st.size); ++ AuDebugOn(p->n >= sizeof(p->a)); ++ } else { ++ p->n = snprintf(p->a, sizeof(p->a), "err %d\n", err); ++ err = 0; ++ } ++ ++out: ++ return err; ++ ++} ++ ++static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int dbgaufs_xib_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xib, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xib_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xib_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DbgaufsXi_PREFIX "xi" ++ ++static int dbgaufs_xino_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ long l; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct file *xf; ++ struct qstr *name; ++ ++ err = -ENOENT; ++ xf = NULL; ++ name = &file->f_dentry->d_name; ++ if (unlikely(name->len < sizeof(DbgaufsXi_PREFIX) ++ || memcmp(name->name, DbgaufsXi_PREFIX, ++ sizeof(DbgaufsXi_PREFIX) - 1))) ++ goto out; ++ err = strict_strtol(name->name + sizeof(DbgaufsXi_PREFIX) - 1, 10, &l); ++ if (unlikely(err)) ++ goto out; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (l <= au_sbend(sb)) { ++ xf = au_sbr(sb, (aufs_bindex_t)l)->br_xino.xi_file; ++ err = dbgaufs_xi_open(xf, file, /*do_fcnt*/1); ++ } else ++ err = -ENOENT; ++ si_read_unlock(sb); ++ ++out: ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xino_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xino_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ aufs_bindex_t bend; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ ++ if (!au_sbi(sb)->si_dbgaufs) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ if (xi->xi_dbgaufs) { ++ debugfs_remove(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = NULL; ++ } ++ } ++} ++ ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_sbinfo *sbinfo; ++ struct dentry *parent; ++ struct au_branch *br; ++ struct au_xino_file *xi; ++ aufs_bindex_t bend; ++ char name[sizeof(DbgaufsXi_PREFIX) + 5]; /* "xi" bindex NULL */ ++ ++ sbinfo = au_sbi(sb); ++ parent = sbinfo->si_dbgaufs; ++ if (!parent) ++ return; ++ ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ snprintf(name, sizeof(name), DbgaufsXi_PREFIX "%d", bindex); ++ br = au_sbr(sb, bindex); ++ xi = &br->br_xino; ++ AuDebugOn(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = debugfs_create_file(name, dbgaufs_mode, parent, ++ sbinfo, &dbgaufs_xino_fop); ++ /* ignore an error */ ++ if (unlikely(!xi->xi_dbgaufs)) ++ AuWarn1("failed %s under debugfs\n", name); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++static int dbgaufs_xigen_open(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ err = dbgaufs_xi_open(sbinfo->si_xigen, file, /*do_fcnt*/0); ++ si_read_unlock(sb); ++ return err; ++} ++ ++static const struct file_operations dbgaufs_xigen_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_xigen_open, ++ .release = dbgaufs_xi_release, ++ .read = dbgaufs_xi_read ++}; ++ ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ /* ++ * This function is a dynamic '__init' fucntion actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -EIO; ++ sbinfo->si_dbgaufs_xigen = debugfs_create_file ++ ("xigen", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xigen_fop); ++ if (sbinfo->si_dbgaufs_xigen) ++ err = 0; ++ ++ return err; ++} ++#else ++static int dbgaufs_xigen_init(struct au_sbinfo *sbinfo) ++{ ++ return 0; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__init' fucntion actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ debugfs_remove_recursive(sbinfo->si_dbgaufs); ++ sbinfo->si_dbgaufs = NULL; ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++int dbgaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ char name[SysaufsSiNameLen]; ++ ++ /* ++ * This function is a dynamic '__init' fucntion actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++ ++ err = -ENOENT; ++ if (!dbgaufs) { ++ AuErr1("/debug/aufs is uninitialized\n"); ++ goto out; ++ } ++ ++ err = -EIO; ++ sysaufs_name(sbinfo, name); ++ sbinfo->si_dbgaufs = debugfs_create_dir(name, dbgaufs); ++ if (unlikely(!sbinfo->si_dbgaufs)) ++ goto out; ++ kobject_get(&sbinfo->si_kobj); ++ ++ sbinfo->si_dbgaufs_xib = debugfs_create_file ++ ("xib", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_xib_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_xib)) ++ goto out_dir; ++ ++ err = dbgaufs_xigen_init(sbinfo); ++ if (!err) ++ goto out; /* success */ ++ ++out_dir: ++ dbgaufs_si_fin(sbinfo); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void dbgaufs_fin(void) ++{ ++ debugfs_remove(dbgaufs); ++} ++ ++int __init dbgaufs_init(void) ++{ ++ int err; ++ ++ err = -EIO; ++ dbgaufs = debugfs_create_dir(AUFS_NAME, NULL); ++ if (dbgaufs) ++ err = 0; ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/dbgaufs.h linux-2.6.36/fs/aufs/dbgaufs.h +--- linux-2.6.36.orig/fs/aufs/dbgaufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dbgaufs.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,52 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * debugfs interface ++ */ ++ ++#ifndef __DBGAUFS_H__ ++#define __DBGAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct super_block; ++struct au_sbinfo; ++ ++#ifdef CONFIG_DEBUG_FS ++/* dbgaufs.c */ ++void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void dbgaufs_si_fin(struct au_sbinfo *sbinfo); ++int dbgaufs_si_init(struct au_sbinfo *sbinfo); ++void dbgaufs_fin(void); ++int __init dbgaufs_init(void); ++#else ++AuStubVoid(dbgaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(dbgaufs_si_fin, struct au_sbinfo *sbinfo) ++AuStubInt0(dbgaufs_si_init, struct au_sbinfo *sbinfo) ++AuStubVoid(dbgaufs_fin, void) ++AuStubInt0(__init dbgaufs_init, void) ++#endif /* CONFIG_DEBUG_FS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __DBGAUFS_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/dcsub.c linux-2.6.36/fs/aufs/dcsub.c +--- linux-2.6.36.orig/fs/aufs/dcsub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dcsub.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#include "aufs.h" ++ ++static void au_dpage_free(struct au_dpage *dpage) ++{ ++ int i; ++ struct dentry **p; ++ ++ p = dpage->dentries; ++ for (i = 0; i < dpage->ndentry; i++) ++ dput(*p++); ++ free_page((unsigned long)dpage->dentries); ++} ++ ++int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp) ++{ ++ int err; ++ void *p; ++ ++ 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: ++ return err; ++} ++ ++void au_dpages_free(struct au_dcsub_pages *dpages) ++{ ++ int i; ++ struct au_dpage *p; ++ ++ p = dpages->dpages; ++ for (i = 0; i < dpages->ndpage; i++) ++ au_dpage_free(p++); ++ 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; ++ ++ dpage = dpages->dpages + dpages->ndpage - 1; ++ sz = PAGE_SIZE / sizeof(dentry); ++ if (unlikely(dpage->ndentry >= sz)) { ++ AuLabel(new dpage); ++ err = -ENOMEM; ++ sz = dpages->ndpage * sizeof(*dpages->dpages); ++ p = au_kzrealloc(dpages->dpages, sz, ++ sz + sizeof(*dpages->dpages), gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpages->dpages = p; ++ dpage = dpages->dpages + dpages->ndpage; ++ p = (void *)__get_free_page(gfp); ++ if (unlikely(!p)) ++ goto out; ++ ++ dpage->ndentry = 0; ++ dpage->dentries = p; ++ dpages->ndpage++; ++ } ++ ++ /* d_count can be zero */ ++ dpage->dentries[dpage->ndentry++] = dget_locked(dentry); ++ return 0; /* success */ ++ ++out: ++ 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; ++ ++ err = 0; ++ spin_lock(&dcache_lock); ++repeat: ++ next = this_parent->d_subdirs.next; ++resume: ++ if (this_parent->d_sb == sb ++ && !IS_ROOT(this_parent) ++ && au_di(this_parent) ++ && (!test || test(this_parent, arg))) { ++ err = au_dpages_append(dpages, this_parent, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ while (next != &this_parent->d_subdirs) { ++ struct list_head *tmp = next; ++ struct dentry *dentry = list_entry(tmp, struct dentry, ++ d_u.d_child); ++ next = tmp->next; ++ if (!list_empty(&dentry->d_subdirs)) { ++ this_parent = dentry; ++ goto repeat; ++ } ++ if (dentry->d_sb == sb ++ && au_di(dentry) ++ && (!test || test(dentry, arg))) { ++ err = au_dpages_append(dpages, dentry, GFP_ATOMIC); ++ if (unlikely(err)) ++ goto out; ++ } ++ } ++ ++ if (this_parent != root) { ++ next = this_parent->d_u.d_child.next; ++ this_parent = this_parent->d_parent; /* dcache_lock is locked */ ++ goto resume; ++ } ++out: ++ spin_unlock(&dcache_lock); ++ 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; ++ ++ 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); ++ ++ return err; ++} ++ ++static inline int au_dcsub_dpages_aufs(struct dentry *dentry, void *arg) ++{ ++ return au_di(dentry) && dentry->d_sb == arg; ++} ++ ++int au_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include) ++{ ++ return au_dcsub_pages_rev(dpages, dentry, do_include, ++ au_dcsub_dpages_aufs, dentry->d_sb); ++} ++ ++int au_test_subdir(struct dentry *d1, struct dentry *d2) ++{ ++ struct path path[2] = { ++ { ++ .dentry = d1 ++ }, ++ { ++ .dentry = d2 ++ } ++ }; ++ ++ return path_is_under(path + 0, path + 1); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/dcsub.h linux-2.6.36/fs/aufs/dcsub.h +--- linux-2.6.36.orig/fs/aufs/dcsub.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dcsub.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,100 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sub-routines for dentry cache ++ */ ++ ++#ifndef __AUFS_DCSUB_H__ ++#define __AUFS_DCSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct dentry; ++ ++struct au_dpage { ++ int ndentry; ++ struct dentry **dentries; ++}; ++ ++struct au_dcsub_pages { ++ int ndpage; ++ struct au_dpage *dpages; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dcsub.c */ ++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_dcsub_pages_rev_aufs(struct au_dcsub_pages *dpages, ++ struct dentry *dentry, int do_include); ++int au_test_subdir(struct dentry *d1, struct dentry *d2); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int au_d_removed(struct dentry *d) ++{ ++ return !IS_ROOT(d) && d_unhashed(d); ++} ++ ++static inline int au_d_hashed_positive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode = d->d_inode; ++ err = 0; ++ if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink)) ++ err = -ENOENT; ++ return err; ++} ++ ++static inline int au_d_alive(struct dentry *d) ++{ ++ int err; ++ struct inode *inode; ++ err = 0; ++ if (!IS_ROOT(d)) ++ err = au_d_hashed_positive(d); ++ else { ++ inode = d->d_inode; ++ if (unlikely(au_d_removed(d) || !inode || !inode->i_nlink)) ++ err = -ENOENT; ++ } ++ return err; ++} ++ ++static inline int au_alive_dir(struct dentry *d) ++{ ++ int err; ++ err = au_d_alive(d); ++ if (unlikely(err || IS_DEADDIR(d->d_inode))) ++ err = -ENOENT; ++ return err; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DCSUB_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/debug.c linux-2.6.36/fs/aufs/debug.c +--- linux-2.6.36.orig/fs/aufs/debug.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/debug.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,468 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++int aufs_debug; ++MODULE_PARM_DESC(debug, "debug print"); ++module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP); ++ ++char *au_plevel = KERN_DEBUG; ++#define dpri(fmt, ...) do { \ ++ if ((au_plevel \ ++ && strcmp(au_plevel, KERN_DEBUG)) \ ++ || au_debug_test()) \ ++ printk("%s" fmt, au_plevel, ##__VA_ARGS__); \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dpri_whlist(struct au_nhash *whlist) ++{ ++ unsigned long ul, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; ul < n; ul++) { ++ 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); ++ head++; ++ } ++} ++ ++void au_dpri_vdir(struct au_vdir *vdir) ++{ ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ unsigned char *o; ++ ++ if (!vdir || IS_ERR(vdir)) { ++ dpri("err %ld\n", PTR_ERR(vdir)); ++ return; ++ } ++ ++ dpri("deblk %u, nblk %lu, deblk %p, last{%lu, %p}, ver %lu\n", ++ vdir->vd_deblk_sz, vdir->vd_nblk, vdir->vd_deblk, ++ vdir->vd_last.ul, vdir->vd_last.p.deblk, vdir->vd_version); ++ for (ul = 0; ul < vdir->vd_nblk; ul++) { ++ p.deblk = vdir->vd_deblk[ul]; ++ o = p.deblk; ++ dpri("[%lu]: %p\n", ul, o); ++ } ++} ++ ++static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, ++ struct dentry *wh) ++{ ++ char *n = NULL; ++ int l = 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; ++ } ++ ++ dpri("i%d: i%lu, %s, cnt %d, nl %u, 0%o, sz %llu, blk %llu," ++ " ct %lld, np %lu, st 0x%lx, f 0x%x, v %llu, 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, ++ i_size_read(inode), (unsigned long long)inode->i_blocks, ++ (long long)timespec_to_ns(&inode->i_ctime) & 0x0ffff, ++ inode->i_mapping ? inode->i_mapping->nrpages : 0, ++ inode->i_state, inode->i_flags, inode->i_version, ++ inode->i_generation, ++ l ? ", wh " : "", l, n); ++ return 0; ++} ++ ++void au_dpri_inode(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_inode(-1, inode, NULL); ++ if (err || !au_test_aufs(inode->i_sb)) ++ return; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ dpri("i-1: bstart %d, bend %d, gen %d\n", ++ iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode)); ++ if (iinfo->ii_bstart < 0) ++ return; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; bindex++) ++ do_pri_inode(bindex, iinfo->ii_hinode[0 + bindex].hi_inode, ++ iinfo->ii_hinode[0 + bindex].hi_whdentry); ++} ++ ++static int do_pri_dentry(aufs_bindex_t bindex, struct dentry *dentry) ++{ ++ struct 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\n", ++ bindex, ++ AuDLNPair(dentry->d_parent), AuDLNPair(dentry), ++ dentry->d_sb ? au_sbtype(dentry->d_sb) : "??", ++ atomic_read(&dentry->d_count), dentry->d_flags); ++ if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) { ++ struct au_iinfo *iinfo = au_ii(dentry->d_inode); ++ if (iinfo) ++ wh = iinfo->ii_hinode[0 + bindex].hi_whdentry; ++ } ++ do_pri_inode(bindex, dentry->d_inode, wh); ++ return 0; ++} ++ ++void au_dpri_dentry(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ aufs_bindex_t bindex; ++ int err; ++ struct au_hdentry *hdp; ++ ++ err = do_pri_dentry(-1, dentry); ++ if (err || !au_test_aufs(dentry->d_sb)) ++ return; ++ ++ dinfo = au_di(dentry); ++ if (!dinfo) ++ return; ++ dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n", ++ dinfo->di_bstart, dinfo->di_bend, ++ dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry)); ++ if (dinfo->di_bstart < 0) ++ return; ++ hdp = dinfo->di_hdentry; ++ for (bindex = dinfo->di_bstart; bindex <= dinfo->di_bend; bindex++) ++ do_pri_dentry(bindex, hdp[0 + bindex].hd_dentry); ++} ++ ++static int do_pri_file(aufs_bindex_t bindex, struct file *file) ++{ ++ char a[32]; ++ ++ if (!file || IS_ERR(file)) { ++ dpri("f%d: err %ld\n", bindex, PTR_ERR(file)); ++ return -1; ++ } ++ a[0] = 0; ++ if (bindex < 0 ++ && file->f_dentry ++ && au_test_aufs(file->f_dentry->d_sb) ++ && au_fi(file)) ++ snprintf(a, sizeof(a), ", gen %d, mmapped %d", ++ au_figen(file), !!au_fi(file)->fi_hvmop); ++ dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n", ++ bindex, file->f_mode, file->f_flags, (long)file_count(file), ++ file->f_version, file->f_pos, a); ++ if (file->f_dentry) ++ do_pri_dentry(bindex, file->f_dentry); ++ return 0; ++} ++ ++void au_dpri_file(struct file *file) ++{ ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ struct au_hfile *hfile; ++ aufs_bindex_t bindex; ++ int err; ++ ++ err = do_pri_file(-1, file); ++ if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb)) ++ return; ++ ++ finfo = au_fi(file); ++ if (!finfo) ++ return; ++ if (finfo->fi_btop < 0) ++ return; ++ fidir = finfo->fi_hdir; ++ if (!fidir) ++ do_pri_file(finfo->fi_btop, finfo->fi_htop.hf_file); ++ else ++ for (bindex = finfo->fi_btop; ++ bindex >= 0 && bindex <= fidir->fd_bbot; ++ bindex++) { ++ hfile = fidir->fd_hfile + bindex; ++ do_pri_file(bindex, hfile ? hfile->hf_file : NULL); ++ } ++} ++ ++static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) ++{ ++ struct vfsmount *mnt; ++ struct super_block *sb; ++ ++ if (!br || IS_ERR(br)) ++ goto out; ++ mnt = br->br_mnt; ++ if (!mnt || IS_ERR(mnt)) ++ goto out; ++ sb = mnt->mnt_sb; ++ if (!sb || IS_ERR(sb)) ++ goto out; ++ ++ dpri("s%d: {perm 0x%x, cnt %d, wbr %p}, " ++ "%s, dev 0x%02x%02x, flags 0x%lx, cnt %d, active %d, " ++ "xino %d\n", ++ bindex, br->br_perm, atomic_read(&br->br_count), br->br_wbr, ++ au_sbtype(sb), MAJOR(sb->s_dev), MINOR(sb->s_dev), ++ sb->s_flags, sb->s_count, ++ atomic_read(&sb->s_active), !!br->br_xino.xi_file); ++ return 0; ++ ++out: ++ dpri("s%d: err %ld\n", bindex, PTR_ERR(br)); ++ return -1; ++} ++ ++void au_dpri_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ aufs_bindex_t bindex; ++ int err; ++ /* to reuduce stack size */ ++ struct { ++ struct vfsmount mnt; ++ struct au_branch fake; ++ } *a; ++ ++ /* this function can be called from magic sysrq */ ++ a = kzalloc(sizeof(*a), GFP_ATOMIC); ++ if (unlikely(!a)) { ++ dpri("no memory\n"); ++ return; ++ } ++ ++ a->mnt.mnt_sb = sb; ++ a->fake.br_perm = 0; ++ a->fake.br_mnt = &a->mnt; ++ a->fake.br_xino.xi_file = NULL; ++ atomic_set(&a->fake.br_count, 0); ++ smp_mb(); /* atomic_set */ ++ err = do_pri_br(-1, &a->fake); ++ kfree(a); ++ dpri("dev 0x%x\n", sb->s_dev); ++ if (err || !au_test_aufs(sb)) ++ return; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ dpri("nw %d, gen %u, kobj %d\n", ++ atomic_read(&sbinfo->si_nowait.nw_len), sbinfo->si_generation, ++ atomic_read(&sbinfo->si_kobj.kref.refcount)); ++ for (bindex = 0; bindex <= sbinfo->si_bend; bindex++) ++ do_pri_br(bindex, sbinfo->si_branch[0 + bindex]); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_dbg_sleep_jiffy(int jiffy) ++{ ++ while (jiffy) ++ jiffy = schedule_timeout_uninterruptible(jiffy); ++} ++ ++void au_dbg_iattr(struct iattr *ia) ++{ ++#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \ ++ dpri(#name "\n") ++ AuBit(MODE); ++ AuBit(UID); ++ AuBit(GID); ++ AuBit(SIZE); ++ AuBit(ATIME); ++ AuBit(MTIME); ++ AuBit(CTIME); ++ AuBit(ATIME_SET); ++ AuBit(MTIME_SET); ++ AuBit(FORCE); ++ AuBit(ATTR_FLAG); ++ AuBit(KILL_SUID); ++ AuBit(KILL_SGID); ++ AuBit(FILE); ++ AuBit(KILL_PRIV); ++ AuBit(OPEN); ++ AuBit(TIMES_SET); ++#undef AuBit ++ dpri("ia_file %p\n", ia->ia_file); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) ++{ ++ struct inode *h_inode, *inode = dentry->d_inode; ++ struct dentry *h_dentry; ++ aufs_bindex_t bindex, bend, bi; ++ ++ if (!inode /* || au_di(dentry)->di_lsc == AuLsc_DI_TMP */) ++ return; ++ ++ bend = au_dbend(dentry); ++ bi = au_ibend(inode); ++ if (bi < bend) ++ bend = bi; ++ bindex = au_dbstart(dentry); ++ bi = au_ibstart(inode); ++ if (bi > bindex) ++ bindex = bi; ++ ++ for (; bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_inode = au_h_iptr(inode, bindex); ++ if (unlikely(h_inode != h_dentry->d_inode)) { ++ int old = au_debug_test(); ++ if (!old) ++ au_debug(1); ++ AuDbg("b%d, %s:%d\n", bindex, func, line); ++ AuDbgDentry(dentry); ++ AuDbgInode(inode); ++ if (!old) ++ au_debug(0); ++ BUG(); ++ } ++ } ++} ++ ++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen) ++{ ++ struct dentry *parent; ++ ++ parent = dget_parent(dentry); ++ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); ++ AuDebugOn(IS_ROOT(dentry)); ++ AuDebugOn(au_digen_test(parent, sigen)); ++ dput(parent); ++} ++ ++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen) ++{ ++ struct dentry *parent; ++ struct inode *inode; ++ ++ parent = dget_parent(dentry); ++ inode = dentry->d_inode; ++ AuDebugOn(inode && S_ISDIR(dentry->d_inode->i_mode)); ++ AuDebugOn(au_digen_test(parent, sigen)); ++ dput(parent); ++} ++ ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen) ++{ ++ int err, i, j; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ AuDebugOn(err); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/1); ++ 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_test(dentries[j], sigen)); ++ } ++ au_dpages_free(&dpages); ++} ++ ++void au_dbg_verify_kthread(void) ++{ ++ if (current->flags & PF_WQ_WORKER) { ++ au_dbg_blocked(); ++ WARN_ON(1); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused) ++{ ++#ifdef AuForceNoPlink ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++#endif ++#ifdef AuForceNoXino ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++#endif ++#ifdef AuForceNoRefrof ++ au_opt_clr(sbinfo->si_mntflags, REFROF); ++#endif ++#ifdef AuForceHnotify ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_HNOTIFY); ++#endif ++#ifdef AuForceRd0 ++ sbinfo->si_rdblk = 0; ++ sbinfo->si_rdhash = 0; ++#endif ++} ++ ++int __init au_debug_init(void) ++{ ++ aufs_bindex_t bindex; ++ struct au_vdir_destr destr; ++ ++ bindex = -1; ++ AuDebugOn(bindex >= 0); ++ ++ destr.len = -1; ++ AuDebugOn(destr.len < NAME_MAX); ++ ++#ifdef CONFIG_4KSTACKS ++ pr_warning("CONFIG_4KSTACKS is defined.\n"); ++#endif ++ ++#ifdef AuForceNoBrs ++ sysaufs_brs = 0; ++#endif ++ ++ return 0; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/debug.h linux-2.6.36/fs/aufs/debug.h +--- linux-2.6.36.orig/fs/aufs/debug.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/debug.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,245 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * debug print functions ++ */ ++ ++#ifndef __AUFS_DEBUG_H__ ++#define __AUFS_DEBUG_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++/* #include */ ++#include ++#include ++#include ++/* #include */ ++#include ++/* #include */ ++#include ++#include ++ ++#include ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDebugOn(a) BUG_ON(a) ++ ++/* module parameter */ ++extern int aufs_debug; ++static inline void au_debug(int n) ++{ ++ aufs_debug = n; ++ smp_mb(); ++} ++ ++static inline int au_debug_test(void) ++{ ++ return aufs_debug; ++} ++#else ++#define AuDebugOn(a) do {} while (0) ++AuStubVoid(au_debug, int n) ++AuStubInt0(au_debug_test, void) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* debug print */ ++ ++#define AuDbg(fmt, ...) do { \ ++ if (au_debug_test()) \ ++ pr_debug("DEBUG: " fmt, ##__VA_ARGS__); \ ++} while (0) ++#define AuLabel(l) AuDbg(#l "\n") ++#define AuIOErr(fmt, ...) pr_err("I/O Error, " fmt, ##__VA_ARGS__) ++#define AuWarn1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_warning(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ pr_err(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuIOErr1(fmt, ...) do { \ ++ static unsigned char _c; \ ++ if (!_c++) \ ++ AuIOErr(fmt, ##__VA_ARGS__); \ ++} while (0) ++ ++#define AuUnsupportMsg "This operation is not supported." \ ++ " Please report this application to aufs-users ML." ++#define AuUnsupport(fmt, ...) do { \ ++ pr_err(AuUnsupportMsg "\n" fmt, ##__VA_ARGS__); \ ++ dump_stack(); \ ++} while (0) ++ ++#define AuTraceErr(e) do { \ ++ if (unlikely((e) < 0)) \ ++ AuDbg("err %d\n", (int)(e)); \ ++} while (0) ++ ++#define AuTraceErrPtr(p) do { \ ++ if (IS_ERR(p)) \ ++ AuDbg("err %ld\n", PTR_ERR(p)); \ ++} while (0) ++ ++/* dirty macros for debug print, use with "%.*s" and caution */ ++#define AuLNPair(qstr) (qstr)->len, (qstr)->name ++#define AuDLNPair(d) AuLNPair(&(d)->d_name) ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sbinfo; ++struct au_finfo; ++struct dentry; ++#ifdef CONFIG_AUFS_DEBUG ++extern char *au_plevel; ++struct au_nhash; ++void au_dpri_whlist(struct au_nhash *whlist); ++struct au_vdir; ++void au_dpri_vdir(struct au_vdir *vdir); ++struct inode; ++void au_dpri_inode(struct inode *inode); ++void au_dpri_dentry(struct dentry *dentry); ++struct file; ++void au_dpri_file(struct file *filp); ++struct super_block; ++void au_dpri_sb(struct super_block *sb); ++ ++void au_dbg_sleep_jiffy(int jiffy); ++struct iattr; ++void au_dbg_iattr(struct iattr *ia); ++ ++#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__) ++void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line); ++void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen); ++void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen); ++void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen); ++void au_dbg_verify_kthread(void); ++ ++int __init au_debug_init(void); ++void au_debug_sbinfo_init(struct au_sbinfo *sbinfo); ++#define AuDbgWhlist(w) do { \ ++ AuDbg(#w "\n"); \ ++ au_dpri_whlist(w); \ ++} while (0) ++ ++#define AuDbgVdir(v) do { \ ++ AuDbg(#v "\n"); \ ++ au_dpri_vdir(v); \ ++} while (0) ++ ++#define AuDbgInode(i) do { \ ++ AuDbg(#i "\n"); \ ++ au_dpri_inode(i); \ ++} while (0) ++ ++#define AuDbgDentry(d) do { \ ++ AuDbg(#d "\n"); \ ++ au_dpri_dentry(d); \ ++} while (0) ++ ++#define AuDbgFile(f) do { \ ++ AuDbg(#f "\n"); \ ++ au_dpri_file(f); \ ++} while (0) ++ ++#define AuDbgSb(sb) do { \ ++ AuDbg(#sb "\n"); \ ++ au_dpri_sb(sb); \ ++} while (0) ++ ++#define AuDbgSleep(sec) do { \ ++ AuDbg("sleep %d sec\n", sec); \ ++ ssleep(sec); \ ++} while (0) ++ ++#define AuDbgSleepJiffy(jiffy) do { \ ++ AuDbg("sleep %d jiffies\n", jiffy); \ ++ au_dbg_sleep_jiffy(jiffy); \ ++} while (0) ++ ++#define AuDbgIAttr(ia) do { \ ++ AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \ ++ au_dbg_iattr(ia); \ ++} while (0) ++ ++#define AuDbgSym(addr) do { \ ++ char sym[KSYM_SYMBOL_LEN]; \ ++ sprint_symbol(sym, (unsigned long)addr); \ ++ AuDbg("%s\n", sym); \ ++} while (0) ++ ++#define AuInfoSym(addr) do { \ ++ char sym[KSYM_SYMBOL_LEN]; \ ++ sprint_symbol(sym, (unsigned long)addr); \ ++ AuInfo("%s\n", sym); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry) ++AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen) ++AuStubVoid(au_dbg_verify_nondir_parent, struct dentry *dentry, ++ unsigned int sigen) ++AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen) ++AuStubVoid(au_dbg_verify_kthread, void) ++AuStubInt0(__init au_debug_init, void) ++AuStubVoid(au_debug_sbinfo_init, struct au_sbinfo *sbinfo) ++ ++#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) ++#define AuDbgSleepJiffy(jiffy) do {} while (0) ++#define AuDbgIAttr(ia) do {} while (0) ++#define AuDbgSym(addr) do {} while (0) ++#define AuInfoSym(addr) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_MAGIC_SYSRQ ++int __init au_sysrq_init(void); ++void au_sysrq_fin(void); ++ ++#ifdef CONFIG_HW_CONSOLE ++#define au_dbg_blocked() do { \ ++ WARN_ON(1); \ ++ handle_sysrq('w'); \ ++} while (0) ++#else ++AuStubVoid(au_dbg_blocked, void) ++#endif ++ ++#else ++AuStubInt0(__init au_sysrq_init, void) ++AuStubVoid(au_sysrq_fin, void) ++AuStubVoid(au_dbg_blocked, void) ++#endif /* CONFIG_AUFS_MAGIC_SYSRQ */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DEBUG_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/dentry.c linux-2.6.36/fs/aufs/dentry.c +--- linux-2.6.36.orig/fs/aufs/dentry.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dentry.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1131 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#include ++#include "aufs.h" ++ ++static void au_h_nd(struct nameidata *h_nd, struct nameidata *nd) ++{ ++ if (nd) { ++ *h_nd = *nd; ++ ++ /* ++ * gave up supporting LOOKUP_CREATE/OPEN for lower fs, ++ * due to whiteout and branch permission. ++ */ ++ h_nd->flags &= ~(/*LOOKUP_PARENT |*/ LOOKUP_OPEN | LOOKUP_CREATE ++ | LOOKUP_FOLLOW | LOOKUP_EXCL); ++ /* unnecessary? */ ++ h_nd->intent.open.file = NULL; ++ } else ++ memset(h_nd, 0, sizeof(*h_nd)); ++} ++ ++struct au_lkup_one_args { ++ struct dentry **errp; ++ struct qstr *name; ++ struct dentry *h_parent; ++ struct au_branch *br; ++ struct nameidata *nd; ++}; ++ ++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent, ++ struct au_branch *br, struct nameidata *nd) ++{ ++ struct dentry *h_dentry; ++ int err; ++ struct nameidata h_nd; ++ ++ if (au_test_fs_null_nd(h_parent->d_sb)) ++ return vfsub_lookup_one_len(name->name, h_parent, name->len); ++ ++ au_h_nd(&h_nd, nd); ++ h_nd.path.dentry = h_parent; ++ h_nd.path.mnt = br->br_mnt; ++ ++ err = __lookup_one_len(name->name, &h_nd.last, NULL, name->len); ++ h_dentry = ERR_PTR(err); ++ if (!err) { ++ path_get(&h_nd.path); ++ h_dentry = vfsub_lookup_hash(&h_nd); ++ path_put(&h_nd.path); ++ } ++ ++ AuTraceErrPtr(h_dentry); ++ return h_dentry; ++} ++ ++static void au_call_lkup_one(void *args) ++{ ++ struct au_lkup_one_args *a = args; ++ *a->errp = au_lkup_one(a->name, a->h_parent, a->br, a->nd); ++} ++ ++#define AuLkup_ALLOW_NEG 1 ++#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name) ++#define au_fset_lkup(flags, name) \ ++ do { (flags) |= AuLkup_##name; } while (0) ++#define au_fclr_lkup(flags, name) \ ++ do { (flags) &= ~AuLkup_##name; } while (0) ++ ++struct au_do_lookup_args { ++ unsigned int flags; ++ mode_t type; ++ struct nameidata *nd; ++}; ++ ++/* ++ * returns positive/negative dentry, NULL or an error. ++ * NULL means whiteout-ed or not-found. ++ */ ++static 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; ++ struct inode *h_inode, *inode; ++ struct au_branch *br; ++ int wh_found, opq; ++ unsigned char wh_able; ++ const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); ++ ++ wh_found = 0; ++ br = au_sbr(dentry->d_sb, bindex); ++ wh_able = !!au_br_whable(br->br_perm); ++ if (wh_able) ++ wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0); ++ h_dentry = ERR_PTR(wh_found); ++ if (!wh_found) ++ goto real_lookup; ++ if (unlikely(wh_found < 0)) ++ goto out; ++ ++ /* We found a whiteout */ ++ /* au_set_dbend(dentry, bindex); */ ++ au_set_dbwh(dentry, bindex); ++ if (!allow_neg) ++ return NULL; /* success */ ++ ++real_lookup: ++ h_dentry = au_lkup_one(&dentry->d_name, h_parent, br, args->nd); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) { ++ if (!allow_neg) ++ goto out_neg; ++ } else if (wh_found ++ || (args->type && args->type != (h_inode->i_mode & S_IFMT))) ++ goto out_neg; ++ ++ if (au_dbend(dentry) <= bindex) ++ au_set_dbend(dentry, bindex); ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++ inode = dentry->d_inode; ++ if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able ++ || (inode && !S_ISDIR(inode->i_mode))) ++ goto out; /* success */ ++ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ opq = au_diropq_test(h_dentry, br); ++ mutex_unlock(&h_inode->i_mutex); ++ if (opq > 0) ++ au_set_dbdiropq(dentry, bindex); ++ else if (unlikely(opq < 0)) { ++ au_set_h_dptr(dentry, bindex, NULL); ++ h_dentry = ERR_PTR(opq); ++ } ++ goto out; ++ ++out_neg: ++ dput(h_dentry); ++ h_dentry = NULL; ++out: ++ return h_dentry; ++} ++ ++static int au_test_shwh(struct super_block *sb, const struct qstr *name) ++{ ++ if (unlikely(!au_opt_test(au_mntflags(sb), SHWH) ++ && !strncmp(name->name, AUFS_WH_PFX, AUFS_WH_PFX_LEN))) ++ return -EPERM; ++ return 0; ++} ++ ++/* ++ * returns the number of lower positive dentries, ++ * otherwise an error. ++ * can be called at unlinking with @type is zero. ++ */ ++int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, ++ struct nameidata *nd) ++{ ++ int npositive, err; ++ aufs_bindex_t bindex, btail, bdiropq; ++ unsigned char isdir; ++ struct qstr whname; ++ struct au_do_lookup_args args = { ++ .flags = 0, ++ .type = type, ++ .nd = nd ++ }; ++ const struct qstr *name = &dentry->d_name; ++ struct dentry *parent; ++ struct inode *inode; ++ ++ err = au_test_shwh(dentry->d_sb, name); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_wh_name_alloc(&whname, name); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ isdir = !!(inode && S_ISDIR(inode->i_mode)); ++ if (!type) ++ au_fset_lkup(args.flags, ALLOW_NEG); ++ ++ npositive = 0; ++ parent = dget_parent(dentry); ++ btail = au_dbtaildir(parent); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ struct dentry *h_parent, *h_dentry; ++ struct inode *h_inode, *h_dir; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) { ++ if (h_dentry->d_inode) ++ npositive++; ++ if (type != S_IFDIR) ++ break; ++ continue; ++ } ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent) ++ continue; ++ h_dir = h_parent->d_inode; ++ if (!h_dir || !S_ISDIR(h_dir->i_mode)) ++ continue; ++ ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname, ++ &args); ++ mutex_unlock(&h_dir->i_mutex); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out_parent; ++ au_fclr_lkup(args.flags, ALLOW_NEG); ++ ++ if (au_dbwh(dentry) >= 0) ++ break; ++ if (!h_dentry) ++ continue; ++ h_inode = h_dentry->d_inode; ++ if (!h_inode) ++ continue; ++ npositive++; ++ if (!args.type) ++ args.type = h_inode->i_mode & S_IFMT; ++ if (args.type != S_IFDIR) ++ break; ++ else if (isdir) { ++ /* the type of lower may be different */ ++ bdiropq = au_dbdiropq(dentry); ++ if (bdiropq >= 0 && bdiropq <= bindex) ++ break; ++ } ++ } ++ ++ if (npositive) { ++ AuLabel(positive); ++ au_update_dbstart(dentry); ++ } ++ err = npositive; ++ if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuIOErr("both of real entry and whiteout found, %.*s, err %d\n", ++ AuDLNPair(dentry), err); ++ } ++ ++out_parent: ++ dput(parent); ++ kfree(whname.name); ++out: ++ return err; ++} ++ ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, ++ struct au_branch *br) ++{ ++ struct dentry *dentry; ++ int wkq_err; ++ ++ if (!au_test_h_perm_sio(parent->d_inode, MAY_EXEC)) ++ dentry = au_lkup_one(name, parent, br, /*nd*/NULL); ++ else { ++ struct au_lkup_one_args args = { ++ .errp = &dentry, ++ .name = name, ++ .h_parent = parent, ++ .br = br, ++ .nd = NULL ++ }; ++ ++ wkq_err = au_wkq_wait(au_call_lkup_one, &args); ++ if (unlikely(wkq_err)) ++ dentry = ERR_PTR(wkq_err); ++ } ++ ++ 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; ++ ++ parent = dget_parent(dentry); ++ h_parent = au_h_dptr(parent, bindex); ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, ++ au_sbr(dentry->d_sb, bindex)); ++ err = PTR_ERR(h_dentry); ++ if (IS_ERR(h_dentry)) ++ goto out; ++ if (unlikely(h_dentry->d_inode)) { ++ err = -EIO; ++ AuIOErr("%.*s should be negative on b%d.\n", ++ AuDLNPair(h_dentry), bindex); ++ dput(h_dentry); ++ goto out; ++ } ++ ++ err = 0; ++ if (bindex < au_dbstart(dentry)) ++ au_set_dbstart(dentry, bindex); ++ if (au_dbend(dentry) < bindex) ++ au_set_dbend(dentry, bindex); ++ au_set_h_dptr(dentry, bindex, h_dentry); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* subset of struct inode */ ++struct au_iattr { ++ unsigned long i_ino; ++ /* unsigned int i_nlink; */ ++ uid_t i_uid; ++ gid_t i_gid; ++ u64 i_version; ++/* ++ loff_t i_size; ++ blkcnt_t i_blocks; ++*/ ++ umode_t i_mode; ++}; ++ ++static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode) ++{ ++ ia->i_ino = h_inode->i_ino; ++ /* ia->i_nlink = h_inode->i_nlink; */ ++ ia->i_uid = h_inode->i_uid; ++ ia->i_gid = h_inode->i_gid; ++ ia->i_version = h_inode->i_version; ++/* ++ ia->i_size = h_inode->i_size; ++ ia->i_blocks = h_inode->i_blocks; ++*/ ++ ia->i_mode = (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode) ++{ ++ return ia->i_ino != h_inode->i_ino ++ /* || ia->i_nlink != h_inode->i_nlink */ ++ || ia->i_uid != h_inode->i_uid ++ || ia->i_gid != h_inode->i_gid ++ || ia->i_version != h_inode->i_version ++/* ++ || ia->i_size != h_inode->i_size ++ || ia->i_blocks != h_inode->i_blocks ++*/ ++ || ia->i_mode != (h_inode->i_mode & S_IFMT); ++} ++ ++static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ int err; ++ struct au_iattr ia; ++ struct inode *h_inode; ++ struct dentry *h_d; ++ struct super_block *h_sb; ++ ++ err = 0; ++ memset(&ia, -1, sizeof(ia)); ++ h_sb = h_dentry->d_sb; ++ h_inode = h_dentry->d_inode; ++ if (h_inode) ++ au_iattr_save(&ia, h_inode); ++ else if (au_test_nfs(h_sb) || au_test_fuse(h_sb)) ++ /* nfs d_revalidate may return 0 for negative dentry */ ++ /* fuse d_revalidate always return 0 for negative dentry */ ++ goto out; ++ ++ /* main purpose is namei.c:cached_lookup() and d_revalidate */ ++ h_d = au_lkup_one(&h_dentry->d_name, h_parent, br, /*nd*/NULL); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out; ++ ++ err = 0; ++ if (unlikely(h_d != h_dentry ++ || h_d->d_inode != h_inode ++ || (h_inode && au_iattr_test(&ia, h_inode)))) ++ err = au_busy_or_stale(); ++ dput(h_d); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br) ++{ ++ int err; ++ ++ err = 0; ++ if (udba == AuOpt_UDBA_REVAL) { ++ IMustLock(h_dir); ++ err = (h_dentry->d_parent->d_inode != h_dir); ++ } else if (udba == AuOpt_UDBA_HNOTIFY) ++ err = au_h_verify_dentry(h_dentry, h_parent, br); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_refresh_hdentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err; ++ aufs_bindex_t new_bindex, bindex, bend, bwh, bdiropq; ++ struct au_hdentry tmp, *p, *q; ++ struct au_dinfo *dinfo; ++ struct super_block *sb; ++ ++ DiMustWriteLock(dentry); ++ ++ sb = dentry->d_sb; ++ dinfo = au_di(dentry); ++ bend = dinfo->di_bend; ++ bwh = dinfo->di_bwh; ++ bdiropq = dinfo->di_bdiropq; ++ p = dinfo->di_hdentry + dinfo->di_bstart; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++, p++) { ++ if (!p->hd_dentry) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hd_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (dinfo->di_bwh == bindex) ++ bwh = new_bindex; ++ if (dinfo->di_bdiropq == bindex) ++ bdiropq = new_bindex; ++ if (new_bindex < 0) { ++ au_hdput(p); ++ p->hd_dentry = NULL; ++ continue; ++ } ++ ++ /* swap two lower dentries, and loop again */ ++ q = dinfo->di_hdentry + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hd_dentry) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ dinfo->di_bwh = -1; ++ if (bwh >= 0 && bwh <= au_sbend(sb) && au_sbr_whable(sb, bwh)) ++ dinfo->di_bwh = bwh; ++ ++ dinfo->di_bdiropq = -1; ++ if (bdiropq >= 0 ++ && bdiropq <= au_sbend(sb) ++ && au_sbr_whable(sb, bdiropq)) ++ dinfo->di_bdiropq = bdiropq; ++ ++ err = -EIO; ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ bend = au_dbend(parent); ++ p = dinfo->di_hdentry; ++ for (bindex = 0; bindex <= bend; bindex++, p++) ++ if (p->hd_dentry) { ++ dinfo->di_bstart = bindex; ++ break; ++ } ++ ++ if (dinfo->di_bstart >= 0) { ++ p = dinfo->di_hdentry + bend; ++ for (bindex = bend; bindex >= 0; bindex--, p--) ++ if (p->hd_dentry) { ++ dinfo->di_bend = bindex; ++ err = 0; ++ break; ++ } ++ } ++ ++ return err; ++} ++ ++static void au_do_hide(struct dentry *dentry) ++{ ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (inode) { ++ if (!S_ISDIR(inode->i_mode)) { ++ if (inode->i_nlink && !d_unhashed(dentry)) ++ drop_nlink(inode); ++ } else { ++ clear_nlink(inode); ++ /* stop next lookup */ ++ inode->i_flags |= S_DEAD; ++ } ++ smp_mb(); /* necessary? */ ++ } ++ d_drop(dentry); ++} ++ ++static int au_hide_children(struct dentry *parent) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry *dentry; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, parent, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ /* in reverse order */ ++ for (i = dpages.ndpage - 1; i >= 0; i--) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = ndentry - 1; j >= 0; j--) { ++ dentry = dpage->dentries[j]; ++ if (dentry != parent) ++ au_do_hide(dentry); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static void au_hide(struct dentry *dentry) ++{ ++ int err; ++ struct inode *inode; ++ ++ AuDbgDentry(dentry); ++ inode = dentry->d_inode; ++ if (inode && S_ISDIR(inode->i_mode)) { ++ /* shrink_dcache_parent(dentry); */ ++ err = au_hide_children(dentry); ++ if (unlikely(err)) ++ AuIOErr("%.*s, failed hiding children, ignored %d\n", ++ AuDLNPair(dentry), err); ++ } ++ au_do_hide(dentry); ++} ++ ++/* ++ * By adding a dirty branch, a cached dentry may be affected in various ways. ++ * ++ * a dirty branch is added ++ * - on the top of layers ++ * - in the middle of layers ++ * - to the bottom of layers ++ * ++ * on the added branch there exists ++ * - a whiteout ++ * - a diropq ++ * - a same named entry ++ * + exist ++ * * negative --> positive ++ * * positive --> positive ++ * - type is unchanged ++ * - type is changed ++ * + doesn't exist ++ * * negative --> negative ++ * * positive --> negative (rejected by au_br_del() for non-dir case) ++ * - none ++ */ ++static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, ++ struct au_dinfo *tmp) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct { ++ struct dentry *dentry; ++ struct inode *inode; ++ mode_t mode; ++ } orig_h, tmp_h; ++ struct au_hdentry *hd; ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry; ++ ++ err = 0; ++ AuDebugOn(dinfo->di_bstart < 0); ++ orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; ++ orig_h.inode = orig_h.dentry->d_inode; ++ orig_h.mode = 0; ++ if (orig_h.inode) ++ orig_h.mode = orig_h.inode->i_mode & S_IFMT; ++ memset(&tmp_h, 0, sizeof(tmp_h)); ++ if (tmp->di_bstart >= 0) { ++ tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; ++ tmp_h.inode = tmp_h.dentry->d_inode; ++ if (tmp_h.inode) ++ tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; ++ } ++ ++ inode = dentry->d_inode; ++ if (!orig_h.inode) { ++ AuDbg("nagative originally\n"); ++ if (inode) { ++ au_hide(dentry); ++ goto out; ++ } ++ AuDebugOn(inode); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ AuDebugOn(dinfo->di_bdiropq != -1); ++ ++ if (!tmp_h.inode) { ++ AuDbg("negative --> negative\n"); ++ /* should have only one negative lower */ ++ if (tmp->di_bstart >= 0 ++ && tmp->di_bstart < dinfo->di_bstart) { ++ AuDebugOn(tmp->di_bstart != tmp->di_bend); ++ AuDebugOn(dinfo->di_bstart != dinfo->di_bend); ++ au_set_h_dptr(dentry, dinfo->di_bstart, NULL); ++ au_di_cp(dinfo, tmp); ++ hd = tmp->di_hdentry + tmp->di_bstart; ++ au_set_h_dptr(dentry, tmp->di_bstart, ++ dget(hd->hd_dentry)); ++ } ++ au_dbg_verify_dinode(dentry); ++ } else { ++ AuDbg("negative --> positive\n"); ++ /* ++ * similar to the behaviour of creating with bypassing ++ * aufs. ++ * unhash it in order to force an error in the ++ * succeeding create operation. ++ * we should not set S_DEAD here. ++ */ ++ d_drop(dentry); ++ /* au_di_swap(tmp, dinfo); */ ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive originally\n"); ++ /* inode may be NULL */ ++ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); ++ if (!tmp_h.inode) { ++ AuDbg("positive --> negative\n"); ++ /* or bypassing aufs */ ++ au_hide(dentry); ++ if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) ++ dinfo->di_bwh = tmp->di_bwh; ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else if (orig_h.mode == tmp_h.mode) { ++ AuDbg("positive --> positive, same type\n"); ++ if (!S_ISDIR(orig_h.mode) ++ && dinfo->di_bstart > tmp->di_bstart) { ++ /* ++ * similar to the behaviour of removing and ++ * creating. ++ */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } else { ++ /* fill empty slots */ ++ if (dinfo->di_bstart > tmp->di_bstart) ++ dinfo->di_bstart = tmp->di_bstart; ++ if (dinfo->di_bend < tmp->di_bend) ++ dinfo->di_bend = tmp->di_bend; ++ dinfo->di_bwh = tmp->di_bwh; ++ dinfo->di_bdiropq = tmp->di_bdiropq; ++ hd = tmp->di_hdentry; ++ bend = dinfo->di_bend; ++ for (bindex = tmp->di_bstart; bindex <= bend; ++ bindex++) { ++ if (au_h_dptr(dentry, bindex)) ++ continue; ++ h_dentry = hd[bindex].hd_dentry; ++ if (!h_dentry) ++ continue; ++ h_inode = h_dentry->d_inode; ++ AuDebugOn(!h_inode); ++ AuDebugOn(orig_h.mode ++ != (h_inode->i_mode ++ & S_IFMT)); ++ au_set_h_dptr(dentry, bindex, ++ dget(h_dentry)); ++ } ++ err = au_refresh_hinode(inode, dentry); ++ au_dbg_verify_dinode(dentry); ++ } ++ } else { ++ AuDbg("positive --> positive, different type\n"); ++ /* similar to the behaviour of removing and creating */ ++ au_hide(dentry); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ } ++ } ++ ++out: ++ return err; ++} ++ ++int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) ++{ ++ int err, ebrange; ++ unsigned int sigen; ++ struct au_dinfo *dinfo, *tmp; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ DiMustWriteLock(dentry); ++ AuDebugOn(IS_ROOT(dentry)); ++ AuDebugOn(!parent->d_inode); ++ ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ sigen = au_sigen(sb); ++ err = au_digen_test(parent, sigen); ++ if (unlikely(err)) ++ goto out; ++ ++ dinfo = au_di(dentry); ++ err = au_di_realloc(dinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ebrange = au_dbrange_test(dentry); ++ if (!ebrange) ++ ebrange = au_do_refresh_hdentry(dentry, parent); ++ ++ if (d_unhashed(dentry) || ebrange) { ++ AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); ++ if (inode) ++ err = au_refresh_hinode_self(inode); ++ au_dbg_verify_dinode(dentry); ++ if (!err) ++ goto out_dgen; /* success */ ++ goto out; ++ } ++ ++ /* temporary dinfo */ ++ AuDbgDentry(dentry); ++ err = -ENOMEM; ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (unlikely(!tmp)) ++ goto out; ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ /* ++ * if current working dir is removed, it returns an error. ++ * but the dentry is legal. ++ */ ++ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0, /*nd*/NULL); ++ AuDbgDentry(dentry); ++ au_di_swap(tmp, dinfo); ++ if (err == -ENOENT) ++ err = 0; ++ if (err >= 0) { ++ /* compare/refresh by dinfo */ ++ AuDbgDentry(dentry); ++ err = au_refresh_by_dinfo(dentry, dinfo, tmp); ++ au_dbg_verify_dinode(dentry); ++ AuTraceErr(err); ++ } ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ if (unlikely(err)) ++ goto out; ++ ++out_dgen: ++ au_update_digen(dentry); ++out: ++ if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { ++ AuIOErr("failed refreshing %.*s, %d\n", ++ AuDLNPair(dentry), err); ++ AuDbgDentry(dentry); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++static noinline_for_stack ++int au_do_h_d_reval(struct dentry *h_dentry, struct nameidata *nd, ++ struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, valid; ++ int (*reval)(struct dentry *, struct nameidata *); ++ ++ err = 0; ++ reval = NULL; ++ if (h_dentry->d_op) ++ reval = h_dentry->d_op->d_revalidate; ++ if (!reval) ++ goto out; ++ ++ AuDbg("b%d\n", bindex); ++ if (au_test_fs_null_nd(h_dentry->d_sb)) ++ /* it may return tri-state */ ++ valid = reval(h_dentry, NULL); ++ else { ++ struct nameidata h_nd; ++ int locked; ++ struct dentry *parent; ++ ++ au_h_nd(&h_nd, nd); ++ parent = nd->path.dentry; ++ locked = (nd && nd->path.dentry != dentry); ++ if (locked) ++ di_read_lock_parent(parent, AuLock_IR); ++ BUG_ON(bindex > au_dbend(parent)); ++ h_nd.path.dentry = au_h_dptr(parent, bindex); ++ BUG_ON(!h_nd.path.dentry); ++ h_nd.path.mnt = au_sbr(parent->d_sb, bindex)->br_mnt; ++ path_get(&h_nd.path); ++ valid = reval(h_dentry, &h_nd); ++ path_put(&h_nd.path); ++ if (locked) ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (unlikely(valid < 0)) ++ err = valid; ++ else if (!valid) ++ err = -EINVAL; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* todo: remove this */ ++static int h_d_revalidate(struct dentry *dentry, struct inode *inode, ++ struct nameidata *nd, int do_udba) ++{ ++ int err; ++ umode_t mode, h_mode; ++ aufs_bindex_t bindex, btail, bstart, ibs, ibe; ++ unsigned char plus, unhashed, is_root, h_plus; ++ struct inode *h_inode, *h_cached_inode; ++ struct dentry *h_dentry; ++ struct qstr *name, *h_name; ++ ++ err = 0; ++ plus = 0; ++ mode = 0; ++ 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 ++ * {FS,I}NOTIFY. ++ * But {fs,i}notify 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); ++ ibs = au_ibstart(inode); ++ ibe = au_ibend(inode); ++ } ++ ++ bstart = au_dbstart(dentry); ++ btail = bstart; ++ if (inode && S_ISDIR(inode->i_mode)) ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ AuDbg("b%d, %.*s\n", bindex, AuDLNPair(h_dentry)); ++ h_name = &h_dentry->d_name; ++ if (unlikely(do_udba ++ && !is_root ++ && (unhashed != !!d_unhashed(h_dentry) ++ || name->len != h_name->len ++ || memcmp(name->name, h_name->name, name->len)) ++ )) { ++ AuDbg("unhash 0x%x 0x%x, %.*s %.*s\n", ++ unhashed, d_unhashed(h_dentry), ++ AuDLNPair(dentry), AuDLNPair(h_dentry)); ++ goto err; ++ } ++ ++ err = au_do_h_d_reval(h_dentry, nd, dentry, bindex); ++ if (unlikely(err)) ++ /* do not goto err, to keep the errno */ ++ break; ++ ++ /* todo: plink too? */ ++ if (!do_udba) ++ continue; ++ ++ /* UDBA tests */ ++ h_inode = h_dentry->d_inode; ++ if (unlikely(!!inode != !!h_inode)) ++ 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); ++ } ++ if (inode && ibs <= bindex && bindex <= ibe) ++ h_cached_inode = au_h_iptr(inode, bindex); ++ ++ if (unlikely(plus != h_plus ++ || mode != h_mode ++ || h_cached_inode != h_inode)) ++ goto err; ++ continue; ++ ++ err: ++ err = -EINVAL; ++ break; ++ } ++ ++ return err; ++} ++ ++/* todo: consolidate with do_refresh() and au_reval_for_attr() */ ++static int simple_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *parent; ++ ++ if (!au_digen_test(dentry, sigen)) ++ return 0; ++ ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ AuDebugOn(au_digen_test(parent, sigen)); ++ au_dbg_verify_gen(parent, sigen); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct dentry *d, *parent; ++ struct inode *inode; ++ ++ if (!au_ftest_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR)) ++ return simple_reval_dpath(dentry, sigen); ++ ++ /* slow loop, keep it simple and stupid */ ++ /* cf: au_cpup_dirs() */ ++ err = 0; ++ parent = NULL; ++ while (au_digen_test(dentry, sigen)) { ++ d = dentry; ++ while (1) { ++ dput(parent); ++ parent = dget_parent(d); ++ if (!au_digen_test(parent, sigen)) ++ break; ++ d = parent; ++ } ++ ++ inode = d->d_inode; ++ if (d != dentry) ++ di_write_lock_child2(d); ++ ++ /* someone might update our dentry while we were sleeping */ ++ if (au_digen_test(d, sigen)) { ++ /* ++ * todo: consolidate with simple_reval_dpath(), ++ * do_refresh() and au_reval_for_attr(). ++ */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(d, parent); ++ di_read_unlock(parent, AuLock_IR); ++ } ++ ++ if (d != dentry) ++ di_write_unlock(d); ++ dput(parent); ++ if (unlikely(err)) ++ break; ++ } ++ ++ return err; ++} ++ ++/* ++ * if valid returns 1, otherwise 0. ++ */ ++static int aufs_d_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ int valid, err; ++ unsigned int sigen; ++ unsigned char do_udba; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ valid = 0; ++ if (unlikely(!au_di(dentry))) ++ goto out; ++ ++ valid = 1; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ /* ++ * todo: very ugly ++ * i_mutex of parent dir may be held, ++ * but we should not return 'invalid' due to busy. ++ */ ++ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); ++ if (unlikely(err)) { ++ valid = err; ++ AuTraceErr(err); ++ goto out; ++ } ++ if (unlikely(au_dbrange_test(dentry))) { ++ err = -EINVAL; ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ ++ sigen = au_sigen(sb); ++ if (au_digen_test(dentry, sigen)) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_dpath(dentry, sigen); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ goto out_dgrade; ++ } ++ } ++ di_downgrade_lock(dentry, AuLock_IR); ++ ++ err = -EINVAL; ++ if (inode && (IS_DEADDIR(inode) || !inode->i_nlink)) ++ goto out_inval; ++ ++ do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); ++ if (do_udba && inode) { ++ aufs_bindex_t bstart = au_ibstart(inode); ++ struct inode *h_inode; ++ ++ if (bstart >= 0) { ++ h_inode = au_h_iptr(inode, bstart); ++ if (h_inode && au_test_higen(inode, h_inode)) ++ goto out_inval; ++ } ++ } ++ ++ err = h_d_revalidate(dentry, inode, nd, do_udba); ++ if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { ++ err = -EIO; ++ AuDbg("both of real entry and whiteout found, %.*s, err %d\n", ++ AuDLNPair(dentry), err); ++ } ++ goto out_inval; ++ ++out_dgrade: ++ di_downgrade_lock(dentry, AuLock_IR); ++out_inval: ++ aufs_read_unlock(dentry, AuLock_IR); ++ AuTraceErr(err); ++ valid = !err; ++out: ++ if (!valid) { ++ AuDbg("%.*s invalid, %d\n", AuDLNPair(dentry), valid); ++ d_drop(dentry); ++ } ++ return valid; ++} ++ ++static void aufs_d_release(struct dentry *dentry) ++{ ++ if (au_di(dentry)) { ++ au_di_fin(dentry); ++ au_hn_di_reinit(dentry); ++ } ++} ++ ++const struct dentry_operations aufs_dop = { ++ .d_revalidate = aufs_d_revalidate, ++ .d_release = aufs_d_release ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/dentry.h linux-2.6.36/fs/aufs/dentry.h +--- linux-2.6.36.orig/fs/aufs/dentry.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dentry.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,237 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * lookup and dentry operations ++ */ ++ ++#ifndef __AUFS_DENTRY_H__ ++#define __AUFS_DENTRY_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++ ++struct au_hdentry { ++ struct dentry *hd_dentry; ++ aufs_bindex_t hd_id; ++}; ++ ++struct au_dinfo { ++ atomic_t di_generation; ++ ++ struct au_rwsem di_rwsem; ++ aufs_bindex_t di_bstart, di_bend, di_bwh, di_bdiropq; ++ struct au_hdentry *di_hdentry; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dentry.c */ ++extern const struct dentry_operations aufs_dop; ++struct au_branch; ++struct dentry *au_lkup_one(struct qstr *name, struct dentry *h_parent, ++ struct au_branch *br, struct nameidata *nd); ++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, ++ struct au_branch *br); ++int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir, ++ struct dentry *h_parent, struct au_branch *br); ++ ++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_dentry(struct dentry *dentry, struct dentry *parent); ++int au_reval_dpath(struct dentry *dentry, unsigned int sigen); ++ ++/* dinfo.c */ ++void au_di_init_once(void *_di); ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); ++void au_di_free(struct au_dinfo *dinfo); ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b); ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src); ++int au_di_init(struct dentry *dentry); ++void au_di_fin(struct dentry *dentry); ++int au_di_realloc(struct au_dinfo *dinfo, int nbr); ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc); ++void di_read_unlock(struct dentry *d, int flags); ++void di_downgrade_lock(struct dentry *d, int flags); ++void di_write_lock(struct dentry *d, unsigned int lsc); ++void di_write_unlock(struct dentry *d); ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_lock2_parent(struct dentry *d1, struct dentry *d2, int isdir); ++void di_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex); ++aufs_bindex_t au_dbtail(struct dentry *dentry); ++aufs_bindex_t au_dbtaildir(struct dentry *dentry); ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++int au_digen_test(struct dentry *dentry, unsigned int sigen); ++int au_dbrange_test(struct dentry *dentry); ++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 struct au_dinfo *au_di(struct dentry *dentry) ++{ ++ return dentry->d_fsdata; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for dinfo */ ++enum { ++ AuLsc_DI_CHILD, /* child first */ ++ AuLsc_DI_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_DI_CHILD3, /* copyup dirs */ ++ AuLsc_DI_PARENT, ++ AuLsc_DI_PARENT2, ++ AuLsc_DI_PARENT3, ++ AuLsc_DI_TMP /* temp for replacing dinfo */ ++}; ++ ++/* ++ * di_read_lock_child, di_write_lock_child, ++ * di_read_lock_child2, di_write_lock_child2, ++ * di_read_lock_child3, di_write_lock_child3, ++ * di_read_lock_parent, di_write_lock_parent, ++ * di_read_lock_parent2, di_write_lock_parent2, ++ * di_read_lock_parent3, di_write_lock_parent3, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void di_read_lock_##name(struct dentry *d, int flags) \ ++{ di_read_lock(d, flags, AuLsc_DI_##lsc); } ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void di_write_lock_##name(struct dentry *d) \ ++{ di_write_lock(d, AuLsc_DI_##lsc); } ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++#define DiMustNoWaiters(d) AuRwMustNoWaiters(&au_di(d)->di_rwsem) ++#define DiMustAnyLock(d) AuRwMustAnyLock(&au_di(d)->di_rwsem) ++#define DiMustWriteLock(d) AuRwMustWriteLock(&au_di(d)->di_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_digen(struct dentry *d) ++{ ++ return atomic_read(&au_di(d)->di_generation); ++} ++ ++static inline void au_h_dentry_init(struct au_hdentry *hdentry) ++{ ++ hdentry->hd_dentry = NULL; ++} ++ ++static inline void au_hdput(struct au_hdentry *hd) ++{ ++ if (hd) ++ dput(hd->hd_dentry); ++} ++ ++static inline aufs_bindex_t au_dbstart(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bstart; ++} ++ ++static inline aufs_bindex_t au_dbend(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bend; ++} ++ ++static inline aufs_bindex_t au_dbwh(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bwh; ++} ++ ++static inline aufs_bindex_t au_dbdiropq(struct dentry *dentry) ++{ ++ DiMustAnyLock(dentry); ++ return au_di(dentry)->di_bdiropq; ++} ++ ++/* todo: hard/soft set? */ ++static inline void au_set_dbstart(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bstart = bindex; ++} ++ ++static inline void au_set_dbend(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bend = bindex; ++} ++ ++static inline void au_set_dbwh(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ /* dbwh can be outside of bstart - bend range */ ++ au_di(dentry)->di_bwh = bindex; ++} ++ ++static inline void au_set_dbdiropq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ DiMustWriteLock(dentry); ++ au_di(dentry)->di_bdiropq = bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_HNOTIFY ++static inline void au_digen_dec(struct dentry *d) ++{ ++ atomic_dec(&au_di(d)->di_generation); ++} ++ ++static inline void au_hn_di_reinit(struct dentry *dentry) ++{ ++ dentry->d_fsdata = NULL; ++} ++#else ++AuStubVoid(au_hn_di_reinit, struct dentry *dentry __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DENTRY_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/dinfo.c linux-2.6.36/fs/aufs/dinfo.c +--- linux-2.6.36.orig/fs/aufs/dinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dinfo.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,494 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * dentry private data ++ */ ++ ++#include "aufs.h" ++ ++void au_di_init_once(void *_dinfo) ++{ ++ struct au_dinfo *dinfo = _dinfo; ++ static struct lock_class_key aufs_di; ++ ++ au_rw_init(&dinfo->di_rwsem); ++ au_rw_class(&dinfo->di_rwsem, &aufs_di); ++} ++ ++struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc) ++{ ++ struct au_dinfo *dinfo; ++ int nbr, i; ++ ++ dinfo = au_cache_alloc_dinfo(); ++ if (unlikely(!dinfo)) ++ goto out; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr <= 0) ++ nbr = 1; ++ dinfo->di_hdentry = kcalloc(nbr, sizeof(*dinfo->di_hdentry), GFP_NOFS); ++ if (dinfo->di_hdentry) { ++ au_rw_write_lock_nested(&dinfo->di_rwsem, lsc); ++ dinfo->di_bstart = -1; ++ dinfo->di_bend = -1; ++ dinfo->di_bwh = -1; ++ dinfo->di_bdiropq = -1; ++ for (i = 0; i < nbr; i++) ++ dinfo->di_hdentry[i].hd_id = -1; ++ goto out; ++ } ++ ++ au_cache_free_dinfo(dinfo); ++ dinfo = NULL; ++ ++out: ++ return dinfo; ++} ++ ++void au_di_free(struct au_dinfo *dinfo) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bend, bindex; ++ ++ /* dentry may not be revalidated */ ++ bindex = dinfo->di_bstart; ++ if (bindex >= 0) { ++ bend = dinfo->di_bend; ++ p = dinfo->di_hdentry + bindex; ++ while (bindex++ <= bend) ++ au_hdput(p++); ++ } ++ kfree(dinfo->di_hdentry); ++ au_cache_free_dinfo(dinfo); ++} ++ ++void au_di_swap(struct au_dinfo *a, struct au_dinfo *b) ++{ ++ struct au_hdentry *p; ++ aufs_bindex_t bi; ++ ++ AuRwMustWriteLock(&a->di_rwsem); ++ AuRwMustWriteLock(&b->di_rwsem); ++ ++#define DiSwap(v, name) \ ++ do { \ ++ v = a->di_##name; \ ++ a->di_##name = b->di_##name; \ ++ b->di_##name = v; \ ++ } while (0) ++ ++ DiSwap(p, hdentry); ++ DiSwap(bi, bstart); ++ DiSwap(bi, bend); ++ DiSwap(bi, bwh); ++ DiSwap(bi, bdiropq); ++ /* smp_mb(); */ ++ ++#undef DiSwap ++} ++ ++void au_di_cp(struct au_dinfo *dst, struct au_dinfo *src) ++{ ++ AuRwMustWriteLock(&dst->di_rwsem); ++ AuRwMustWriteLock(&src->di_rwsem); ++ ++ dst->di_bstart = src->di_bstart; ++ dst->di_bend = src->di_bend; ++ dst->di_bwh = src->di_bwh; ++ dst->di_bdiropq = src->di_bdiropq; ++ /* smp_mb(); */ ++} ++ ++int au_di_init(struct dentry *dentry) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_dinfo *dinfo; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ dinfo = au_di_alloc(sb, AuLsc_DI_CHILD); ++ if (dinfo) { ++ atomic_set(&dinfo->di_generation, au_sigen(sb)); ++ /* smp_mb(); */ /* atomic_set */ ++ dentry->d_op = &aufs_dop; ++ dentry->d_fsdata = dinfo; ++ } else ++ err = -ENOMEM; ++ ++ return err; ++} ++ ++void au_di_fin(struct dentry *dentry) ++{ ++ struct au_dinfo *dinfo; ++ ++ dinfo = au_di(dentry); ++ AuRwDestroy(&dinfo->di_rwsem); ++ au_di_free(dinfo); ++} ++ ++int au_di_realloc(struct au_dinfo *dinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hdentry *hdp; ++ ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hdp) * (dinfo->di_bend + 1); ++ if (!sz) ++ sz = sizeof(*hdp); ++ hdp = au_kzrealloc(dinfo->di_hdentry, sz, sizeof(*hdp) * nbr, GFP_NOFS); ++ if (hdp) { ++ dinfo->di_hdentry = hdp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++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: ++ ii_write_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_write_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ 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: ++ ii_read_lock_parent(inode); ++ break; ++ case AuLsc_DI_PARENT2: ++ ii_read_lock_parent2(inode); ++ break; ++ case AuLsc_DI_PARENT3: ++ ii_read_lock_parent3(inode); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++void di_read_lock(struct dentry *d, int flags, unsigned int lsc) ++{ ++ au_rw_read_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) ++ do_ii_write_lock(d->d_inode, lsc); ++ else if (au_ftest_lock(flags, IR)) ++ do_ii_read_lock(d->d_inode, lsc); ++ } ++} ++ ++void di_read_unlock(struct dentry *d, int flags) ++{ ++ if (d->d_inode) { ++ if (au_ftest_lock(flags, IW)) { ++ au_dbg_verify_dinode(d); ++ ii_write_unlock(d->d_inode); ++ } else if (au_ftest_lock(flags, IR)) { ++ au_dbg_verify_dinode(d); ++ ii_read_unlock(d->d_inode); ++ } ++ } ++ au_rw_read_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_downgrade_lock(struct dentry *d, int flags) ++{ ++ if (d->d_inode && au_ftest_lock(flags, IR)) ++ ii_downgrade_lock(d->d_inode); ++ au_rw_dgrade_lock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock(struct dentry *d, unsigned int lsc) ++{ ++ au_rw_write_lock_nested(&au_di(d)->di_rwsem, lsc); ++ if (d->d_inode) ++ do_ii_write_lock(d->d_inode, lsc); ++} ++ ++void di_write_unlock(struct dentry *d) ++{ ++ au_dbg_verify_dinode(d); ++ if (d->d_inode) ++ ii_write_unlock(d->d_inode); ++ au_rw_write_unlock(&au_di(d)->di_rwsem); ++} ++ ++void di_write_lock2_child(struct dentry *d1, struct dentry *d2, int isdir) ++{ ++ 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) ++{ ++ AuDebugOn(d1 == d2 ++ || d1->d_inode == d2->d_inode ++ || d1->d_sb != d2->d_sb); ++ ++ if (isdir && au_test_subdir(d1, d2)) { ++ di_write_lock_parent(d1); ++ di_write_lock_parent2(d2); ++ } else { ++ /* there should be no races */ ++ di_write_lock_parent(d2); ++ di_write_lock_parent2(d1); ++ } ++} ++ ++void di_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock(d1); ++ if (d1->d_inode == d2->d_inode) ++ au_rw_write_unlock(&au_di(d2)->di_rwsem); ++ else ++ di_write_unlock(d2); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_h_dptr(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct dentry *d; ++ ++ DiMustAnyLock(dentry); ++ ++ if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) ++ return NULL; ++ AuDebugOn(bindex < 0); ++ d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry; ++ AuDebugOn(d && (atomic_read(&d->d_count) <= 0)); ++ return d; ++} ++ ++aufs_bindex_t au_dbtail(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bwh; ++ ++ bend = au_dbend(dentry); ++ if (0 <= bend) { ++ bwh = au_dbwh(dentry); ++ if (!bwh) ++ return bwh; ++ if (0 < bwh && bwh < bend) ++ return bwh - 1; ++ } ++ return bend; ++} ++ ++aufs_bindex_t au_dbtaildir(struct dentry *dentry) ++{ ++ aufs_bindex_t bend, bopq; ++ ++ bend = au_dbtail(dentry); ++ if (0 <= bend) { ++ bopq = au_dbdiropq(dentry); ++ if (0 <= bopq && bopq < bend) ++ bend = bopq; ++ } ++ return bend; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_set_h_dptr(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct au_hdentry *hd = au_di(dentry)->di_hdentry + bindex; ++ struct au_branch *br; ++ ++ DiMustWriteLock(dentry); ++ ++ au_hdput(hd); ++ hd->hd_dentry = h_dentry; ++ if (h_dentry) { ++ br = au_sbr(dentry->d_sb, bindex); ++ hd->hd_id = br->br_id; ++ } ++} ++ ++int au_dbrange_test(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart, bend; ++ ++ err = 0; ++ bstart = au_dbstart(dentry); ++ bend = au_dbend(dentry); ++ if (bstart >= 0) ++ AuDebugOn(bend < 0 && bstart > bend); ++ else { ++ err = -EIO; ++ AuDebugOn(bend >= 0); ++ } ++ ++ return err; ++} ++ ++int au_digen_test(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(au_digen(dentry) != sigen ++ || au_iigen_test(dentry->d_inode, sigen))) ++ err = -EIO; ++ ++ return err; ++} ++ ++void au_update_digen(struct dentry *dentry) ++{ ++ atomic_set(&au_di(dentry)->di_generation, au_sigen(dentry->d_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++void au_update_dbrange(struct dentry *dentry, int do_put_zero) ++{ ++ struct au_dinfo *dinfo; ++ struct dentry *h_d; ++ struct au_hdentry *hdp; ++ ++ DiMustWriteLock(dentry); ++ ++ dinfo = au_di(dentry); ++ if (!dinfo || dinfo->di_bstart < 0) ++ return; ++ ++ hdp = dinfo->di_hdentry; ++ if (do_put_zero) { ++ aufs_bindex_t bindex, bend; ++ ++ bend = dinfo->di_bend; ++ for (bindex = dinfo->di_bstart; bindex <= bend; bindex++) { ++ h_d = hdp[0 + bindex].hd_dentry; ++ if (h_d && !h_d->d_inode) ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++ } ++ ++ dinfo->di_bstart = -1; ++ while (++dinfo->di_bstart <= dinfo->di_bend) ++ if (hdp[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 (hdp[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, bend; ++ struct dentry *h_dentry; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbstart(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++void au_update_dbend(struct dentry *dentry) ++{ ++ aufs_bindex_t bindex, bstart; ++ struct dentry *h_dentry; ++ ++ bstart = au_dbstart(dentry); ++ for (bindex = au_dbend(dentry); bindex >= bstart; bindex--) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ if (h_dentry->d_inode) { ++ au_set_dbend(dentry, bindex); ++ return; ++ } ++ au_set_h_dptr(dentry, bindex, NULL); ++ } ++} ++ ++int au_find_dbindex(struct dentry *dentry, struct dentry *h_dentry) ++{ ++ aufs_bindex_t bindex, bend; ++ ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) ++ if (au_h_dptr(dentry, bindex) == h_dentry) ++ return bindex; ++ return -1; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/dir.c linux-2.6.36/fs/aufs/dir.c +--- linux-2.6.36.orig/fs/aufs/dir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dir.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,648 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++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 (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 (h_dir->i_nlink < 2) ++ dir->i_nlink -= 2; ++} ++ ++loff_t au_dir_size(struct file *file, struct dentry *dentry) ++{ ++ loff_t sz; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ sz = 0; ++ if (file) { ++ AuDebugOn(!file->f_dentry); ++ AuDebugOn(!file->f_dentry->d_inode); ++ AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)); ++ ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file ++ && h_file->f_dentry ++ && h_file->f_dentry->d_inode) ++ sz += i_size_read(h_file->f_dentry->d_inode); ++ } ++ } else { ++ AuDebugOn(!dentry); ++ AuDebugOn(!dentry->d_inode); ++ AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode)); ++ ++ bend = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); ++ bindex <= bend && sz < KMALLOC_MAX_SIZE; ++ bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) ++ sz += i_size_read(h_dentry->d_inode); ++ } ++ } ++ if (sz < KMALLOC_MAX_SIZE) ++ sz = roundup_pow_of_two(sz); ++ if (sz > KMALLOC_MAX_SIZE) ++ sz = KMALLOC_MAX_SIZE; ++ else if (sz < NAME_MAX) { ++ BUILD_BUG_ON(AUFS_RDBLK_DEF < NAME_MAX); ++ sz = AUFS_RDBLK_DEF; ++ } ++ return sz; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int reopen_dir(struct file *file) ++{ ++ int err; ++ unsigned int flags; ++ aufs_bindex_t bindex, btail, bstart; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ /* open all lower dirs */ ++ dentry = file->f_dentry; ++ bstart = au_dbstart(dentry); ++ for (bindex = au_fbstart(file); bindex < bstart; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, bstart); ++ ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_fbend_dir(file); btail < bindex; bindex--) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbend_dir(file, btail); ++ ++ flags = vfsub_file_flags(file); ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* close all? */ ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ err = 0; ++ ++out: ++ return err; ++} ++ ++static int do_open_dir(struct file *file, int flags) ++{ ++ int err; ++ aufs_bindex_t bindex, btail; ++ struct dentry *dentry, *h_dentry; ++ struct file *h_file; ++ ++ FiMustWriteLock(file); ++ ++ dentry = file->f_dentry; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ file->f_version = dentry->d_inode->i_version; ++ bindex = au_dbstart(dentry); ++ au_set_fbstart(file, bindex); ++ btail = au_dbtaildir(dentry); ++ au_set_fbend_dir(file, btail); ++ for (; !err && bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!h_dentry) ++ continue; ++ ++ h_file = au_h_open(dentry, bindex, flags, file); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ break; ++ } ++ au_set_h_fptr(file, bindex, h_file); ++ } ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ if (!err) ++ return 0; /* success */ ++ ++ /* close all */ ++ for (bindex = au_fbstart(file); bindex <= btail; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ au_set_fbstart(file, -1); ++ au_set_fbend_dir(file, -1); ++ ++out: ++ return err; ++} ++ ++static int aufs_open_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ err = -ENOMEM; ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ fidir = au_fidir_alloc(sb); ++ if (fidir) { ++ err = au_do_open(file, do_open_dir, fidir); ++ if (unlikely(err)) ++ kfree(fidir); ++ } ++ si_read_unlock(sb); ++ return err; ++} ++ ++static int aufs_release_dir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ struct au_vdir *vdir_cache; ++ struct super_block *sb; ++ struct au_finfo *finfo; ++ struct au_fidir *fidir; ++ aufs_bindex_t bindex, bend; ++ ++ sb = file->f_dentry->d_sb; ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ if (fidir) { ++ /* remove me from sb->s_files */ ++ file_sb_list_del(file); ++ ++ vdir_cache = fidir->fd_vdir_cache; /* lock-free */ ++ if (vdir_cache) ++ au_vdir_free(vdir_cache); ++ ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) { ++ /* ++ * calls fput() instead of filp_close(), ++ * since no dnotify or lock for the lower file. ++ */ ++ bend = fidir->fd_bbot; ++ for (; bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ kfree(fidir); ++ finfo->fi_hdir = NULL; ++ } ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_dir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct file *h_file; ++ ++ err = 0; ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ } ++ return err; ++} ++ ++static int aufs_flush_dir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_dir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_fsync_dir_no_file(struct dentry *dentry, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= bend; bindex++) { ++ struct path h_path; ++ struct inode *h_inode; ++ ++ if (au_test_ro(sb, bindex, inode)) ++ continue; ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ if (!h_path.dentry) ++ continue; ++ h_inode = h_path.dentry->d_inode; ++ if (!h_inode) ++ continue; ++ ++ /* no mnt_want_write() */ ++ /* cf. fs/nsfd/vfs.c and fs/nfsd/nfs4recover.c */ ++ /* todo: inotiry fired? */ ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ mutex_lock(&h_inode->i_mutex); ++ err = filemap_fdatawrite(h_inode->i_mapping); ++ AuDebugOn(!h_inode->i_fop); ++ if (!err && h_inode->i_fop->fsync) ++ err = h_inode->i_fop->fsync(NULL, datasync); ++ if (!err) ++ err = filemap_fdatawrite(h_inode->i_mapping); ++ if (!err) ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++ mutex_unlock(&h_inode->i_mutex); ++ } ++ ++ return err; ++} ++ ++static int au_do_fsync_dir(struct file *file, int datasync) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct file *h_file; ++ struct super_block *sb; ++ struct inode *inode; ++ struct mutex *h_mtx; ++ ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = file->f_dentry->d_sb; ++ inode = file->f_dentry->d_inode; ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { ++ h_file = au_hf_dir(file, bindex); ++ if (!h_file || au_test_ro(sb, bindex, inode)) ++ continue; ++ ++ err = vfs_fsync(h_file, datasync); ++ if (!err) { ++ h_mtx = &h_file->f_dentry->d_inode->i_mutex; ++ mutex_lock(h_mtx); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ mutex_unlock(h_mtx); ++ } ++ } ++ ++out: ++ return err; ++} ++ ++/* ++ * @file may be NULL ++ */ ++static int aufs_fsync_dir(struct file *file, int datasync) ++{ ++ int err; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ IMustLock(dentry->d_inode); ++ ++ err = 0; ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (file) ++ err = au_do_fsync_dir(file, datasync); ++ else { ++ di_write_lock_child(dentry); ++ err = au_do_fsync_dir_no_file(dentry, datasync); ++ } ++ au_cpup_attr_timesizes(dentry->d_inode); ++ di_write_unlock(dentry); ++ if (file) ++ fi_write_unlock(file); ++ ++ si_read_unlock(sb); ++ 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; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ err = au_alive_dir(dentry); ++ if (!err) ++ err = au_vdir_init(file); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ if (!au_test_nfsd()) { ++ err = au_vdir_fill_de(file, dirent, filldir); ++ fsstack_copy_attr_atime(inode, ++ au_h_iptr(inode, au_ibstart(inode))); ++ } else { ++ /* ++ * nfsd filldir may call lookup_one_len(), vfs_getattr(), ++ * encode_fh() and others. ++ */ ++ struct inode *h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++ err = au_vdir_fill_de(file, dirent, filldir); ++ fsstack_copy_attr_atime(inode, h_inode); ++ fi_write_unlock(file); ++ ++ AuTraceErr(err); ++ return err; ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuTestEmpty_WHONLY 1 ++#define AuTestEmpty_CALLED (1 << 1) ++#define AuTestEmpty_SHWH (1 << 2) ++#define au_ftest_testempty(flags, name) ((flags) & AuTestEmpty_##name) ++#define au_fset_testempty(flags, name) \ ++ do { (flags) |= AuTestEmpty_##name; } while (0) ++#define au_fclr_testempty(flags, name) \ ++ do { (flags) &= ~AuTestEmpty_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuTestEmpty_SHWH ++#define AuTestEmpty_SHWH 0 ++#endif ++ ++struct test_empty_arg { ++ struct au_nhash *whlist; ++ unsigned int flags; ++ int err; ++ aufs_bindex_t bindex; ++}; ++ ++static int test_empty_cb(void *__arg, const char *__name, int namelen, ++ loff_t offset __maybe_unused, u64 ino, ++ unsigned int d_type) ++{ ++ struct test_empty_arg *arg = __arg; ++ char *name = (void *)__name; ++ ++ arg->err = 0; ++ au_fset_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (name[0] == '.' ++ && (namelen == 1 || (name[1] == '.' && namelen == 2))) ++ goto out; /* success */ ++ ++ if (namelen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (au_ftest_testempty(arg->flags, WHONLY) ++ && !au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = -ENOTEMPTY; ++ goto out; ++ } ++ ++ name += AUFS_WH_PFX_LEN; ++ namelen -= AUFS_WH_PFX_LEN; ++ if (!au_nhash_test_known_wh(arg->whlist, name, namelen)) ++ arg->err = au_nhash_append_wh ++ (arg->whlist, name, namelen, ino, d_type, arg->bindex, ++ au_ftest_testempty(arg->flags, SHWH)); ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int do_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err; ++ struct file *h_file; ++ ++ h_file = au_h_open(dentry, arg->bindex, ++ O_RDONLY | O_NONBLOCK | O_DIRECTORY | O_LARGEFILE, ++ /*file*/NULL); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) ++ && !h_file->f_dentry->d_inode->i_nlink) ++ goto out_put; ++ ++ do { ++ arg->err = 0; ++ au_fclr_testempty(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_readdir(h_file, test_empty_cb, arg); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_testempty(arg->flags, CALLED)); ++ ++out_put: ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, arg->bindex); ++out: ++ return err; ++} ++ ++struct do_test_empty_args { ++ int *errp; ++ struct dentry *dentry; ++ struct test_empty_arg *arg; ++}; ++ ++static void call_do_test_empty(void *args) ++{ ++ struct do_test_empty_args *a = args; ++ *a->errp = do_test_empty(a->dentry, a->arg); ++} ++ ++static int sio_test_empty(struct dentry *dentry, struct test_empty_arg *arg) ++{ ++ int err, wkq_err; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, arg->bindex); ++ h_inode = h_dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ); ++ mutex_unlock(&h_inode->i_mutex); ++ if (!err) ++ err = do_test_empty(dentry, arg); ++ else { ++ struct do_test_empty_args args = { ++ .errp = &err, ++ .dentry = dentry, ++ .arg = arg ++ }; ++ unsigned int flags = arg->flags; ++ ++ wkq_err = au_wkq_wait(call_do_test_empty, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ arg->flags = flags; ++ } ++ ++ return err; ++} ++ ++int au_test_empty_lower(struct dentry *dentry) ++{ ++ int err; ++ unsigned int rdhash; ++ aufs_bindex_t bindex, bstart, btail; ++ struct au_nhash whlist; ++ struct test_empty_arg arg; ++ ++ SiMustAnyLock(dentry->d_sb); ++ ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ arg.flags = 0; ++ arg.whlist = &whlist; ++ bstart = au_dbstart(dentry); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ arg.bindex = bstart; ++ err = do_test_empty(dentry, &arg); ++ if (unlikely(err)) ++ goto out_whlist; ++ ++ au_fset_testempty(arg.flags, WHONLY); ++ btail = au_dbtaildir(dentry); ++ for (bindex = bstart + 1; !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = do_test_empty(dentry, &arg); ++ } ++ } ++ ++out_whlist: ++ au_nhash_wh_free(&whlist); ++out: ++ return err; ++} ++ ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct test_empty_arg arg; ++ aufs_bindex_t bindex, btail; ++ ++ err = 0; ++ arg.whlist = whlist; ++ arg.flags = AuTestEmpty_WHONLY; ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH)) ++ au_fset_testempty(arg.flags, SHWH); ++ btail = au_dbtaildir(dentry); ++ for (bindex = au_dbstart(dentry); !err && bindex <= btail; bindex++) { ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry && h_dentry->d_inode) { ++ arg.bindex = bindex; ++ err = sio_test_empty(dentry, &arg); ++ } ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_dir_fop = { ++ .owner = THIS_MODULE, ++ .read = generic_read_dir, ++ .readdir = aufs_readdir, ++ .unlocked_ioctl = aufs_ioctl_dir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_compat_ioctl_dir, ++#endif ++ .open = aufs_open_dir, ++ .release = aufs_release_dir, ++ .flush = aufs_flush_dir, ++ .fsync = aufs_fsync_dir ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/dir.h linux-2.6.36/fs/aufs/dir.h +--- linux-2.6.36.orig/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dir.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,138 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * directory operations ++ */ ++ ++#ifndef __AUFS_DIR_H__ ++#define __AUFS_DIR_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* need to be faster and smaller */ ++ ++struct au_nhash { ++ unsigned int nh_num; ++ struct hlist_head *nh_head; ++}; ++ ++struct au_vdir_destr { ++ unsigned char len; ++ unsigned char name[0]; ++} __packed; ++ ++struct au_vdir_dehstr { ++ struct hlist_node hash; ++ struct au_vdir_destr *str; ++} ____cacheline_aligned_in_smp; ++ ++struct au_vdir_de { ++ ino_t de_ino; ++ unsigned char de_type; ++ /* caution: packed */ ++ struct au_vdir_destr de_str; ++} __packed; ++ ++struct au_vdir_wh { ++ struct hlist_node wh_hash; ++#ifdef CONFIG_AUFS_SHWH ++ ino_t wh_ino; ++ aufs_bindex_t wh_bindex; ++ unsigned char wh_type; ++#else ++ aufs_bindex_t wh_bindex; ++#endif ++ /* caution: packed */ ++ struct au_vdir_destr wh_str; ++} __packed; ++ ++union au_vdir_deblk_p { ++ unsigned char *deblk; ++ struct au_vdir_de *de; ++}; ++ ++struct au_vdir { ++ unsigned char **vd_deblk; ++ unsigned long vd_nblk; ++ struct { ++ unsigned long ul; ++ union au_vdir_deblk_p p; ++ } vd_last; ++ ++ unsigned long vd_version; ++ unsigned int vd_deblk_sz; ++ unsigned long vd_jiffy; ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dir.c */ ++extern const struct file_operations aufs_dir_fop; ++void au_add_nlink(struct inode *dir, struct inode *h_dir); ++void au_sub_nlink(struct inode *dir, struct inode *h_dir); ++loff_t au_dir_size(struct file *file, struct dentry *dentry); ++int au_test_empty_lower(struct dentry *dentry); ++int au_test_empty(struct dentry *dentry, struct au_nhash *whlist); ++ ++/* vdir.c */ ++unsigned int au_rdhash_est(loff_t sz); ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp); ++void au_nhash_wh_free(struct au_nhash *whlist); ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit); ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen); ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh); ++void au_vdir_free(struct au_vdir *vdir); ++int au_vdir_init(struct file *file); ++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir); ++ ++/* ioctl.c */ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg); ++ ++#ifdef CONFIG_AUFS_RDU ++/* rdu.c */ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++#else ++static inline long au_rdu_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return -EINVAL; ++} ++#ifdef CONFIG_COMPAT ++static inline long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return -EINVAL; ++} ++#endif ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DIR_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/dynop.c linux-2.6.36/fs/aufs/dynop.c +--- linux-2.6.36.orig/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dynop.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,425 @@ ++/* ++ * Copyright (C) 2010-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * dynamically customizable operations for regular files ++ */ ++ ++#include "aufs.h" ++ ++#define DyPrSym(key) AuDbgSym(key->dk_op.dy_hop) ++ ++/* ++ * How large will these lists be? ++ * Usually just a few elements, 20-30 at most for each, I guess. ++ */ ++static struct au_splhead dynop[AuDyLast]; ++ ++static struct au_dykey *dy_gfind_get(struct au_splhead *spl, const void *h_op) ++{ ++ struct au_dykey *key, *tmp; ++ struct list_head *head; ++ ++ key = NULL; ++ head = &spl->head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ key = tmp; ++ kref_get(&key->dk_kref); ++ break; ++ } ++ rcu_read_unlock(); ++ ++ return key; ++} ++ ++static struct au_dykey *dy_bradd(struct au_branch *br, struct au_dykey *key) ++{ ++ struct au_dykey **k, *found; ++ const void *h_op = key->dk_op.dy_hop; ++ int i; ++ ++ found = NULL; ++ k = br->br_dykey; ++ for (i = 0; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else ++ break; ++ if (!found) { ++ spin_lock(&br->br_dykey_lock); ++ for (; i < AuBrDynOp; i++) ++ if (k[i]) { ++ if (k[i]->dk_op.dy_hop == h_op) { ++ found = k[i]; ++ break; ++ } ++ } else { ++ k[i] = key; ++ break; ++ } ++ spin_unlock(&br->br_dykey_lock); ++ BUG_ON(i == AuBrDynOp); /* expand the array */ ++ } ++ ++ return found; ++} ++ ++/* kref_get() if @key is already added */ ++static struct au_dykey *dy_gadd(struct au_splhead *spl, struct au_dykey *key) ++{ ++ struct au_dykey *tmp, *found; ++ struct list_head *head; ++ const void *h_op = key->dk_op.dy_hop; ++ ++ found = NULL; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(tmp, head, dk_list) ++ if (tmp->dk_op.dy_hop == h_op) { ++ kref_get(&tmp->dk_kref); ++ found = tmp; ++ break; ++ } ++ if (!found) ++ list_add_rcu(&key->dk_list, head); ++ spin_unlock(&spl->spin); ++ ++ if (!found) ++ DyPrSym(key); ++ return found; ++} ++ ++static void dy_free_rcu(struct rcu_head *rcu) ++{ ++ struct au_dykey *key; ++ ++ key = container_of(rcu, struct au_dykey, dk_rcu); ++ DyPrSym(key); ++ kfree(key); ++} ++ ++static void dy_free(struct kref *kref) ++{ ++ struct au_dykey *key; ++ struct au_splhead *spl; ++ ++ key = container_of(kref, struct au_dykey, dk_kref); ++ spl = dynop + key->dk_op.dy_type; ++ au_spl_del_rcu(&key->dk_list, spl); ++ call_rcu(&key->dk_rcu, dy_free_rcu); ++} ++ ++void au_dy_put(struct au_dykey *key) ++{ ++ kref_put(&key->dk_kref, dy_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define DyDbgSize(cnt, op) AuDebugOn(cnt != sizeof(op)/sizeof(void *)) ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define DyDbgDeclare(cnt) unsigned int cnt = 0 ++#define DyDbgInc(cnt) do { cnt++; } while (0) ++#else ++#define DyDbgDeclare(cnt) do {} while (0) ++#define DyDbgInc(cnt) do {} while (0) ++#endif ++ ++#define DySet(func, dst, src, h_op, h_sb) do { \ ++ DyDbgInc(cnt); \ ++ if (h_op->func) { \ ++ if (src.func) \ ++ dst.func = src.func; \ ++ else \ ++ AuDbg("%s %s\n", au_sbtype(h_sb), #func); \ ++ } \ ++} while (0) ++ ++#define DySetForce(func, dst, src) do { \ ++ AuDebugOn(!src.func); \ ++ DyDbgInc(cnt); \ ++ dst.func = src.func; \ ++} while (0) ++ ++#define DySetAop(func) \ ++ DySet(func, dyaop->da_op, aufs_aop, h_aop, h_sb) ++#define DySetAopForce(func) \ ++ DySetForce(func, dyaop->da_op, aufs_aop) ++ ++static void dy_aop(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused) ++{ ++ struct au_dyaop *dyaop = (void *)key; ++ const struct address_space_operations *h_aop = h_op; ++ DyDbgDeclare(cnt); ++ ++ AuDbg("%s\n", au_sbtype(h_sb)); ++ ++ DySetAop(writepage); ++ DySetAopForce(readpage); /* force */ ++ DySetAop(sync_page); ++ DySetAop(writepages); ++ DySetAop(set_page_dirty); ++ DySetAop(readpages); ++ DySetAop(write_begin); ++ DySetAop(write_end); ++ DySetAop(bmap); ++ DySetAop(invalidatepage); ++ DySetAop(releasepage); ++ /* these two will be changed according to an aufs mount option */ ++ DySetAop(direct_IO); ++ DySetAop(get_xip_mem); ++ DySetAop(migratepage); ++ DySetAop(launder_page); ++ DySetAop(is_partially_uptodate); ++ DySetAop(error_remove_page); ++ ++ DyDbgSize(cnt, *h_aop); ++ dyaop->da_get_xip_mem = h_aop->get_xip_mem; ++} ++ ++#define DySetVmop(func) \ ++ DySet(func, dyvmop->dv_op, aufs_vm_ops, h_vmop, h_sb) ++#define DySetVmopForce(func) \ ++ DySetForce(func, dyvmop->dv_op, aufs_vm_ops) ++ ++static void dy_vmop(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused) ++{ ++ struct au_dyvmop *dyvmop = (void *)key; ++ const struct vm_operations_struct *h_vmop = h_op; ++ DyDbgDeclare(cnt); ++ ++ AuDbg("%s\n", au_sbtype(h_sb)); ++ ++ DySetVmop(open); ++ DySetVmop(close); ++ DySetVmop(fault); ++ DySetVmop(page_mkwrite); ++ DySetVmop(access); ++#ifdef CONFIG_NUMA ++ DySetVmop(set_policy); ++ DySetVmop(get_policy); ++ DySetVmop(migrate); ++#endif ++ ++ DyDbgSize(cnt, *h_vmop); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void dy_bug(struct kref *kref) ++{ ++ BUG(); ++} ++ ++static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) ++{ ++ struct au_dykey *key, *old; ++ struct au_splhead *spl; ++ struct op { ++ unsigned int sz; ++ void (*set)(struct au_dykey *key, const void *h_op, ++ struct super_block *h_sb __maybe_unused); ++ }; ++ static const struct op a[] = { ++ [AuDy_AOP] = { ++ .sz = sizeof(struct au_dyaop), ++ .set = dy_aop ++ }, ++ [AuDy_VMOP] = { ++ .sz = sizeof(struct au_dyvmop), ++ .set = dy_vmop ++ } ++ }; ++ const struct op *p; ++ ++ spl = dynop + op->dy_type; ++ key = dy_gfind_get(spl, op->dy_hop); ++ if (key) ++ goto out_add; /* success */ ++ ++ p = a + op->dy_type; ++ key = kzalloc(p->sz, GFP_NOFS); ++ if (unlikely(!key)) { ++ key = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ key->dk_op.dy_hop = op->dy_hop; ++ kref_init(&key->dk_kref); ++ p->set(key, op->dy_hop, br->br_mnt->mnt_sb); ++ old = dy_gadd(spl, key); ++ if (old) { ++ kfree(key); ++ key = old; ++ } ++ ++out_add: ++ old = dy_bradd(br, key); ++ if (old) ++ /* its ref-count should never be zero here */ ++ kref_put(&key->dk_kref, dy_bug); ++out: ++ return key; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * Aufs prohibits O_DIRECT by defaut even if the branch supports it. ++ * This behaviour is neccessary to return an error from open(O_DIRECT) instead ++ * of the succeeding I/O. The dio mount option enables O_DIRECT and makes ++ * open(O_DIRECT) always succeed, but the succeeding I/O may return an error. ++ * See the aufs manual in detail. ++ * ++ * To keep this behaviour, aufs has to set NULL to ->get_xip_mem too, and the ++ * performance of fadvise() and madvise() may be affected. ++ */ ++static void dy_adx(struct au_dyaop *dyaop, int do_dx) ++{ ++ if (!do_dx) { ++ dyaop->da_op.direct_IO = NULL; ++ dyaop->da_op.get_xip_mem = NULL; ++ } else { ++ dyaop->da_op.direct_IO = aufs_aop.direct_IO; ++ dyaop->da_op.get_xip_mem = aufs_aop.get_xip_mem; ++ if (!dyaop->da_get_xip_mem) ++ dyaop->da_op.get_xip_mem = NULL; ++ } ++} ++ ++static struct au_dyaop *dy_aget(struct au_branch *br, ++ const struct address_space_operations *h_aop, ++ int do_dx) ++{ ++ struct au_dyaop *dyaop; ++ struct au_dynop op; ++ ++ op.dy_type = AuDy_AOP; ++ op.dy_haop = h_aop; ++ dyaop = (void *)dy_get(&op, br); ++ if (IS_ERR(dyaop)) ++ goto out; ++ dy_adx(dyaop, do_dx); ++ ++out: ++ return dyaop; ++} ++ ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode) ++{ ++ int err, do_dx; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_dyaop *dyaop; ++ ++ AuDebugOn(!S_ISREG(h_inode->i_mode)); ++ IiMustWriteLock(inode); ++ ++ sb = inode->i_sb; ++ br = au_sbr(sb, bindex); ++ do_dx = !!au_opt_test(au_mntflags(sb), DIO); ++ dyaop = dy_aget(br, h_inode->i_mapping->a_ops, do_dx); ++ err = PTR_ERR(dyaop); ++ if (IS_ERR(dyaop)) ++ /* unnecessary to call dy_fput() */ ++ goto out; ++ ++ err = 0; ++ inode->i_mapping->a_ops = &dyaop->da_op; ++ ++out: ++ return err; ++} ++ ++/* ++ * Is it safe to replace a_ops during the inode/file is in operation? ++ * Yes, I hope so. ++ */ ++int au_dy_irefresh(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct inode *h_inode; ++ ++ err = 0; ++ if (S_ISREG(inode->i_mode)) { ++ bstart = au_ibstart(inode); ++ h_inode = au_h_iptr(inode, bstart); ++ err = au_dy_iaop(inode, bstart, h_inode); ++ } ++ return err; ++} ++ ++void au_dy_arefresh(int do_dx) ++{ ++ struct au_splhead *spl; ++ struct list_head *head; ++ struct au_dykey *key; ++ ++ spl = dynop + AuDy_AOP; ++ head = &spl->head; ++ spin_lock(&spl->spin); ++ list_for_each_entry(key, head, dk_list) ++ dy_adx((void *)key, do_dx); ++ spin_unlock(&spl->spin); ++} ++ ++const struct vm_operations_struct * ++au_dy_vmop(struct file *file, struct au_branch *br, ++ const struct vm_operations_struct *h_vmop) ++{ ++ struct au_dyvmop *dyvmop; ++ struct au_dynop op; ++ ++ op.dy_type = AuDy_VMOP; ++ op.dy_hvmop = h_vmop; ++ dyvmop = (void *)dy_get(&op, br); ++ if (IS_ERR(dyvmop)) ++ return (void *)dyvmop; ++ return &dyvmop->dv_op; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void __init au_dy_init(void) ++{ ++ int i; ++ ++ /* make sure that 'struct au_dykey *' can be any type */ ++ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key)); ++ BUILD_BUG_ON(offsetof(struct au_dyvmop, dv_key)); ++ ++ for (i = 0; i < AuDyLast; i++) ++ au_spl_init(dynop + i); ++} ++ ++void au_dy_fin(void) ++{ ++ int i; ++ ++ for (i = 0; i < AuDyLast; i++) ++ WARN_ON(!list_empty(&dynop[i].head)); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/dynop.h linux-2.6.36/fs/aufs/dynop.h +--- linux-2.6.36.orig/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/dynop.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (C) 2010-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * dynamically customizable operations (for regular files only) ++ */ ++ ++#ifndef __AUFS_DYNOP_H__ ++#define __AUFS_DYNOP_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++#include "inode.h" ++ ++enum {AuDy_AOP, AuDy_VMOP, AuDyLast}; ++ ++struct au_dynop { ++ int dy_type; ++ union { ++ const void *dy_hop; ++ const struct address_space_operations *dy_haop; ++ const struct vm_operations_struct *dy_hvmop; ++ }; ++}; ++ ++struct au_dykey { ++ union { ++ struct list_head dk_list; ++ struct rcu_head dk_rcu; ++ }; ++ struct au_dynop dk_op; ++ ++ /* ++ * during I am in the branch local array, kref is gotten. when the ++ * branch is removed, kref is put. ++ */ ++ struct kref dk_kref; ++}; ++ ++/* stop unioning since their sizes are very different from each other */ ++struct au_dyaop { ++ struct au_dykey da_key; ++ struct address_space_operations da_op; /* not const */ ++ int (*da_get_xip_mem)(struct address_space *, pgoff_t, int, ++ void **, unsigned long *); ++}; ++ ++struct au_dyvmop { ++ struct au_dykey dv_key; ++ struct vm_operations_struct dv_op; /* not const */ ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dynop.c */ ++struct au_branch; ++void au_dy_put(struct au_dykey *key); ++int au_dy_iaop(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode); ++int au_dy_irefresh(struct inode *inode); ++void au_dy_arefresh(int do_dio); ++const struct vm_operations_struct * ++au_dy_vmop(struct file *file, struct au_branch *br, ++ const struct vm_operations_struct *h_vmop); ++ ++void __init au_dy_init(void); ++void au_dy_fin(void); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_DYNOP_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/export.c linux-2.6.36/fs/aufs/export.c +--- linux-2.6.36.orig/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/export.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,798 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * export via nfs ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++union conv { ++#ifdef CONFIG_AUFS_INO_T_64 ++ __u32 a[2]; ++#else ++ __u32 a[1]; ++#endif ++ ino_t ino; ++}; ++ ++static ino_t decode_ino(__u32 *a) ++{ ++ union conv u; ++ ++ BUILD_BUG_ON(sizeof(u.ino) != sizeof(u.a)); ++ u.a[0] = a[0]; ++#ifdef CONFIG_AUFS_INO_T_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]; ++#ifdef CONFIG_AUFS_INO_T_64 ++ a[1] = u.a[1]; ++#endif ++} ++ ++/* NFS file handle */ ++enum { ++ Fh_br_id, ++ Fh_sigen, ++#ifdef CONFIG_AUFS_INO_T_64 ++ /* support 64bit inode number */ ++ Fh_ino1, ++ Fh_ino2, ++ Fh_dir_ino1, ++ Fh_dir_ino2, ++#else ++ Fh_ino1, ++ Fh_dir_ino1, ++#endif ++ Fh_igen, ++ Fh_h_type, ++ Fh_tail, ++ ++ Fh_ino = Fh_ino1, ++ Fh_dir_ino = Fh_dir_ino1 ++}; ++ ++static int au_test_anon(struct dentry *dentry) ++{ ++ return !!(dentry->d_flags & DCACHE_DISCONNECTED); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* inode generation external table */ ++ ++void au_xigen_inc(struct inode *inode) ++{ ++ loff_t pos; ++ ssize_t sz; ++ __u32 igen; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ sb = inode->i_sb; ++ AuDebugOn(!au_opt_test(au_mntflags(sb), XINO)); ++ ++ sbinfo = au_sbi(sb); ++ pos = inode->i_ino; ++ pos *= sizeof(igen); ++ igen = inode->i_generation + 1; ++ sz = xino_fwrite(sbinfo->si_xwrite, sbinfo->si_xigen, &igen, ++ sizeof(igen), &pos); ++ if (sz == sizeof(igen)) ++ return; /* success */ ++ ++ if (unlikely(sz >= 0)) ++ AuIOErr("xigen error (%zd)\n", sz); ++} ++ ++int au_xigen_new(struct inode *inode) ++{ ++ int err; ++ loff_t pos; ++ ssize_t sz; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ err = 0; ++ /* todo: dirty, at mount time */ ++ if (inode->i_ino == AUFS_ROOT_INO) ++ goto out; ++ sb = inode->i_sb; ++ SiMustAnyLock(sb); ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ goto out; ++ ++ err = -EFBIG; ++ pos = inode->i_ino; ++ if (unlikely(au_loff_max / sizeof(inode->i_generation) - 1 < pos)) { ++ AuIOErr1("too large i%lld\n", pos); ++ goto out; ++ } ++ pos *= sizeof(inode->i_generation); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ file = sbinfo->si_xigen; ++ BUG_ON(!file); ++ ++ if (i_size_read(file->f_dentry->d_inode) ++ < pos + sizeof(inode->i_generation)) { ++ inode->i_generation = atomic_inc_return(&sbinfo->si_xigen_next); ++ sz = xino_fwrite(sbinfo->si_xwrite, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ } else ++ sz = xino_fread(sbinfo->si_xread, file, &inode->i_generation, ++ sizeof(inode->i_generation), &pos); ++ if (sz == sizeof(inode->i_generation)) ++ goto out; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xigen error (%zd)\n", sz); ++ } ++ ++out: ++ return err; ++} ++ ++int au_xigen_set(struct super_block *sb, struct file *base) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xigen); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ err = 0; ++ if (sbinfo->si_xigen) ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = file; ++ ++out: ++ return err; ++} ++ ++void au_xigen_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_xigen) { ++ fput(sbinfo->si_xigen); ++ sbinfo->si_xigen = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino) ++{ ++ struct dentry *dentry, *d; ++ struct inode *inode; ++ unsigned int sigen; ++ ++ dentry = NULL; ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ dentry = ERR_PTR(-ESTALE); ++ sigen = au_sigen(sb); ++ if (unlikely(is_bad_inode(inode) ++ || IS_DEADDIR(inode) ++ || sigen != au_iigen(inode))) ++ goto out_iput; ++ ++ dentry = NULL; ++ if (!dir_ino || S_ISDIR(inode->i_mode)) ++ dentry = d_find_alias(inode); ++ else { ++ spin_lock(&dcache_lock); ++ list_for_each_entry(d, &inode->i_dentry, d_alias) ++ if (!au_test_anon(d) ++ && d->d_parent->d_inode->i_ino == dir_ino) { ++ dentry = dget_locked(d); ++ break; ++ } ++ spin_unlock(&dcache_lock); ++ } ++ if (unlikely(dentry && au_digen_test(dentry, sigen))) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ ++out_iput: ++ iput(inode); ++out: ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: dirty? */ ++/* if exportfs_decode_fh() passed vfsmount*, we could be happy */ ++ ++struct au_compare_mnt_args { ++ /* input */ ++ struct super_block *sb; ++ ++ /* output */ ++ struct vfsmount *mnt; ++}; ++ ++static int au_compare_mnt(struct vfsmount *mnt, void *arg) ++{ ++ struct au_compare_mnt_args *a = arg; ++ ++ if (mnt->mnt_sb != a->sb) ++ return 0; ++ a->mnt = mntget(mnt); ++ return 1; ++} ++ ++static struct vfsmount *au_mnt_get(struct super_block *sb) ++{ ++ int err; ++ struct au_compare_mnt_args args = { ++ .sb = sb ++ }; ++ struct mnt_namespace *ns; ++ ++ br_read_lock(vfsmount_lock); ++ /* no get/put ?? */ ++ AuDebugOn(!current->nsproxy); ++ ns = current->nsproxy->mnt_ns; ++ AuDebugOn(!ns); ++ err = iterate_mounts(au_compare_mnt, &args, ns->root); ++ br_read_unlock(vfsmount_lock); ++ AuDebugOn(!err); ++ AuDebugOn(!args.mnt); ++ return args.mnt; ++} ++ ++struct au_nfsd_si_lock { ++ unsigned int sigen; ++ aufs_bindex_t bindex, br_id; ++ unsigned char force_lock; ++}; ++ ++static int si_nfsd_read_lock(struct super_block *sb, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ ++ si_read_lock(sb, AuLock_FLUSH); ++ ++ /* branch id may be wrapped around */ ++ err = 0; ++ bindex = au_br_index(sb, nsi_lock->br_id); ++ if (bindex >= 0 && nsi_lock->sigen + AUFS_BRANCH_MAX > au_sigen(sb)) ++ goto out; /* success */ ++ ++ err = -ESTALE; ++ bindex = -1; ++ if (!nsi_lock->force_lock) ++ si_read_unlock(sb); ++ ++out: ++ nsi_lock->bindex = bindex; ++ return err; ++} ++ ++struct find_name_by_ino { ++ int called, found; ++ ino_t ino; ++ char *name; ++ int namelen; ++}; ++ ++static int ++find_name_by_ino(void *arg, const char *name, int namelen, loff_t offset, ++ u64 ino, unsigned int d_type) ++{ ++ struct find_name_by_ino *a = arg; ++ ++ a->called++; ++ if (a->ino != ino) ++ return 0; ++ ++ memcpy(a->name, name, namelen); ++ a->namelen = namelen; ++ a->found = 1; ++ return 1; ++} ++ ++static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *parent; ++ struct file *file; ++ struct inode *dir; ++ struct find_name_by_ino arg; ++ int err; ++ ++ parent = path->dentry; ++ if (nsi_lock) ++ si_read_unlock(parent->d_sb); ++ file = vfsub_dentry_open(path, au_dir_roflags); ++ dentry = (void *)file; ++ if (IS_ERR(file)) ++ goto out; ++ ++ dentry = ERR_PTR(-ENOMEM); ++ arg.name = __getname_gfp(GFP_NOFS); ++ if (unlikely(!arg.name)) ++ goto out_file; ++ arg.ino = ino; ++ arg.found = 0; ++ do { ++ arg.called = 0; ++ /* smp_mb(); */ ++ err = vfsub_readdir(file, find_name_by_ino, &arg); ++ } while (!err && !arg.found && arg.called); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_name; ++ dentry = ERR_PTR(-ENOENT); ++ if (!arg.found) ++ goto out_name; ++ ++ /* do not call au_lkup_one() */ ++ dir = parent->d_inode; ++ mutex_lock(&dir->i_mutex); ++ dentry = vfsub_lookup_one_len(arg.name, parent, arg.namelen); ++ mutex_unlock(&dir->i_mutex); ++ AuTraceErrPtr(dentry); ++ if (IS_ERR(dentry)) ++ goto out_name; ++ AuDebugOn(au_test_anon(dentry)); ++ if (unlikely(!dentry->d_inode)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ } ++ ++out_name: ++ __putname(arg.name); ++out_file: ++ fput(file); ++out: ++ if (unlikely(nsi_lock ++ && si_nfsd_read_lock(parent->d_sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++static struct dentry *decode_by_dir_ino(struct super_block *sb, ino_t ino, ++ ino_t dir_ino, ++ struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry; ++ struct path path; ++ ++ if (dir_ino != AUFS_ROOT_INO) { ++ path.dentry = decode_by_ino(sb, dir_ino, 0); ++ dentry = path.dentry; ++ if (!path.dentry || IS_ERR(path.dentry)) ++ goto out; ++ AuDebugOn(au_test_anon(path.dentry)); ++ } else ++ path.dentry = dget(sb->s_root); ++ ++ path.mnt = au_mnt_get(sb); ++ dentry = au_lkup_by_ino(&path, ino, nsi_lock); ++ path_put(&path); ++ ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_acceptable(void *expv, struct dentry *dentry) ++{ ++ return 1; ++} ++ ++static char *au_build_path(struct dentry *h_parent, struct path *h_rootpath, ++ char *buf, int len, struct super_block *sb) ++{ ++ char *p; ++ int n; ++ struct path path; ++ ++ p = d_path(h_rootpath, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ n = strlen(p); ++ ++ path.mnt = h_rootpath->mnt; ++ path.dentry = h_parent; ++ p = d_path(&path, buf, len); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p += n; ++ ++ path.mnt = au_mnt_get(sb); ++ path.dentry = sb->s_root; ++ p = d_path(&path, buf, len - strlen(p)); ++ mntput(path.mnt); ++ if (IS_ERR(p)) ++ goto out; ++ if (n != 1) ++ p[strlen(p)] = '/'; ++ ++out: ++ AuTraceErrPtr(p); ++ return p; ++} ++ ++static ++struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, ++ int fh_len, struct au_nfsd_si_lock *nsi_lock) ++{ ++ struct dentry *dentry, *h_parent, *root; ++ struct super_block *h_sb; ++ char *pathname, *p; ++ struct vfsmount *h_mnt; ++ struct au_branch *br; ++ int err; ++ struct path path; ++ ++ br = au_sbr(sb, nsi_lock->bindex); ++ h_mnt = br->br_mnt; ++ h_sb = h_mnt->mnt_sb; ++ /* todo: call lower fh_to_dentry()? fh_to_parent()? */ ++ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), ++ fh_len - Fh_tail, fh[Fh_h_type], ++ h_acceptable, /*context*/NULL); ++ dentry = h_parent; ++ if (unlikely(!h_parent || IS_ERR(h_parent))) { ++ AuWarn1("%s decode_fh failed, %ld\n", ++ au_sbtype(h_sb), PTR_ERR(h_parent)); ++ goto out; ++ } ++ dentry = NULL; ++ if (unlikely(au_test_anon(h_parent))) { ++ AuWarn1("%s decode_fh returned a disconnected dentry\n", ++ au_sbtype(h_sb)); ++ goto out_h_parent; ++ } ++ ++ dentry = ERR_PTR(-ENOMEM); ++ pathname = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!pathname)) ++ goto out_h_parent; ++ ++ root = sb->s_root; ++ path.mnt = h_mnt; ++ di_read_lock_parent(root, !AuLock_IR); ++ path.dentry = au_h_dptr(root, nsi_lock->bindex); ++ di_read_unlock(root, !AuLock_IR); ++ p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); ++ dentry = (void *)p; ++ if (IS_ERR(p)) ++ goto out_pathname; ++ ++ si_read_unlock(sb); ++ err = vfsub_kern_path(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &path); ++ dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_relock; ++ ++ dentry = ERR_PTR(-ENOENT); ++ AuDebugOn(au_test_anon(path.dentry)); ++ if (unlikely(!path.dentry->d_inode)) ++ goto out_path; ++ ++ if (ino != path.dentry->d_inode->i_ino) ++ dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); ++ else ++ dentry = dget(path.dentry); ++ ++out_path: ++ path_put(&path); ++out_relock: ++ if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) ++ if (!IS_ERR(dentry)) { ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++ } ++out_pathname: ++ free_page((unsigned long)pathname); ++out_h_parent: ++ dput(h_parent); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry * ++aufs_fh_to_dentry(struct super_block *sb, struct fid *fid, int fh_len, ++ int fh_type) ++{ ++ struct dentry *dentry; ++ __u32 *fh = fid->raw; ++ struct au_branch *br; ++ ino_t ino, dir_ino; ++ struct au_nfsd_si_lock nsi_lock = { ++ .force_lock = 0 ++ }; ++ ++ dentry = ERR_PTR(-ESTALE); ++ /* it should never happen, but the file handle is unreliable */ ++ if (unlikely(fh_len < Fh_tail)) ++ goto out; ++ nsi_lock.sigen = fh[Fh_sigen]; ++ nsi_lock.br_id = fh[Fh_br_id]; ++ ++ /* branch id may be wrapped around */ ++ br = NULL; ++ if (unlikely(si_nfsd_read_lock(sb, &nsi_lock))) ++ goto out; ++ nsi_lock.force_lock = 1; ++ ++ /* is this inode still cached? */ ++ ino = decode_ino(fh + Fh_ino); ++ /* it should never happen */ ++ if (unlikely(ino == AUFS_ROOT_INO)) ++ goto out; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ dentry = decode_by_ino(sb, ino, dir_ino); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* is the parent dir cached? */ ++ br = au_sbr(sb, nsi_lock.bindex); ++ atomic_inc(&br->br_count); ++ dentry = decode_by_dir_ino(sb, ino, dir_ino, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (dentry) ++ goto accept; ++ ++ /* lookup path */ ++ dentry = decode_by_path(sb, ino, fh, fh_len, &nsi_lock); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ if (unlikely(!dentry)) ++ /* todo?: make it ESTALE */ ++ goto out_unlock; ++ ++accept: ++ if (!au_digen_test(dentry, au_sigen(sb)) ++ && dentry->d_inode->i_generation == fh[Fh_igen]) ++ goto out_unlock; /* success */ ++ ++ dput(dentry); ++ dentry = ERR_PTR(-ESTALE); ++out_unlock: ++ if (br) ++ atomic_dec(&br->br_count); ++ si_read_unlock(sb); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++#if 0 /* reserved for future use */ ++/* support subtreecheck option */ ++static struct dentry *aufs_fh_to_parent(struct super_block *sb, struct fid *fid, ++ int fh_len, int fh_type) ++{ ++ struct dentry *parent; ++ __u32 *fh = fid->raw; ++ ino_t dir_ino; ++ ++ dir_ino = decode_ino(fh + Fh_dir_ino); ++ parent = decode_by_ino(sb, dir_ino, 0); ++ if (IS_ERR(parent)) ++ goto out; ++ if (!parent) ++ parent = decode_by_path(sb, au_br_index(sb, fh[Fh_br_id]), ++ dir_ino, fh, fh_len); ++ ++out: ++ AuTraceErrPtr(parent); ++ return parent; ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, ++ int connectable) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ AuDebugOn(au_test_anon(dentry)); ++ ++ parent = NULL; ++ err = -ENOSPC; ++ if (unlikely(*max_len <= Fh_tail)) { ++ AuWarn1("NFSv2 client (max_len %d)?\n", *max_len); ++ goto out; ++ } ++ ++ err = FILEID_ROOT; ++ if (IS_ROOT(dentry)) { ++ AuDebugOn(dentry->d_inode->i_ino != AUFS_ROOT_INO); ++ goto out; ++ } ++ ++ h_parent = NULL; ++ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ inode = dentry->d_inode; ++ AuDebugOn(!inode); ++ sb = dentry->d_sb; ++#ifdef CONFIG_AUFS_DEBUG ++ if (unlikely(!au_opt_test(au_mntflags(sb), XINO))) ++ AuWarn1("NFS-exporting requires xino\n"); ++#endif ++ err = -EIO; ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, !AuLock_IR); ++ bend = au_dbtaildir(parent); ++ for (bindex = au_dbstart(parent); bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (h_parent) { ++ dget(h_parent); ++ break; ++ } ++ } ++ if (unlikely(!h_parent)) ++ goto out_unlock; ++ ++ err = -EPERM; ++ br = au_sbr(sb, bindex); ++ h_sb = br->br_mnt->mnt_sb; ++ if (unlikely(!h_sb->s_export_op)) { ++ AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); ++ goto out_dput; ++ } ++ ++ fh[Fh_br_id] = br->br_id; ++ fh[Fh_sigen] = au_sigen(sb); ++ encode_ino(fh + Fh_ino, inode->i_ino); ++ encode_ino(fh + Fh_dir_ino, parent->d_inode->i_ino); ++ fh[Fh_igen] = inode->i_generation; ++ ++ *max_len -= Fh_tail; ++ fh[Fh_h_type] = exportfs_encode_fh(h_parent, (void *)(fh + Fh_tail), ++ max_len, ++ /*connectable or subtreecheck*/0); ++ err = fh[Fh_h_type]; ++ *max_len += Fh_tail; ++ /* todo: macros? */ ++ if (err != 255) ++ err = 99; ++ else ++ AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); ++ ++out_dput: ++ dput(h_parent); ++out_unlock: ++ di_read_unlock(parent, !AuLock_IR); ++ dput(parent); ++ aufs_read_unlock(dentry, AuLock_IR); ++out: ++ if (unlikely(err < 0)) ++ err = 255; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_commit_metadata(struct inode *inode) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct super_block *sb; ++ struct inode *h_inode; ++ int (*f)(struct inode *inode); ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ ii_write_lock_child(inode); ++ bindex = au_ibstart(inode); ++ AuDebugOn(bindex < 0); ++ h_inode = au_h_iptr(inode, bindex); ++ ++ f = h_inode->i_sb->s_export_op->commit_metadata; ++ if (f) ++ err = f(h_inode); ++ else { ++ struct writeback_control wbc = { ++ .sync_mode = WB_SYNC_ALL, ++ .nr_to_write = 0 /* metadata only */ ++ }; ++ ++ err = sync_inode(h_inode, &wbc); ++ } ++ ++ au_cpup_attr_timesizes(inode); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct export_operations aufs_export_op = { ++ .fh_to_dentry = aufs_fh_to_dentry, ++ /* .fh_to_parent = aufs_fh_to_parent, */ ++ .encode_fh = aufs_encode_fh, ++ .commit_metadata = aufs_commit_metadata ++}; ++ ++void au_export_init(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ __u32 u; ++ ++ sb->s_export_op = &aufs_export_op; ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xigen = NULL; ++ get_random_bytes(&u, sizeof(u)); ++ BUILD_BUG_ON(sizeof(u) != sizeof(int)); ++ atomic_set(&sbinfo->si_xigen_next, u); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/f_op.c linux-2.6.36/fs/aufs/f_op.c +--- linux-2.6.36.orig/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/f_op.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,906 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * file and vm operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int au_do_open_nondir(struct file *file, int flags) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ ++ FiMustWriteLock(file); ++ ++ dentry = file->f_dentry; ++ err = au_d_alive(dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ finfo = au_fi(file); ++ memset(&finfo->fi_htop, 0, sizeof(finfo->fi_htop)); ++ finfo->fi_hvmop = NULL; ++ bindex = au_dbstart(dentry); ++ h_file = au_h_open(dentry, bindex, flags, file); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ au_set_fbstart(file, bindex); ++ au_set_h_fptr(file, bindex, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ } ++ ++out: ++ return err; ++} ++ ++static int aufs_open_nondir(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ ++ AuDbg("%.*s, f_ flags 0x%x, f_mode 0x%x\n", ++ AuDLNPair(file->f_dentry), vfsub_file_flags(file), ++ file->f_mode); ++ ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_do_open(file, au_do_open_nondir, /*fidir*/NULL); ++ si_read_unlock(sb); ++ return err; ++} ++ ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file) ++{ ++ struct au_finfo *finfo; ++ aufs_bindex_t bindex; ++ ++ finfo = au_fi(file); ++ bindex = finfo->fi_btop; ++ if (bindex >= 0) { ++ /* remove me from sb->s_files */ ++ file_sb_list_del(file); ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ ++ au_finfo_fin(file); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_do_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ int err; ++ struct file *h_file; ++ ++ err = 0; ++ h_file = au_hf_top(file); ++ if (h_file) ++ err = vfsub_flush(h_file, id); ++ return err; ++} ++ ++static int aufs_flush_nondir(struct file *file, fl_owner_t id) ++{ ++ return au_do_flush(file, id, au_do_flush_nondir); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t aufs_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ struct dentry *dentry; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ++ * todo: very ugly ++ * it locks both of i_mutex and si_rwsem for read in safe. ++ * if the plink maintenance mode continues forever (that is the problem), ++ * may loop forever. ++ */ ++static void au_mtx_and_read_lock(struct inode *inode) ++{ ++ int err; ++ struct super_block *sb = inode->i_sb; ++ ++ while (1) { ++ mutex_lock(&inode->i_mutex); ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) ++ break; ++ mutex_unlock(&inode->i_mutex); ++ si_read_lock(sb, AuLock_NOPLMW); ++ si_read_unlock(sb); ++ } ++} ++ ++static ssize_t aufs_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ h_file = au_hf_top(file); ++ au_unpin(&pin); ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = h_file->f_dentry->d_inode->i_mode; ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++static ssize_t au_do_aio(struct file *h_file, int rw, struct kiocb *kio, ++ const struct iovec *iov, unsigned long nv, loff_t pos) ++{ ++ ssize_t err; ++ struct file *file; ++ ssize_t (*func)(struct kiocb *, const struct iovec *, unsigned long, ++ loff_t); ++ ++ err = security_file_permission(h_file, rw); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -ENOSYS; ++ func = NULL; ++ if (rw == MAY_READ) ++ func = h_file->f_op->aio_read; ++ else if (rw == MAY_WRITE) ++ func = h_file->f_op->aio_write; ++ if (func) { ++ file = kio->ki_filp; ++ kio->ki_filp = h_file; ++ err = func(kio, iov, nv, pos); ++ kio->ki_filp = file; ++ } else ++ /* currently there is no such fs */ ++ WARN_ON_ONCE(1); ++ ++out: ++ return err; ++} ++ ++static ssize_t aufs_aio_read(struct kiocb *kio, const struct iovec *iov, ++ unsigned long nv, loff_t pos) ++{ ++ ssize_t err; ++ struct file *file, *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ err = au_do_aio(h_file, MAY_READ, kio, iov, nv, pos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static ssize_t aufs_aio_write(struct kiocb *kio, const struct iovec *iov, ++ unsigned long nv, loff_t pos) ++{ ++ ssize_t err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ au_unpin(&pin); ++ h_file = au_hf_top(file); ++ err = au_do_aio(h_file, MAY_WRITE, kio, iov, nv, pos); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = h_file->f_dentry->d_inode->i_mode; ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++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; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ err = -EINVAL; ++ h_file = au_hf_top(file); ++ if (au_test_loopback_kthread()) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ 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 au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ h_file = au_hf_top(file); ++ au_unpin(&pin); ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = h_file->f_dentry->d_inode->i_mode; ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(inode->i_sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct file *au_safe_file(struct vm_area_struct *vma) ++{ ++ struct file *file; ++ ++ file = vma->vm_file; ++ if (au_fi(file) && au_test_aufs(file->f_dentry->d_sb)) ++ return file; ++ return NULL; ++} ++ ++static void au_reset_file(struct vm_area_struct *vma, struct file *file) ++{ ++ vma->vm_file = file; ++ /* smp_mb(); */ /* flush vm_file */ ++} ++ ++static int aufs_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ int err; ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ /* todo: non-robr mode, user vm_file as it is? */ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ /* do not revalidate, no si lock */ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ h_file = finfo->fi_htop.hf_file; ++ AuDebugOn(!h_file || !finfo->fi_hvmop); ++ ++ mutex_lock(&finfo->fi_vm_mtx); ++ vma->vm_file = h_file; ++ err = finfo->fi_hvmop->fault(vma, vmf); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ au_reset_file(vma, file); ++ mutex_unlock(&finfo->fi_vm_mtx); ++#if 0 /* def CONFIG_SMP */ ++ /* wake_up_nr(&wq, online_cpu - 1); */ ++ wake_up_all(&wq); ++#else ++ wake_up(&wq); ++#endif ++ ++ return err; ++} ++ ++static int aufs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) ++{ ++ int err; ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ h_file = finfo->fi_htop.hf_file; ++ AuDebugOn(!h_file || !finfo->fi_hvmop); ++ ++ mutex_lock(&finfo->fi_vm_mtx); ++ vma->vm_file = h_file; ++ err = finfo->fi_hvmop->page_mkwrite(vma, vmf); ++ au_reset_file(vma, file); ++ mutex_unlock(&finfo->fi_vm_mtx); ++ wake_up(&wq); ++ ++ return err; ++} ++ ++static void aufs_vm_close(struct vm_area_struct *vma) ++{ ++ static DECLARE_WAIT_QUEUE_HEAD(wq); ++ struct file *file, *h_file; ++ struct au_finfo *finfo; ++ ++ wait_event(wq, (file = au_safe_file(vma))); ++ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ h_file = finfo->fi_htop.hf_file; ++ AuDebugOn(!h_file || !finfo->fi_hvmop); ++ ++ mutex_lock(&finfo->fi_vm_mtx); ++ vma->vm_file = h_file; ++ finfo->fi_hvmop->close(vma); ++ au_reset_file(vma, file); ++ mutex_unlock(&finfo->fi_vm_mtx); ++ wake_up(&wq); ++} ++ ++const struct vm_operations_struct aufs_vm_ops = { ++ .close = aufs_vm_close, ++ .fault = aufs_fault, ++ .page_mkwrite = aufs_page_mkwrite ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cf. linux/include/linux/mman.h: calc_vm_prot_bits() */ ++#define AuConv_VM_PROT(f, b) _calc_vm_trans(f, VM_##b, PROT_##b) ++ ++static unsigned long au_arch_prot_conv(unsigned long flags) ++{ ++ /* currently ppc64 only */ ++#ifdef CONFIG_PPC64 ++ /* cf. linux/arch/powerpc/include/asm/mman.h */ ++ AuDebugOn(arch_calc_vm_prot_bits(-1) != VM_SAO); ++ return AuConv_VM_PROT(flags, SAO); ++#else ++ AuDebugOn(arch_calc_vm_prot_bits(-1)); ++ return 0; ++#endif ++} ++ ++static unsigned long au_prot_conv(unsigned long flags) ++{ ++ return AuConv_VM_PROT(flags, READ) ++ | AuConv_VM_PROT(flags, WRITE) ++ | AuConv_VM_PROT(flags, EXEC) ++ | au_arch_prot_conv(flags); ++} ++ ++/* cf. linux/include/linux/mman.h: calc_vm_flag_bits() */ ++#define AuConv_VM_MAP(f, b) _calc_vm_trans(f, VM_##b, MAP_##b) ++ ++static unsigned long au_flag_conv(unsigned long flags) ++{ ++ return AuConv_VM_MAP(flags, GROWSDOWN) ++ | AuConv_VM_MAP(flags, DENYWRITE) ++ | AuConv_VM_MAP(flags, EXECUTABLE) ++ | AuConv_VM_MAP(flags, LOCKED); ++} ++ ++static struct vm_operations_struct * ++au_hvmop(struct file *h_file, struct vm_area_struct *vma, unsigned long *flags) ++{ ++ struct vm_operations_struct *h_vmop; ++ unsigned long prot; ++ int err; ++ ++ h_vmop = ERR_PTR(-ENODEV); ++ if (!h_file->f_op || !h_file->f_op->mmap) ++ goto out; ++ ++ prot = au_prot_conv(vma->vm_flags); ++ err = security_file_mmap(h_file, /*reqprot*/prot, prot, ++ au_flag_conv(vma->vm_flags), vma->vm_start, 0); ++ h_vmop = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = h_file->f_op->mmap(h_file, vma); ++ h_vmop = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ /* oops, it became 'const' */ ++ h_vmop = (struct vm_operations_struct *)vma->vm_ops; ++ *flags = vma->vm_flags; ++ 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); ++ h_vmop = ERR_PTR(-EIO); ++ } ++ ++out: ++ return h_vmop; ++} ++ ++/* ++ * This is another ugly approach to keep the lock order, particularly ++ * mm->mmap_sem and aufs rwsem. The previous approach was reverted and you can ++ * find it in git-log, if you want. ++ * ++ * native readdir: i_mutex, copy_to_user, mmap_sem ++ * aufs readdir: i_mutex, rwsem, nested-i_mutex, copy_to_user, mmap_sem ++ * ++ * Before aufs_mmap() mmap_sem is acquired already, but aufs_mmap() has to ++ * acquire aufs rwsem. It introduces a circular locking dependency. ++ * To address this problem, aufs_mmap() delegates the part which requires aufs ++ * rwsem to its internal workqueue. ++ */ ++ ++/* very ugly approach */ ++#include "mtx.h" ++ ++struct au_mmap_pre_args { ++ /* input */ ++ struct file *file; ++ struct vm_area_struct *vma; ++ ++ /* output */ ++ int *errp; ++ struct file *h_file; ++ struct au_branch *br; ++ int mmapped; ++}; ++ ++static int au_mmap_pre(struct file *file, struct vm_area_struct *vma, ++ struct file **h_file, struct au_branch **br, ++ int *mmapped) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ const unsigned char wlock ++ = !!(file->f_mode & FMODE_WRITE) && (vma->vm_flags & VM_SHARED); ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ *mmapped = !!au_test_mmapped(file); ++ if (wlock) { ++ struct au_pin pin; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_write_unlock(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ } else ++ di_write_unlock(dentry); ++ bstart = au_fbstart(file); ++ *br = au_sbr(sb, bstart); ++ *h_file = au_hf_top(file); ++ get_file(*h_file); ++ au_fi_mmap_lock(file); ++ ++out_unlock: ++ fi_write_unlock(file); ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++static void au_call_mmap_pre(void *args) ++{ ++ struct au_mmap_pre_args *a = args; ++ *a->errp = au_mmap_pre(a->file, a->vma, &a->h_file, &a->br, ++ &a->mmapped); ++} ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ int err, wkq_err; ++ unsigned long h_vmflags; ++ struct au_finfo *finfo; ++ struct dentry *h_dentry; ++ struct vm_operations_struct *h_vmop, *vmop; ++ struct au_mmap_pre_args args = { ++ .file = file, ++ .vma = vma, ++ .errp = &err ++ }; ++ ++ wkq_err = au_wkq_wait_pre(au_call_mmap_pre, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ if (unlikely(err)) ++ goto out; ++ finfo = au_fi(file); ++ mutex_set_owner(&finfo->fi_mmap); ++ ++ h_dentry = args.h_file->f_dentry; ++ if (!args.mmapped && au_test_fs_bad_mapping(h_dentry->d_sb)) { ++ /* ++ * by this assignment, f_mapping will differs from aufs inode ++ * i_mapping. ++ * if someone else mixes the use of f_dentry->d_inode and ++ * f_mapping->host, then a problem may arise. ++ */ ++ file->f_mapping = args.h_file->f_mapping; ++ } ++ ++ /* always try this internal mmap to get vma flags */ ++ h_vmflags = 0; /* gcc warning */ ++ h_vmop = au_hvmop(args.h_file, vma, &h_vmflags); ++ err = PTR_ERR(h_vmop); ++ if (IS_ERR(h_vmop)) ++ goto out_unlock; ++ AuDebugOn(args.mmapped && h_vmop != finfo->fi_hvmop); ++ ++ vmop = (void *)au_dy_vmop(file, args.br, h_vmop); ++ err = PTR_ERR(vmop); ++ if (IS_ERR(vmop)) ++ goto out_unlock; ++ ++ /* ++ * unnecessary to handle MAP_DENYWRITE and deny_write_access()? ++ * currently MAP_DENYWRITE from userspace is ignored, but elf loader ++ * sets it. when FMODE_EXEC is set (by open_exec() or sys_uselib()), ++ * both of the aufs file and the lower file is deny_write_access()-ed. ++ * finally I hope we can skip handlling MAP_DENYWRITE here. ++ */ ++ err = generic_file_mmap(file, vma); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ vma->vm_ops = vmop; ++ vma->vm_flags = h_vmflags; ++ if (!args.mmapped) ++ finfo->fi_hvmop = h_vmop; ++ ++ vfsub_file_accessed(args.h_file); ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(file->f_dentry->d_inode, h_dentry->d_inode); ++ ++out_unlock: ++ au_fi_mmap_unlock(file); ++ fput(args.h_file); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_fsync_nondir(struct file *file, int datasync) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ IMustLock(file->f_mapping->host); ++ if (inode != file->f_mapping->host) { ++ mutex_unlock(&file->f_mapping->host->i_mutex); ++ mutex_lock(&inode->i_mutex); ++ } ++ IMustLock(inode); ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out_si; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ ++ err = -EINVAL; ++ h_file = au_hf_top(file); ++ if (h_file->f_op && h_file->f_op->fsync) { ++ struct mutex *h_mtx; ++ ++ /* ++ * no filemap_fdatawrite() since aufs file has no its own ++ * mapping, but dir. ++ */ ++ h_mtx = &h_file->f_dentry->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ err = h_file->f_op->fsync(h_file, datasync); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ au_cpup_attr_timesizes(inode); ++ mutex_unlock(h_mtx); ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out_si: ++ si_read_unlock(sb); ++out: ++ if (inode != file->f_mapping->host) { ++ mutex_unlock(&inode->i_mutex); ++ mutex_lock(&file->f_mapping->host->i_mutex); ++ } ++ return err; ++} ++ ++/* no one supports this operation, currently */ ++#if 0 ++static int aufs_aio_fsync_nondir(struct kiocb *kio, int datasync) ++{ ++ int err; ++ struct au_pin pin; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ au_mtx_and_read_lock(inode); ++ ++ err = 0; /* -EBADF; */ /* posix? */ ++ if (unlikely(!(file->f_mode & FMODE_WRITE))) ++ goto out; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_ready_to_write(file, -1, &pin); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_unpin(&pin); ++ ++ err = -ENOSYS; ++ h_file = au_hf_top(file); ++ if (h_file->f_op && h_file->f_op->aio_fsync) { ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ h_d = h_file->f_dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ if (!is_sync_kiocb(kio)) { ++ get_file(h_file); ++ fput(file); ++ } ++ kio->ki_filp = h_file; ++ err = h_file->f_op->aio_fsync(kio, datasync); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ if (!err) ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); ++ /*ignore*/ ++ au_cpup_attr_timesizes(inode); ++ mutex_unlock(h_mtx); ++ } ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out: ++ si_read_unlock(inode->sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++#endif ++ ++static int aufs_fasync(int fd, struct file *file, int flag) ++{ ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ h_file = au_hf_top(file); ++ if (h_file->f_op && h_file->f_op->fasync) ++ err = h_file->f_op->fasync(fd, h_file, flag); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* no one supports this operation, currently */ ++#if 0 ++static ssize_t aufs_sendpage(struct file *file, struct page *page, int offset, ++ size_t len, loff_t *pos , int more) ++{ ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++const struct file_operations aufs_file_fop = { ++ .owner = THIS_MODULE, ++ /* ++ * while generic_file_llseek/_unlocked() don't use BKL, ++ * don't use it since it operates file->f_mapping->host. ++ * in aufs, it may be a real file and may confuse users by UDBA. ++ */ ++ /* .llseek = generic_file_llseek, */ ++ ++ .read = aufs_read, ++ .write = aufs_write, ++ .aio_read = aufs_aio_read, ++ .aio_write = aufs_aio_write, ++#ifdef CONFIG_AUFS_POLL ++ .poll = aufs_poll, ++#endif ++ .unlocked_ioctl = aufs_ioctl_nondir, ++#ifdef CONFIG_COMPAT ++ .compat_ioctl = aufs_ioctl_nondir, /* same */ ++#endif ++ .mmap = aufs_mmap, ++ .open = aufs_open_nondir, ++ .flush = aufs_flush_nondir, ++ .release = aufs_release_nondir, ++ .fsync = aufs_fsync_nondir, ++ /* .aio_fsync = aufs_aio_fsync_nondir, */ ++ .fasync = aufs_fasync, ++ /* .sendpage = aufs_sendpage, */ ++ .splice_write = aufs_splice_write, ++ .splice_read = aufs_splice_read, ++#if 0 ++ .aio_splice_write = aufs_aio_splice_write, ++ .aio_splice_read = aufs_aio_splice_read ++#endif ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/f_op_sp.c linux-2.6.36/fs/aufs/f_op_sp.c +--- linux-2.6.36.orig/fs/aufs/f_op_sp.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/f_op_sp.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,299 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * file operations for special files. ++ * while they exist in aufs virtually, ++ * their file I/O is handled out of aufs. ++ */ ++ ++#include ++#include "aufs.h" ++ ++static ssize_t aufs_aio_read_sp(struct kiocb *kio, const struct iovec *iov, ++ unsigned long nv, loff_t pos) ++{ ++ ssize_t err; ++ aufs_bindex_t bstart; ++ unsigned char wbr; ++ struct file *file, *h_file; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ fi_read_lock(file); ++ bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ fi_read_unlock(file); ++ wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm); ++ si_read_unlock(sb); ++ ++ /* do not change the file in kio */ ++ AuDebugOn(!h_file->f_op || !h_file->f_op->aio_read); ++ err = h_file->f_op->aio_read(kio, iov, nv, pos); ++ if (err > 0 && wbr) ++ file_accessed(h_file); ++ ++ return err; ++} ++ ++static ssize_t aufs_aio_write_sp(struct kiocb *kio, const struct iovec *iov, ++ unsigned long nv, loff_t pos) ++{ ++ ssize_t err; ++ aufs_bindex_t bstart; ++ unsigned char wbr; ++ struct super_block *sb; ++ struct file *file, *h_file; ++ ++ file = kio->ki_filp; ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ fi_read_lock(file); ++ bstart = au_fbstart(file); ++ h_file = au_hf_top(file); ++ fi_read_unlock(file); ++ wbr = !!au_br_writable(au_sbr(sb, bstart)->br_perm); ++ si_read_unlock(sb); ++ ++ /* do not change the file in kio */ ++ AuDebugOn(!h_file->f_op || !h_file->f_op->aio_write); ++ err = h_file->f_op->aio_write(kio, iov, nv, pos); ++ if (err > 0 && wbr) ++ file_update_time(h_file); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_release_sp(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct file *h_file; ++ ++ fi_read_lock(file); ++ h_file = au_hf_top(file); ++ fi_read_unlock(file); ++ /* close this fifo in aufs */ ++ err = h_file->f_op->release(inode, file); /* ignore */ ++ aufs_release_nondir(inode, file); /* ignore */ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* currently, support only FIFO */ ++enum { ++ AuSp_FIFO, AuSp_FIFO_R, AuSp_FIFO_W, AuSp_FIFO_RW, ++ /* AuSp_SOCK, AuSp_CHR, AuSp_BLK, */ ++ AuSp_Last ++}; ++static int aufs_open_sp(struct inode *inode, struct file *file); ++static struct au_sp_fop { ++ int done; ++ struct file_operations fop; /* not 'const' */ ++ spinlock_t spin; ++} au_sp_fop[AuSp_Last] = { ++ [AuSp_FIFO] = { ++ .fop = { ++ .owner = THIS_MODULE, ++ .open = aufs_open_sp ++ } ++ } ++}; ++ ++static void au_init_fop_sp(struct file *file) ++{ ++ struct au_sp_fop *p; ++ int i; ++ struct file *h_file; ++ ++ p = au_sp_fop; ++ if (unlikely(!p->done)) { ++ /* initialize first time only */ ++ static DEFINE_SPINLOCK(spin); ++ ++ spin_lock(&spin); ++ if (!p->done) { ++ BUILD_BUG_ON(sizeof(au_sp_fop)/sizeof(*au_sp_fop) ++ != AuSp_Last); ++ for (i = 0; i < AuSp_Last; i++) ++ spin_lock_init(&p[i].spin); ++ p->done = 1; ++ } ++ spin_unlock(&spin); ++ } ++ ++ switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) { ++ case FMODE_READ: ++ i = AuSp_FIFO_R; ++ break; ++ case FMODE_WRITE: ++ i = AuSp_FIFO_W; ++ break; ++ case FMODE_READ | FMODE_WRITE: ++ i = AuSp_FIFO_RW; ++ break; ++ default: ++ BUG(); ++ } ++ ++ p += i; ++ if (unlikely(!p->done)) { ++ /* initialize first time only */ ++ h_file = au_hf_top(file); ++ spin_lock(&p->spin); ++ if (!p->done) { ++ p->fop = *h_file->f_op; ++ p->fop.owner = THIS_MODULE; ++ if (p->fop.aio_read) ++ p->fop.aio_read = aufs_aio_read_sp; ++ if (p->fop.aio_write) ++ p->fop.aio_write = aufs_aio_write_sp; ++ p->fop.release = aufs_release_sp; ++ p->done = 1; ++ } ++ spin_unlock(&p->spin); ++ } ++ file->f_op = &p->fop; ++} ++ ++static int au_cpup_sp(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bcpup; ++ struct au_pin pin; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ AuDbg("%.*s\n", AuDLNPair(dentry)); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ di_write_lock_child(dentry); ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out; ++ bcpup = err; ++ err = 0; ++ if (bcpup == au_dbstart(dentry)) ++ goto out; /* success */ ++ ++ err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb), ++ AuPin_MNT_WRITE); ++ if (!err) { ++ err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME); ++ au_unpin(&pin); ++ } ++ ++out: ++ di_downgrade_lock(dentry, AuLock_IR); ++ return err; ++} ++ ++static int au_do_open_sp(struct file *file, int flags) ++{ ++ int err; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct file *h_file; ++ struct inode *h_inode; ++ ++ dentry = file->f_dentry; ++ AuDbg("%.*s\n", AuDLNPair(dentry)); ++ ++ /* ++ * try copying-up. ++ * operate on the ro branch is not an error. ++ */ ++ au_cpup_sp(dentry); /* ignore */ ++ ++ /* prepare h_file */ ++ err = au_do_open_nondir(file, vfsub_file_flags(file)); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ h_file = au_hf_top(file); ++ h_inode = h_file->f_dentry->d_inode; ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ si_read_unlock(sb); ++ /* open this fifo in aufs */ ++ err = h_inode->i_fop->open(file->f_dentry->d_inode, file); ++ si_noflush_read_lock(sb); ++ fi_write_lock(file); ++ di_read_lock_child(dentry, AuLock_IR); ++ if (!err) ++ au_init_fop_sp(file); ++ ++out: ++ return err; ++} ++ ++static int aufs_open_sp(struct inode *inode, struct file *file) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ err = au_do_open(file, au_do_open_sp, /*fidir*/NULL); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_init_special_fop(struct inode *inode, umode_t mode, dev_t rdev) ++{ ++ init_special_inode(inode, mode, rdev); ++ ++ switch (mode & S_IFMT) { ++ case S_IFIFO: ++ inode->i_fop = &au_sp_fop[AuSp_FIFO].fop; ++ /*FALLTHROUGH*/ ++ case S_IFCHR: ++ case S_IFBLK: ++ case S_IFSOCK: ++ break; ++ default: ++ AuDebugOn(1); ++ } ++} ++ ++int au_special_file(umode_t mode) ++{ ++ int ret; ++ ++ ret = 0; ++ switch (mode & S_IFMT) { ++ case S_IFIFO: ++#if 0 ++ case S_IFCHR: ++ case S_IFBLK: ++ case S_IFSOCK: ++#endif ++ ret = 1; ++ } ++ ++ return ret; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/file.c linux-2.6.36/fs/aufs/file.c +--- linux-2.6.36.orig/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/file.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,676 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * handling file/dir, and address_space operation ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* drop flags for writing */ ++unsigned int au_file_roflags(unsigned int flags) ++{ ++ flags &= ~(O_WRONLY | O_RDWR | O_APPEND | O_CREAT | O_TRUNC); ++ flags |= O_RDONLY | O_NOATIME; ++ return flags; ++} ++ ++/* common functions to regular file and dir */ ++struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, ++ struct file *file) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct path h_path; ++ int err, exec_flag; ++ ++ /* a race condition can happen between open and unlink/rmdir */ ++ h_file = ERR_PTR(-ENOENT); ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (au_test_nfsd() && !h_dentry) ++ goto out; ++ h_inode = h_dentry->d_inode; ++ if (au_test_nfsd() && !h_inode) ++ goto out; ++ if (unlikely((!d_unhashed(dentry) && au_d_removed(h_dentry)) ++ || !h_inode ++ /* || !dentry->d_inode->i_nlink */ ++ )) ++ goto out; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_file = ERR_PTR(-EACCES); ++ exec_flag = flags & vfsub_fmode_to_uint(FMODE_EXEC); ++ if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) ++ goto out; ++ ++ /* drop flags for writing */ ++ if (au_test_ro(sb, bindex, dentry->d_inode)) ++ flags = au_file_roflags(flags); ++ flags &= ~O_CREAT; ++ atomic_inc(&br->br_count); ++ h_path.dentry = h_dentry; ++ h_path.mnt = br->br_mnt; ++ if (!au_special_file(h_inode->i_mode)) ++ h_file = vfsub_dentry_open(&h_path, flags); ++ else { ++ /* this block depends upon the configuration */ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ si_read_unlock(sb); ++ h_file = vfsub_dentry_open(&h_path, flags); ++ si_noflush_read_lock(sb); ++ fi_write_lock(file); ++ di_read_lock_child(dentry, AuLock_IR); ++ } ++ if (IS_ERR(h_file)) ++ goto out_br; ++ ++ if (exec_flag) { ++ err = deny_write_access(h_file); ++ if (unlikely(err)) { ++ fput(h_file); ++ h_file = ERR_PTR(err); ++ goto out_br; ++ } ++ } ++ fsnotify_open(h_file); ++ goto out; /* success */ ++ ++out_br: ++ atomic_dec(&br->br_count); ++out: ++ return h_file; ++} ++ ++int au_do_open(struct file *file, int (*open)(struct file *file, int flags), ++ struct au_fidir *fidir) ++{ ++ int err; ++ struct dentry *dentry; ++ ++ err = au_finfo_init(file, fidir); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_dentry; ++ di_read_lock_child(dentry, AuLock_IR); ++ err = open(file, vfsub_file_flags(file)); ++ di_read_unlock(dentry, AuLock_IR); ++ ++ fi_write_unlock(file); ++ if (unlikely(err)) { ++ au_fi(file)->fi_hdir = NULL; ++ au_finfo_fin(file); ++ } ++ ++out: ++ return err; ++} ++ ++int au_reopen_nondir(struct file *file) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct dentry *dentry; ++ struct file *h_file, *h_file_tmp; ++ ++ dentry = file->f_dentry; ++ AuDebugOn(au_special_file(dentry->d_inode->i_mode)); ++ bstart = au_dbstart(dentry); ++ h_file_tmp = NULL; ++ if (au_fbstart(file) == bstart) { ++ h_file = au_hf_top(file); ++ if (file->f_mode == h_file->f_mode) ++ return 0; /* success */ ++ h_file_tmp = h_file; ++ get_file(h_file_tmp); ++ au_set_h_fptr(file, bstart, NULL); ++ } ++ AuDebugOn(au_fi(file)->fi_hdir); ++ AuDebugOn(au_fbstart(file) < bstart); ++ ++ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, ++ file); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out; /* todo: close all? */ ++ ++ err = 0; ++ au_set_fbstart(file, bstart); ++ au_set_h_fptr(file, bstart, h_file); ++ au_update_figen(file); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ ++out: ++ if (h_file_tmp) ++ fput(h_file_tmp); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, ++ struct dentry *hi_wh) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_dinfo *dinfo; ++ struct dentry *h_dentry; ++ struct au_hdentry *hdp; ++ ++ dinfo = au_di(file->f_dentry); ++ AuRwMustWriteLock(&dinfo->di_rwsem); ++ ++ bstart = dinfo->di_bstart; ++ dinfo->di_bstart = btgt; ++ hdp = dinfo->di_hdentry; ++ h_dentry = hdp[0 + btgt].hd_dentry; ++ hdp[0 + btgt].hd_dentry = hi_wh; ++ err = au_reopen_nondir(file); ++ hdp[0 + btgt].hd_dentry = h_dentry; ++ dinfo->di_bstart = bstart; ++ ++ return err; ++} ++ ++static int au_ready_to_write_wh(struct file *file, loff_t len, ++ aufs_bindex_t bcpup) ++{ ++ int err; ++ struct inode *inode, *h_inode; ++ struct dentry *dentry, *h_dentry, *hi_wh; ++ ++ dentry = file->f_dentry; ++ au_update_dbstart(dentry); ++ inode = dentry->d_inode; ++ h_inode = NULL; ++ if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) { ++ h_dentry = au_h_dptr(dentry, bcpup); ++ if (h_dentry) ++ h_inode = h_dentry->d_inode; ++ } ++ hi_wh = au_hi_wh(inode, bcpup); ++ if (!hi_wh && !h_inode) ++ err = au_sio_cpup_wh(dentry, bcpup, len, file); ++ else ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bcpup, hi_wh); ++ ++ if (!err ++ && inode->i_nlink > 1 ++ && au_opt_test(au_mntflags(dentry->d_sb), PLINK)) ++ au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup)); ++ ++ return err; ++} ++ ++/* ++ * prepare the @file for writing. ++ */ ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) ++{ ++ int err; ++ aufs_bindex_t bstart, bcpup, dbstart; ++ struct dentry *dentry, *parent, *h_dentry; ++ struct inode *h_inode, *inode; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ AuDebugOn(au_special_file(inode->i_mode)); ++ bstart = au_fbstart(file); ++ err = au_test_ro(sb, bstart, inode); ++ if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { ++ err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); ++ goto out; ++ } ++ ++ /* need to cpup or reopen */ ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out_dgrade; ++ err = 0; ++ ++ if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) { ++ err = au_cpup_dirs(dentry, bcpup); ++ if (unlikely(err)) ++ goto out_dgrade; ++ } ++ ++ err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_dgrade; ++ ++ h_dentry = au_hf_top(file)->f_dentry; ++ h_inode = h_dentry->d_inode; ++ dbstart = au_dbstart(dentry); ++ if (dbstart <= bcpup) { ++ h_dentry = au_h_dptr(dentry, bcpup); ++ AuDebugOn(!h_dentry); ++ h_inode = h_dentry->d_inode; ++ AuDebugOn(!h_inode); ++ bstart = bcpup; ++ } ++ ++ if (dbstart <= bcpup /* just reopen */ ++ || !d_unhashed(dentry) /* copyup and reopen */ ++ ) { ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ h_file = au_h_open_pre(dentry, bstart); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else { ++ di_downgrade_lock(parent, AuLock_IR); ++ if (dbstart > bcpup) ++ err = au_sio_cpup_simple(dentry, bcpup, len, ++ AuCpup_DTIME); ++ if (!err) ++ err = au_reopen_nondir(file); ++ } ++ mutex_unlock(&h_inode->i_mutex); ++ au_h_open_post(dentry, bstart, h_file); ++ } else { /* copyup as wh and reopen */ ++ /* ++ * since writable hfsplus branch is not supported, ++ * h_open_pre/post() are unnecessary. ++ */ ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ err = au_ready_to_write_wh(file, len, bcpup); ++ di_downgrade_lock(parent, AuLock_IR); ++ mutex_unlock(&h_inode->i_mutex); ++ } ++ ++ if (!err) { ++ au_pin_set_parent_lflag(pin, /*lflag*/0); ++ goto out_dput; /* success */ ++ } ++ au_unpin(pin); ++ goto out_unlock; ++ ++out_dgrade: ++ di_downgrade_lock(parent, AuLock_IR); ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_dput: ++ dput(parent); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)) ++{ ++ int err; ++ struct dentry *dentry; ++ struct super_block *sb; ++ struct inode *inode; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ si_noflush_read_lock(sb); ++ fi_read_lock(file); ++ ii_read_lock_child(inode); ++ ++ err = flush(file, id); ++ au_cpup_attr_timesizes(inode); ++ ++ ii_read_unlock(inode); ++ fi_read_unlock(file); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_file_refresh_by_inode(struct file *file, int *need_reopen) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ struct au_pin pin; ++ struct au_finfo *finfo; ++ struct dentry *dentry, *parent, *hi_wh; ++ struct inode *inode; ++ struct super_block *sb; ++ ++ FiMustWriteLock(file); ++ ++ err = 0; ++ finfo = au_fi(file); ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ bstart = au_ibstart(inode); ++ if (bstart == finfo->fi_btop || IS_ROOT(dentry)) ++ goto out; ++ ++ parent = dget_parent(dentry); ++ if (au_test_ro(sb, bstart, inode)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ bstart = err; ++ di_read_unlock(parent, !AuLock_IR); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ err = 0; ++ } ++ ++ di_read_lock_parent(parent, AuLock_IR); ++ hi_wh = au_hi_wh(inode, bstart); ++ if (!S_ISDIR(inode->i_mode) ++ && au_opt_test(au_mntflags(sb), PLINK) ++ && au_plink_test(inode) ++ && !d_unhashed(dentry)) { ++ err = au_test_and_cpup_dirs(dentry, bstart); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ /* always superio. */ ++ err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (!err) ++ err = au_sio_cpup_simple(dentry, bstart, -1, ++ AuCpup_DTIME); ++ au_unpin(&pin); ++ } else if (hi_wh) { ++ /* already copied-up after unlink */ ++ err = au_reopen_wh(file, bstart, hi_wh); ++ *need_reopen = 0; ++ } ++ ++out_unlock: ++ di_read_unlock(parent, AuLock_IR); ++out_parent: ++ dput(parent); ++out: ++ return err; ++} ++ ++static void au_do_refresh_dir(struct file *file) ++{ ++ aufs_bindex_t bindex, bend, new_bindex, brid; ++ struct au_hfile *p, tmp, *q; ++ struct au_finfo *finfo; ++ struct super_block *sb; ++ struct au_fidir *fidir; ++ ++ FiMustWriteLock(file); ++ ++ sb = file->f_dentry->d_sb; ++ finfo = au_fi(file); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ p = fidir->fd_hfile + finfo->fi_btop; ++ brid = p->hf_br->br_id; ++ bend = fidir->fd_bbot; ++ for (bindex = finfo->fi_btop; bindex <= bend; bindex++, p++) { ++ if (!p->hf_file) ++ continue; ++ ++ new_bindex = au_br_index(sb, p->hf_br->br_id); ++ if (new_bindex == bindex) ++ continue; ++ if (new_bindex < 0) { ++ au_set_h_fptr(file, bindex, NULL); ++ continue; ++ } ++ ++ /* swap two lower inode, and loop again */ ++ q = fidir->fd_hfile + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hf_file) { ++ bindex--; ++ p--; ++ } ++ } ++ ++ p = fidir->fd_hfile; ++ if (!au_test_mmapped(file) && !au_d_removed(file->f_dentry)) { ++ bend = au_sbend(sb); ++ for (finfo->fi_btop = 0; finfo->fi_btop <= bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) { ++ if (p->hf_file->f_dentry ++ && p->hf_file->f_dentry->d_inode) ++ break; ++ else ++ au_hfput(p, file); ++ } ++ } else { ++ bend = au_br_index(sb, brid); ++ for (finfo->fi_btop = 0; finfo->fi_btop < bend; ++ finfo->fi_btop++, p++) ++ if (p->hf_file) ++ au_hfput(p, file); ++ bend = au_sbend(sb); ++ } ++ ++ p = fidir->fd_hfile + bend; ++ for (fidir->fd_bbot = bend; fidir->fd_bbot >= finfo->fi_btop; ++ fidir->fd_bbot--, p--) ++ if (p->hf_file) { ++ if (p->hf_file->f_dentry ++ && p->hf_file->f_dentry->d_inode) ++ break; ++ else ++ au_hfput(p, file); ++ } ++ AuDebugOn(fidir->fd_bbot < finfo->fi_btop); ++} ++ ++/* ++ * after branch manipulating, refresh the file. ++ */ ++static int refresh_file(struct file *file, int (*reopen)(struct file *file)) ++{ ++ int err, need_reopen; ++ aufs_bindex_t bend, bindex; ++ struct dentry *dentry; ++ struct au_finfo *finfo; ++ struct au_hfile *hfile; ++ ++ dentry = file->f_dentry; ++ finfo = au_fi(file); ++ if (!finfo->fi_hdir) { ++ hfile = &finfo->fi_htop; ++ AuDebugOn(!hfile->hf_file); ++ bindex = au_br_index(dentry->d_sb, hfile->hf_br->br_id); ++ AuDebugOn(bindex < 0); ++ if (bindex != finfo->fi_btop) ++ au_set_fbstart(file, bindex); ++ } else { ++ err = au_fidir_realloc(finfo, au_sbend(dentry->d_sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ au_do_refresh_dir(file); ++ } ++ ++ err = 0; ++ need_reopen = 1; ++ if (!au_test_mmapped(file)) ++ err = au_file_refresh_by_inode(file, &need_reopen); ++ if (!err && need_reopen && !au_d_removed(dentry)) ++ err = reopen(file); ++ if (!err) { ++ au_update_figen(file); ++ goto out; /* success */ ++ } ++ ++ /* error, close all lower files */ ++ if (finfo->fi_hdir) { ++ bend = au_fbend_dir(file); ++ for (bindex = au_fbstart(file); bindex <= bend; bindex++) ++ au_set_h_fptr(file, bindex, NULL); ++ } ++ ++out: ++ return err; ++} ++ ++/* common function to regular file and dir */ ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock) ++{ ++ int err; ++ unsigned int sigen, figen; ++ aufs_bindex_t bstart; ++ unsigned char pseudo_link; ++ struct dentry *dentry; ++ struct inode *inode; ++ ++ err = 0; ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++ AuDebugOn(au_special_file(inode->i_mode)); ++ sigen = au_sigen(dentry->d_sb); ++ fi_write_lock(file); ++ figen = au_figen(file); ++ di_write_lock_child(dentry); ++ bstart = au_dbstart(dentry); ++ pseudo_link = (bstart != au_ibstart(inode)); ++ if (sigen == figen && !pseudo_link && au_fbstart(file) == bstart) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ goto out; /* success */ ++ } ++ ++ AuDbg("sigen %d, figen %d\n", sigen, figen); ++ if (au_digen_test(dentry, sigen)) { ++ err = au_reval_dpath(dentry, sigen); ++ AuDebugOn(!err && au_digen_test(dentry, sigen)); ++ } ++ ++ if (!err) ++ err = refresh_file(file, reopen); ++ if (!err) { ++ if (!wlock) { ++ di_downgrade_lock(dentry, AuLock_IR); ++ fi_downgrade_lock(file); ++ } ++ } else { ++ di_write_unlock(dentry); ++ fi_write_unlock(file); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* cf. aufs_nopage() */ ++/* for madvise(2) */ ++static int aufs_readpage(struct file *file __maybe_unused, struct page *page) ++{ ++ unlock_page(page); ++ return 0; ++} ++ ++/* it will never be called, but necessary to support O_DIRECT */ ++static ssize_t aufs_direct_IO(int rw, struct kiocb *iocb, ++ const struct iovec *iov, loff_t offset, ++ unsigned long nr_segs) ++{ BUG(); return 0; } ++ ++/* ++ * it will never be called, but madvise and fadvise behaves differently ++ * when get_xip_mem is defined ++ */ ++static int aufs_get_xip_mem(struct address_space *mapping, pgoff_t pgoff, ++ int create, void **kmem, unsigned long *pfn) ++{ BUG(); return 0; } ++ ++/* they will never be called. */ ++#ifdef CONFIG_AUFS_DEBUG ++static int aufs_write_begin(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned flags, ++ struct page **pagep, void **fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_write_end(struct file *file, struct address_space *mapping, ++ loff_t pos, unsigned len, unsigned copied, ++ struct page *page, void *fsdata) ++{ AuUnsupport(); return 0; } ++static int aufs_writepage(struct page *page, struct writeback_control *wbc) ++{ AuUnsupport(); return 0; } ++static void aufs_sync_page(struct page *page) ++{ AuUnsupport(); } ++ ++static int aufs_set_page_dirty(struct page *page) ++{ AuUnsupport(); return 0; } ++static void aufs_invalidatepage(struct page *page, unsigned long offset) ++{ AuUnsupport(); } ++static int aufs_releasepage(struct page *page, gfp_t gfp) ++{ AuUnsupport(); return 0; } ++static int aufs_migratepage(struct address_space *mapping, struct page *newpage, ++ struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_launder_page(struct page *page) ++{ AuUnsupport(); return 0; } ++static int aufs_is_partially_uptodate(struct page *page, ++ read_descriptor_t *desc, ++ unsigned long from) ++{ AuUnsupport(); return 0; } ++static int aufs_error_remove_page(struct address_space *mapping, ++ struct page *page) ++{ AuUnsupport(); return 0; } ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++const struct address_space_operations aufs_aop = { ++ .readpage = aufs_readpage, ++ .direct_IO = aufs_direct_IO, ++ .get_xip_mem = aufs_get_xip_mem, ++#ifdef CONFIG_AUFS_DEBUG ++ .writepage = aufs_writepage, ++ .sync_page = aufs_sync_page, ++ /* no writepages, because of writepage */ ++ .set_page_dirty = aufs_set_page_dirty, ++ /* no readpages, because of readpage */ ++ .write_begin = aufs_write_begin, ++ .write_end = aufs_write_end, ++ /* no bmap, no block device */ ++ .invalidatepage = aufs_invalidatepage, ++ .releasepage = aufs_releasepage, ++ .migratepage = aufs_migratepage, ++ .launder_page = aufs_launder_page, ++ .is_partially_uptodate = aufs_is_partially_uptodate, ++ .error_remove_page = aufs_error_remove_page ++#endif /* CONFIG_AUFS_DEBUG */ ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/file.h linux-2.6.36/fs/aufs/file.h +--- linux-2.6.36.orig/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/file.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,238 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * file operations ++ */ ++ ++#ifndef __AUFS_FILE_H__ ++#define __AUFS_FILE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++ ++struct au_branch; ++struct au_hfile { ++ struct file *hf_file; ++ struct au_branch *hf_br; ++}; ++ ++struct au_vdir; ++struct au_fidir { ++ aufs_bindex_t fd_bbot; ++ aufs_bindex_t fd_nent; ++ struct au_vdir *fd_vdir_cache; ++ struct au_hfile fd_hfile[]; ++}; ++ ++static inline int au_fidir_sz(int nent) ++{ ++ AuDebugOn(nent < 0); ++ return sizeof(struct au_fidir) + sizeof(struct au_hfile) * nent; ++} ++ ++struct au_finfo { ++ atomic_t fi_generation; ++ ++ struct au_rwsem fi_rwsem; ++ aufs_bindex_t fi_btop; ++ ++ /* do not union them */ ++ struct { /* for non-dir */ ++ struct au_hfile fi_htop; ++ struct vm_operations_struct *fi_hvmop; ++ struct mutex fi_vm_mtx; ++ struct mutex fi_mmap; ++ }; ++ struct au_fidir *fi_hdir; /* for dir only */ ++} ____cacheline_aligned_in_smp; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* file.c */ ++extern const 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 file *file, int (*open)(struct file *file, int flags), ++ struct au_fidir *fidir); ++int au_reopen_nondir(struct file *file); ++struct au_pin; ++int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin); ++int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file), ++ int wlock); ++int au_do_flush(struct file *file, fl_owner_t id, ++ int (*flush)(struct file *file, fl_owner_t id)); ++ ++/* poll.c */ ++#ifdef CONFIG_AUFS_POLL ++unsigned int aufs_poll(struct file *file, poll_table *wait); ++#endif ++ ++#ifdef CONFIG_AUFS_BR_HFSPLUS ++/* hfsplus.c */ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex); ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#else ++static inline ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ return NULL; ++} ++ ++AuStubVoid(au_h_open_post, struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file); ++#endif ++ ++/* f_op.c */ ++extern const struct file_operations aufs_file_fop; ++extern const struct vm_operations_struct aufs_vm_ops; ++int au_do_open_nondir(struct file *file, int flags); ++int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file); ++ ++#ifdef CONFIG_AUFS_SP_IATTR ++/* f_op_sp.c */ ++int au_special_file(umode_t mode); ++void au_init_special_fop(struct inode *inode, umode_t mode, dev_t rdev); ++#else ++AuStubInt0(au_special_file, umode_t mode) ++static inline void au_init_special_fop(struct inode *inode, umode_t mode, ++ dev_t rdev) ++{ ++ init_special_inode(inode, mode, rdev); ++} ++#endif ++ ++/* finfo.c */ ++void au_hfput(struct au_hfile *hf, struct file *file); ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, ++ struct file *h_file); ++ ++void au_update_figen(struct file *file); ++void au_fi_mmap_lock(struct file *file); ++void au_fi_mmap_unlock(struct file *file); ++struct au_fidir *au_fidir_alloc(struct super_block *sb); ++int au_fidir_realloc(struct au_finfo *finfo, int nbr); ++ ++void au_fi_init_once(void *_fi); ++void au_finfo_fin(struct file *file); ++int au_finfo_init(struct file *file, struct au_fidir *fidir); ++ ++/* ioctl.c */ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg); ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_finfo *au_fi(struct file *file) ++{ ++ return file->private_data; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * fi_read_lock, fi_write_lock, ++ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem); ++ ++#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem) ++#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem) ++#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* todo: hard/soft set? */ ++static inline aufs_bindex_t au_fbstart(struct file *file) ++{ ++ FiMustAnyLock(file); ++ return au_fi(file)->fi_btop; ++} ++ ++static inline aufs_bindex_t au_fbend_dir(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_bbot; ++} ++ ++static inline struct au_vdir *au_fvdir_cache(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_vdir_cache; ++} ++ ++static inline void au_set_fbstart(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ au_fi(file)->fi_btop = bindex; ++} ++ ++static inline void au_set_fbend_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_bbot = bindex; ++} ++ ++static inline void au_set_fvdir_cache(struct file *file, ++ struct au_vdir *vdir_cache) ++{ ++ FiMustWriteLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ au_fi(file)->fi_hdir->fd_vdir_cache = vdir_cache; ++} ++ ++static inline struct file *au_hf_top(struct file *file) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_htop.hf_file; ++} ++ ++static inline struct file *au_hf_dir(struct file *file, aufs_bindex_t bindex) ++{ ++ FiMustAnyLock(file); ++ AuDebugOn(!au_fi(file)->fi_hdir); ++ return au_fi(file)->fi_hdir->fd_hfile[0 + bindex].hf_file; ++} ++ ++/* todo: memory barrier? */ ++static inline unsigned int au_figen(struct file *f) ++{ ++ return atomic_read(&au_fi(f)->fi_generation); ++} ++ ++static inline int au_test_mmapped(struct file *f) ++{ ++ FiMustAnyLock(f); ++ return !!(au_fi(f)->fi_hvmop); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FILE_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/finfo.c linux-2.6.36/fs/aufs/finfo.c +--- linux-2.6.36.orig/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/finfo.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,174 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * file private data ++ */ ++ ++#include ++#include "aufs.h" ++ ++void au_hfput(struct au_hfile *hf, struct file *file) ++{ ++ /* todo: direct access f_flags */ ++ if (vfsub_file_flags(file) & vfsub_fmode_to_uint(FMODE_EXEC)) ++ allow_write_access(hf->hf_file); ++ fput(hf->hf_file); ++ hf->hf_file = NULL; ++ atomic_dec(&hf->hf_br->br_count); ++ hf->hf_br = NULL; ++} ++ ++void au_set_h_fptr(struct file *file, aufs_bindex_t bindex, struct file *val) ++{ ++ struct au_finfo *finfo = au_fi(file); ++ struct au_hfile *hf; ++ struct au_fidir *fidir; ++ ++ fidir = finfo->fi_hdir; ++ if (!fidir) { ++ AuDebugOn(finfo->fi_btop != bindex); ++ hf = &finfo->fi_htop; ++ } else ++ hf = fidir->fd_hfile + bindex; ++ ++ if (hf && hf->hf_file) ++ au_hfput(hf, file); ++ if (val) { ++ FiMustWriteLock(file); ++ hf->hf_file = val; ++ hf->hf_br = au_sbr(file->f_dentry->d_sb, bindex); ++ } ++} ++ ++void au_update_figen(struct file *file) ++{ ++ atomic_set(&au_fi(file)->fi_generation, au_digen(file->f_dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_fi_mmap_lock(struct file *file) ++{ ++ FiMustWriteLock(file); ++ lockdep_off(); ++ mutex_lock(&au_fi(file)->fi_mmap); ++ lockdep_on(); ++} ++ ++void au_fi_mmap_unlock(struct file *file) ++{ ++ lockdep_off(); ++ mutex_unlock(&au_fi(file)->fi_mmap); ++ lockdep_on(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_fidir *au_fidir_alloc(struct super_block *sb) ++{ ++ struct au_fidir *fidir; ++ int nbr; ++ ++ nbr = au_sbend(sb) + 1; ++ if (nbr < 2) ++ nbr = 2; /* initial allocate for 2 branches */ ++ fidir = kzalloc(au_fidir_sz(nbr), GFP_NOFS); ++ if (fidir) { ++ fidir->fd_bbot = -1; ++ fidir->fd_nent = nbr; ++ fidir->fd_vdir_cache = NULL; ++ } ++ ++ return fidir; ++} ++ ++int au_fidir_realloc(struct au_finfo *finfo, int nbr) ++{ ++ int err; ++ struct au_fidir *fidir, *p; ++ ++ AuRwMustWriteLock(&finfo->fi_rwsem); ++ fidir = finfo->fi_hdir; ++ AuDebugOn(!fidir); ++ ++ err = -ENOMEM; ++ p = au_kzrealloc(fidir, au_fidir_sz(fidir->fd_nent), au_fidir_sz(nbr), ++ GFP_NOFS); ++ if (p) { ++ p->fd_nent = nbr; ++ finfo->fi_hdir = p; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_finfo_fin(struct file *file) ++{ ++ struct au_finfo *finfo; ++ ++ au_nfiles_dec(file->f_dentry->d_sb); ++ ++ finfo = au_fi(file); ++ AuDebugOn(finfo->fi_hdir); ++ AuRwDestroy(&finfo->fi_rwsem); ++ au_cache_free_finfo(finfo); ++} ++ ++void au_fi_init_once(void *_finfo) ++{ ++ struct au_finfo *finfo = _finfo; ++ static struct lock_class_key aufs_fi, aufs_fi_vm, aufs_fi_mmap; ++ ++ au_rw_init(&finfo->fi_rwsem); ++ au_rw_class(&finfo->fi_rwsem, &aufs_fi); ++ mutex_init(&finfo->fi_vm_mtx); ++ lockdep_set_class(&finfo->fi_vm_mtx, &aufs_fi_vm); ++ mutex_init(&finfo->fi_mmap); ++ lockdep_set_class(&finfo->fi_mmap, &aufs_fi_mmap); ++} ++ ++int au_finfo_init(struct file *file, struct au_fidir *fidir) ++{ ++ int err; ++ struct au_finfo *finfo; ++ struct dentry *dentry; ++ ++ err = -ENOMEM; ++ dentry = file->f_dentry; ++ finfo = au_cache_alloc_finfo(); ++ if (unlikely(!finfo)) ++ goto out; ++ ++ err = 0; ++ au_nfiles_inc(dentry->d_sb); ++ au_rw_write_lock(&finfo->fi_rwsem); ++ finfo->fi_btop = -1; ++ finfo->fi_hdir = fidir; ++ atomic_set(&finfo->fi_generation, au_digen(dentry)); ++ /* smp_mb(); */ /* atomic_set */ ++ ++ file->private_data = finfo; ++ ++out: ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/fstype.h linux-2.6.36/fs/aufs/fstype.h +--- linux-2.6.36.orig/fs/aufs/fstype.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/fstype.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,497 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * judging filesystem type ++ */ ++ ++#ifndef __AUFS_FSTYPE_H__ ++#define __AUFS_FSTYPE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include ++ ++static inline int au_test_aufs(struct super_block *sb) ++{ ++ return sb->s_magic == AUFS_SUPER_MAGIC; ++} ++ ++static inline const char *au_sbtype(struct super_block *sb) ++{ ++ return sb->s_type->name; ++} ++ ++static inline int au_test_iso9660(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ROMFS_FS) || defined(CONFIG_ROMFS_FS_MODULE) ++ return sb->s_magic == ROMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_romfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ISO9660_FS) || defined(CONFIG_ISO9660_FS_MODULE) ++ return sb->s_magic == ISOFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cramfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CRAMFS) || defined(CONFIG_CRAMFS_MODULE) ++ return sb->s_magic == CRAMFS_MAGIC; ++#endif ++ return 0; ++} ++ ++static inline int au_test_nfs(struct super_block *sb __maybe_unused) ++{ ++#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_fuse(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FUSE_FS) || defined(CONFIG_FUSE_FS_MODULE) ++ return sb->s_magic == FUSE_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XFS_FS) || defined(CONFIG_XFS_FS_MODULE) ++ return sb->s_magic == XFS_SB_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_tmpfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_TMPFS ++ return sb->s_magic == TMPFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_ECRYPT_FS) || defined(CONFIG_ECRYPT_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "ecryptfs"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_smbfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE) ++ return sb->s_magic == SMB_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ocfs2(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE) ++ return sb->s_magic == OCFS2_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ocfs2_dlmfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_OCFS2_FS_O2CB) || defined(CONFIG_OCFS2_FS_O2CB_MODULE) ++ return sb->s_magic == DLMFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_coda(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CODA_FS) || defined(CONFIG_CODA_FS_MODULE) ++ return sb->s_magic == CODA_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_v9fs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_9P_FS) || defined(CONFIG_9P_FS_MODULE) ++ return sb->s_magic == V9FS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ext4(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE) ++ return sb->s_magic == EXT4_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysv(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SYSV_FS) || defined(CONFIG_SYSV_FS_MODULE) ++ return !strcmp(au_sbtype(sb), "sysv"); ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_ramfs(struct super_block *sb) ++{ ++ return sb->s_magic == RAMFS_MAGIC; ++} ++ ++static inline int au_test_ubifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_UBIFS_FS) || defined(CONFIG_UBIFS_FS_MODULE) ++ return sb->s_magic == UBIFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_procfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_PROC_FS ++ return sb->s_magic == PROC_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_sysfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SYSFS ++ return sb->s_magic == SYSFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_configfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CONFIGFS_FS) || defined(CONFIG_CONFIGFS_FS_MODULE) ++ return sb->s_magic == CONFIGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_minix(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_MINIX_FS) || defined(CONFIG_MINIX_FS_MODULE) ++ return sb->s_magic == MINIX3_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC ++ || sb->s_magic == MINIX2_SUPER_MAGIC2 ++ || sb->s_magic == MINIX_SUPER_MAGIC ++ || sb->s_magic == MINIX_SUPER_MAGIC2; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_cifs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_CIFS_FS) || defined(CONFIGCIFS_FS_MODULE) ++ return sb->s_magic == CIFS_MAGIC_NUMBER; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_fat(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_FAT_FS) || defined(CONFIG_FAT_FS_MODULE) ++ return sb->s_magic == MSDOS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_msdos(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_vfat(struct super_block *sb) ++{ ++ return au_test_fat(sb); ++} ++ ++static inline int au_test_securityfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_SECURITYFS ++ return sb->s_magic == SECURITYFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_squashfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_SQUASHFS) || defined(CONFIG_SQUASHFS_MODULE) ++ return sb->s_magic == SQUASHFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_btrfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_BTRFS_FS) || defined(CONFIG_BTRFS_FS_MODULE) ++ return sb->s_magic == BTRFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_xenfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_XENFS) || defined(CONFIG_XENFS_MODULE) ++ return sb->s_magic == XENFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_debugfs(struct super_block *sb __maybe_unused) ++{ ++#ifdef CONFIG_DEBUG_FS ++ return sb->s_magic == DEBUGFS_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_nilfs(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_NILFS) || defined(CONFIG_NILFS_MODULE) ++ return sb->s_magic == NILFS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++static inline int au_test_hfsplus(struct super_block *sb __maybe_unused) ++{ ++#if defined(CONFIG_HFSPLUS_FS) || defined(CONFIG_HFSPLUS_FS_MODULE) ++ return sb->s_magic == HFSPLUS_SUPER_MAGIC; ++#else ++ return 0; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * they can't be an aufs branch. ++ */ ++static inline int au_test_fs_unsuppoted(struct super_block *sb) ++{ ++ return ++#ifndef CONFIG_AUFS_BR_RAMFS ++ au_test_ramfs(sb) || ++#endif ++ au_test_procfs(sb) ++ || au_test_sysfs(sb) ++ || au_test_configfs(sb) ++ || au_test_debugfs(sb) ++ || au_test_securityfs(sb) ++ || au_test_xenfs(sb) ++ || au_test_ecryptfs(sb) ++ /* || !strcmp(au_sbtype(sb), "unionfs") */ ++ || au_test_aufs(sb); /* will be supported in next version */ ++} ++ ++/* ++ * If the filesystem supports NFS-export, then it has to support NULL as ++ * a nameidata parameter for ->create(), ->lookup() and ->d_revalidate(). ++ * We can apply this principle when we handle a lower filesystem. ++ */ ++static inline int au_test_fs_null_nd(struct super_block *sb) ++{ ++ return !!sb->s_export_op; ++} ++ ++static inline int au_test_fs_remote(struct super_block *sb) ++{ ++ return !au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ && !au_test_ramfs(sb) ++#endif ++ && !(sb->s_type->fs_flags & FS_REQUIRES_DEV); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * Note: these functions (below) are created after reading ->getattr() in all ++ * filesystems under linux/fs. it means we have to do so in every update... ++ */ ++ ++/* ++ * some filesystems require getattr to refresh the inode attributes before ++ * referencing. ++ * in most cases, we can rely on the inode attribute in NFS (or every remote fs) ++ * and leave the work for d_revalidate() ++ */ ++static inline int au_test_fs_refresh_iattr(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ /* || au_test_smbfs(sb) */ /* untested */ ++ /* || au_test_ocfs2(sb) */ /* untested */ ++ /* || au_test_btrfs(sb) */ /* untested */ ++ /* || au_test_coda(sb) */ /* untested */ ++ /* || au_test_v9fs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't maintain i_size or i_blocks. ++ */ ++static inline int au_test_fs_bad_iattr_size(struct super_block *sb) ++{ ++ return au_test_xfs(sb) ++ || au_test_btrfs(sb) ++ || au_test_ubifs(sb) ++ || au_test_hfsplus(sb) /* maintained, but incorrect */ ++ /* || au_test_ext4(sb) */ /* untested */ ++ /* || au_test_ocfs2(sb) */ /* untested */ ++ /* || au_test_ocfs2_dlmfs(sb) */ /* untested */ ++ /* || au_test_sysv(sb) */ /* untested */ ++ /* || au_test_minix(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which don't store the correct value in some of their inode ++ * attributes. ++ */ ++static inline int au_test_fs_bad_iattr(struct super_block *sb) ++{ ++ return au_test_fs_bad_iattr_size(sb) ++ /* || au_test_cifs(sb) */ /* untested */ ++ || au_test_fat(sb) ++ || au_test_msdos(sb) ++ || au_test_vfat(sb); ++} ++ ++/* they don't check i_nlink in link(2) */ ++static inline int au_test_fs_no_limit_nlink(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || au_test_ramfs(sb) ++#endif ++ || au_test_ubifs(sb) ++ || au_test_btrfs(sb) ++ || au_test_hfsplus(sb); ++} ++ ++/* ++ * filesystems which sets S_NOATIME and S_NOCMTIME. ++ */ ++static inline int au_test_fs_notime(struct super_block *sb) ++{ ++ return au_test_nfs(sb) ++ || au_test_fuse(sb) ++ || au_test_ubifs(sb) ++ /* || au_test_cifs(sb) */ /* untested */ ++ ; ++} ++ ++/* ++ * filesystems which requires replacing i_mapping. ++ */ ++static inline int au_test_fs_bad_mapping(struct super_block *sb) ++{ ++ return au_test_fuse(sb) ++ || au_test_ubifs(sb); ++} ++ ++/* temporary support for i#1 in cramfs */ ++static inline int au_test_fs_unique_ino(struct inode *inode) ++{ ++ if (au_test_cramfs(inode->i_sb)) ++ return inode->i_ino != 1; ++ return 1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * the filesystem where the xino files placed must support i/o after unlink and ++ * maintain i_size and i_blocks. ++ */ ++static inline int au_test_fs_bad_xino(struct super_block *sb) ++{ ++ return au_test_fs_remote(sb) ++ || au_test_fs_bad_iattr_size(sb) ++#ifdef CONFIG_AUFS_BR_RAMFS ++ || !(au_test_ramfs(sb) || au_test_fs_null_nd(sb)) ++#else ++ || !au_test_fs_null_nd(sb) /* to keep xino code simple */ ++#endif ++ /* don't want unnecessary work for xino */ ++ || au_test_aufs(sb) ++ || au_test_ecryptfs(sb) ++ || au_test_nilfs(sb); ++} ++ ++static inline int au_test_fs_trunc_xino(struct super_block *sb) ++{ ++ return au_test_tmpfs(sb) ++ || au_test_ramfs(sb); ++} ++ ++/* ++ * test if the @sb is real-readonly. ++ */ ++static inline int au_test_fs_rr(struct super_block *sb) ++{ ++ return au_test_squashfs(sb) ++ || au_test_iso9660(sb) ++ || au_test_cramfs(sb) ++ || au_test_romfs(sb); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_FSTYPE_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/hfsnotify.c linux-2.6.36/fs/aufs/hfsnotify.c +--- linux-2.6.36.orig/fs/aufs/hfsnotify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/hfsnotify.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,247 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * fsnotify for the lower directories ++ */ ++ ++#include "aufs.h" ++ ++/* FS_IN_IGNORED is unnecessary */ ++static const __u32 AuHfsnMask = (FS_MOVED_TO | FS_MOVED_FROM | FS_DELETE ++ | FS_CREATE | FS_EVENT_ON_CHILD); ++static DECLARE_WAIT_QUEUE_HEAD(au_hfsn_wq); ++ ++static void au_hfsn_free_mark(struct fsnotify_mark *mark) ++{ ++ struct au_hnotify *hn = container_of(mark, struct au_hnotify, ++ hn_mark); ++ AuDbg("here\n"); ++ hn->hn_mark_dead = 1; ++ smp_mb(); ++ wake_up_all(&au_hfsn_wq); ++} ++ ++static int au_hfsn_alloc(struct au_hinode *hinode) ++{ ++ struct au_hnotify *hn; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct fsnotify_mark *mark; ++ aufs_bindex_t bindex; ++ ++ hn = hinode->hi_notify; ++ sb = hn->hn_aufs_inode->i_sb; ++ bindex = au_br_index(sb, hinode->hi_id); ++ br = au_sbr(sb, bindex); ++ hn->hn_mark_dead = 0; ++ mark = &hn->hn_mark; ++ fsnotify_init_mark(mark, au_hfsn_free_mark); ++ mark->mask = AuHfsnMask; ++ /* ++ * by udba rename or rmdir, aufs assign a new inode to the known ++ * h_inode, so specify 1 to allow dups. ++ */ ++ return fsnotify_add_mark(mark, br->br_hfsn_group, hinode->hi_inode, ++ /*mnt*/NULL, /*allow_dups*/1); ++} ++ ++static void au_hfsn_free(struct au_hinode *hinode) ++{ ++ struct au_hnotify *hn; ++ struct fsnotify_mark *mark; ++ ++ hn = hinode->hi_notify; ++ mark = &hn->hn_mark; ++ fsnotify_destroy_mark(mark); ++ fsnotify_put_mark(mark); ++ ++ /* TODO: bad approach */ ++ wait_event(au_hfsn_wq, hn->hn_mark_dead); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ struct fsnotify_mark *mark; ++ ++ mark = &hinode->hi_notify->hn_mark; ++ spin_lock(&mark->lock); ++ if (do_set) { ++ AuDebugOn(mark->mask & AuHfsnMask); ++ mark->mask |= AuHfsnMask; ++ } else { ++ AuDebugOn(!(mark->mask & AuHfsnMask)); ++ mark->mask &= ~AuHfsnMask; ++ } ++ spin_unlock(&mark->lock); ++ /* fsnotify_recalc_inode_mask(hinode->hi_inode); */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* #define AuDbgHnotify */ ++#ifdef AuDbgHnotify ++static char *au_hfsn_name(u32 mask) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++#define test_ret(flag) if (mask & flag) \ ++ return #flag; ++ test_ret(FS_ACCESS); ++ test_ret(FS_MODIFY); ++ test_ret(FS_ATTRIB); ++ test_ret(FS_CLOSE_WRITE); ++ test_ret(FS_CLOSE_NOWRITE); ++ test_ret(FS_OPEN); ++ test_ret(FS_MOVED_FROM); ++ test_ret(FS_MOVED_TO); ++ test_ret(FS_CREATE); ++ test_ret(FS_DELETE); ++ test_ret(FS_DELETE_SELF); ++ test_ret(FS_MOVE_SELF); ++ test_ret(FS_UNMOUNT); ++ test_ret(FS_Q_OVERFLOW); ++ test_ret(FS_IN_IGNORED); ++ test_ret(FS_IN_ISDIR); ++ test_ret(FS_IN_ONESHOT); ++ test_ret(FS_EVENT_ON_CHILD); ++ return ""; ++#undef test_ret ++#else ++ return "??"; ++#endif ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_hfsn_handle_event(struct fsnotify_group *group, ++ struct fsnotify_mark *inode_mark, ++ struct fsnotify_mark *vfsmount_mark, ++ struct fsnotify_event *event) ++{ ++ int err; ++ struct au_hnotify *hnotify; ++ struct inode *h_dir, *h_inode; ++ __u32 mask; ++ struct qstr h_child_qstr = { ++ .name = event->file_name, ++ .len = event->name_len ++ }; ++ ++ AuDebugOn(event->data_type != FSNOTIFY_EVENT_INODE); ++ ++ err = 0; ++ /* if FS_UNMOUNT happens, there must be another bug */ ++ mask = event->mask; ++ AuDebugOn(mask & FS_UNMOUNT); ++ if (mask & (FS_IN_IGNORED | FS_UNMOUNT)) ++ goto out; ++ ++ h_dir = event->to_tell; ++ h_inode = event->inode; ++#ifdef AuDbgHnotify ++ au_debug(1); ++ if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 ++ || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { ++ AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", ++ h_dir->i_ino, mask, au_hfsn_name(mask), ++ AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); ++ /* WARN_ON(1); */ ++ } ++ au_debug(0); ++#endif ++ ++ AuDebugOn(!inode_mark); ++ hnotify = container_of(inode_mark, struct au_hnotify, hn_mark); ++ err = au_hnotify(h_dir, hnotify, mask, &h_child_qstr, h_inode); ++ ++out: ++ return err; ++} ++ ++/* isn't it waste to ask every registered 'group'? */ ++/* copied from linux/fs/notify/inotify/inotify_fsnotiry.c */ ++/* it should be exported to modules */ ++static bool au_hfsn_should_send_event(struct fsnotify_group *group, ++ struct inode *h_inode, ++ struct fsnotify_mark *inode_mark, ++ struct fsnotify_mark *vfsmount_mark, ++ __u32 mask, void *data, int data_type) ++{ ++ mask = (mask & ~FS_EVENT_ON_CHILD); ++ return inode_mark->mask & mask; ++} ++ ++static struct fsnotify_ops au_hfsn_ops = { ++ .should_send_event = au_hfsn_should_send_event, ++ .handle_event = au_hfsn_handle_event ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_hfsn_fin_br(struct au_branch *br) ++{ ++ if (br->br_hfsn_group) ++ fsnotify_put_group(br->br_hfsn_group); ++} ++ ++static int au_hfsn_init_br(struct au_branch *br, int perm) ++{ ++ br->br_hfsn_group = NULL; ++ br->br_hfsn_ops = au_hfsn_ops; ++ return 0; ++} ++ ++static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (udba != AuOpt_UDBA_HNOTIFY ++ || !au_br_hnotifyable(perm)) { ++ au_hfsn_fin_br(br); ++ br->br_hfsn_group = NULL; ++ goto out; ++ } ++ ++ if (br->br_hfsn_group) ++ goto out; ++ ++ br->br_hfsn_group = fsnotify_alloc_group(&br->br_hfsn_ops); ++ if (IS_ERR(br->br_hfsn_group)) { ++ err = PTR_ERR(br->br_hfsn_group); ++ pr_err("fsnotify_alloc_group() failed, %d\n", err); ++ br->br_hfsn_group = NULL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++const struct au_hnotify_op au_hnotify_op = { ++ .ctl = au_hfsn_ctl, ++ .alloc = au_hfsn_alloc, ++ .free = au_hfsn_free, ++ ++ .reset_br = au_hfsn_reset_br, ++ .fin_br = au_hfsn_fin_br, ++ .init_br = au_hfsn_init_br ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/hfsplus.c linux-2.6.36/fs/aufs/hfsplus.c +--- linux-2.6.36.orig/fs/aufs/hfsplus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/hfsplus.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,58 @@ ++/* ++ * Copyright (C) 2010-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * special support for filesystems which aqucires an inode mutex ++ * at final closing a file, eg, hfsplus. ++ * ++ * This trick is very simple and stupid, just to open the file before really ++ * neceeary open to tell hfsplus that this is not the final closing. ++ * The caller should call au_h_open_pre() after acquiring the inode mutex, ++ * and au_h_open_post() after releasing it. ++ */ ++ ++#include ++#include "aufs.h" ++ ++struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ struct file *h_file; ++ struct dentry *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ AuDebugOn(!h_dentry); ++ AuDebugOn(!h_dentry->d_inode); ++ IMustLock(h_dentry->d_inode); ++ ++ h_file = NULL; ++ if (au_test_hfsplus(h_dentry->d_sb) ++ && S_ISREG(h_dentry->d_inode->i_mode)) ++ h_file = au_h_open(dentry, bindex, ++ O_RDONLY | O_NOATIME | O_LARGEFILE, ++ /*file*/NULL); ++ return h_file; ++} ++ ++void au_h_open_post(struct dentry *dentry, aufs_bindex_t bindex, ++ struct file *h_file) ++{ ++ if (h_file) { ++ fput(h_file); ++ au_sbr_put(dentry->d_sb, bindex); ++ } ++} +diff -Nur linux-2.6.36.orig/fs/aufs/hnotify.c linux-2.6.36/fs/aufs/hnotify.c +--- linux-2.6.36.orig/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/hnotify.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,694 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * abstraction to notify the direct changes on lower directories ++ */ ++ ++#include "aufs.h" ++ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode) ++{ ++ int err; ++ struct au_hnotify *hn; ++ ++ err = -ENOMEM; ++ hn = au_cache_alloc_hnotify(); ++ if (hn) { ++ hn->hn_aufs_inode = inode; ++ hinode->hi_notify = hn; ++ err = au_hnotify_op.alloc(hinode); ++ AuTraceErr(err); ++ if (unlikely(err)) { ++ hinode->hi_notify = NULL; ++ au_cache_free_hnotify(hn); ++ /* ++ * The upper dir was removed by udba, but the same named ++ * dir left. In this case, aufs assignes a new inode ++ * number and set the monitor again. ++ * For the lower dir, the old monitnor is still left. ++ */ ++ if (err == -EEXIST) ++ err = 0; ++ } ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hn_free(struct au_hinode *hinode) ++{ ++ struct au_hnotify *hn; ++ ++ hn = hinode->hi_notify; ++ if (hn) { ++ au_hnotify_op.free(hinode); ++ au_cache_free_hnotify(hn); ++ hinode->hi_notify = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_hn_ctl(struct au_hinode *hinode, int do_set) ++{ ++ if (hinode->hi_notify) ++ au_hnotify_op.ctl(hinode, do_set); ++} ++ ++void au_hn_reset(struct inode *inode, unsigned int flags) ++{ ++ aufs_bindex_t bindex, bend; ++ struct inode *hi; ++ struct dentry *iwhdentry; ++ ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ hi = au_h_iptr(inode, bindex); ++ if (!hi) ++ continue; ++ ++ /* mutex_lock_nested(&hi->i_mutex, AuLsc_I_CHILD); */ ++ iwhdentry = au_hi_wh(inode, bindex); ++ if (iwhdentry) ++ dget(iwhdentry); ++ au_igrab(hi); ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ au_set_h_iptr(inode, bindex, au_igrab(hi), ++ flags & ~AuHi_XINO); ++ iput(hi); ++ dput(iwhdentry); ++ /* mutex_unlock(&hi->i_mutex); */ ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int hn_xino(struct inode *inode, struct inode *h_inode) ++{ ++ int err; ++ aufs_bindex_t bindex, bend, bfound, bstart; ++ struct inode *h_i; ++ ++ err = 0; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warning("branch root dir was changed\n"); ++ goto out; ++ } ++ ++ bfound = -1; ++ bend = au_ibend(inode); ++ bstart = au_ibstart(inode); ++#if 0 /* reserved for future use */ ++ if (bindex == bend) { ++ /* keep this ino in rename case */ ++ goto out; ++ } ++#endif ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex) == h_inode) { ++ bfound = bindex; ++ break; ++ } ++ if (bfound < 0) ++ goto out; ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(inode, bindex); ++ if (!h_i) ++ continue; ++ ++ err = au_xino_write(inode->i_sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ /* bad action? */ ++ } ++ ++ /* children inode number will be broken */ ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_tree(struct dentry *dentry) ++{ ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ 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]; ++ if (IS_ROOT(d)) ++ continue; ++ ++ au_digen_dec(d); ++ if (d->d_inode) ++ /* todo: reset children xino? ++ cached children only? */ ++ au_iigen_dec(d->d_inode); ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++ ++ /* discard children */ ++ dentry_unhash(dentry); ++ dput(dentry); ++out: ++ return err; ++} ++ ++/* ++ * return 0 if processed. ++ */ ++static int hn_gen_by_inode(char *name, unsigned int nlen, struct inode *inode, ++ const unsigned int isdir) ++{ ++ int err; ++ struct dentry *d; ++ struct qstr *dname; ++ ++ err = 1; ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warning("branch root dir was changed\n"); ++ err = 0; ++ goto out; ++ } ++ ++ if (!isdir) { ++ AuDebugOn(!name); ++ au_iigen_dec(inode); ++ spin_lock(&dcache_lock); ++ list_for_each_entry(d, &inode->i_dentry, d_alias) { ++ dname = &d->d_name; ++ if (dname->len != nlen ++ && memcmp(dname->name, name, nlen)) ++ continue; ++ err = 0; ++ au_digen_dec(d); ++ break; ++ } ++ spin_unlock(&dcache_lock); ++ } else { ++ au_fset_si(au_sbi(inode->i_sb), FAILED_REFRESH_DIR); ++ d = d_find_alias(inode); ++ if (!d) { ++ au_iigen_dec(inode); ++ goto out; ++ } ++ ++ dname = &d->d_name; ++ if (dname->len == nlen && !memcmp(dname->name, name, nlen)) ++ err = hn_gen_tree(d); ++ dput(d); ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int hn_gen_by_name(struct dentry *dentry, const unsigned int isdir) ++{ ++ int err; ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ if (IS_ROOT(dentry) ++ /* || (inode && inode->i_ino == AUFS_ROOT_INO) */ ++ ) { ++ pr_warning("branch root dir was changed\n"); ++ return 0; ++ } ++ ++ err = 0; ++ if (!isdir) { ++ au_digen_dec(dentry); ++ if (inode) ++ au_iigen_dec(inode); ++ } else { ++ au_fset_si(au_sbi(dentry->d_sb), FAILED_REFRESH_DIR); ++ if (inode) ++ err = hn_gen_tree(dentry); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* hnotify job flags */ ++#define AuHnJob_XINO0 1 ++#define AuHnJob_GEN (1 << 1) ++#define AuHnJob_DIRENT (1 << 2) ++#define AuHnJob_ISDIR (1 << 3) ++#define AuHnJob_TRYXINO0 (1 << 4) ++#define AuHnJob_MNTPNT (1 << 5) ++#define au_ftest_hnjob(flags, name) ((flags) & AuHnJob_##name) ++#define au_fset_hnjob(flags, name) \ ++ do { (flags) |= AuHnJob_##name; } while (0) ++#define au_fclr_hnjob(flags, name) \ ++ do { (flags) &= ~AuHnJob_##name; } while (0) ++ ++enum { ++ AuHn_CHILD, ++ AuHn_PARENT, ++ AuHnLast ++}; ++ ++struct au_hnotify_args { ++ struct inode *h_dir, *dir, *h_child_inode; ++ u32 mask; ++ unsigned int flags[AuHnLast]; ++ unsigned int h_child_nlen; ++ char h_child_name[]; ++}; ++ ++struct hn_job_args { ++ unsigned int flags; ++ struct inode *inode, *h_inode, *dir, *h_dir; ++ struct dentry *dentry; ++ char *h_name; ++ int h_nlen; ++}; ++ ++static int hn_job(struct hn_job_args *a) ++{ ++ const unsigned int isdir = au_ftest_hnjob(a->flags, ISDIR); ++ ++ /* reset xino */ ++ if (au_ftest_hnjob(a->flags, XINO0) && a->inode) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ ++ if (au_ftest_hnjob(a->flags, TRYXINO0) ++ && a->inode ++ && a->h_inode) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ if (!a->h_inode->i_nlink) ++ hn_xino(a->inode, a->h_inode); /* ignore this error */ ++ mutex_unlock(&a->h_inode->i_mutex); ++ } ++ ++ /* make the generation obsolete */ ++ if (au_ftest_hnjob(a->flags, GEN)) { ++ int err = -1; ++ if (a->inode) ++ err = hn_gen_by_inode(a->h_name, a->h_nlen, a->inode, ++ isdir); ++ if (err && a->dentry) ++ hn_gen_by_name(a->dentry, isdir); ++ /* ignore this error */ ++ } ++ ++ /* make dir entries obsolete */ ++ if (au_ftest_hnjob(a->flags, DIRENT) && a->inode) { ++ struct au_vdir *vdir; ++ ++ vdir = au_ivdir(a->inode); ++ if (vdir) ++ vdir->vd_jiffy = 0; ++ /* IMustLock(a->inode); */ ++ /* a->inode->i_version++; */ ++ } ++ ++ /* can do nothing but warn */ ++ if (au_ftest_hnjob(a->flags, MNTPNT) ++ && a->dentry ++ && d_mountpoint(a->dentry)) ++ pr_warning("mount-point %.*s is removed or renamed\n", ++ AuDLNPair(a->dentry)); ++ ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *lookup_wlock_by_name(char *name, unsigned int nlen, ++ struct inode *dir) ++{ ++ struct dentry *dentry, *d, *parent; ++ struct qstr *dname; ++ ++ 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) { ++ /* AuDbg("%.*s\n", AuDLNPair(d)); */ ++ dname = &d->d_name; ++ if (dname->len != nlen || memcmp(dname->name, name, nlen)) ++ continue; ++ if (au_di(d)) ++ au_digen_dec(d); ++ else ++ continue; ++ if (!atomic_read(&d->d_count)) ++ 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; ++ ino_t ino; ++ int err; ++ ++ inode = NULL; ++ err = au_xino_read(sb, bindex, h_ino, &ino); ++ if (!err && ino) ++ inode = ilookup(sb, ino); ++ if (!inode) ++ goto out; ++ ++ if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { ++ pr_warning("wrong root branch\n"); ++ iput(inode); ++ inode = NULL; ++ goto out; ++ } ++ ++ ii_write_lock_child(inode); ++ ++out: ++ return inode; ++} ++ ++static void au_hn_bh(void *_args) ++{ ++ struct au_hnotify_args *a = _args; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend, bfound; ++ unsigned char xino, try_iput; ++ int err; ++ struct inode *inode; ++ ino_t h_ino; ++ struct hn_job_args args; ++ struct dentry *dentry; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!_args); ++ AuDebugOn(!a->h_dir); ++ AuDebugOn(!a->dir); ++ AuDebugOn(!a->mask); ++ AuDbg("mask 0x%x, i%lu, hi%lu, hci%lu\n", ++ a->mask, a->dir->i_ino, a->h_dir->i_ino, ++ a->h_child_inode ? a->h_child_inode->i_ino : 0); ++ ++ inode = NULL; ++ dentry = NULL; ++ /* ++ * do not lock a->dir->i_mutex here ++ * because of d_revalidate() may cause a deadlock. ++ */ ++ sb = a->dir->i_sb; ++ AuDebugOn(!sb); ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!sbinfo); ++ si_write_lock(sb, AuLock_NOPLMW); ++ ++ ii_read_lock_parent(a->dir); ++ bfound = -1; ++ bend = au_ibend(a->dir); ++ for (bindex = au_ibstart(a->dir); bindex <= bend; bindex++) ++ if (au_h_iptr(a->dir, bindex) == a->h_dir) { ++ bfound = bindex; ++ break; ++ } ++ ii_read_unlock(a->dir); ++ if (unlikely(bfound < 0)) ++ goto out; ++ ++ xino = !!au_opt_test(au_mntflags(sb), XINO); ++ h_ino = 0; ++ if (a->h_child_inode) ++ h_ino = a->h_child_inode->i_ino; ++ ++ if (a->h_child_nlen ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], GEN) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], MNTPNT))) ++ dentry = lookup_wlock_by_name(a->h_child_name, a->h_child_nlen, ++ a->dir); ++ try_iput = 0; ++ if (dentry) ++ inode = dentry->d_inode; ++ if (xino && !inode && h_ino ++ && (au_ftest_hnjob(a->flags[AuHn_CHILD], XINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], TRYXINO0) ++ || au_ftest_hnjob(a->flags[AuHn_CHILD], GEN))) { ++ inode = lookup_wlock_by_ino(sb, bfound, h_ino); ++ try_iput = 1; ++ } ++ ++ args.flags = a->flags[AuHn_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 = hn_job(&args); ++ if (dentry) { ++ if (au_di(dentry)) ++ di_write_unlock(dentry); ++ dput(dentry); ++ } ++ if (inode && try_iput) { ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ ++ ii_write_lock_parent(a->dir); ++ args.flags = a->flags[AuHn_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 = hn_job(&args); ++ ii_write_unlock(a->dir); ++ ++out: ++ iput(a->h_child_inode); ++ iput(a->h_dir); ++ iput(a->dir); ++ si_write_unlock(sb); ++ au_nwt_done(&sbinfo->si_nowait); ++ kfree(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode) ++{ ++ int err, len; ++ unsigned int flags[AuHnLast]; ++ unsigned char isdir, isroot, wh; ++ struct inode *dir; ++ struct au_hnotify_args *args; ++ char *p, *h_child_name; ++ ++ err = 0; ++ AuDebugOn(!hnotify || !hnotify->hn_aufs_inode); ++ dir = igrab(hnotify->hn_aufs_inode); ++ if (!dir) ++ goto out; ++ ++ isroot = (dir->i_ino == AUFS_ROOT_INO); ++ wh = 0; ++ h_child_name = (void *)h_child_qstr->name; ++ len = h_child_qstr->len; ++ if (h_child_name) { ++ if (len > AUFS_WH_PFX_LEN ++ && !memcmp(h_child_name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ h_child_name += AUFS_WH_PFX_LEN; ++ len -= AUFS_WH_PFX_LEN; ++ wh = 1; ++ } ++ } ++ ++ isdir = 0; ++ if (h_child_inode) ++ isdir = !!S_ISDIR(h_child_inode->i_mode); ++ flags[AuHn_PARENT] = AuHnJob_ISDIR; ++ flags[AuHn_CHILD] = 0; ++ if (isdir) ++ flags[AuHn_CHILD] = AuHnJob_ISDIR; ++ au_fset_hnjob(flags[AuHn_PARENT], DIRENT); ++ au_fset_hnjob(flags[AuHn_CHILD], GEN); ++ switch (mask & FS_EVENTS_POSS_ON_CHILD) { ++ case FS_MOVED_FROM: ++ case FS_MOVED_TO: ++ au_fset_hnjob(flags[AuHn_CHILD], XINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ /*FALLTHROUGH*/ ++ case FS_CREATE: ++ AuDebugOn(!h_child_name || !h_child_inode); ++ break; ++ ++ case FS_DELETE: ++ /* ++ * aufs never be able to get this child inode. ++ * revalidation should be in d_revalidate() ++ * by checking i_nlink, i_generation or d_unhashed(). ++ */ ++ AuDebugOn(!h_child_name); ++ au_fset_hnjob(flags[AuHn_CHILD], TRYXINO0); ++ au_fset_hnjob(flags[AuHn_CHILD], MNTPNT); ++ break; ++ ++ default: ++ AuDebugOn(1); ++ } ++ ++ if (wh) ++ h_child_inode = NULL; ++ ++ err = -ENOMEM; ++ /* iput() and kfree() will be called in au_hnotify() */ ++ args = kmalloc(sizeof(*args) + len + 1, GFP_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ iput(dir); ++ goto out; ++ } ++ args->flags[AuHn_PARENT] = flags[AuHn_PARENT]; ++ args->flags[AuHn_CHILD] = flags[AuHn_CHILD]; ++ args->mask = mask; ++ args->dir = dir; ++ args->h_dir = igrab(h_dir); ++ if (h_child_inode) ++ h_child_inode = igrab(h_child_inode); /* can be NULL */ ++ 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); ++ p[len] = 0; ++ } ++ ++ err = au_wkq_nowait(au_hn_bh, args, dir->i_sb); ++ if (unlikely(err)) { ++ pr_err("wkq %d\n", err); ++ iput(args->h_child_inode); ++ iput(args->h_dir); ++ iput(args->dir); ++ kfree(args); ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm) ++{ ++ int err; ++ ++ AuDebugOn(!(udba & AuOptMask_UDBA)); ++ ++ err = 0; ++ if (au_hnotify_op.reset_br) ++ err = au_hnotify_op.reset_br(udba, br, perm); ++ ++ return err; ++} ++ ++int au_hnotify_init_br(struct au_branch *br, int perm) ++{ ++ int err; ++ ++ err = 0; ++ if (au_hnotify_op.init_br) ++ err = au_hnotify_op.init_br(br, perm); ++ ++ return err; ++} ++ ++void au_hnotify_fin_br(struct au_branch *br) ++{ ++ if (au_hnotify_op.fin_br) ++ au_hnotify_op.fin_br(br); ++} ++ ++static void au_hn_destroy_cache(void) ++{ ++ kmem_cache_destroy(au_cachep[AuCache_HNOTIFY]); ++ au_cachep[AuCache_HNOTIFY] = NULL; ++} ++ ++int __init au_hnotify_init(void) ++{ ++ int err; ++ ++ err = -ENOMEM; ++ au_cachep[AuCache_HNOTIFY] = AuCache(au_hnotify); ++ if (au_cachep[AuCache_HNOTIFY]) { ++ err = 0; ++ if (au_hnotify_op.init) ++ err = au_hnotify_op.init(); ++ if (unlikely(err)) ++ au_hn_destroy_cache(); ++ } ++ AuTraceErr(err); ++ return err; ++} ++ ++void au_hnotify_fin(void) ++{ ++ if (au_hnotify_op.fin) ++ au_hnotify_op.fin(); ++ /* cf. au_cache_fin() */ ++ if (au_cachep[AuCache_HNOTIFY]) ++ au_hn_destroy_cache(); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/i_op.c linux-2.6.36/fs/aufs/i_op.c +--- linux-2.6.36.orig/fs/aufs/i_op.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/i_op.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,971 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode operations (except add/del/rename) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++static int h_permission(struct inode *h_inode, int mask, ++ struct vfsmount *h_mnt, int brperm) ++{ ++ int err; ++ const unsigned char write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ ++ err = -EACCES; ++ if ((write_mask && IS_IMMUTABLE(h_inode)) ++ || ((mask & MAY_EXEC) ++ && S_ISREG(h_inode->i_mode) ++ && ((h_mnt->mnt_flags & MNT_NOEXEC) ++ || !(h_inode->i_mode & S_IXUGO)))) ++ goto out; ++ ++ /* ++ * - skip the lower fs test in the case of write to ro branch. ++ * - nfs dir permission write check is optimized, but a policy for ++ * link/rename requires a real check. ++ */ ++ if ((write_mask && !au_br_writable(brperm)) ++ || (au_test_nfs(h_inode->i_sb) && S_ISDIR(h_inode->i_mode) ++ && write_mask && !(mask & MAY_READ)) ++ || !h_inode->i_op->permission) { ++ /* AuLabel(generic_permission); */ ++ err = generic_permission(h_inode, mask, ++ h_inode->i_op->check_acl); ++ } else { ++ /* AuLabel(h_inode->permission); */ ++ err = h_inode->i_op->permission(h_inode, mask); ++ AuTraceErr(err); ++ } ++ ++ if (!err) ++ err = devcgroup_inode_permission(h_inode, mask); ++ if (!err) ++ err = security_inode_permission(h_inode, mask); ++ ++#if 0 ++ if (!err) { ++ /* todo: do we need to call ima_path_check()? */ ++ struct path h_path = { ++ .dentry = ++ .mnt = h_mnt ++ }; ++ err = ima_path_check(&h_path, ++ mask & (MAY_READ | MAY_WRITE | MAY_EXEC), ++ IMA_COUNT_LEAVE); ++ } ++#endif ++ ++out: ++ return err; ++} ++ ++static int aufs_permission(struct inode *inode, int mask) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ const unsigned char isdir = !!S_ISDIR(inode->i_mode), ++ write_mask = !!(mask & (MAY_WRITE | MAY_APPEND)); ++ struct inode *h_inode; ++ struct super_block *sb; ++ struct au_branch *br; ++ ++ sb = inode->i_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_read_lock_child(inode); ++#if 0 ++ err = au_iigen_test(inode, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ if (!isdir || write_mask) { ++ err = au_busy_or_stale(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ if (unlikely(!h_inode ++ || (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT))) ++ goto out; ++ ++ err = 0; ++ bindex = au_ibstart(inode); ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); ++ if (write_mask ++ && !err ++ && !special_file(h_inode->i_mode)) { ++ /* test whether the upper writable branch exists */ ++ err = -EROFS; ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = 0; ++ break; ++ } ++ } ++ goto out; ++ } ++ ++ /* non-write to dir */ ++ err = 0; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); !err && bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode) { ++ err = au_busy_or_stale(); ++ if (unlikely(!S_ISDIR(h_inode->i_mode))) ++ break; ++ ++ br = au_sbr(sb, bindex); ++ err = h_permission(h_inode, mask, br->br_mnt, ++ br->br_perm); ++ } ++ } ++ ++out: ++ ii_read_unlock(inode); ++ si_read_unlock(sb); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ int err, npositive; ++ ++ IMustLock(dir); ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ ret = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out_si; ++ err = au_di_init(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_si; ++ ++ npositive = 0; /* suppress a warning */ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_alive_dir(parent); ++ if (!err) ++ err = au_digen_test(parent, au_sigen(sb)); ++ if (!err) { ++ npositive = au_lkup_dentry(dentry, au_dbstart(parent), ++ /*type*/0, nd); ++ err = npositive; ++ } ++ di_read_unlock(parent, AuLock_IR); ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ inode = NULL; ++ if (npositive) { ++ inode = au_new_inode(dentry, /*must_new*/0); ++ ret = (void *)inode; ++ } ++ if (IS_ERR(inode)) ++ goto out_unlock; ++ ++ ret = d_splice_alias(inode, dentry); ++ if (unlikely(IS_ERR(ret) && inode)) { ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++ ++out_unlock: ++ di_write_unlock(dentry); ++out_si: ++ si_read_unlock(sb); ++out: ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, ++ const unsigned char add_entry, aufs_bindex_t bcpup, ++ aufs_bindex_t bstart) ++{ ++ int err; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ if (add_entry) ++ IMustLock(parent->d_inode); ++ else ++ di_write_lock_parent(parent); ++ ++ err = 0; ++ if (!au_h_dptr(parent, bcpup)) { ++ if (bstart < bcpup) ++ err = au_cpdown_dirs(dentry, bcpup); ++ else ++ err = au_cpup_dirs(dentry, bcpup); ++ } ++ if (!err && add_entry) { ++ h_parent = au_h_dptr(parent, bcpup); ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ err = au_lkup_neg(dentry, bcpup); ++ /* todo: no unlock here */ ++ mutex_unlock(&h_dir->i_mutex); ++ ++ AuDbg("bcpup %d\n", bcpup); ++ if (!err) { ++ if (!dentry->d_inode) ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_update_dbrange(dentry, /*do_put_zero*/0); ++ } ++ } ++ ++ if (!add_entry) ++ di_write_unlock(parent); ++ if (!err) ++ err = bcpup; /* success */ ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * 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; ++ const unsigned char add_entry = !!au_ftest_wrdir(args->flags, ++ ADD_ENTRY); ++ struct super_block *sb; ++ struct dentry *parent; ++ struct au_sbinfo *sbinfo; ++ ++ sb = dentry->d_sb; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(dentry); ++ bcpup = bstart; ++ if (args->force_btgt < 0) { ++ if (src_dentry) { ++ src_bstart = au_dbstart(src_dentry); ++ if (src_bstart < bstart) ++ bcpup = src_bstart; ++ } else if (add_entry) { ++ err = AuWbrCreate(sbinfo, dentry, ++ au_ftest_wrdir(args->flags, ISDIR)); ++ bcpup = err; ++ } ++ ++ if (bcpup < 0 || au_test_ro(sb, bcpup, dentry->d_inode)) { ++ if (add_entry) ++ err = AuWbrCopyup(sbinfo, dentry); ++ else { ++ if (!IS_ROOT(dentry)) { ++ di_read_lock_parent(parent, !AuLock_IR); ++ err = AuWbrCopyup(sbinfo, dentry); ++ di_read_unlock(parent, !AuLock_IR); ++ } else ++ err = AuWbrCopyup(sbinfo, dentry); ++ } ++ bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else { ++ bcpup = args->force_btgt; ++ AuDebugOn(au_test_ro(sb, bcpup, dentry->d_inode)); ++ } ++ ++ AuDbg("bstart %d, bcpup %d\n", bstart, bcpup); ++ err = bcpup; ++ if (bcpup == bstart) ++ goto out; /* success */ ++ ++ /* copyup the new parent into the branch we process */ ++ err = au_wr_dir_cpup(dentry, parent, add_entry, bcpup, bstart); ++ if (err >= 0) { ++ if (!dentry->d_inode) { ++ au_set_h_dptr(dentry, bstart, NULL); ++ au_set_dbstart(dentry, bcpup); ++ au_set_dbend(dentry, bcpup); ++ } ++ AuDebugOn(add_entry && !au_h_dptr(dentry, bcpup)); ++ } ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin) ++{ ++ if (pin && pin->parent) ++ return au_h_dptr(pin->parent, pin->bindex); ++ return NULL; ++} ++ ++void au_unpin(struct au_pin *p) ++{ ++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) ++ mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ au_hn_imtx_unlock(p->hdir); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ iput(p->hdir->hi_inode); ++ dput(p->parent); ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = br->br_mnt; ++ err = mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ h_dentry = NULL; ++ if (p->bindex <= au_dbend(p->dentry)) ++ h_dentry = au_h_dptr(p->dentry, p->bindex); ++ ++ p->parent = dget_parent(p->dentry); ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_lock(p->parent, AuLock_IR, p->lsc_di); ++ ++ h_dir = NULL; ++ h_parent = au_h_dptr(p->parent, p->bindex); ++ p->hdir = au_hi(p->parent->d_inode, p->bindex); ++ if (p->hdir) ++ h_dir = p->hdir->hi_inode; ++ ++ /* ++ * udba case, or ++ * if DI_LOCKED is not set, then p->parent may be different ++ * and h_parent can be NULL. ++ */ ++ if (unlikely(!p->hdir || !h_dir || !h_parent)) { ++ err = -EBUSY; ++ if (!au_ftest_pin(p->flags, DI_LOCKED)) ++ di_read_unlock(p->parent, AuLock_IR); ++ dput(p->parent); ++ p->parent = NULL; ++ goto out_err; ++ } ++ ++ au_igrab(h_dir); ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) { ++ err = -EBUSY; ++ goto out_unpin; ++ } ++ if (h_dentry) { ++ err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_unpin; ++ } ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = br->br_mnt; ++ err = mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_unpin; ++ } ++ } ++ goto out; /* success */ ++ ++out_unpin: ++ au_unpin(p); ++out_err: ++ pr_err("err %d\n", err); ++ err = au_busy_or_stale(); ++out: ++ return err; ++} ++ ++void au_pin_init(struct au_pin *p, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags) ++{ ++ p->dentry = dentry; ++ p->udba = udba; ++ p->lsc_di = lsc_di; ++ p->lsc_hi = lsc_hi; ++ p->flags = flags; ++ p->bindex = bindex; ++ ++ p->parent = NULL; ++ p->hdir = NULL; ++ p->h_mnt = NULL; ++} ++ ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) ++{ ++ au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2, ++ udba, flags); ++ return au_do_pin(pin); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * ->setattr() and ->getattr() are called in various cases. ++ * chmod, stat: dentry is revalidated. ++ * fchmod, fstat: file and dentry are not revalidated, additionally they may be ++ * unhashed. ++ * for ->setattr(), ia->ia_file is passed from ftruncate only. ++ */ ++/* todo: consolidate with do_refresh() and simple_reval_dpath() */ ++static int au_reval_for_attr(struct dentry *dentry, unsigned int sigen) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *parent; ++ ++ err = 0; ++ inode = dentry->d_inode; ++ if (au_digen_test(dentry, sigen)) { ++ parent = dget_parent(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ di_read_unlock(parent, AuLock_IR); ++ dput(parent); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++#define AuIcpup_DID_CPUP 1 ++#define au_ftest_icpup(flags, name) ((flags) & AuIcpup_##name) ++#define au_fset_icpup(flags, name) \ ++ do { (flags) |= AuIcpup_##name; } while (0) ++#define au_fclr_icpup(flags, name) \ ++ do { (flags) &= ~AuIcpup_##name; } while (0) ++ ++struct au_icpup_args { ++ unsigned char flags; ++ unsigned char pin_flags; ++ aufs_bindex_t btgt; ++ unsigned int udba; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *h_inode; ++}; ++ ++static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, ++ struct au_icpup_args *a) ++{ ++ int err; ++ loff_t sz; ++ aufs_bindex_t bstart, ibstart; ++ struct dentry *hi_wh, *parent; ++ struct inode *inode; ++ struct file *h_file; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = 0 ++ }; ++ ++ bstart = au_dbstart(dentry); ++ inode = dentry->d_inode; ++ if (S_ISDIR(inode->i_mode)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ /* plink or hi_wh() case */ ++ ibstart = au_ibstart(inode); ++ if (bstart != ibstart && !au_test_ro(inode->i_sb, ibstart, inode)) ++ wr_dir_args.force_btgt = ibstart; ++ err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); ++ if (unlikely(err < 0)) ++ goto out; ++ a->btgt = err; ++ if (err != bstart) ++ au_fset_icpup(a->flags, DID_CPUP); ++ ++ err = 0; ++ a->pin_flags = AuPin_MNT_WRITE; ++ parent = NULL; ++ if (!IS_ROOT(dentry)) { ++ au_fset_pin(a->pin_flags, DI_LOCKED); ++ parent = dget_parent(dentry); ++ di_write_lock_parent(parent); ++ } ++ ++ err = au_pin(&a->pin, dentry, a->btgt, a->udba, a->pin_flags); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ a->h_path.dentry = au_h_dptr(dentry, bstart); ++ a->h_inode = a->h_path.dentry->d_inode; ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ sz = -1; ++ if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode)) ++ sz = ia->ia_size; ++ ++ h_file = NULL; ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && au_d_removed(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL); ++ if (unlikely(err)) ++ goto out_unlock; ++ hi_wh = au_hi_wh(inode, a->btgt); ++ /* todo: revalidate hi_wh? */ ++ } ++ } ++ ++ if (parent) { ++ au_pin_set_parent_lflag(&a->pin, /*lflag*/0); ++ di_downgrade_lock(parent, AuLock_IR); ++ dput(parent); ++ parent = NULL; ++ } ++ if (!au_ftest_icpup(a->flags, DID_CPUP)) ++ goto out; /* success */ ++ ++ if (!d_unhashed(dentry)) { ++ h_file = au_h_open_pre(dentry, bstart); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else ++ err = au_sio_cpup_simple(dentry, a->btgt, sz, ++ AuCpup_DTIME); ++ if (!err) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ } else if (!hi_wh) ++ a->h_path.dentry = au_h_dptr(dentry, a->btgt); ++ else ++ a->h_path.dentry = hi_wh; /* do not dget here */ ++ ++out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ au_h_open_post(dentry, bstart, h_file); ++ a->h_inode = a->h_path.dentry->d_inode; ++ if (!err) { ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ goto out; /* success */ ++ } ++ ++ au_unpin(&a->pin); ++out_parent: ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++out: ++ return err; ++} ++ ++static int aufs_setattr(struct dentry *dentry, struct iattr *ia) ++{ ++ int err; ++ struct inode *inode; ++ struct super_block *sb; ++ struct file *file; ++ struct au_icpup_args *a; ++ ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ if (ia->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) ++ ia->ia_valid &= ~ATTR_MODE; ++ ++ file = NULL; ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_kfree; ++ ++ if (ia->ia_valid & ATTR_FILE) { ++ /* currently ftruncate(2) only */ ++ AuDebugOn(!S_ISREG(inode->i_mode)); ++ file = ia->ia_file; ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out_si; ++ ia->ia_file = au_hf_top(file); ++ a->udba = AuOpt_UDBA_NONE; ++ } else { ++ /* fchmod() doesn't pass ia_file */ ++ a->udba = au_opt_udba(sb); ++ /* no au_d_removed(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ di_write_lock_child(dentry); ++ if (a->udba != AuOpt_UDBA_NONE) { ++ AuDebugOn(IS_ROOT(dentry)); ++ err = au_reval_for_attr(dentry, au_sigen(sb)); ++ if (unlikely(err)) ++ goto out_dentry; ++ } ++ } ++ ++ err = au_pin_and_icpup(dentry, ia, a); ++ if (unlikely(err < 0)) ++ goto out_dentry; ++ if (au_ftest_icpup(a->flags, DID_CPUP)) { ++ ia->ia_file = NULL; ++ ia->ia_valid &= ~ATTR_FILE; ++ } ++ ++ a->h_path.mnt = au_sbr_mnt(sb, a->btgt); ++ if ((ia->ia_valid & (ATTR_MODE | ATTR_CTIME)) ++ == (ATTR_MODE | ATTR_CTIME)) { ++ err = security_path_chmod(a->h_path.dentry, a->h_path.mnt, ++ ia->ia_mode); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) ++ && (ia->ia_valid & ATTR_CTIME)) { ++ err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ ++ if (ia->ia_valid & ATTR_SIZE) { ++ struct file *f; ++ ++ if (ia->ia_size < i_size_read(inode)) ++ /* unmap only */ ++ truncate_setsize(inode, ia->ia_size); ++ ++ f = NULL; ++ if (ia->ia_valid & ATTR_FILE) ++ f = ia->ia_file; ++ mutex_unlock(&a->h_inode->i_mutex); ++ err = vfsub_trunc(&a->h_path, ia->ia_size, ia->ia_valid, f); ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ } else ++ err = vfsub_notify_change(&a->h_path, ia); ++ if (!err) ++ au_cpup_attr_changeable(inode); ++ ++out_unlock: ++ mutex_unlock(&a->h_inode->i_mutex); ++ au_unpin(&a->pin); ++ if (unlikely(err)) ++ au_update_dbstart(dentry); ++out_dentry: ++ di_write_unlock(dentry); ++ if (file) { ++ fi_write_unlock(file); ++ ia->ia_file = file; ++ ia->ia_valid |= ATTR_FILE; ++ } ++out_si: ++ si_read_unlock(sb); ++out_kfree: ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static void au_refresh_iattr(struct inode *inode, struct kstat *st, ++ unsigned int nlink) ++{ ++ inode->i_mode = st->mode; ++ inode->i_uid = st->uid; ++ inode->i_gid = st->gid; ++ inode->i_atime = st->atime; ++ inode->i_mtime = st->mtime; ++ inode->i_ctime = st->ctime; ++ ++ au_cpup_attr_nlink(inode, /*force*/0); ++ if (S_ISDIR(inode->i_mode)) { ++ inode->i_nlink -= nlink; ++ inode->i_nlink += st->nlink; ++ } ++ ++ spin_lock(&inode->i_lock); ++ inode->i_blocks = st->blocks; ++ i_size_write(inode, st->size); ++ spin_unlock(&inode->i_lock); ++} ++ ++static int aufs_getattr(struct vfsmount *mnt __maybe_unused, ++ struct dentry *dentry, struct kstat *st) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex; ++ unsigned char udba_none, positive; ++ struct super_block *sb, *h_sb; ++ struct inode *inode; ++ struct vfsmount *h_mnt; ++ struct dentry *h_dentry; ++ ++ sb = dentry->d_sb; ++ inode = dentry->d_inode; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out; ++ mnt_flags = au_mntflags(sb); ++ udba_none = !!au_opt_test(mnt_flags, UDBA_NONE); ++ ++ /* support fstat(2) */ ++ if (!au_d_removed(dentry) && !udba_none) { ++ unsigned int sigen = au_sigen(sb); ++ err = au_digen_test(dentry, sigen); ++ if (!err) { ++ di_read_lock_child(dentry, AuLock_IR); ++ err = au_dbrange_test(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else { ++ AuDebugOn(IS_ROOT(dentry)); ++ di_write_lock_child(dentry); ++ err = au_dbrange_test(dentry); ++ if (!err) ++ err = au_reval_for_attr(dentry, sigen); ++ di_downgrade_lock(dentry, AuLock_IR); ++ if (unlikely(err)) ++ goto out_unlock; ++ } ++ } else ++ di_read_lock_child(dentry, AuLock_IR); ++ ++ bindex = au_ibstart(inode); ++ h_mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_mnt->mnt_sb; ++ if (!au_test_fs_bad_iattr(h_sb) && udba_none) ++ goto out_fill; /* success */ ++ ++ h_dentry = NULL; ++ if (au_dbstart(dentry) == bindex) ++ h_dentry = dget(au_h_dptr(dentry, bindex)); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_dentry)) ++ goto out_fill; /* pretending success */ ++ } ++ /* illegally overlapped or something */ ++ if (unlikely(!h_dentry)) ++ goto out_fill; /* pretending success */ ++ ++ positive = !!h_dentry->d_inode; ++ if (positive) ++ err = vfs_getattr(h_mnt, h_dentry, st); ++ dput(h_dentry); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, h_dentry->d_inode->i_nlink); ++ goto out_fill; /* success */ ++ } ++ AuTraceErr(err); ++ goto out_unlock; ++ ++out_fill: ++ generic_fillattr(inode, st); ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ si_read_unlock(sb); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int h_readlink(struct dentry *dentry, int bindex, char __user *buf, ++ int bufsiz) ++{ ++ int err; ++ struct super_block *sb; ++ struct dentry *h_dentry; ++ ++ err = -EINVAL; ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (unlikely(!h_dentry->d_inode->i_op->readlink)) ++ goto out; ++ ++ err = security_inode_readlink(h_dentry); ++ if (unlikely(err)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ if (!au_test_ro(sb, bindex, dentry->d_inode)) { ++ vfsub_touch_atime(au_sbr_mnt(sb, bindex), h_dentry); ++ fsstack_copy_attr_atime(dentry->d_inode, h_dentry->d_inode); ++ } ++ err = h_dentry->d_inode->i_op->readlink(h_dentry, buf, bufsiz); ++ ++out: ++ return err; ++} ++ ++static int aufs_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ++{ ++ int err; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ err = au_d_hashed_positive(dentry); ++ if (!err) ++ err = h_readlink(dentry, au_dbstart(dentry), buf, bufsiz); ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++out: ++ return err; ++} ++ ++static void *aufs_follow_link(struct dentry *dentry, struct nameidata *nd) ++{ ++ int err; ++ mm_segment_t old_fs; ++ union { ++ char *k; ++ char __user *u; ++ } buf; ++ ++ err = -ENOMEM; ++ buf.k = __getname_gfp(GFP_NOFS); ++ if (unlikely(!buf.k)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_IR | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_name; ++ ++ err = au_d_hashed_positive(dentry); ++ if (!err) { ++ old_fs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = h_readlink(dentry, au_dbstart(dentry), buf.u, PATH_MAX); ++ set_fs(old_fs); ++ } ++ aufs_read_unlock(dentry, AuLock_IR); ++ ++ if (err >= 0) { ++ buf.k[err] = 0; ++ /* will be freed by put_link */ ++ nd_set_link(nd, buf.k); ++ return NULL; /* success */ ++ } ++ ++out_name: ++ __putname(buf.k); ++out: ++ path_put(&nd->path); ++ AuTraceErr(err); ++ return ERR_PTR(err); ++} ++ ++static void aufs_put_link(struct dentry *dentry __maybe_unused, ++ struct nameidata *nd, void *cookie __maybe_unused) ++{ ++ __putname(nd_get_link(nd)); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void aufs_truncate_range(struct inode *inode __maybe_unused, ++ loff_t start __maybe_unused, ++ loff_t end __maybe_unused) ++{ ++ AuUnsupport(); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct inode_operations aufs_symlink_iop = { ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ .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, ++ .getattr = aufs_getattr ++}; ++ ++struct inode_operations aufs_iop = { ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ .truncate_range = aufs_truncate_range ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/i_op_add.c linux-2.6.36/fs/aufs/i_op_add.c +--- linux-2.6.36.orig/fs/aufs/i_op_add.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/i_op_add.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,702 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode operations (add entry) ++ */ ++ ++#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, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct dentry *dentry) ++{ ++ int err, rerr; ++ aufs_bindex_t bwh; ++ struct path h_path; ++ struct inode *inode, *h_dir; ++ struct dentry *wh; ++ ++ bwh = -1; ++ if (wh_dentry) { ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ AuDebugOn(au_h_iptr(dir, bindex) != h_dir); ++ bwh = au_dbwh(dentry); ++ h_path.dentry = wh_dentry; ++ h_path.mnt = au_sbr_mnt(dir->i_sb, bindex); ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ inode = au_new_inode(dentry, /*must_new*/1); ++ if (!IS_ERR(inode)) { ++ d_instantiate(dentry, inode); ++ dir = dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(dir); ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ dir->i_version++; ++ return 0; /* success */ ++ } ++ ++ err = PTR_ERR(inode); ++ if (!wh_dentry) ++ goto out; ++ ++ /* revert */ ++ /* dir inode is locked */ ++ wh = au_wh_create(dentry, bwh, wh_dentry->d_parent); ++ rerr = PTR_ERR(wh); ++ if (IS_ERR(wh)) { ++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } else ++ dput(wh); ++ ++out: ++ return err; ++} ++ ++static int au_d_may_add(struct dentry *dentry) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(d_unhashed(dentry))) ++ err = -ENOENT; ++ if (unlikely(dentry->d_inode)) ++ err = -EEXIST; ++ return err; ++} ++ ++/* ++ * simple tests for the adding inode operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ ++ err = -ENAMETOOLONG; ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (!dentry->d_inode) { ++ err = -EEXIST; ++ if (unlikely(h_inode)) ++ goto out; ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } ++ ++ err = 0; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ err = -EIO; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * initial procedure of adding a new entry. ++ * prepare writable branch and the parent dir, lock it, ++ * and 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_pin *pin, ++ struct au_wr_dir_args *wr_dir_args) ++{ ++ struct dentry *wh_dentry, *h_parent; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ AuDbg("%.*s\n", AuDLNPair(dentry)); ++ ++ err = au_wr_dir(dentry, src_dentry, wr_dir_args); ++ bcpup = err; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_parent = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) ++ err = au_may_add(dentry, bcpup, h_parent, ++ au_ftest_wrdir(wr_dir_args->flags, ISDIR)); ++ else if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ br = au_sbr(sb, bcpup); ++ if (dt) { ++ struct path tmp = { ++ .dentry = h_parent, ++ .mnt = br->br_mnt ++ }; ++ au_dtime_store(dt, au_pinned_parent(pin), &tmp); ++ } ++ ++ wh_dentry = NULL; ++ if (bcpup != au_dbwh(dentry)) ++ goto out; /* success */ ++ ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ ++out_unpin: ++ if (IS_ERR(wh_dentry)) ++ au_unpin(pin); ++out: ++ 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; ++ aufs_bindex_t bstart; ++ unsigned char created; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent; ++ struct inode *h_dir; ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ AuDbg("%.*s\n", AuDLNPair(dentry)); ++ IMustLock(dir); ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bstart); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ h_dir = au_pinned_h_dir(&pin); ++ switch (arg->type) { ++ case Creat: ++ err = vfsub_create(h_dir, &h_path, arg->u.c.mode); ++ break; ++ case Symlink: ++ err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname); ++ break; ++ case Mknod: ++ err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev); ++ break; ++ default: ++ BUG(); ++ } ++ created = !err; ++ if (!err) ++ err = epilog(dir, bstart, wh_dentry, dentry); ++ ++ /* revert */ ++ if (unlikely(created && err && h_path.dentry->d_inode)) { ++ int rerr; ++ rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ if (rerr) { ++ AuIOErr("%.*s revert failure(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++ } ++ ++ au_unpin(&pin); ++ dput(wh_dentry); ++ ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_unlock(dentry, AuLock_DW); ++out: ++ 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 au_link_args { ++ aufs_bindex_t bdst, bsrc; ++ struct au_pin pin; ++ struct path h_path; ++ struct dentry *src_parent, *parent; ++}; ++ ++static int au_cpup_before_link(struct dentry *src_dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ struct dentry *h_src_dentry; ++ struct mutex *h_mtx; ++ struct file *h_file; ++ ++ di_read_lock_parent(a->src_parent, AuLock_IR); ++ err = au_test_and_cpup_dirs(src_dentry, a->bdst); ++ if (unlikely(err)) ++ goto out; ++ ++ h_src_dentry = au_h_dptr(src_dentry, a->bsrc); ++ h_mtx = &h_src_dentry->d_inode->i_mutex; ++ err = au_pin(&a->pin, src_dentry, a->bdst, ++ au_opt_udba(src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ h_file = au_h_open_pre(src_dentry, a->bsrc); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else ++ err = au_sio_cpup_simple(src_dentry, a->bdst, a->bsrc, ++ AuCpup_DTIME /* | AuCpup_KEEPLINO */); ++ mutex_unlock(h_mtx); ++ au_h_open_post(src_dentry, a->bsrc, h_file); ++ au_unpin(&a->pin); ++ ++out: ++ di_read_unlock(a->src_parent, AuLock_IR); ++ return err; ++} ++ ++static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ struct inode *h_inode, *inode; ++ struct dentry *h_src_dentry; ++ struct super_block *sb; ++ struct file *h_file; ++ ++ plink = 0; ++ h_inode = NULL; ++ sb = src_dentry->d_sb; ++ inode = src_dentry->d_inode; ++ if (au_ibstart(inode) <= a->bdst) ++ h_inode = au_h_iptr(inode, a->bdst); ++ if (!h_inode || !h_inode->i_nlink) { ++ /* copyup src_dentry as the name of dentry. */ ++ au_set_dbstart(src_dentry, a->bdst); ++ au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry)); ++ h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ h_file = au_h_open_pre(src_dentry, a->bsrc); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else ++ err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, ++ -1, AuCpup_KEEPLINO, ++ a->parent); ++ mutex_unlock(&h_inode->i_mutex); ++ au_h_open_post(src_dentry, a->bsrc, h_file); ++ au_set_h_dptr(src_dentry, a->bdst, NULL); ++ au_set_dbstart(src_dentry, a->bsrc); ++ } else { ++ /* the inode of src_dentry already exists on a.bdst branch */ ++ h_src_dentry = d_find_alias(h_inode); ++ if (!h_src_dentry && au_plink_test(inode)) { ++ plink = 1; ++ h_src_dentry = au_plink_lkup(inode, a->bdst); ++ err = PTR_ERR(h_src_dentry); ++ if (IS_ERR(h_src_dentry)) ++ goto out; ++ ++ if (unlikely(!h_src_dentry->d_inode)) { ++ dput(h_src_dentry); ++ h_src_dentry = NULL; ++ } ++ ++ } ++ if (h_src_dentry) { ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ dput(h_src_dentry); ++ } else { ++ AuIOErr("no dentry found for hi%lu on b%d\n", ++ h_inode->i_ino, a->bdst); ++ err = -EIO; ++ } ++ } ++ ++ if (!err && !plink) ++ au_plink_append(inode, a->bdst, a->h_path.dentry); ++ ++out: ++ return err; ++} ++ ++int aufs_link(struct dentry *src_dentry, struct inode *dir, ++ struct dentry *dentry) ++{ ++ int err, rerr; ++ struct au_dtime dt; ++ struct au_link_args *a; ++ struct dentry *wh_dentry, *h_src_dentry; ++ struct inode *inode; ++ struct super_block *sb; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ IMustLock(dir); ++ inode = src_dentry->d_inode; ++ IMustLock(inode); ++ ++ err = -ENOMEM; ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->parent = dentry->d_parent; /* dir inode is locked */ ++ err = aufs_read_and_write_lock2(dentry, src_dentry, ++ AuLock_NOPLM | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_kfree; ++ err = au_d_hashed_positive(src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ a->src_parent = dget_parent(src_dentry); ++ wr_dir_args.force_btgt = au_dbstart(src_dentry); ++ ++ di_write_lock_parent(a->parent); ++ wr_dir_args.force_btgt = au_wbr(dentry, wr_dir_args.force_btgt); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &dt, src_dentry, &a->pin, ++ &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ err = 0; ++ sb = dentry->d_sb; ++ a->bdst = au_dbstart(dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ a->h_path.mnt = au_sbr_mnt(sb, a->bdst); ++ a->bsrc = au_dbstart(src_dentry); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) ++ err = au_cpup_or_link(src_dentry, a); ++ else { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ } ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ if (a->bdst < a->bsrc ++ /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) { ++ au_unpin(&a->pin); ++ di_write_unlock(a->parent); ++ err = au_cpup_before_link(src_dentry, a); ++ di_write_lock_parent(a->parent); ++ if (!err) ++ err = au_pin(&a->pin, dentry, a->bdst, ++ au_opt_udba(sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_wh; ++ } ++ if (!err) { ++ h_src_dentry = au_h_dptr(src_dentry, a->bdst); ++ err = -ENOENT; ++ if (h_src_dentry && h_src_dentry->d_inode) ++ err = vfsub_link(h_src_dentry, ++ au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ } ++ } ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ if (wh_dentry) { ++ a->h_path.dentry = wh_dentry; ++ err = au_wh_unlink_dentry(au_pinned_h_dir(&a->pin), &a->h_path, ++ dentry); ++ if (unlikely(err)) ++ goto out_revert; ++ } ++ ++ dir->i_version++; ++ if (au_ibstart(dir) == au_dbstart(dentry)) ++ au_cpup_attr_timesizes(dir); ++ inc_nlink(inode); ++ inode->i_ctime = dir->i_ctime; ++ d_instantiate(dentry, au_igrab(inode)); ++ if (d_unhashed(a->h_path.dentry)) ++ /* some filesystem calls d_drop() */ ++ d_drop(dentry); ++ goto out_unpin; /* success */ ++ ++out_revert: ++ rerr = vfsub_unlink(au_pinned_h_dir(&a->pin), &a->h_path, /*force*/0); ++ if (unlikely(rerr)) { ++ AuIOErr("%.*s reverting failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&dt); ++out_unpin: ++ au_unpin(&a->pin); ++out_wh: ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(a->parent); ++ dput(a->src_parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_and_write_unlock2(dentry, src_dentry); ++out_kfree: ++ kfree(a); ++out: ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ int err, rerr; ++ aufs_bindex_t bindex; ++ unsigned char diropq; ++ struct path h_path; ++ struct dentry *wh_dentry, *parent, *opq_dentry; ++ struct mutex *h_mtx; ++ struct super_block *sb; ++ struct { ++ struct au_pin pin; ++ struct au_dtime dt; ++ } *a; /* reduce the stack usage */ ++ struct au_wr_dir_args wr_dir_args = { ++ .force_btgt = -1, ++ .flags = AuWrDir_ADD_ENTRY | AuWrDir_ISDIR ++ }; ++ ++ IMustLock(dir); ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out_free; ++ err = au_d_may_add(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, ++ &a->pin, &wr_dir_args); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ sb = dentry->d_sb; ++ bindex = au_dbstart(dentry); ++ h_path.dentry = au_h_dptr(dentry, bindex); ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ err = vfsub_mkdir(au_pinned_h_dir(&a->pin), &h_path, mode); ++ if (unlikely(err)) ++ goto out_unpin; ++ ++ /* make the dir opaque */ ++ diropq = 0; ++ h_mtx = &h_path.dentry->d_inode->i_mutex; ++ if (wh_dentry ++ || au_opt_test(au_mntflags(sb), ALWAYS_DIROPQ)) { ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ opq_dentry = au_diropq_create(dentry, bindex); ++ mutex_unlock(h_mtx); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out_dir; ++ dput(opq_dentry); ++ diropq = 1; ++ } ++ ++ err = epilog(dir, bindex, wh_dentry, dentry); ++ if (!err) { ++ inc_nlink(dir); ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (diropq) { ++ AuLabel(revert opq); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bindex); ++ mutex_unlock(h_mtx); ++ if (rerr) { ++ AuIOErr("%.*s reverting diropq failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ } ++ ++out_dir: ++ AuLabel(revert dir); ++ rerr = vfsub_rmdir(au_pinned_h_dir(&a->pin), &h_path); ++ if (rerr) { ++ AuIOErr("%.*s reverting dir failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ err = -EIO; ++ } ++ au_dtime_revert(&a->dt); ++out_unpin: ++ au_unpin(&a->pin); ++ dput(wh_dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ if (unlikely(err)) { ++ au_update_dbstart(dentry); ++ d_drop(dentry); ++ } ++ aufs_read_unlock(dentry, AuLock_DW); ++out_free: ++ kfree(a); ++out: ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/i_op_del.c linux-2.6.36/fs/aufs/i_op_del.c +--- linux-2.6.36.orig/fs/aufs/i_op_del.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/i_op_del.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,481 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode operations (del entry) ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * decide if a new whiteout for @dentry is necessary or not. ++ * when it is necessary, prepare the parent dir for the upper branch whose ++ * branch index is @bcpup for creation. the actual creation of the whiteout will ++ * be done by caller. ++ * return value: ++ * 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) ++{ ++ int need_wh, err; ++ aufs_bindex_t bstart; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ if (*bcpup < 0) { ++ *bcpup = bstart; ++ if (au_test_ro(sb, bstart, dentry->d_inode)) { ++ err = AuWbrCopyup(au_sbi(sb), dentry); ++ *bcpup = err; ++ if (unlikely(err < 0)) ++ goto out; ++ } ++ } else ++ AuDebugOn(bstart < *bcpup ++ || au_test_ro(sb, *bcpup, dentry->d_inode)); ++ AuDbg("bcpup %d, bstart %d\n", *bcpup, bstart); ++ ++ if (*bcpup != bstart) { ++ err = au_cpup_dirs(dentry, *bcpup); ++ if (unlikely(err)) ++ goto out; ++ need_wh = 1; ++ } else { ++ struct au_dinfo *dinfo, *tmp; ++ ++ need_wh = -ENOMEM; ++ dinfo = au_di(dentry); ++ tmp = au_di_alloc(sb, AuLsc_DI_TMP); ++ if (tmp) { ++ au_di_cp(tmp, dinfo); ++ au_di_swap(tmp, dinfo); ++ /* returns the number of positive dentries */ ++ need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, ++ /*nd*/NULL); ++ au_di_swap(tmp, dinfo); ++ au_rw_write_unlock(&tmp->di_rwsem); ++ au_di_free(tmp); ++ } ++ } ++ AuDbg("need_wh %d\n", need_wh); ++ err = need_wh; ++ ++out: ++ return err; ++} ++ ++/* ++ * simple tests for the del-entry operations. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir) ++{ ++ int err; ++ umode_t h_mode; ++ struct dentry *h_dentry, *h_latest; ++ struct inode *h_inode; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ h_inode = h_dentry->d_inode; ++ if (dentry->d_inode) { ++ err = -ENOENT; ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ ++ h_mode = h_inode->i_mode; ++ if (!isdir) { ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(h_mode))) ++ goto out; ++ } else if (unlikely(!S_ISDIR(h_mode))) { ++ err = -ENOTDIR; ++ goto out; ++ } ++ } else { ++ /* rename(2) case */ ++ err = -EIO; ++ if (unlikely(h_inode)) ++ goto out; ++ } ++ ++ err = -ENOENT; ++ /* expected parent dir is locked */ ++ if (unlikely(h_parent != h_dentry->d_parent)) ++ goto out; ++ err = 0; ++ ++ /* ++ * rmdir a dir may break the consistency on some filesystem. ++ * let's try heavy test. ++ */ ++ err = -EACCES; ++ if (unlikely(au_test_h_perm(h_parent->d_inode, MAY_EXEC | MAY_WRITE))) ++ goto out; ++ ++ h_latest = au_sio_lkup_one(&dentry->d_name, h_parent, ++ au_sbr(dentry->d_sb, bindex)); ++ err = -EIO; ++ if (IS_ERR(h_latest)) ++ goto out; ++ if (h_latest == h_dentry) ++ err = 0; ++ dput(h_latest); ++ ++out: ++ return err; ++} ++ ++/* ++ * decide the branch where we operate for @dentry. the branch index will be set ++ * @rbcpup. after diciding it, 'pin' it and store the timestamps of the parent ++ * dir for reverting. ++ * when a new whiteout is necessary, create it. ++ */ ++static struct dentry* ++lock_hdir_create_wh(struct dentry *dentry, int isdir, aufs_bindex_t *rbcpup, ++ struct au_dtime *dt, struct au_pin *pin) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ struct path h_path; ++ int err, need_wh; ++ unsigned int udba; ++ aufs_bindex_t bcpup; ++ ++ need_wh = au_wr_dir_need_wh(dentry, isdir, rbcpup); ++ wh_dentry = ERR_PTR(need_wh); ++ if (unlikely(need_wh < 0)) ++ goto out; ++ ++ sb = dentry->d_sb; ++ udba = au_opt_udba(sb); ++ bcpup = *rbcpup; ++ err = au_pin(pin, dentry, bcpup, udba, ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path.dentry = au_pinned_h_parent(pin); ++ if (udba != AuOpt_UDBA_NONE ++ && au_dbstart(dentry) == bcpup) { ++ err = au_may_del(dentry, bcpup, h_path.dentry, isdir); ++ wh_dentry = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_unpin; ++ } ++ ++ h_path.mnt = au_sbr_mnt(sb, bcpup); ++ au_dtime_store(dt, au_pinned_parent(pin), &h_path); ++ wh_dentry = NULL; ++ if (!need_wh) ++ goto out; /* success, no need to create whiteout */ ++ ++ wh_dentry = au_wh_create(dentry, bcpup, h_path.dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_unpin; ++ ++ /* returns with the parent is locked and wh_dentry is dget-ed */ ++ goto out; /* success */ ++ ++out_unpin: ++ au_unpin(pin); ++out: ++ return wh_dentry; ++} ++ ++/* ++ * when removing a dir, rename it to a unique temporary whiteout-ed name first ++ * in order to be revertible and save time for removing many child whiteouts ++ * under the dir. ++ * returns 1 when there are too many child whiteout and caller should remove ++ * them asynchronously. returns 0 when the number of children is enough small to ++ * remove now or the branch fs is a remote fs. ++ * otherwise return an error. ++ */ ++static int renwh_and_rmdir(struct dentry *dentry, aufs_bindex_t bindex, ++ struct au_nhash *whlist, struct inode *dir) ++{ ++ int rmdir_later, err, dirwh; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ SiMustAnyLock(sb); ++ h_dentry = au_h_dptr(dentry, bindex); ++ err = au_whtmp_ren(h_dentry, au_sbr(sb, bindex)); ++ if (unlikely(err)) ++ goto out; ++ ++ /* stop monitoring */ ++ au_hn_free(au_hi(dentry->d_inode, bindex)); ++ ++ if (!au_test_fs_remote(h_dentry->d_sb)) { ++ dirwh = au_sbi(sb)->si_dirwh; ++ rmdir_later = (dirwh <= 1); ++ if (!rmdir_later) ++ rmdir_later = au_nhash_test_longer_wh(whlist, bindex, ++ dirwh); ++ if (rmdir_later) ++ return rmdir_later; ++ } ++ ++ err = au_whtmp_rmdir(dir, bindex, h_dentry, whlist); ++ if (unlikely(err)) { ++ AuIOErr("rmdir %.*s, b%d failed, %d. ignored\n", ++ AuDLNPair(h_dentry), bindex, err); ++ err = 0; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ++ * final procedure for deleting a entry. ++ * maintain dentry and iattr. ++ */ ++static void epilog(struct inode *dir, struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ struct inode *inode; ++ ++ inode = dentry->d_inode; ++ d_drop(dentry); ++ inode->i_ctime = dir->i_ctime; ++ ++ if (au_ibstart(dir) == bindex) ++ au_cpup_attr_timesizes(dir); ++ dir->i_version++; ++} ++ ++/* ++ * when an error happened, remove the created whiteout and revert everything. ++ */ ++static int do_revert(int err, struct inode *dir, aufs_bindex_t bindex, ++ aufs_bindex_t bwh, struct dentry *wh_dentry, ++ struct dentry *dentry, struct au_dtime *dt) ++{ ++ int rerr; ++ struct path h_path = { ++ .dentry = wh_dentry, ++ .mnt = au_sbr_mnt(dir->i_sb, bindex) ++ }; ++ ++ rerr = au_wh_unlink_dentry(au_h_iptr(dir, bindex), &h_path, dentry); ++ if (!rerr) { ++ au_set_dbwh(dentry, bwh); ++ au_dtime_revert(dt); ++ return 0; ++ } ++ ++ AuIOErr("%.*s reverting whiteout failed(%d, %d)\n", ++ AuDLNPair(dentry), err, rerr); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct path h_path; ++ struct inode *inode, *h_dir; ++ struct dentry *parent, *wh_dentry; ++ ++ IMustLock(dir); ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ err = au_d_hashed_positive(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = dentry->d_inode; ++ IMustLock(inode); ++ err = -EISDIR; ++ if (unlikely(S_ISDIR(inode->i_mode))) ++ goto out_unlock; /* possible? */ ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); ++ h_path.dentry = au_h_dptr(dentry, bstart); ++ dget(h_path.dentry); ++ if (bindex == bstart) { ++ h_dir = au_pinned_h_dir(&pin); ++ err = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ } else { ++ /* dir inode is locked */ ++ h_dir = wh_dentry->d_parent->d_inode; ++ IMustLock(h_dir); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_drop_nlink(inode); ++ epilog(dir, dentry, bindex); ++ ++ /* update target timestamps */ ++ if (bindex == bstart) { ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++ inode->i_ctime = h_path.dentry->d_inode->i_ctime; ++ } else ++ /* todo: this timestamp may be reverted later */ ++ inode->i_ctime = h_dir->i_ctime; ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&pin); ++ dput(wh_dentry); ++ dput(h_path.dentry); ++out_parent: ++ di_write_unlock(parent); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out: ++ return err; ++} ++ ++int aufs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int err, rmdir_later; ++ aufs_bindex_t bwh, bindex, bstart; ++ struct au_dtime dt; ++ struct au_pin pin; ++ struct inode *inode; ++ struct dentry *parent, *wh_dentry, *h_dentry; ++ struct au_whtmp_rmdir *args; ++ ++ IMustLock(dir); ++ ++ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); ++ if (unlikely(err)) ++ goto out; ++ ++ /* VFS already unhashes it */ ++ inode = dentry->d_inode; ++ err = -ENOENT; ++ if (unlikely(!inode || !inode->i_nlink ++ || IS_DEADDIR(inode))) ++ goto out_unlock; ++ IMustLock(inode); ++ err = -ENOTDIR; ++ if (unlikely(!S_ISDIR(inode->i_mode))) ++ goto out_unlock; /* possible? */ ++ ++ err = -ENOMEM; ++ args = au_whtmp_rmdir_alloc(dir->i_sb, GFP_NOFS); ++ if (unlikely(!args)) ++ goto out_unlock; ++ ++ parent = dentry->d_parent; /* dir inode is locked */ ++ di_write_lock_parent(parent); ++ err = au_test_empty(dentry, &args->whlist); ++ if (unlikely(err)) ++ goto out_parent; ++ ++ bstart = au_dbstart(dentry); ++ bwh = au_dbwh(dentry); ++ bindex = -1; ++ wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); ++ err = PTR_ERR(wh_dentry); ++ if (IS_ERR(wh_dentry)) ++ goto out_parent; ++ ++ h_dentry = au_h_dptr(dentry, bstart); ++ dget(h_dentry); ++ rmdir_later = 0; ++ if (bindex == bstart) { ++ err = renwh_and_rmdir(dentry, bstart, &args->whlist, dir); ++ if (err > 0) { ++ rmdir_later = err; ++ err = 0; ++ } ++ } else { ++ /* stop monitoring */ ++ au_hn_free(au_hi(inode, bstart)); ++ ++ /* dir inode is locked */ ++ IMustLock(wh_dentry->d_parent->d_inode); ++ err = 0; ++ } ++ ++ if (!err) { ++ vfsub_dead_dir(inode); ++ au_set_dbdiropq(dentry, -1); ++ epilog(dir, dentry, bindex); ++ ++ if (rmdir_later) { ++ au_whtmp_kick_rmdir(dir, bstart, h_dentry, args); ++ args = NULL; ++ } ++ ++ goto out_unpin; /* success */ ++ } ++ ++ /* revert */ ++ AuLabel(revert); ++ if (wh_dentry) { ++ int rerr; ++ ++ rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); ++ if (rerr) ++ err = rerr; ++ } ++ ++out_unpin: ++ au_unpin(&pin); ++ dput(wh_dentry); ++ dput(h_dentry); ++out_parent: ++ di_write_unlock(parent); ++ if (args) ++ au_whtmp_rmdir_free(args); ++out_unlock: ++ aufs_read_unlock(dentry, AuLock_DW); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/i_op_ren.c linux-2.6.36/fs/aufs/i_op_ren.c +--- linux-2.6.36.orig/fs/aufs/i_op_ren.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/i_op_ren.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1017 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode operation (rename entry) ++ * todo: this is crazy monster ++ */ ++ ++#include "aufs.h" ++ ++enum { AuSRC, AuDST, AuSrcDst }; ++enum { AuPARENT, AuCHILD, AuParentChild }; ++ ++#define AuRen_ISDIR 1 ++#define AuRen_ISSAMEDIR (1 << 1) ++#define AuRen_WHSRC (1 << 2) ++#define AuRen_WHDST (1 << 3) ++#define AuRen_MNT_WRITE (1 << 4) ++#define AuRen_DT_DSTDIR (1 << 5) ++#define AuRen_DIROPQ (1 << 6) ++#define AuRen_CPUP (1 << 7) ++#define au_ftest_ren(flags, name) ((flags) & AuRen_##name) ++#define au_fset_ren(flags, name) \ ++ do { (flags) |= AuRen_##name; } while (0) ++#define au_fclr_ren(flags, name) \ ++ do { (flags) &= ~AuRen_##name; } while (0) ++ ++struct au_ren_args { ++ struct { ++ struct dentry *dentry, *h_dentry, *parent, *h_parent, ++ *wh_dentry; ++ struct inode *dir, *inode; ++ struct au_hinode *hdir; ++ struct au_dtime dt[AuParentChild]; ++ aufs_bindex_t bstart; ++ } sd[AuSrcDst]; ++ ++#define src_dentry sd[AuSRC].dentry ++#define src_dir sd[AuSRC].dir ++#define src_inode sd[AuSRC].inode ++#define src_h_dentry sd[AuSRC].h_dentry ++#define src_parent sd[AuSRC].parent ++#define src_h_parent sd[AuSRC].h_parent ++#define src_wh_dentry sd[AuSRC].wh_dentry ++#define src_hdir sd[AuSRC].hdir ++#define src_h_dir sd[AuSRC].hdir->hi_inode ++#define src_dt sd[AuSRC].dt ++#define src_bstart sd[AuSRC].bstart ++ ++#define dst_dentry sd[AuDST].dentry ++#define dst_dir sd[AuDST].dir ++#define dst_inode sd[AuDST].inode ++#define dst_h_dentry sd[AuDST].h_dentry ++#define dst_parent sd[AuDST].parent ++#define dst_h_parent sd[AuDST].h_parent ++#define dst_wh_dentry sd[AuDST].wh_dentry ++#define dst_hdir sd[AuDST].hdir ++#define dst_h_dir sd[AuDST].hdir->hi_inode ++#define dst_dt sd[AuDST].dt ++#define dst_bstart sd[AuDST].bstart ++ ++ struct dentry *h_trap; ++ struct au_branch *br; ++ struct au_hinode *src_hinode; ++ struct path h_path; ++ struct au_nhash whlist; ++ aufs_bindex_t btgt, src_bwh, src_bdiropq; ++ ++ unsigned int flags; ++ ++ struct au_whtmp_rmdir *thargs; ++ struct dentry *h_dst; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * functions for reverting. ++ * when an error happened in a single rename systemcall, we should revert ++ * everything as if nothing happend. ++ * we don't need to revert the copied-up/down the parent dir since they are ++ * harmless. ++ */ ++ ++#define RevertFailure(fmt, ...) do { \ ++ AuIOErr("revert failure: " fmt " (%d, %d)\n", \ ++ ##__VA_ARGS__, err, rerr); \ ++ err = -EIO; \ ++} while (0) ++ ++static void au_ren_rev_diropq(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ au_set_dbdiropq(a->src_dentry, a->src_bdiropq); ++ if (rerr) ++ RevertFailure("remove diropq %.*s", AuDLNPair(a->src_dentry)); ++} ++ ++static void au_ren_rev_rename(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = au_lkup_one(&a->src_dentry->d_name, a->src_h_parent, ++ a->br, /*nd*/NULL); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("au_lkup_one %.*s", AuDLNPair(a->src_dentry)); ++ return; ++ } ++ ++ rerr = vfsub_rename(a->dst_h_dir, ++ au_h_dptr(a->src_dentry, a->btgt), ++ a->src_h_dir, &a->h_path); ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ /* au_set_h_dptr(a->src_dentry, a->btgt, NULL); */ ++ if (rerr) ++ RevertFailure("rename %.*s", AuDLNPair(a->src_dentry)); ++} ++ ++static void au_ren_rev_cpup(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->dst_h_dentry; ++ rerr = vfsub_unlink(a->dst_h_dir, &a->h_path, /*force*/0); ++ au_set_h_dptr(a->src_dentry, a->btgt, NULL); ++ au_set_dbstart(a->src_dentry, a->src_bstart); ++ if (rerr) ++ RevertFailure("unlink %.*s", AuDLNPair(a->dst_h_dentry)); ++} ++ ++static void au_ren_rev_whtmp(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = au_lkup_one(&a->dst_dentry->d_name, a->dst_h_parent, ++ a->br, /*nd*/NULL); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lookup %.*s", AuDLNPair(a->dst_dentry)); ++ return; ++ } ++ if (a->h_path.dentry->d_inode) { ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ return; ++ } ++ ++ rerr = vfsub_rename(a->dst_h_dir, a->h_dst, a->dst_h_dir, &a->h_path); ++ d_drop(a->h_path.dentry); ++ dput(a->h_path.dentry); ++ if (!rerr) ++ au_set_h_dptr(a->dst_dentry, a->btgt, dget(a->h_dst)); ++ else ++ RevertFailure("rename %.*s", AuDLNPair(a->h_dst)); ++} ++ ++static void au_ren_rev_whsrc(int err, struct au_ren_args *a) ++{ ++ int rerr; ++ ++ a->h_path.dentry = a->src_wh_dentry; ++ rerr = au_wh_unlink_dentry(a->src_h_dir, &a->h_path, a->src_dentry); ++ au_set_dbwh(a->src_dentry, a->src_bwh); ++ if (rerr) ++ RevertFailure("unlink %.*s", AuDLNPair(a->src_wh_dentry)); ++} ++#undef RevertFailure ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * when we have to copyup the renaming entry, do it with the rename-target name ++ * in order to minimize the cost (the later actual rename is unnecessary). ++ * otherwise rename it on the target branch. ++ */ ++static int au_ren_or_cpup(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d; ++ ++ d = a->src_dentry; ++ if (au_dbstart(d) == a->btgt) { ++ a->h_path.dentry = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, DIROPQ) ++ && au_dbdiropq(d) == a->btgt) ++ au_fclr_ren(a->flags, DIROPQ); ++ AuDebugOn(au_dbstart(d) != a->btgt); ++ err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), ++ a->dst_h_dir, &a->h_path); ++ } else { ++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++ struct file *h_file; ++ ++ au_fset_ren(a->flags, CPUP); ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_set_dbstart(d, a->btgt); ++ au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); ++ h_file = au_h_open_pre(d, a->src_bstart); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else ++ err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, ++ !AuCpup_DTIME, a->dst_parent); ++ mutex_unlock(h_mtx); ++ au_h_open_post(d, a->src_bstart, h_file); ++ if (!err) { ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ au_update_dbstart(d); ++ } else { ++ au_set_h_dptr(d, a->btgt, NULL); ++ au_set_dbstart(d, a->src_bstart); ++ } ++ } ++ if (!err && a->h_dst) ++ /* it will be set to dinfo later */ ++ dget(a->h_dst); ++ ++ return err; ++} ++ ++/* cf. aufs_rmdir() */ ++static int au_ren_del_whtmp(struct au_ren_args *a) ++{ ++ int err; ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ SiMustAnyLock(dir->i_sb); ++ if (!au_nhash_test_longer_wh(&a->whlist, a->btgt, ++ au_sbi(dir->i_sb)->si_dirwh) ++ || au_test_fs_remote(a->h_dst->d_sb)) { ++ err = au_whtmp_rmdir(dir, a->btgt, a->h_dst, &a->whlist); ++ if (unlikely(err)) ++ pr_warning("failed removing whtmp dir %.*s (%d), " ++ "ignored.\n", AuDLNPair(a->h_dst), err); ++ } else { ++ au_nhash_wh_free(&a->thargs->whlist); ++ a->thargs->whlist = a->whlist; ++ a->whlist.nh_num = 0; ++ au_whtmp_kick_rmdir(dir, a->btgt, a->h_dst, a->thargs); ++ dput(a->h_dst); ++ a->thargs = NULL; ++ } ++ ++ return 0; ++} ++ ++/* make it 'opaque' dir. */ ++static int au_ren_diropq(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *diropq; ++ ++ err = 0; ++ a->src_bdiropq = au_dbdiropq(a->src_dentry); ++ a->src_hinode = au_hi(a->src_inode, a->btgt); ++ au_hn_imtx_lock_nested(a->src_hinode, AuLsc_I_CHILD); ++ diropq = au_diropq_create(a->src_dentry, a->btgt); ++ au_hn_imtx_unlock(a->src_hinode); ++ if (IS_ERR(diropq)) ++ err = PTR_ERR(diropq); ++ dput(diropq); ++ ++ return err; ++} ++ ++static int do_rename(struct au_ren_args *a) ++{ ++ int err; ++ struct dentry *d, *h_d; ++ ++ /* prepare workqueue args for asynchronous rmdir */ ++ h_d = a->dst_h_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) && h_d->d_inode) { ++ err = -ENOMEM; ++ a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb, GFP_NOFS); ++ if (unlikely(!a->thargs)) ++ goto out; ++ a->h_dst = dget(h_d); ++ } ++ ++ /* create whiteout for src_dentry */ ++ if (au_ftest_ren(a->flags, WHSRC)) { ++ a->src_bwh = au_dbwh(a->src_dentry); ++ AuDebugOn(a->src_bwh >= 0); ++ a->src_wh_dentry ++ = au_wh_create(a->src_dentry, a->btgt, a->src_h_parent); ++ err = PTR_ERR(a->src_wh_dentry); ++ if (IS_ERR(a->src_wh_dentry)) ++ goto out_thargs; ++ } ++ ++ /* lookup whiteout for dentry */ ++ if (au_ftest_ren(a->flags, WHDST)) { ++ h_d = au_wh_lkup(a->dst_h_parent, &a->dst_dentry->d_name, ++ a->br); ++ err = PTR_ERR(h_d); ++ if (IS_ERR(h_d)) ++ goto out_whsrc; ++ if (!h_d->d_inode) ++ dput(h_d); ++ else ++ a->dst_wh_dentry = h_d; ++ } ++ ++ /* rename dentry to tmpwh */ ++ if (a->thargs) { ++ err = au_whtmp_ren(a->dst_h_dentry, a->br); ++ if (unlikely(err)) ++ goto out_whdst; ++ ++ d = a->dst_dentry; ++ au_set_h_dptr(d, a->btgt, NULL); ++ err = au_lkup_neg(d, a->btgt); ++ if (unlikely(err)) ++ goto out_whtmp; ++ a->dst_h_dentry = au_h_dptr(d, a->btgt); ++ } ++ ++ /* cpup src */ ++ if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { ++ struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; ++ struct file *h_file; ++ ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); ++ h_file = au_h_open_pre(a->src_dentry, a->src_bstart); ++ if (IS_ERR(h_file)) { ++ err = PTR_ERR(h_file); ++ h_file = NULL; ++ } else ++ err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, ++ !AuCpup_DTIME); ++ mutex_unlock(h_mtx); ++ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ if (unlikely(err)) ++ goto out_whtmp; ++ } ++ ++ /* rename by vfs_rename or cpup */ ++ d = a->dst_dentry; ++ if (au_ftest_ren(a->flags, ISDIR) ++ && (a->dst_wh_dentry ++ || au_dbdiropq(d) == a->btgt ++ /* hide the lower to keep xino */ ++ || a->btgt < au_dbend(d) ++ || au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ))) ++ au_fset_ren(a->flags, DIROPQ); ++ err = au_ren_or_cpup(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ goto out_whtmp; ++ ++ /* make dir opaque */ ++ if (au_ftest_ren(a->flags, DIROPQ)) { ++ err = au_ren_diropq(a); ++ if (unlikely(err)) ++ goto out_rename; ++ } ++ ++ /* update target timestamps */ ++ AuDebugOn(au_dbstart(a->src_dentry) != a->btgt); ++ a->h_path.dentry = au_h_dptr(a->src_dentry, a->btgt); ++ vfsub_update_h_iattr(&a->h_path, /*did*/NULL); /*ignore*/ ++ a->src_inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; ++ ++ /* remove whiteout for dentry */ ++ if (a->dst_wh_dentry) { ++ a->h_path.dentry = a->dst_wh_dentry; ++ err = au_wh_unlink_dentry(a->dst_h_dir, &a->h_path, ++ a->dst_dentry); ++ if (unlikely(err)) ++ goto out_diropq; ++ } ++ ++ /* remove whtmp */ ++ if (a->thargs) ++ au_ren_del_whtmp(a); /* ignore this error */ ++ ++ err = 0; ++ goto out_success; ++ ++out_diropq: ++ if (au_ftest_ren(a->flags, DIROPQ)) ++ au_ren_rev_diropq(err, a); ++out_rename: ++ if (!au_ftest_ren(a->flags, CPUP)) ++ au_ren_rev_rename(err, a); ++ else ++ au_ren_rev_cpup(err, a); ++ dput(a->h_dst); ++out_whtmp: ++ if (a->thargs) ++ au_ren_rev_whtmp(err, a); ++out_whdst: ++ dput(a->dst_wh_dentry); ++ a->dst_wh_dentry = NULL; ++out_whsrc: ++ if (a->src_wh_dentry) ++ au_ren_rev_whsrc(err, a); ++out_success: ++ dput(a->src_wh_dentry); ++ dput(a->dst_wh_dentry); ++out_thargs: ++ if (a->thargs) { ++ dput(a->h_dst); ++ au_whtmp_rmdir_free(a->thargs); ++ a->thargs = NULL; ++ } ++out: ++ 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, struct au_nhash *whlist) ++{ ++ 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, ++ * - 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; ++ unsigned int rdhash; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ if (bstart != btgt) { ++ struct au_nhash whlist; ++ ++ SiMustAnyLock(dentry->d_sb); ++ rdhash = au_sbi(dentry->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, ++ dentry)); ++ err = au_nhash_alloc(&whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_test_empty(dentry, &whlist); ++ au_nhash_wh_free(&whlist); ++ goto out; ++ } ++ ++ if (bstart == au_dbtaildir(dentry)) ++ return 0; /* success */ ++ ++ err = au_test_empty_lower(dentry); ++ ++out: ++ if (err == -ENOTEMPTY) { ++ AuWarn1("renaming dir who has child(ren) on multiple branches," ++ " is not supported\n"); ++ err = -EXDEV; ++ } ++ return err; ++} ++ ++/* side effect: sets whlist and h_dentry */ ++static int au_ren_may_dir(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int rdhash; ++ struct dentry *d; ++ ++ d = a->dst_dentry; ++ SiMustAnyLock(d->d_sb); ++ ++ err = 0; ++ if (au_ftest_ren(a->flags, ISDIR) && a->dst_inode) { ++ rdhash = au_sbi(d->d_sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(/*file*/NULL, d)); ++ err = au_nhash_alloc(&a->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ ++ au_set_dbstart(d, a->dst_bstart); ++ err = may_rename_dstdir(d, &a->whlist); ++ au_set_dbstart(d, a->btgt); ++ } ++ a->dst_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (unlikely(err)) ++ goto out; ++ ++ d = a->src_dentry; ++ a->src_h_dentry = au_h_dptr(d, au_dbstart(d)); ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ err = may_rename_srcdir(d, a->btgt); ++ if (unlikely(err)) { ++ au_nhash_wh_free(&a->whlist); ++ a->whlist.nh_num = 0; ++ } ++ } ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * simple tests for rename. ++ * following the checks in vfs, plus the parent-child relationship. ++ */ ++static int au_may_ren(struct au_ren_args *a) ++{ ++ int err, isdir; ++ struct inode *h_inode; ++ ++ if (a->src_bstart == a->btgt) { ++ err = au_may_del(a->src_dentry, a->btgt, a->src_h_parent, ++ au_ftest_ren(a->flags, ISDIR)); ++ if (unlikely(err)) ++ goto out; ++ err = -EINVAL; ++ if (unlikely(a->src_h_dentry == a->h_trap)) ++ goto out; ++ } ++ ++ err = 0; ++ if (a->dst_bstart != a->btgt) ++ goto out; ++ ++ err = -EIO; ++ h_inode = a->dst_h_dentry->d_inode; ++ isdir = !!au_ftest_ren(a->flags, ISDIR); ++ if (!a->dst_dentry->d_inode) { ++ if (unlikely(h_inode)) ++ goto out; ++ err = au_may_add(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ } else { ++ if (unlikely(!h_inode || !h_inode->i_nlink)) ++ goto out; ++ err = au_may_del(a->dst_dentry, a->btgt, a->dst_h_parent, ++ isdir); ++ if (unlikely(err)) ++ goto out; ++ err = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ goto out; ++ err = 0; ++ } ++ ++out: ++ if (unlikely(err == -ENOENT || err == -EEXIST)) ++ err = -EIO; ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * locking order ++ * (VFS) ++ * - src_dir and dir by lock_rename() ++ * - inode if exitsts ++ * (aufs) ++ * - lock all ++ * + src_dentry and dentry by aufs_read_and_write_lock2() which calls, ++ * + si_read_lock ++ * + di_write_lock2_child() ++ * + di_write_lock_child() ++ * + ii_write_lock_child() ++ * + di_write_lock_child2() ++ * + ii_write_lock_child2() ++ * + src_parent and parent ++ * + di_write_lock_parent() ++ * + ii_write_lock_parent() ++ * + di_write_lock_parent2() ++ * + ii_write_lock_parent2() ++ * + lower src_dir and dir by vfsub_lock_rename() ++ * + verify the every relationships between child and parent. if any ++ * of them failed, unlock all and return -EBUSY. ++ */ ++static void au_ren_unlock(struct au_ren_args *a) ++{ ++ struct super_block *sb; ++ ++ sb = a->dst_dentry->d_sb; ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ mnt_drop_write(a->br->br_mnt); ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++} ++ ++static int au_ren_lock(struct au_ren_args *a) ++{ ++ int err; ++ unsigned int udba; ++ ++ err = 0; ++ a->src_h_parent = au_h_dptr(a->src_parent, a->btgt); ++ a->src_hdir = au_hi(a->src_dir, a->btgt); ++ a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); ++ a->dst_hdir = au_hi(a->dst_dir, a->btgt); ++ a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ udba = au_opt_udba(a->src_dentry->d_sb); ++ if (unlikely(a->src_hdir->hi_inode != a->src_h_parent->d_inode ++ || a->dst_hdir->hi_inode != a->dst_h_parent->d_inode)) ++ err = au_busy_or_stale(); ++ if (!err && au_dbstart(a->src_dentry) == a->btgt) ++ err = au_h_verify(a->src_h_dentry, udba, ++ a->src_h_parent->d_inode, a->src_h_parent, ++ a->br); ++ if (!err && au_dbstart(a->dst_dentry) == a->btgt) ++ err = au_h_verify(a->dst_h_dentry, udba, ++ a->dst_h_parent->d_inode, a->dst_h_parent, ++ a->br); ++ if (!err) { ++ err = mnt_want_write(a->br->br_mnt); ++ if (unlikely(err)) ++ goto out_unlock; ++ au_fset_ren(a->flags, MNT_WRITE); ++ goto out; /* success */ ++ } ++ ++ err = au_busy_or_stale(); ++ ++out_unlock: ++ au_ren_unlock(a); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_ren_refresh_dir(struct au_ren_args *a) ++{ ++ struct inode *dir; ++ ++ dir = a->dst_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) { ++ /* is this updating defined in POSIX? */ ++ au_cpup_attr_timesizes(a->src_inode); ++ au_cpup_attr_nlink(dir, /*force*/1); ++ } ++ ++ if (au_ibstart(dir) == a->btgt) ++ au_cpup_attr_timesizes(dir); ++ ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ return; ++ ++ dir = a->src_dir; ++ dir->i_version++; ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_cpup_attr_nlink(dir, /*force*/1); ++ if (au_ibstart(dir) == a->btgt) ++ au_cpup_attr_timesizes(dir); ++} ++ ++static void au_ren_refresh(struct au_ren_args *a) ++{ ++ aufs_bindex_t bend, bindex; ++ struct dentry *d, *h_d; ++ struct inode *i, *h_i; ++ struct super_block *sb; ++ ++ d = a->dst_dentry; ++ d_drop(d); ++ if (a->h_dst) ++ /* already dget-ed by au_ren_or_cpup() */ ++ au_set_h_dptr(d, a->btgt, a->h_dst); ++ ++ i = a->dst_inode; ++ if (i) { ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ vfsub_drop_nlink(i); ++ else { ++ vfsub_dead_dir(i); ++ au_cpup_attr_timesizes(i); ++ } ++ au_update_dbrange(d, /*do_put_zero*/1); ++ } else { ++ bend = a->btgt; ++ for (bindex = au_dbstart(d); bindex < bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) ++ au_set_h_dptr(d, bindex, NULL); ++ au_update_dbrange(d, /*do_put_zero*/0); ++ } ++ ++ d = a->src_dentry; ++ au_set_dbwh(d, -1); ++ bend = au_dbend(d); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_d = au_h_dptr(d, bindex); ++ if (h_d) ++ au_set_h_dptr(d, bindex, NULL); ++ } ++ au_set_dbend(d, a->btgt); ++ ++ sb = d->d_sb; ++ i = a->src_inode; ++ if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i)) ++ return; /* success */ ++ ++ bend = au_ibend(i); ++ for (bindex = a->btgt + 1; bindex <= bend; bindex++) { ++ h_i = au_h_iptr(i, bindex); ++ if (h_i) { ++ au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0); ++ /* ignore this error */ ++ au_set_h_iptr(i, bindex, NULL, 0); ++ } ++ } ++ au_set_ibend(i, a->btgt); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* 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; ++ struct au_branch *br; ++ ++ parent = dentry->d_parent; ++ IMustLock(parent->d_inode); /* dir is locked */ ++ ++ bdiropq = au_dbdiropq(parent); ++ bwh = au_dbwh(dentry); ++ br = au_sbr(dentry->d_sb, btgt); ++ if (au_br_rdonly(br) ++ || (0 <= bdiropq && bdiropq < btgt) ++ || (0 <= bwh && bwh < btgt)) ++ btgt = -1; ++ ++ AuDbg("btgt %d\n", btgt); ++ return btgt; ++} ++ ++/* sets src_bstart, dst_bstart and btgt */ ++static int au_ren_wbr(struct au_ren_args *a) ++{ ++ int err; ++ struct au_wr_dir_args wr_dir_args = { ++ /* .force_btgt = -1, */ ++ .flags = AuWrDir_ADD_ENTRY ++ }; ++ ++ a->src_bstart = au_dbstart(a->src_dentry); ++ a->dst_bstart = au_dbstart(a->dst_dentry); ++ if (au_ftest_ren(a->flags, ISDIR)) ++ au_fset_wrdir(wr_dir_args.flags, ISDIR); ++ wr_dir_args.force_btgt = a->src_bstart; ++ if (a->dst_inode && a->dst_bstart < a->src_bstart) ++ wr_dir_args.force_btgt = a->dst_bstart; ++ wr_dir_args.force_btgt = au_wbr(a->dst_dentry, wr_dir_args.force_btgt); ++ err = au_wr_dir(a->dst_dentry, a->src_dentry, &wr_dir_args); ++ a->btgt = err; ++ ++ return err; ++} ++ ++static void au_ren_dt(struct au_ren_args *a) ++{ ++ a->h_path.dentry = a->src_h_parent; ++ au_dtime_store(a->src_dt + AuPARENT, a->src_parent, &a->h_path); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) { ++ a->h_path.dentry = a->dst_h_parent; ++ au_dtime_store(a->dst_dt + AuPARENT, a->dst_parent, &a->h_path); ++ } ++ ++ au_fclr_ren(a->flags, DT_DSTDIR); ++ if (!au_ftest_ren(a->flags, ISDIR)) ++ return; ++ ++ a->h_path.dentry = a->src_h_dentry; ++ au_dtime_store(a->src_dt + AuCHILD, a->src_dentry, &a->h_path); ++ if (a->dst_h_dentry->d_inode) { ++ au_fset_ren(a->flags, DT_DSTDIR); ++ a->h_path.dentry = a->dst_h_dentry; ++ au_dtime_store(a->dst_dt + AuCHILD, a->dst_dentry, &a->h_path); ++ } ++} ++ ++static void au_ren_rev_dt(int err, struct au_ren_args *a) ++{ ++ struct dentry *h_d; ++ struct mutex *h_mtx; ++ ++ au_dtime_revert(a->src_dt + AuPARENT); ++ if (!au_ftest_ren(a->flags, ISSAMEDIR)) ++ au_dtime_revert(a->dst_dt + AuPARENT); ++ ++ if (au_ftest_ren(a->flags, ISDIR) && err != -EIO) { ++ h_d = a->src_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->src_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ ++ if (au_ftest_ren(a->flags, DT_DSTDIR)) { ++ h_d = a->dst_dt[AuCHILD].dt_h_path.dentry; ++ h_mtx = &h_d->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD); ++ au_dtime_revert(a->dst_dt + AuCHILD); ++ mutex_unlock(h_mtx); ++ } ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, ++ struct inode *_dst_dir, struct dentry *_dst_dentry) ++{ ++ int err, flags; ++ /* reduce stack space */ ++ struct au_ren_args *a; ++ ++ AuDbg("%.*s, %.*s\n", AuDLNPair(_src_dentry), AuDLNPair(_dst_dentry)); ++ IMustLock(_src_dir); ++ IMustLock(_dst_dir); ++ ++ err = -ENOMEM; ++ BUILD_BUG_ON(sizeof(*a) > PAGE_SIZE); ++ a = kzalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ a->src_dir = _src_dir; ++ a->src_dentry = _src_dentry; ++ a->src_inode = a->src_dentry->d_inode; ++ a->src_parent = a->src_dentry->d_parent; /* dir inode is locked */ ++ a->dst_dir = _dst_dir; ++ a->dst_dentry = _dst_dentry; ++ a->dst_inode = a->dst_dentry->d_inode; ++ a->dst_parent = a->dst_dentry->d_parent; /* dir inode is locked */ ++ if (a->dst_inode) { ++ IMustLock(a->dst_inode); ++ au_igrab(a->dst_inode); ++ } ++ ++ err = -ENOTDIR; ++ flags = AuLock_FLUSH | AuLock_NOPLM | AuLock_GEN; ++ if (S_ISDIR(a->src_inode->i_mode)) { ++ au_fset_ren(a->flags, ISDIR); ++ if (unlikely(a->dst_inode && !S_ISDIR(a->dst_inode->i_mode))) ++ goto out_free; ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ AuLock_DIR | flags); ++ } else ++ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, ++ flags); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = au_d_hashed_positive(a->src_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ err = -ENOENT; ++ if (a->dst_inode) { ++ /* ++ * If it is a dir, VFS unhash dst_dentry before this ++ * function. It means we cannot rely upon d_unhashed(). ++ */ ++ if (unlikely(!a->dst_inode->i_nlink)) ++ goto out_unlock; ++ if (!S_ISDIR(a->dst_inode->i_mode)) { ++ err = au_d_hashed_positive(a->dst_dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ } else if (unlikely(IS_DEADDIR(a->dst_inode))) ++ goto out_unlock; ++ } else if (unlikely(d_unhashed(a->dst_dentry))) ++ goto out_unlock; ++ ++ au_fset_ren(a->flags, ISSAMEDIR); /* temporary */ ++ di_write_lock_parent(a->dst_parent); ++ ++ /* which branch we process */ ++ err = au_ren_wbr(a); ++ if (unlikely(err < 0)) ++ goto out_parent; ++ a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); ++ a->h_path.mnt = a->br->br_mnt; ++ ++ /* are they available to be renamed */ ++ err = au_ren_may_dir(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ /* prepare the writable parent dir on the same branch */ ++ if (a->dst_bstart == a->btgt) { ++ au_fset_ren(a->flags, WHDST); ++ } else { ++ err = au_cpup_dirs(a->dst_dentry, a->btgt); ++ if (unlikely(err)) ++ goto out_children; ++ } ++ ++ if (a->src_dir != a->dst_dir) { ++ /* ++ * this temporary unlock is safe, ++ * because both dir->i_mutex are locked. ++ */ ++ di_write_unlock(a->dst_parent); ++ di_write_lock_parent(a->src_parent); ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ di_write_unlock(a->src_parent); ++ di_write_lock2_parent(a->src_parent, a->dst_parent, /*isdir*/1); ++ au_fclr_ren(a->flags, ISSAMEDIR); ++ } else ++ err = au_wr_dir_need_wh(a->src_dentry, ++ au_ftest_ren(a->flags, ISDIR), ++ &a->btgt); ++ if (unlikely(err < 0)) ++ goto out_children; ++ if (err) ++ au_fset_ren(a->flags, WHSRC); ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ goto out_children; ++ ++ if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) ++ err = au_may_ren(a); ++ else if (unlikely(a->dst_dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ err = -ENAMETOOLONG; ++ if (unlikely(err)) ++ goto out_hdir; ++ ++ /* store timestamps to be revertible */ ++ au_ren_dt(a); ++ ++ /* here we go */ ++ err = do_rename(a); ++ if (unlikely(err)) ++ goto out_dt; ++ ++ /* update dir attributes */ ++ au_ren_refresh_dir(a); ++ ++ /* dput/iput all lower dentries */ ++ au_ren_refresh(a); ++ ++ goto out_hdir; /* success */ ++ ++out_dt: ++ au_ren_rev_dt(err, a); ++out_hdir: ++ au_ren_unlock(a); ++out_children: ++ au_nhash_wh_free(&a->whlist); ++ if (err && a->dst_inode && a->dst_bstart != a->btgt) { ++ AuDbg("bstart %d, btgt %d\n", a->dst_bstart, a->btgt); ++ au_set_h_dptr(a->dst_dentry, a->btgt, NULL); ++ au_set_dbstart(a->dst_dentry, a->dst_bstart); ++ } ++out_parent: ++ if (!err) ++ d_move(a->src_dentry, a->dst_dentry); ++ else { ++ au_update_dbstart(a->dst_dentry); ++ if (!a->dst_inode) ++ d_drop(a->dst_dentry); ++ } ++ if (au_ftest_ren(a->flags, ISSAMEDIR)) ++ di_write_unlock(a->dst_parent); ++ else ++ di_write_unlock2(a->src_parent, a->dst_parent); ++out_unlock: ++ aufs_read_and_write_unlock2(a->dst_dentry, a->src_dentry); ++out_free: ++ iput(a->dst_inode); ++ if (a->thargs) ++ au_whtmp_rmdir_free(a->thargs); ++ kfree(a); ++out: ++ AuTraceErr(err); ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/iinfo.c linux-2.6.36/fs/aufs/iinfo.c +--- linux-2.6.36.orig/fs/aufs/iinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/iinfo.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,263 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode private data ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct inode *h_inode; ++ ++ IiMustAnyLock(inode); ++ ++ h_inode = au_ii(inode)->ii_hinode[0 + bindex].hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ return h_inode; ++} ++ ++/* todo: hard/soft set? */ ++void au_hiput(struct au_hinode *hinode) ++{ ++ au_hn_free(hinode); ++ dput(hinode->hi_whdentry); ++ iput(hinode->hi_inode); ++} ++ ++unsigned int au_hi_flags(struct inode *inode, int isdir) ++{ ++ unsigned int flags; ++ const unsigned int mnt_flags = au_mntflags(inode->i_sb); ++ ++ flags = 0; ++ if (au_opt_test(mnt_flags, XINO)) ++ au_fset_hi(flags, XINO); ++ if (isdir && au_opt_test(mnt_flags, UDBA_HNOTIFY)) ++ au_fset_hi(flags, HNOTIFY); ++ return flags; ++} ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags) ++{ ++ struct au_hinode *hinode; ++ struct inode *hi; ++ struct au_iinfo *iinfo = au_ii(inode); ++ ++ IiMustWriteLock(inode); ++ ++ hinode = iinfo->ii_hinode + bindex; ++ hi = hinode->hi_inode; ++ AuDebugOn(h_inode && atomic_read(&h_inode->i_count) <= 0); ++ ++ if (hi) ++ au_hiput(hinode); ++ hinode->hi_inode = h_inode; ++ if (h_inode) { ++ int err; ++ struct super_block *sb = inode->i_sb; ++ struct au_branch *br; ++ ++ AuDebugOn(inode->i_mode ++ && (h_inode->i_mode & S_IFMT) ++ != (inode->i_mode & S_IFMT)); ++ if (bindex == iinfo->ii_bstart) ++ au_cpup_igen(inode, h_inode); ++ br = au_sbr(sb, bindex); ++ hinode->hi_id = br->br_id; ++ if (au_ftest_hi(flags, XINO)) { ++ err = au_xino_write(sb, bindex, h_inode->i_ino, ++ inode->i_ino); ++ if (unlikely(err)) ++ AuIOErr1("failed au_xino_write() %d\n", err); ++ } ++ ++ if (au_ftest_hi(flags, HNOTIFY) ++ && au_br_hnotifyable(br->br_perm)) { ++ err = au_hn_alloc(hinode, inode); ++ if (unlikely(err)) ++ AuIOErr1("au_hn_alloc() %d\n", err); ++ } ++ } ++} ++ ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh) ++{ ++ struct au_hinode *hinode; ++ ++ IiMustWriteLock(inode); ++ ++ hinode = au_ii(inode)->ii_hinode + bindex; ++ AuDebugOn(hinode->hi_whdentry); ++ hinode->hi_whdentry = h_wh; ++} ++ ++void au_update_iigen(struct inode *inode) ++{ ++ atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb)); ++ /* smp_mb(); */ /* atomic_set */ ++} ++ ++/* it may be called at remount time, too */ ++void au_update_ibrange(struct inode *inode, int do_put_zero) ++{ ++ struct au_iinfo *iinfo; ++ aufs_bindex_t bindex, bend; ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ IiMustWriteLock(inode); ++ ++ if (do_put_zero && iinfo->ii_bstart >= 0) { ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++) { ++ struct inode *h_i; ++ ++ h_i = iinfo->ii_hinode[0 + bindex].hi_inode; ++ if (h_i && !h_i->i_nlink) ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ } ++ } ++ ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ bend = au_sbend(inode->i_sb); ++ for (bindex = 0; bindex <= bend; bindex++) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bstart = bindex; ++ break; ++ } ++ if (iinfo->ii_bstart >= 0) ++ for (bindex = bend; bindex >= iinfo->ii_bstart; bindex--) ++ if (iinfo->ii_hinode[0 + bindex].hi_inode) { ++ iinfo->ii_bend = bindex; ++ break; ++ } ++ AuDebugOn(iinfo->ii_bstart > iinfo->ii_bend); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_icntnr_init_once(void *_c) ++{ ++ struct au_icntnr *c = _c; ++ struct au_iinfo *iinfo = &c->iinfo; ++ static struct lock_class_key aufs_ii; ++ ++ au_rw_init(&iinfo->ii_rwsem); ++ au_rw_class(&iinfo->ii_rwsem, &aufs_ii); ++ inode_init_once(&c->vfs_inode); ++} ++ ++int au_iinfo_init(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ int nbr, i; ++ ++ sb = inode->i_sb; ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ nbr = au_sbend(sb) + 1; ++ if (unlikely(nbr <= 0)) ++ nbr = 1; ++ iinfo->ii_hinode = kcalloc(nbr, sizeof(*iinfo->ii_hinode), GFP_NOFS); ++ if (iinfo->ii_hinode) { ++ au_ninodes_inc(sb); ++ 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 */ ++ iinfo->ii_bstart = -1; ++ iinfo->ii_bend = -1; ++ iinfo->ii_vdir = NULL; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr) ++{ ++ int err, sz; ++ struct au_hinode *hip; ++ ++ AuRwMustWriteLock(&iinfo->ii_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*hip) * (iinfo->ii_bend + 1); ++ if (!sz) ++ sz = sizeof(*hip); ++ hip = au_kzrealloc(iinfo->ii_hinode, sz, sizeof(*hip) * nbr, GFP_NOFS); ++ if (hip) { ++ iinfo->ii_hinode = hip; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++void au_iinfo_fin(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ struct au_hinode *hi; ++ struct super_block *sb; ++ aufs_bindex_t bindex, bend; ++ const unsigned char unlinked = !inode->i_nlink; ++ ++ iinfo = au_ii(inode); ++ /* bad_inode case */ ++ if (!iinfo) ++ return; ++ ++ sb = inode->i_sb; ++ au_ninodes_dec(sb); ++ if (si_pid_test(sb)) ++ au_xino_delete_inode(inode, unlinked); ++ else { ++ /* ++ * it is safe to hide the dependency between sbinfo and ++ * sb->s_umount. ++ */ ++ lockdep_off(); ++ si_noflush_read_lock(sb); ++ au_xino_delete_inode(inode, unlinked); ++ si_read_unlock(sb); ++ lockdep_on(); ++ } ++ ++ if (iinfo->ii_vdir) ++ au_vdir_free(iinfo->ii_vdir); ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex >= 0) { ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ while (bindex++ <= bend) { ++ if (hi->hi_inode) ++ au_hiput(hi); ++ hi++; ++ } ++ } ++ kfree(iinfo->ii_hinode); ++ AuRwDestroy(&iinfo->ii_rwsem); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/inode.c linux-2.6.36/fs/aufs/inode.c +--- linux-2.6.36.orig/fs/aufs/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/inode.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,471 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode functions ++ */ ++ ++#include "aufs.h" ++ ++struct inode *au_igrab(struct inode *inode) ++{ ++ if (inode) { ++ AuDebugOn(!atomic_read(&inode->i_count)); ++ atomic_inc(&inode->i_count); ++ } ++ return inode; ++} ++ ++static void au_refresh_hinode_attr(struct inode *inode, int do_version) ++{ ++ au_cpup_attr_all(inode, /*force*/0); ++ au_update_iigen(inode); ++ if (do_version) ++ inode->i_version++; ++} ++ ++static int au_ii_refresh(struct inode *inode, int *update) ++{ ++ int err, e; ++ umode_t type; ++ aufs_bindex_t bindex, new_bindex; ++ struct super_block *sb; ++ struct au_iinfo *iinfo; ++ struct au_hinode *p, *q, tmp; ++ ++ IiMustWriteLock(inode); ++ ++ *update = 0; ++ sb = inode->i_sb; ++ type = inode->i_mode & S_IFMT; ++ iinfo = au_ii(inode); ++ err = au_ii_realloc(iinfo, au_sbend(sb) + 1); ++ if (unlikely(err)) ++ goto out; ++ ++ AuDebugOn(iinfo->ii_bstart < 0); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ for (bindex = iinfo->ii_bstart; bindex <= iinfo->ii_bend; ++ bindex++, p++) { ++ if (!p->hi_inode) ++ continue; ++ ++ AuDebugOn(type != (p->hi_inode->i_mode & S_IFMT)); ++ new_bindex = au_br_index(sb, p->hi_id); ++ if (new_bindex == bindex) ++ continue; ++ ++ if (new_bindex < 0) { ++ *update = 1; ++ au_hiput(p); ++ p->hi_inode = NULL; ++ continue; ++ } ++ ++ if (new_bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = new_bindex; ++ if (iinfo->ii_bend < new_bindex) ++ iinfo->ii_bend = new_bindex; ++ /* swap two lower inode, and loop again */ ++ q = iinfo->ii_hinode + new_bindex; ++ tmp = *q; ++ *q = *p; ++ *p = tmp; ++ if (tmp.hi_inode) { ++ bindex--; ++ p--; ++ } ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode_self(struct inode *inode) ++{ ++ int err, update; ++ ++ err = au_ii_refresh(inode, &update); ++ if (!err) ++ au_refresh_hinode_attr(inode, update && S_ISDIR(inode->i_mode)); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry) ++{ ++ int err, e, update; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bend; ++ unsigned char isdir; ++ struct au_hinode *p; ++ struct au_iinfo *iinfo; ++ ++ err = au_ii_refresh(inode, &update); ++ if (unlikely(err)) ++ goto out; ++ ++ update = 0; ++ iinfo = au_ii(inode); ++ p = iinfo->ii_hinode + iinfo->ii_bstart; ++ mode = (inode->i_mode & S_IFMT); ++ isdir = S_ISDIR(mode); ++ flags = au_hi_flags(inode, isdir); ++ bend = au_dbend(dentry); ++ for (bindex = au_dbstart(dentry); bindex <= bend; bindex++) { ++ struct inode *h_i; ++ struct dentry *h_d; ++ ++ h_d = au_h_dptr(dentry, bindex); ++ if (!h_d || !h_d->d_inode) ++ continue; ++ ++ AuDebugOn(mode != (h_d->d_inode->i_mode & S_IFMT)); ++ if (iinfo->ii_bstart <= bindex && bindex <= iinfo->ii_bend) { ++ h_i = au_h_iptr(inode, bindex); ++ if (h_i) { ++ if (h_i == h_d->d_inode) ++ continue; ++ err = -EIO; ++ break; ++ } ++ } ++ if (bindex < iinfo->ii_bstart) ++ iinfo->ii_bstart = bindex; ++ if (iinfo->ii_bend < bindex) ++ iinfo->ii_bend = bindex; ++ au_set_h_iptr(inode, bindex, au_igrab(h_d->d_inode), flags); ++ update = 1; ++ } ++ au_update_ibrange(inode, /*do_put_zero*/0); ++ e = au_dy_irefresh(inode); ++ if (unlikely(e && !err)) ++ err = e; ++ if (!err) ++ au_refresh_hinode_attr(inode, update && isdir); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int set_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ unsigned int flags; ++ umode_t mode; ++ aufs_bindex_t bindex, bstart, btail; ++ unsigned char isdir; ++ struct dentry *h_dentry; ++ struct inode *h_inode; ++ struct au_iinfo *iinfo; ++ ++ IiMustWriteLock(inode); ++ ++ err = 0; ++ isdir = 0; ++ bstart = au_dbstart(dentry); ++ h_inode = au_h_dptr(dentry, bstart)->d_inode; ++ mode = h_inode->i_mode; ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ inode->i_fop = &aufs_file_fop; ++ err = au_dy_iaop(inode, bstart, h_inode); ++ if (unlikely(err)) ++ goto out; ++ break; ++ case S_IFDIR: ++ isdir = 1; ++ btail = au_dbtaildir(dentry); ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ break; ++ case S_IFLNK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_symlink_iop; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ case S_IFIFO: ++ case S_IFSOCK: ++ btail = au_dbtail(dentry); ++ inode->i_op = &aufs_iop; ++ au_init_special_fop(inode, mode, h_inode->i_rdev); ++ break; ++ default: ++ AuIOErr("Unknown file type 0%o\n", mode); ++ err = -EIO; ++ goto out; ++ } ++ ++ /* do not set hnotify for whiteouted dirs (SHWH mode) */ ++ flags = au_hi_flags(inode, isdir); ++ if (au_opt_test(au_mntflags(dentry->d_sb), SHWH) ++ && au_ftest_hi(flags, HNOTIFY) ++ && dentry->d_name.len > AUFS_WH_PFX_LEN ++ && !memcmp(dentry->d_name.name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) ++ au_fclr_hi(flags, HNOTIFY); ++ iinfo = au_ii(inode); ++ iinfo->ii_bstart = bstart; ++ iinfo->ii_bend = btail; ++ for (bindex = bstart; bindex <= btail; bindex++) { ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (h_dentry) ++ au_set_h_iptr(inode, bindex, ++ au_igrab(h_dentry->d_inode), flags); ++ } ++ au_cpup_attr_all(inode, /*force*/1); ++ ++out: ++ return err; ++} ++ ++/* ++ * successful returns with iinfo write_locked ++ * minus: errno ++ * zero: success, matched ++ * plus: no error, but unmatched ++ */ ++static int reval_inode(struct inode *inode, struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct inode *h_inode, *h_dinode; ++ ++ /* ++ * 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 = 1; ++ ii_write_lock_new_child(inode); ++ h_dinode = au_h_dptr(dentry, au_dbstart(dentry))->d_inode; ++ bend = au_ibend(inode); ++ for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { ++ h_inode = au_h_iptr(inode, bindex); ++ if (h_inode && h_inode == h_dinode) { ++ err = 0; ++ if (au_iigen_test(inode, au_digen(dentry))) ++ err = au_refresh_hinode(inode, dentry); ++ break; ++ } ++ } ++ ++ if (unlikely(err)) ++ ii_write_unlock(inode); ++out: ++ return err; ++} ++ ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino) ++{ ++ int err; ++ struct mutex *mtx; ++ ++ /* prevent hardlinked inode number from race condition */ ++ mtx = NULL; ++ if (d_type != DT_DIR) { ++ mtx = &au_sbr(sb, bindex)->br_xino.xi_nondir_mtx; ++ mutex_lock(mtx); ++ } ++ err = au_xino_read(sb, bindex, h_ino, ino); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!*ino) { ++ err = -EIO; ++ *ino = au_xino_new_ino(sb); ++ if (unlikely(!*ino)) ++ goto out; ++ err = au_xino_write(sb, bindex, h_ino, *ino); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return err; ++} ++ ++/* successful returns with iinfo write_locked */ ++/* todo: return with unlocked? */ ++struct inode *au_new_inode(struct dentry *dentry, int must_new) ++{ ++ struct inode *inode, *h_inode; ++ struct dentry *h_dentry; ++ struct super_block *sb; ++ struct mutex *mtx; ++ ino_t h_ino, ino; ++ int err; ++ aufs_bindex_t bstart; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ h_dentry = au_h_dptr(dentry, bstart); ++ h_inode = h_dentry->d_inode; ++ h_ino = h_inode->i_ino; ++ ++ /* ++ * stop 'race'-ing between hardlinks under different ++ * parents. ++ */ ++ mtx = NULL; ++ if (!S_ISDIR(h_inode->i_mode)) ++ mtx = &au_sbr(sb, bstart)->br_xino.xi_nondir_mtx; ++ ++new_ino: ++ if (mtx) ++ mutex_lock(mtx); ++ err = au_xino_read(sb, bstart, h_ino, &ino); ++ inode = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ if (!ino) { ++ ino = au_xino_new_ino(sb); ++ if (unlikely(!ino)) { ++ inode = ERR_PTR(-EIO); ++ goto out; ++ } ++ } ++ ++ AuDbg("i%lu\n", (unsigned long)ino); ++ inode = au_iget_locked(sb, ino); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); ++ if (inode->i_state & I_NEW) { ++ ii_write_lock_new_child(inode); ++ err = set_inode(inode, dentry); ++ if (!err) { ++ unlock_new_inode(inode); ++ goto out; /* success */ ++ } ++ ++ /* ++ * iget_failed() calls iput(), but we need to call ++ * ii_write_unlock() after iget_failed(). so dirty hack for ++ * i_count. ++ */ ++ atomic_inc(&inode->i_count); ++ iget_failed(inode); ++ ii_write_unlock(inode); ++ au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ /* ignore this error */ ++ goto out_iput; ++ } else if (!must_new && !IS_DEADDIR(inode) && inode->i_nlink) { ++ /* ++ * horrible race condition between lookup, readdir and copyup ++ * (or something). ++ */ ++ if (mtx) ++ mutex_unlock(mtx); ++ err = reval_inode(inode, dentry); ++ if (unlikely(err < 0)) { ++ mtx = NULL; ++ goto out_iput; ++ } ++ ++ if (!err) { ++ mtx = NULL; ++ goto out; /* success */ ++ } else if (mtx) ++ mutex_lock(mtx); ++ } ++ ++ if (unlikely(au_test_fs_unique_ino(h_dentry->d_inode))) ++ AuWarn1("Warning: 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), ++ (unsigned long)h_ino, (unsigned long)ino); ++ ino = 0; ++ err = au_xino_write(sb, bstart, h_ino, /*ino*/0); ++ if (!err) { ++ iput(inode); ++ if (mtx) ++ mutex_unlock(mtx); ++ goto new_ino; ++ } ++ ++out_iput: ++ iput(inode); ++ inode = ERR_PTR(err); ++out: ++ if (mtx) ++ mutex_unlock(mtx); ++ return inode; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode) ++{ ++ int err; ++ ++ err = au_br_rdonly(au_sbr(sb, bindex)); ++ ++ /* pseudo-link after flushed may happen out of bounds */ ++ if (!err ++ && inode ++ && au_ibstart(inode) <= bindex ++ && bindex <= au_ibend(inode)) { ++ /* ++ * permission check is unnecessary since vfsub routine ++ * will be called later ++ */ ++ struct inode *hi = au_h_iptr(inode, bindex); ++ if (hi) ++ err = IS_IMMUTABLE(hi) ? -EROFS : 0; ++ } ++ ++ return err; ++} ++ ++int au_test_h_perm(struct inode *h_inode, int mask) ++{ ++ if (!current_fsuid()) ++ return 0; ++ return inode_permission(h_inode, mask); ++} ++ ++int au_test_h_perm_sio(struct inode *h_inode, int mask) ++{ ++ if (au_test_nfs(h_inode->i_sb) ++ && (mask & MAY_WRITE) ++ && S_ISDIR(h_inode->i_mode)) ++ mask |= MAY_READ; /* force permission check */ ++ return au_test_h_perm(h_inode, mask); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/inode.h linux-2.6.36/fs/aufs/inode.h +--- linux-2.6.36.orig/fs/aufs/inode.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/inode.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,546 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * inode operations ++ */ ++ ++#ifndef __AUFS_INODE_H__ ++#define __AUFS_INODE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#include "rwsem.h" ++ ++struct vfsmount; ++ ++struct au_hnotify { ++#ifdef CONFIG_AUFS_HNOTIFY ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ /* never use fsnotify_add_vfsmount_mark() */ ++ struct fsnotify_mark hn_mark; ++ int hn_mark_dead; ++#endif ++ struct inode *hn_aufs_inode; /* no get/put */ ++#endif ++} ____cacheline_aligned_in_smp; ++ ++struct au_hinode { ++ struct inode *hi_inode; ++ aufs_bindex_t hi_id; ++#ifdef CONFIG_AUFS_HNOTIFY ++ struct au_hnotify *hi_notify; ++#endif ++ ++ /* reference to the copied-up whiteout with get/put */ ++ struct dentry *hi_whdentry; ++}; ++ ++struct au_vdir; ++struct au_iinfo { ++ atomic_t ii_generation; ++ struct super_block *ii_hsb1; /* no get/put */ ++ ++ struct au_rwsem ii_rwsem; ++ aufs_bindex_t ii_bstart, ii_bend; ++ __u32 ii_higen; ++ struct au_hinode *ii_hinode; ++ struct au_vdir *ii_vdir; ++}; ++ ++struct au_icntnr { ++ struct au_iinfo iinfo; ++ struct inode vfs_inode; ++} ____cacheline_aligned_in_smp; ++ ++/* au_pin flags */ ++#define AuPin_DI_LOCKED 1 ++#define AuPin_MNT_WRITE (1 << 1) ++#define au_ftest_pin(flags, name) ((flags) & AuPin_##name) ++#define au_fset_pin(flags, name) \ ++ do { (flags) |= AuPin_##name; } while (0) ++#define au_fclr_pin(flags, name) \ ++ do { (flags) &= ~AuPin_##name; } while (0) ++ ++struct au_pin { ++ /* input */ ++ struct dentry *dentry; ++ unsigned int udba; ++ unsigned char lsc_di, lsc_hi, flags; ++ aufs_bindex_t bindex; ++ ++ /* output */ ++ struct dentry *parent; ++ struct au_hinode *hdir; ++ struct vfsmount *h_mnt; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_iinfo *au_ii(struct inode *inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = &(container_of(inode, struct au_icntnr, vfs_inode)->iinfo); ++ if (iinfo->ii_hinode) ++ return iinfo; ++ return NULL; /* debugging bad_inode case */ ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* inode.c */ ++struct inode *au_igrab(struct inode *inode); ++int au_refresh_hinode_self(struct inode *inode); ++int au_refresh_hinode(struct inode *inode, struct dentry *dentry); ++int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ unsigned int d_type, ino_t *ino); ++struct inode *au_new_inode(struct dentry *dentry, int must_new); ++int au_test_ro(struct super_block *sb, aufs_bindex_t bindex, ++ struct inode *inode); ++int au_test_h_perm(struct inode *h_inode, int mask); ++int au_test_h_perm_sio(struct inode *h_inode, int mask); ++ ++static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex, ++ ino_t h_ino, unsigned int d_type, ino_t *ino) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ return au_ino(sb, bindex, h_ino, d_type, ino); ++#else ++ return 0; ++#endif ++} ++ ++/* i_op.c */ ++extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; ++ ++/* au_wr_dir flags */ ++#define AuWrDir_ADD_ENTRY 1 ++#define AuWrDir_ISDIR (1 << 1) ++#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) ++#define au_fset_wrdir(flags, name) \ ++ do { (flags) |= AuWrDir_##name; } while (0) ++#define au_fclr_wrdir(flags, name) \ ++ do { (flags) &= ~AuWrDir_##name; } while (0) ++ ++struct au_wr_dir_args { ++ aufs_bindex_t force_btgt; ++ unsigned char flags; ++}; ++int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, ++ struct au_wr_dir_args *args); ++ ++struct dentry *au_pinned_h_parent(struct au_pin *pin); ++void au_pin_init(struct au_pin *pin, struct dentry *dentry, ++ aufs_bindex_t bindex, int lsc_di, int lsc_hi, ++ unsigned int udba, unsigned char flags); ++int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int udba, unsigned char flags) __must_check; ++int au_do_pin(struct au_pin *pin) __must_check; ++void au_unpin(struct au_pin *pin); ++ ++/* i_op_add.c */ ++int au_may_add(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++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); ++int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent, int isdir); ++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); ++ ++/* iinfo.c */ ++struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); ++void au_hiput(struct au_hinode *hinode); ++void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_wh); ++unsigned int au_hi_flags(struct inode *inode, int isdir); ++ ++/* hinode flags */ ++#define AuHi_XINO 1 ++#define AuHi_HNOTIFY (1 << 1) ++#define au_ftest_hi(flags, name) ((flags) & AuHi_##name) ++#define au_fset_hi(flags, name) \ ++ do { (flags) |= AuHi_##name; } while (0) ++#define au_fclr_hi(flags, name) \ ++ do { (flags) &= ~AuHi_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuHi_HNOTIFY ++#define AuHi_HNOTIFY 0 ++#endif ++ ++void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, ++ struct inode *h_inode, unsigned int flags); ++ ++void au_update_iigen(struct inode *inode); ++void au_update_ibrange(struct inode *inode, int do_put_zero); ++ ++void au_icntnr_init_once(void *_c); ++int au_iinfo_init(struct inode *inode); ++void au_iinfo_fin(struct inode *inode); ++int au_ii_realloc(struct au_iinfo *iinfo, int nbr); ++ ++#ifdef CONFIG_PROC_FS ++/* plink.c */ ++int au_plink_maint(struct super_block *sb, int flags); ++void au_plink_maint_leave(struct au_sbinfo *sbinfo); ++int au_plink_maint_enter(struct super_block *sb); ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb); ++#else ++AuStubVoid(au_plink_list, struct super_block *sb) ++#endif ++int au_plink_test(struct inode *inode); ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex); ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++void au_plink_put(struct super_block *sb, int verbose); ++void au_plink_clean(struct super_block *sb, int verbose); ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id); ++#else ++AuStubInt0(au_plink_maint, struct super_block *sb, int flags); ++AuStubVoid(au_plink_maint_leave, struct au_sbinfo *sbinfo); ++AuStubInt0(au_plink_maint_enter, struct super_block *sb); ++AuStubVoid(au_plink_list, struct super_block *sb); ++AuStubInt0(au_plink_test, struct inode *inode); ++AuStub(struct dentry *, au_plink_lkup, return NULL, ++ struct inode *inode, aufs_bindex_t bindex); ++AuStubVoid(au_plink_append, struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry); ++AuStubVoid(au_plink_put, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_clean, struct super_block *sb, int verbose); ++AuStubVoid(au_plink_half_refresh, struct super_block *sb, aufs_bindex_t br_id); ++#endif /* CONFIG_PROC_FS */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for iinfo */ ++enum { ++ AuLsc_II_CHILD, /* child first */ ++ AuLsc_II_CHILD2, /* rename(2), link(2), and cpup at hnotify */ ++ AuLsc_II_CHILD3, /* copyup dirs */ ++ AuLsc_II_PARENT, /* see AuLsc_I_PARENT in vfsub.h */ ++ AuLsc_II_PARENT2, ++ AuLsc_II_PARENT3, /* copyup dirs */ ++ AuLsc_II_NEW_CHILD ++}; ++ ++/* ++ * 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_new_child, ii_write_lock_new_child, ++ */ ++#define AuReadLockFunc(name, lsc) \ ++static inline void ii_read_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_read_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuWriteLockFunc(name, lsc) \ ++static inline void ii_write_lock_##name(struct inode *i) \ ++{ \ ++ au_rw_write_lock_nested(&au_ii(i)->ii_rwsem, AuLsc_II_##lsc); \ ++} ++ ++#define AuRWLockFuncs(name, lsc) \ ++ AuReadLockFunc(name, lsc) \ ++ AuWriteLockFunc(name, lsc) ++ ++AuRWLockFuncs(child, CHILD); ++AuRWLockFuncs(child2, CHILD2); ++AuRWLockFuncs(child3, CHILD3); ++AuRWLockFuncs(parent, PARENT); ++AuRWLockFuncs(parent2, PARENT2); ++AuRWLockFuncs(parent3, PARENT3); ++AuRWLockFuncs(new_child, NEW_CHILD); ++ ++#undef AuReadLockFunc ++#undef AuWriteLockFunc ++#undef AuRWLockFuncs ++ ++/* ++ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock ++ */ ++AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem); ++ ++#define IiMustNoWaiters(i) AuRwMustNoWaiters(&au_ii(i)->ii_rwsem) ++#define IiMustAnyLock(i) AuRwMustAnyLock(&au_ii(i)->ii_rwsem) ++#define IiMustWriteLock(i) AuRwMustWriteLock(&au_ii(i)->ii_rwsem) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void au_icntnr_init(struct au_icntnr *c) ++{ ++#ifdef CONFIG_AUFS_DEBUG ++ c->vfs_inode.i_mode = 0; ++#endif ++} ++ ++static inline unsigned int au_iigen(struct inode *inode) ++{ ++ return atomic_read(&au_ii(inode)->ii_generation); ++} ++ ++/* tiny test for inode number */ ++/* tmpfs generation is too rough */ ++static inline int au_test_higen(struct inode *inode, struct inode *h_inode) ++{ ++ struct au_iinfo *iinfo; ++ ++ iinfo = au_ii(inode); ++ AuRwMustAnyLock(&iinfo->ii_rwsem); ++ return !(iinfo->ii_hsb1 == h_inode->i_sb ++ && iinfo->ii_higen == h_inode->i_generation); ++} ++ ++static inline void au_iigen_dec(struct inode *inode) ++{ ++ atomic_dec(&au_ii(inode)->ii_generation); ++} ++ ++static inline int au_iigen_test(struct inode *inode, unsigned int sigen) ++{ ++ int err; ++ ++ err = 0; ++ if (unlikely(inode && au_iigen(inode) != sigen)) ++ err = -EIO; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_ii_br_id(struct inode *inode, ++ aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_id; ++} ++ ++static inline aufs_bindex_t au_ibstart(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bstart; ++} ++ ++static inline aufs_bindex_t au_ibend(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_bend; ++} ++ ++static inline struct au_vdir *au_ivdir(struct inode *inode) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_vdir; ++} ++ ++static inline struct dentry *au_hi_wh(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode[0 + bindex].hi_whdentry; ++} ++ ++static inline void au_set_ibstart(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bstart = bindex; ++} ++ ++static inline void au_set_ibend(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_bend = bindex; ++} ++ ++static inline void au_set_ivdir(struct inode *inode, struct au_vdir *vdir) ++{ ++ IiMustWriteLock(inode); ++ au_ii(inode)->ii_vdir = vdir; ++} ++ ++static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) ++{ ++ IiMustAnyLock(inode); ++ return au_ii(inode)->ii_hinode + bindex; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_pinned_parent(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->parent; ++ return NULL; ++} ++ ++static inline struct inode *au_pinned_h_dir(struct au_pin *pin) ++{ ++ if (pin && pin->hdir) ++ return pin->hdir->hi_inode; ++ return NULL; ++} ++ ++static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin) ++{ ++ if (pin) ++ return pin->hdir; ++ return NULL; ++} ++ ++static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry) ++{ ++ if (pin) ++ pin->dentry = dentry; ++} ++ ++static inline void au_pin_set_parent_lflag(struct au_pin *pin, ++ unsigned char lflag) ++{ ++ if (pin) { ++ if (lflag) ++ au_fset_pin(pin->flags, DI_LOCKED); ++ else ++ au_fclr_pin(pin->flags, DI_LOCKED); ++ } ++} ++ ++static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent) ++{ ++ if (pin) { ++ dput(pin->parent); ++ pin->parent = dget(parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_branch; ++#ifdef CONFIG_AUFS_HNOTIFY ++struct au_hnotify_op { ++ void (*ctl)(struct au_hinode *hinode, int do_set); ++ int (*alloc)(struct au_hinode *hinode); ++ void (*free)(struct au_hinode *hinode); ++ ++ void (*fin)(void); ++ int (*init)(void); ++ ++ int (*reset_br)(unsigned int udba, struct au_branch *br, int perm); ++ void (*fin_br)(struct au_branch *br); ++ int (*init_br)(struct au_branch *br, int perm); ++}; ++ ++/* hnotify.c */ ++int au_hn_alloc(struct au_hinode *hinode, struct inode *inode); ++void au_hn_free(struct au_hinode *hinode); ++void au_hn_ctl(struct au_hinode *hinode, int do_set); ++void au_hn_reset(struct inode *inode, unsigned int flags); ++int au_hnotify(struct inode *h_dir, struct au_hnotify *hnotify, u32 mask, ++ struct qstr *h_child_qstr, struct inode *h_child_inode); ++int au_hnotify_reset_br(unsigned int udba, struct au_branch *br, int perm); ++int au_hnotify_init_br(struct au_branch *br, int perm); ++void au_hnotify_fin_br(struct au_branch *br); ++int __init au_hnotify_init(void); ++void au_hnotify_fin(void); ++ ++/* hfsnotify.c */ ++extern const struct au_hnotify_op au_hnotify_op; ++ ++static inline ++void au_hn_init(struct au_hinode *hinode) ++{ ++ hinode->hi_notify = NULL; ++} ++ ++#else ++static inline ++int au_hn_alloc(struct au_hinode *hinode __maybe_unused, ++ struct inode *inode __maybe_unused) ++{ ++ return -EOPNOTSUPP; ++} ++ ++AuStubVoid(au_hn_free, struct au_hinode *hinode __maybe_unused) ++AuStubVoid(au_hn_ctl, struct au_hinode *hinode __maybe_unused, ++ int do_set __maybe_unused) ++AuStubVoid(au_hn_reset, struct inode *inode __maybe_unused, ++ unsigned int flags __maybe_unused) ++AuStubInt0(au_hnotify_reset_br, unsigned int udba __maybe_unused, ++ struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubInt0(au_hnotify_init_br, struct au_branch *br __maybe_unused, ++ int perm __maybe_unused) ++AuStubVoid(au_hnotify_fin_br, struct au_branch *br __maybe_unused) ++AuStubInt0(__init au_hnotify_init, void) ++AuStubVoid(au_hnotify_fin, void) ++AuStubVoid(au_hn_init, struct au_hinode *hinode __maybe_unused) ++#endif /* CONFIG_AUFS_HNOTIFY */ ++ ++static inline void au_hn_suspend(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/0); ++} ++ ++static inline void au_hn_resume(struct au_hinode *hdir) ++{ ++ au_hn_ctl(hdir, /*do_set*/1); ++} ++ ++static inline void au_hn_imtx_lock(struct au_hinode *hdir) ++{ ++ mutex_lock(&hdir->hi_inode->i_mutex); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_lock_nested(struct au_hinode *hdir, ++ unsigned int sc __maybe_unused) ++{ ++ mutex_lock_nested(&hdir->hi_inode->i_mutex, sc); ++ au_hn_suspend(hdir); ++} ++ ++static inline void au_hn_imtx_unlock(struct au_hinode *hdir) ++{ ++ au_hn_resume(hdir); ++ mutex_unlock(&hdir->hi_inode->i_mutex); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_INODE_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/ioctl.c linux-2.6.36/fs/aufs/ioctl.c +--- linux-2.6.36.orig/fs/aufs/ioctl.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/ioctl.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,150 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * ioctl ++ * plink-management and readdir in userspace. ++ * assist the pathconf(3) wrapper library. ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_wbr_fd(struct path *path) ++{ ++ int err, fd; ++ aufs_bindex_t wbi, bindex, bend; ++ struct file *h_file; ++ struct super_block *sb; ++ struct dentry *root; ++ struct au_branch *wbr; ++ ++ err = get_unused_fd(); ++ if (unlikely(err < 0)) ++ goto out; ++ fd = err; ++ ++ wbi = 0; ++ sb = path->dentry->d_sb; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_IR); ++ wbr = au_sbr(sb, wbi); ++ if (!(path->mnt->mnt_flags & MNT_READONLY) ++ && !au_br_writable(wbr->br_perm)) { ++ bend = au_sbend(sb); ++ for (bindex = 1; bindex <= bend; bindex++) { ++ wbr = au_sbr(sb, bindex); ++ if (au_br_writable(wbr->br_perm)) { ++ wbi = bindex; ++ break; ++ } ++ } ++ wbr = au_sbr(sb, wbi); ++ } ++ AuDbg("wbi %d\n", wbi); ++ h_file = au_h_open(root, wbi, O_RDONLY | O_DIRECTORY | O_LARGEFILE, ++ NULL); ++ aufs_read_unlock(root, AuLock_IR); ++ err = PTR_ERR(h_file); ++ if (IS_ERR(h_file)) ++ goto out_fd; ++ ++ atomic_dec(&wbr->br_count); /* cf. au_h_open() */ ++ fd_install(fd, h_file); ++ err = fd; ++ goto out; /* success */ ++ ++out_fd: ++ put_unused_fd(fd); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ioctl(file, cmd, arg); ++ break; ++ ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path); ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_WBR_FD: ++ err = au_wbr_fd(&file->f_path); ++ break; ++ ++ default: ++ /* do not call the lower */ ++ AuDbg("0x%x\n", cmd); ++ err = -ENOTTY; ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ long err; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_compat_ioctl(file, cmd, arg); ++ break; ++ ++ default: ++ err = aufs_ioctl_dir(file, cmd, arg); ++ } ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++#if 0 /* unused yet */ ++long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); ++} ++#endif ++#endif +diff -Nur linux-2.6.36.orig/fs/aufs/loop.c linux-2.6.36/fs/aufs/loop.c +--- linux-2.6.36.orig/fs/aufs/loop.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/loop.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,63 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * support for loopback block device as a branch ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* ++ * test if two lower dentries have overlapping branches. ++ */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding) ++{ ++ struct super_block *h_sb; ++ struct loop_device *l; ++ ++ h_sb = h_adding->d_sb; ++ if (MAJOR(h_sb->s_dev) != LOOP_MAJOR) ++ return 0; ++ ++ l = h_sb->s_bdev->bd_disk->private_data; ++ h_adding = l->lo_backing_file->f_dentry; ++ /* ++ * h_adding can be local NFS. ++ * in this case aufs cannot detect the loop. ++ */ ++ if (unlikely(h_adding->d_sb == sb)) ++ return 1; ++ return !!au_test_subdir(h_adding, sb->s_root); ++} ++ ++/* true if a kernel thread named 'loop[0-9].*' accesses a file */ ++int au_test_loopback_kthread(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ const char c = tsk->comm[4]; ++ ret = ('0' <= c && c <= '9' ++ && !strncmp(tsk->comm, "loop", 4)); ++ } ++ ++ return ret; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/loop.h linux-2.6.36/fs/aufs/loop.h +--- linux-2.6.36.orig/fs/aufs/loop.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/loop.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * support for loopback mount as a branch ++ */ ++ ++#ifndef __AUFS_LOOP_H__ ++#define __AUFS_LOOP_H__ ++ ++#ifdef __KERNEL__ ++ ++struct dentry; ++struct super_block; ++ ++#ifdef CONFIG_AUFS_BDEV_LOOP ++/* loop.c */ ++int au_test_loopback_overlap(struct super_block *sb, struct dentry *h_adding); ++int au_test_loopback_kthread(void); ++#else ++AuStubInt0(au_test_loopback_overlap, struct super_block *sb, ++ struct dentry *h_adding) ++AuStubInt0(au_test_loopback_kthread, void) ++#endif /* BLK_DEV_LOOP */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_LOOP_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/magic.mk linux-2.6.36/fs/aufs/magic.mk +--- linux-2.6.36.orig/fs/aufs/magic.mk 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/magic.mk 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,54 @@ ++ ++# defined in ${srctree}/fs/fuse/inode.c ++# tristate ++ifdef CONFIG_FUSE_FS ++ccflags-y += -DFUSE_SUPER_MAGIC=0x65735546 ++endif ++ ++# defined in ${srctree}/fs/ocfs2/ocfs2_fs.h ++# tristate ++ifdef CONFIG_OCFS2_FS ++ccflags-y += -DOCFS2_SUPER_MAGIC=0x7461636f ++endif ++ ++# defined in ${srctree}/fs/ocfs2/dlm/userdlm.h ++# tristate ++ifdef CONFIG_OCFS2_FS_O2CB ++ccflags-y += -DDLMFS_MAGIC=0x76a9f425 ++endif ++ ++# defined in ${srctree}/fs/cifs/cifsfs.c ++# tristate ++ifdef CONFIG_CIFS_FS ++ccflags-y += -DCIFS_MAGIC_NUMBER=0xFF534D42 ++endif ++ ++# defined in ${srctree}/fs/xfs/xfs_sb.h ++# tristate ++ifdef CONFIG_XFS_FS ++ccflags-y += -DXFS_SB_MAGIC=0x58465342 ++endif ++ ++# defined in ${srctree}/fs/configfs/mount.c ++# tristate ++ifdef CONFIG_CONFIGFS_FS ++ccflags-y += -DCONFIGFS_MAGIC=0x62656570 ++endif ++ ++# defined in ${srctree}/fs/9p/v9fs.h ++# tristate ++ifdef CONFIG_9P_FS ++ccflags-y += -DV9FS_MAGIC=0x01021997 ++endif ++ ++# defined in ${srctree}/fs/ubifs/ubifs.h ++# tristate ++ifdef CONFIG_UBIFS_FS ++ccflags-y += -DUBIFS_SUPER_MAGIC=0x24051905 ++endif ++ ++# defined in ${srctree}/fs/hfsplus/hfsplus_raw.h ++# tristate ++ifdef CONFIG_HFSPLUS_FS ++ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b ++endif +diff -Nur linux-2.6.36.orig/fs/aufs/module.c linux-2.6.36/fs/aufs/module.c +--- linux-2.6.36.orig/fs/aufs/module.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/module.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,182 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * module global variables and operations ++ */ ++ ++#include ++#include ++#include "aufs.h" ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp) ++{ ++ if (new_sz <= nused) ++ return p; ++ ++ p = krealloc(p, new_sz, gfp); ++ if (p) ++ memset(p + nused, 0, new_sz - nused); ++ return p; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * aufs caches ++ */ ++struct kmem_cache *au_cachep[AuCache_Last]; ++static int __init au_cache_init(void) ++{ ++ au_cachep[AuCache_DINFO] = AuCacheCtor(au_dinfo, au_di_init_once); ++ if (au_cachep[AuCache_DINFO]) ++ au_cachep[AuCache_ICNTNR] = AuCacheCtor(au_icntnr, ++ au_icntnr_init_once); ++ if (au_cachep[AuCache_ICNTNR]) ++ au_cachep[AuCache_FINFO] = AuCacheCtor(au_finfo, ++ au_fi_init_once); ++ if (au_cachep[AuCache_FINFO]) ++ au_cachep[AuCache_VDIR] = AuCache(au_vdir); ++ if (au_cachep[AuCache_VDIR]) ++ au_cachep[AuCache_DEHSTR] = AuCache(au_vdir_dehstr); ++ if (au_cachep[AuCache_DEHSTR]) ++ return 0; ++ ++ return -ENOMEM; ++} ++ ++static void au_cache_fin(void) ++{ ++ int i; ++ ++ /* including AuCache_HNOTIFY */ ++ for (i = 0; i < AuCache_Last; i++) ++ if (au_cachep[i]) { ++ kmem_cache_destroy(au_cachep[i]); ++ au_cachep[i] = NULL; ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_dir_roflags; ++ ++#ifdef CONFIG_AUFS_SBILIST ++struct au_splhead au_sbilist; ++#endif ++ ++/* ++ * functions for module interface. ++ */ ++MODULE_LICENSE("GPL"); ++/* MODULE_LICENSE("GPL v2"); */ ++MODULE_AUTHOR("Junjiro R. Okajima "); ++MODULE_DESCRIPTION(AUFS_NAME ++ " -- Advanced multi layered unification filesystem"); ++MODULE_VERSION(AUFS_VERSION); ++ ++/* this module parameter has no meaning when SYSFS is disabled */ ++int sysaufs_brs = 1; ++MODULE_PARM_DESC(brs, "use /fs/aufs/si_*/brN"); ++module_param_named(brs, sysaufs_brs, int, S_IRUGO); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static char au_esc_chars[0x20 + 3]; /* 0x01-0x20, backslash, del, and NULL */ ++ ++int au_seq_path(struct seq_file *seq, struct path *path) ++{ ++ return seq_path(seq, path, au_esc_chars); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int __init aufs_init(void) ++{ ++ int err, i; ++ char *p; ++ ++ 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); ++ ++ au_sbilist_init(); ++ sysaufs_brs_init(); ++ au_debug_init(); ++ au_dy_init(); ++ err = sysaufs_init(); ++ if (unlikely(err)) ++ goto out; ++ err = au_procfs_init(); ++ if (unlikely(err)) ++ goto out_sysaufs; ++ err = au_wkq_init(); ++ if (unlikely(err)) ++ goto out_procfs; ++ err = au_hnotify_init(); ++ if (unlikely(err)) ++ goto out_wkq; ++ err = au_sysrq_init(); ++ if (unlikely(err)) ++ goto out_hin; ++ err = au_cache_init(); ++ if (unlikely(err)) ++ goto out_sysrq; ++ err = register_filesystem(&aufs_fs_type); ++ if (unlikely(err)) ++ goto out_cache; ++ /* since we define pr_fmt, call printk directly */ ++ printk(KERN_INFO AUFS_NAME " " AUFS_VERSION "\n"); ++ goto out; /* success */ ++ ++out_cache: ++ au_cache_fin(); ++out_sysrq: ++ au_sysrq_fin(); ++out_hin: ++ au_hnotify_fin(); ++out_wkq: ++ au_wkq_fin(); ++out_procfs: ++ au_procfs_fin(); ++out_sysaufs: ++ sysaufs_fin(); ++ au_dy_fin(); ++out: ++ return err; ++} ++ ++static void __exit aufs_exit(void) ++{ ++ unregister_filesystem(&aufs_fs_type); ++ au_cache_fin(); ++ au_sysrq_fin(); ++ au_hnotify_fin(); ++ au_wkq_fin(); ++ au_procfs_fin(); ++ sysaufs_fin(); ++ au_dy_fin(); ++} ++ ++module_init(aufs_init); ++module_exit(aufs_exit); +diff -Nur linux-2.6.36.orig/fs/aufs/module.h linux-2.6.36/fs/aufs/module.h +--- linux-2.6.36.orig/fs/aufs/module.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/module.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,91 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * module initialization and module-global ++ */ ++ ++#ifndef __AUFS_MODULE_H__ ++#define __AUFS_MODULE_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++struct path; ++struct seq_file; ++ ++/* module parameters */ ++extern int sysaufs_brs; ++ ++/* ---------------------------------------------------------------------- */ ++ ++extern int au_dir_roflags; ++ ++void *au_kzrealloc(void *p, unsigned int nused, unsigned int new_sz, gfp_t gfp); ++int au_seq_path(struct seq_file *seq, struct path *path); ++ ++#ifdef CONFIG_PROC_FS ++/* procfs.c */ ++int __init au_procfs_init(void); ++void au_procfs_fin(void); ++#else ++AuStubInt0(au_procfs_init, void); ++AuStubVoid(au_procfs_fin, void); ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* kmem cache */ ++enum { ++ AuCache_DINFO, ++ AuCache_ICNTNR, ++ AuCache_FINFO, ++ AuCache_VDIR, ++ AuCache_DEHSTR, ++#ifdef CONFIG_AUFS_HNOTIFY ++ AuCache_HNOTIFY, ++#endif ++ AuCache_Last ++}; ++ ++#define AuCacheFlags (SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD) ++#define AuCache(type) KMEM_CACHE(type, AuCacheFlags) ++#define AuCacheCtor(type, ctor) \ ++ kmem_cache_create(#type, sizeof(struct type), \ ++ __alignof__(struct type), AuCacheFlags, ctor) ++ ++extern struct kmem_cache *au_cachep[]; ++ ++#define AuCacheFuncs(name, index) \ ++static inline struct au_##name *au_cache_alloc_##name(void) \ ++{ return kmem_cache_alloc(au_cachep[AuCache_##index], GFP_NOFS); } \ ++static inline void au_cache_free_##name(struct au_##name *p) \ ++{ kmem_cache_free(au_cachep[AuCache_##index], p); } ++ ++AuCacheFuncs(dinfo, DINFO); ++AuCacheFuncs(icntnr, ICNTNR); ++AuCacheFuncs(finfo, FINFO); ++AuCacheFuncs(vdir, VDIR); ++AuCacheFuncs(vdir_dehstr, DEHSTR); ++#ifdef CONFIG_AUFS_HNOTIFY ++AuCacheFuncs(hnotify, HNOTIFY); ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_MODULE_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/mtx.h linux-2.6.36/fs/aufs/mtx.h +--- linux-2.6.36.orig/fs/aufs/mtx.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/mtx.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,48 @@ ++/* ++ * Copyright (C) 2010-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * very ugly approach for aufs_mmap() ++ * never include this file from other than f_op.c. ++ * see f_op.c in detail. ++ */ ++ ++#ifndef __AUFS_MTX_H__ ++#define __AUFS_MTX_H__ ++ ++#ifdef __KERNEL__ ++ ++/* copied from ../kernel/mutex{,-debug}.h */ ++struct mutex; ++struct thread_info; ++#ifdef CONFIG_DEBUG_MUTEXES ++static inline void mutex_set_owner(struct mutex *lock) ++{ ++ lock->owner = current_thread_info(); ++} ++#else ++static inline void mutex_set_owner(struct mutex *lock) ++{ ++#ifdef CONFIG_SMP ++ lock->owner = current_thread_info(); ++#endif ++} ++#endif ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_MTX_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/opts.c linux-2.6.36/fs/aufs/opts.c +--- linux-2.6.36.orig/fs/aufs/opts.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/opts.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1595 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#include ++#include ++#include ++#include /* a distribution requires */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++enum { ++ Opt_br, ++ Opt_add, Opt_del, Opt_mod, Opt_reorder, Opt_append, Opt_prepend, ++ Opt_idel, Opt_imod, Opt_ireorder, ++ Opt_dirwh, Opt_rdcache, Opt_rdblk, Opt_rdhash, Opt_rendir, ++ Opt_rdblk_def, Opt_rdhash_def, ++ Opt_xino, Opt_zxino, Opt_noxino, ++ Opt_trunc_xino, Opt_trunc_xino_v, Opt_notrunc_xino, ++ Opt_trunc_xino_path, Opt_itrunc_xino, ++ Opt_trunc_xib, Opt_notrunc_xib, ++ Opt_shwh, Opt_noshwh, ++ Opt_plink, Opt_noplink, Opt_list_plink, ++ Opt_udba, ++ Opt_dio, Opt_nodio, ++ /* 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_refrof, Opt_norefrof, ++ Opt_verbose, Opt_noverbose, ++ Opt_sum, Opt_nosum, Opt_wsum, ++ Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err ++}; ++ ++static match_table_t options = { ++ {Opt_br, "br=%s"}, ++ {Opt_br, "br:%s"}, ++ ++ {Opt_add, "add=%d:%s"}, ++ {Opt_add, "add:%d:%s"}, ++ {Opt_add, "ins=%d:%s"}, ++ {Opt_add, "ins:%d:%s"}, ++ {Opt_append, "append=%s"}, ++ {Opt_append, "append:%s"}, ++ {Opt_prepend, "prepend=%s"}, ++ {Opt_prepend, "prepend:%s"}, ++ ++ {Opt_del, "del=%s"}, ++ {Opt_del, "del:%s"}, ++ /* {Opt_idel, "idel:%d"}, */ ++ {Opt_mod, "mod=%s"}, ++ {Opt_mod, "mod:%s"}, ++ /* {Opt_imod, "imod:%d:%s"}, */ ++ ++ {Opt_dirwh, "dirwh=%d"}, ++ ++ {Opt_xino, "xino=%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_itrunc_xino, "itrunc_xino=%d"}, ++ /* {Opt_zxino, "zxino=%s"}, */ ++ {Opt_trunc_xib, "trunc_xib"}, ++ {Opt_notrunc_xib, "notrunc_xib"}, ++ ++#ifdef CONFIG_PROC_FS ++ {Opt_plink, "plink"}, ++#else ++ {Opt_ignore_silent, "plink"}, ++#endif ++ ++ {Opt_noplink, "noplink"}, ++ ++#ifdef CONFIG_AUFS_DEBUG ++ {Opt_list_plink, "list_plink"}, ++#endif ++ ++ {Opt_udba, "udba=%s"}, ++ ++ {Opt_dio, "dio"}, ++ {Opt_nodio, "nodio"}, ++ ++ {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"}, ++ ++ /* keep them temporary */ ++ {Opt_ignore_silent, "coo=%s"}, ++ {Opt_ignore_silent, "nodlgt"}, ++ {Opt_ignore_silent, "nodirperm1"}, ++ {Opt_ignore_silent, "clean_plink"}, ++ ++#ifdef CONFIG_AUFS_SHWH ++ {Opt_shwh, "shwh"}, ++#endif ++ {Opt_noshwh, "noshwh"}, ++ ++ {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_sum, "sum"}, ++ {Opt_nosum, "nosum"}, ++ {Opt_wsum, "wsum"}, ++ ++ {Opt_rdcache, "rdcache=%d"}, ++ {Opt_rdblk, "rdblk=%d"}, ++ {Opt_rdblk_def, "rdblk=def"}, ++ {Opt_rdhash, "rdhash=%d"}, ++ {Opt_rdhash_def, "rdhash=def"}, ++ ++ {Opt_wbr_create, "create=%s"}, ++ {Opt_wbr_create, "create_policy=%s"}, ++ {Opt_wbr_copyup, "cpup=%s"}, ++ {Opt_wbr_copyup, "copyup=%s"}, ++ {Opt_wbr_copyup, "copyup_policy=%s"}, ++ ++ /* internal use for the scripts */ ++ {Opt_ignore_silent, "si=%s"}, ++ ++ {Opt_br, "dirs=%s"}, ++ {Opt_ignore, "debug=%d"}, ++ {Opt_ignore, "delete=whiteout"}, ++ {Opt_ignore, "delete=all"}, ++ {Opt_ignore, "imap=%s"}, ++ ++ /* temporary workaround, due to old mount(8)? */ ++ {Opt_ignore_silent, "relatime"}, ++ ++ {Opt_err, NULL} ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static const char *au_parser_pattern(int val, struct match_token *token) ++{ ++ while (token->pattern) { ++ if (token->token == val) ++ return token->pattern; ++ token++; ++ } ++ BUG(); ++ return "??"; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t brperms = { ++ {AuBrPerm_RO, AUFS_BRPERM_RO}, ++ {AuBrPerm_RR, AUFS_BRPERM_RR}, ++ {AuBrPerm_RW, AUFS_BRPERM_RW}, ++ ++ {AuBrPerm_ROWH, AUFS_BRPERM_ROWH}, ++ {AuBrPerm_RRWH, AUFS_BRPERM_RRWH}, ++ {AuBrPerm_RWNoLinkWH, AUFS_BRPERM_RWNLWH}, ++ ++ {AuBrPerm_ROWH, "nfsro"}, ++ {AuBrPerm_RO, NULL} ++}; ++ ++static int noinline_for_stack br_perm_val(char *perm) ++{ ++ int val; ++ substring_t args[MAX_OPT_ARGS]; ++ ++ val = match_token(perm, brperms, args); ++ return val; ++} ++ ++const char *au_optstr_br_perm(int brperm) ++{ ++ return au_parser_pattern(brperm, (void *)brperms); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static match_table_t udbalevel = { ++ {AuOpt_UDBA_REVAL, "reval"}, ++ {AuOpt_UDBA_NONE, "none"}, ++#ifdef CONFIG_AUFS_HNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "notify"}, /* abstraction */ ++#ifdef CONFIG_AUFS_HFSNOTIFY ++ {AuOpt_UDBA_HNOTIFY, "fsnotify"}, ++#endif ++#endif ++ {-1, NULL} ++}; ++ ++static int noinline_for_stack udba_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, udbalevel, args); ++} ++ ++const char *au_optstr_udba(int udba) ++{ ++ return au_parser_pattern(udba, (void *)udbalevel); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++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 and cmdline.c ++ * gave up calling memparse() since it uses simple_strtoull() instead of ++ * strict_...(). ++ */ ++static int noinline_for_stack ++au_match_ull(substring_t *s, unsigned long long *result) ++{ ++ int err; ++ unsigned int len; ++ char a[32]; ++ ++ err = -ERANGE; ++ len = s->to - s->from; ++ if (len + 1 <= sizeof(a)) { ++ memcpy(a, s->from, len); ++ a[len] = '\0'; ++ err = strict_strtoull(a, 0, result); ++ } ++ return err; ++} ++ ++static int au_wbr_mfs_wmark(substring_t *arg, char *str, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ unsigned long long ull; ++ ++ err = 0; ++ if (!au_match_ull(arg, &ull)) ++ create->mfsrr_watermark = ull; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ 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 && n <= AUFS_MFS_MAX_SEC) ++ create->mfs_second = n; ++ else { ++ pr_err("bad integer in %s\n", str); ++ err = -EINVAL; ++ } ++ ++ return err; ++} ++ ++static int noinline_for_stack ++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_DEF_SEC; ++ break; ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFSV: ++ e = au_wbr_mfs_sec(&args[0], str, create); ++ if (unlikely(e)) ++ err = e; ++ break; ++ } ++ ++ return err; ++} ++ ++const char *au_optstr_wbr_create(int wbr_create) ++{ ++ return au_parser_pattern(wbr_create, (void *)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 noinline_for_stack au_wbr_copyup_val(char *str) ++{ ++ substring_t args[MAX_OPT_ARGS]; ++ ++ return match_token(str, au_wbr_copyup_policy, args); ++} ++ ++const char *au_optstr_wbr_copyup(int wbr_copyup) ++{ ++ return au_parser_pattern(wbr_copyup, (void *)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; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ u.add = &opt->add; ++ AuDbg("add {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ u.del = &opt->del; ++ AuDbg("del {%s, %p}\n", ++ u.del->pathname, u.del->h_path.dentry); ++ break; ++ case Opt_mod: ++ case Opt_imod: ++ u.mod = &opt->mod; ++ AuDbg("mod {%s, 0x%x, %p}\n", ++ u.mod->path, u.mod->perm, u.mod->h_root); ++ break; ++ case Opt_append: ++ u.add = &opt->add; ++ AuDbg("append {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_prepend: ++ u.add = &opt->add; ++ AuDbg("prepend {b%d, %s, 0x%x, %p}\n", ++ u.add->bindex, u.add->pathname, u.add->perm, ++ u.add->path.dentry); ++ break; ++ case Opt_dirwh: ++ AuDbg("dirwh %d\n", opt->dirwh); ++ break; ++ case Opt_rdcache: ++ AuDbg("rdcache %d\n", opt->rdcache); ++ break; ++ case Opt_rdblk: ++ AuDbg("rdblk %u\n", opt->rdblk); ++ break; ++ case Opt_rdblk_def: ++ AuDbg("rdblk_def\n"); ++ break; ++ case Opt_rdhash: ++ AuDbg("rdhash %u\n", opt->rdhash); ++ break; ++ case Opt_rdhash_def: ++ AuDbg("rdhash_def\n"); ++ break; ++ case Opt_xino: ++ u.xino = &opt->xino; ++ AuDbg("xino {%s %.*s}\n", ++ u.xino->path, ++ AuDLNPair(u.xino->file->f_dentry)); ++ break; ++ case Opt_trunc_xino: ++ AuLabel(trunc_xino); ++ break; ++ case Opt_notrunc_xino: ++ AuLabel(notrunc_xino); ++ break; ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ AuDbg("trunc_xino %d\n", u.xino_itrunc->bindex); ++ break; ++ ++ case Opt_noxino: ++ AuLabel(noxino); ++ break; ++ case Opt_trunc_xib: ++ AuLabel(trunc_xib); ++ break; ++ case Opt_notrunc_xib: ++ AuLabel(notrunc_xib); ++ break; ++ case Opt_shwh: ++ AuLabel(shwh); ++ break; ++ case Opt_noshwh: ++ AuLabel(noshwh); ++ break; ++ case Opt_plink: ++ AuLabel(plink); ++ break; ++ case Opt_noplink: ++ AuLabel(noplink); ++ break; ++ case Opt_list_plink: ++ AuLabel(list_plink); ++ break; ++ case Opt_udba: ++ AuDbg("udba %d, %s\n", ++ opt->udba, au_optstr_udba(opt->udba)); ++ break; ++ case Opt_dio: ++ AuLabel(dio); ++ break; ++ case Opt_nodio: ++ AuLabel(nodio); ++ break; ++ case Opt_diropq_a: ++ AuLabel(diropq_a); ++ break; ++ case Opt_diropq_w: ++ AuLabel(diropq_w); ++ break; ++ case Opt_warn_perm: ++ AuLabel(warn_perm); ++ break; ++ case Opt_nowarn_perm: ++ AuLabel(nowarn_perm); ++ break; ++ case Opt_refrof: ++ AuLabel(refrof); ++ break; ++ case Opt_norefrof: ++ AuLabel(norefrof); ++ break; ++ case Opt_verbose: ++ AuLabel(verbose); ++ break; ++ case Opt_noverbose: ++ AuLabel(noverbose); ++ break; ++ case Opt_sum: ++ AuLabel(sum); ++ break; ++ case Opt_nosum: ++ AuLabel(nosum); ++ break; ++ case Opt_wsum: ++ AuLabel(wsum); ++ break; ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ AuDbg("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: ++ AuDbg("%d sec\n", u.create->mfs_second); ++ break; ++ case AuWbrCreate_MFSRR: ++ AuDbg("%llu watermark\n", ++ u.create->mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ AuDbg("%llu watermark, %d sec\n", ++ u.create->mfsrr_watermark, ++ u.create->mfs_second); ++ break; ++ } ++ break; ++ case Opt_wbr_copyup: ++ AuDbg("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; ++ ++ opt = opts->opt; ++ while (opt->type != Opt_tail) { ++ switch (opt->type) { ++ case Opt_add: ++ case Opt_append: ++ case Opt_prepend: ++ path_put(&opt->add.path); ++ break; ++ case Opt_del: ++ case Opt_idel: ++ path_put(&opt->del.h_path); ++ 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, unsigned long sb_flags, ++ aufs_bindex_t bindex) ++{ ++ int err; ++ struct au_opt_add *add = &opt->add; ++ char *p; ++ ++ add->bindex = bindex; ++ add->perm = AuBrPerm_Last; ++ add->pathname = opt_str; ++ p = strchr(opt_str, '='); ++ if (p) { ++ *p++ = 0; ++ if (*p) ++ add->perm = br_perm_val(p); ++ } ++ ++ err = vfsub_kern_path(add->pathname, lkup_dirflags, &add->path); ++ if (!err) { ++ if (!p) { ++ add->perm = AuBrPerm_RO; ++ if (au_test_fs_rr(add->path.dentry->d_sb)) ++ add->perm = AuBrPerm_RR; ++ else if (!bindex && !(sb_flags & MS_RDONLY)) ++ add->perm = AuBrPerm_RW; ++ } ++ opt->type = Opt_add; ++ goto out; ++ } ++ pr_err("lookup failed %s (%d)\n", add->pathname, err); ++ err = -EINVAL; ++ ++out: ++ return err; ++} ++ ++static int au_opts_parse_del(struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ ++ del->pathname = args[0].from; ++ AuDbg("del path %s\n", del->pathname); ++ ++ err = vfsub_kern_path(del->pathname, lkup_dirflags, &del->h_path); ++ if (unlikely(err)) ++ pr_err("lookup failed %s (%d)\n", del->pathname, err); ++ ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_idel(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_del *del, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ del->h_path.dentry = dget(au_h_dptr(root, bindex)); ++ del->h_path.mnt = mntget(au_sbr_mnt(sb, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int noinline_for_stack ++au_opts_parse_mod(struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct path path; ++ char *p; ++ ++ err = -EINVAL; ++ mod->path = args[0].from; ++ p = strchr(mod->path, '='); ++ if (unlikely(!p)) { ++ pr_err("no permssion %s\n", args[0].from); ++ goto out; ++ } ++ ++ *p++ = 0; ++ err = vfsub_kern_path(mod->path, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", mod->path, err); ++ goto out; ++ } ++ ++ mod->perm = br_perm_val(p); ++ AuDbg("mod path %s, perm 0x%x, %s\n", mod->path, mod->perm, p); ++ mod->h_root = dget(path.dentry); ++ path_put(&path); ++ ++out: ++ return err; ++} ++ ++#if 0 /* reserved for future use */ ++static int au_opts_parse_imod(struct super_block *sb, aufs_bindex_t bindex, ++ struct au_opt_mod *mod, substring_t args[]) ++{ ++ int err; ++ struct dentry *root; ++ ++ err = -EINVAL; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (bindex < 0 || au_sbend(sb) < bindex) { ++ pr_err("out of bounds, %d\n", bindex); ++ goto out; ++ } ++ ++ err = 0; ++ mod->perm = br_perm_val(args[1].from); ++ AuDbg("mod path %s, perm 0x%x, %s\n", ++ mod->path, mod->perm, args[1].from); ++ mod->h_root = dget(au_h_dptr(root, bindex)); ++ ++out: ++ aufs_read_unlock(root, !AuLock_IR); ++ return err; ++} ++#endif ++ ++static int au_opts_parse_xino(struct super_block *sb, struct au_opt_xino *xino, ++ substring_t args[]) ++{ ++ int err; ++ struct file *file; ++ ++ file = au_xino_create(sb, args[0].from, /*silent*/0); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(file->f_dentry->d_sb == sb)) { ++ fput(file); ++ pr_err("%s must be outside\n", args[0].from); ++ goto out; ++ } ++ ++ err = 0; ++ xino->file = file; ++ xino->path = args[0].from; ++ ++out: ++ return err; ++} ++ ++static int noinline_for_stack ++au_opts_parse_xino_itrunc_path(struct super_block *sb, ++ struct au_opt_xino_itrunc *xino_itrunc, ++ substring_t args[]) ++{ ++ int err; ++ aufs_bindex_t bend, bindex; ++ struct path path; ++ struct dentry *root; ++ ++ err = vfsub_kern_path(args[0].from, lkup_dirflags, &path); ++ if (unlikely(err)) { ++ pr_err("lookup failed %s (%d)\n", args[0].from, err); ++ goto out; ++ } ++ ++ xino_itrunc->bindex = -1; ++ root = sb->s_root; ++ aufs_read_lock(root, AuLock_FLUSH); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (au_h_dptr(root, bindex) == path.dentry) { ++ xino_itrunc->bindex = bindex; ++ break; ++ } ++ } ++ aufs_read_unlock(root, !AuLock_IR); ++ path_put(&path); ++ ++ if (unlikely(xino_itrunc->bindex < 0)) { ++ pr_err("no such branch %s\n", args[0].from); ++ err = -EINVAL; ++ } ++ ++out: ++ return err; ++} ++ ++/* called without aufs lock */ ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts) ++{ ++ int err, n, token; ++ aufs_bindex_t bindex; ++ unsigned char skipped; ++ struct dentry *root; ++ struct au_opt *opt, *opt_tail; ++ char *opt_str; ++ /* reduce the stack space */ ++ union { ++ struct au_opt_xino_itrunc *xino_itrunc; ++ struct au_opt_wbr_create *create; ++ } u; ++ struct { ++ substring_t args[MAX_OPT_ARGS]; ++ } *a; ++ ++ err = -ENOMEM; ++ a = kmalloc(sizeof(*a), GFP_NOFS); ++ if (unlikely(!a)) ++ goto out; ++ ++ 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; ++ skipped = 0; ++ token = match_token(opt_str, options, a->args); ++ switch (token) { ++ case Opt_br: ++ err = 0; ++ while (!err && (opt_str = strsep(&a->args[0].from, ":")) ++ && *opt_str) { ++ err = opt_add(opt, opt_str, opts->sb_flags, ++ bindex++); ++ if (unlikely(!err && ++opt > opt_tail)) { ++ err = -E2BIG; ++ break; ++ } ++ opt->type = Opt_tail; ++ skipped = 1; ++ } ++ break; ++ case Opt_add: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ bindex = n; ++ err = opt_add(opt, a->args[1].from, opts->sb_flags, ++ bindex); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_append: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*dummy bindex*/1); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_prepend: ++ err = opt_add(opt, a->args[0].from, opts->sb_flags, ++ /*bindex*/0); ++ if (!err) ++ opt->type = token; ++ break; ++ case Opt_del: ++ err = au_opts_parse_del(&opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#if 0 /* reserved for future use */ ++ case Opt_idel: ++ del->pathname = "(indexed)"; ++ if (unlikely(match_int(&args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_idel(sb, n, &opt->del, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_mod: ++ err = au_opts_parse_mod(&opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#ifdef IMOD /* reserved for future use */ ++ case Opt_imod: ++ u.mod->path = "(indexed)"; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ err = au_opts_parse_imod(sb, n, &opt->mod, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++#endif ++ case Opt_xino: ++ err = au_opts_parse_xino(sb, &opt->xino, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_trunc_xino_path: ++ err = au_opts_parse_xino_itrunc_path ++ (sb, &opt->xino_itrunc, a->args); ++ if (!err) ++ opt->type = token; ++ break; ++ ++ case Opt_itrunc_xino: ++ u.xino_itrunc = &opt->xino_itrunc; ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ u.xino_itrunc->bindex = n; ++ aufs_read_lock(root, AuLock_FLUSH); ++ if (n < 0 || au_sbend(sb) < n) { ++ pr_err("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(&a->args[0], &opt->dirwh))) ++ break; ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_rdcache: ++ if (unlikely(match_int(&a->args[0], &n))) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n > AUFS_RDCACHE_MAX)) { ++ pr_err("rdcache must be smaller than %d\n", ++ AUFS_RDCACHE_MAX); ++ break; ++ } ++ opt->rdcache = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdblk: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ if (unlikely(n && n < NAME_MAX)) { ++ pr_err("rdblk must be larger than %d\n", ++ NAME_MAX); ++ break; ++ } ++ opt->rdblk = n; ++ err = 0; ++ opt->type = token; ++ break; ++ case Opt_rdhash: ++ if (unlikely(match_int(&a->args[0], &n) ++ || n < 0 ++ || n * sizeof(struct hlist_head) ++ > KMALLOC_MAX_SIZE)) { ++ pr_err("bad integer in %s\n", opt_str); ++ break; ++ } ++ opt->rdhash = n; ++ 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_shwh: ++ case Opt_noshwh: ++ case Opt_plink: ++ case Opt_noplink: ++ case Opt_list_plink: ++ case Opt_dio: ++ case Opt_nodio: ++ case Opt_diropq_a: ++ case Opt_diropq_w: ++ case Opt_warn_perm: ++ case Opt_nowarn_perm: ++ case Opt_refrof: ++ case Opt_norefrof: ++ case Opt_verbose: ++ case Opt_noverbose: ++ case Opt_sum: ++ case Opt_nosum: ++ case Opt_wsum: ++ case Opt_rdblk_def: ++ case Opt_rdhash_def: ++ err = 0; ++ opt->type = token; ++ break; ++ ++ case Opt_udba: ++ opt->udba = udba_val(a->args[0].from); ++ if (opt->udba >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_wbr_create: ++ u.create = &opt->wbr_create; ++ u.create->wbr_create ++ = au_wbr_create_val(a->args[0].from, u.create); ++ if (u.create->wbr_create >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ case Opt_wbr_copyup: ++ opt->wbr_copyup = au_wbr_copyup_val(a->args[0].from); ++ if (opt->wbr_copyup >= 0) { ++ err = 0; ++ opt->type = token; ++ } else ++ pr_err("wrong value, %s\n", opt_str); ++ break; ++ ++ case Opt_ignore: ++ pr_warning("ignored %s\n", opt_str); ++ /*FALLTHROUGH*/ ++ case Opt_ignore_silent: ++ skipped = 1; ++ err = 0; ++ break; ++ case Opt_err: ++ pr_err("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; ++ } ++ } ++ ++ kfree(a); ++ dump_opts(opts); ++ if (unlikely(err)) ++ au_opts_free(opts); ++ ++out: ++ return err; ++} ++ ++static int au_opt_wbr_create(struct super_block *sb, ++ struct au_opt_wbr_create *create) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ if (sbinfo->si_wbr_create_ops->fin) { ++ err = sbinfo->si_wbr_create_ops->fin(sb); ++ if (!err) ++ err = 1; ++ } ++ ++ sbinfo->si_wbr_create = create->wbr_create; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + create->wbr_create; ++ switch (create->wbr_create) { ++ case AuWbrCreate_MFSRRV: ++ case AuWbrCreate_MFSRR: ++ sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; ++ /*FALLTHROUGH*/ ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_MFSV: ++ case AuWbrCreate_PMFS: ++ case AuWbrCreate_PMFSV: ++ sbinfo->si_wbr_mfs.mfs_expire ++ = msecs_to_jiffies(create->mfs_second * MSEC_PER_SEC); ++ break; ++ } ++ ++ if (sbinfo->si_wbr_create_ops->init) ++ sbinfo->si_wbr_create_ops->init(sb); /* ignore */ ++ ++ return err; ++} ++ ++/* ++ * returns, ++ * plus: processed without an error ++ * zero: unprocessed ++ */ ++static int au_opt_simple(struct super_block *sb, struct au_opt *opt, ++ struct au_opts *opts) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 1; /* handled */ ++ sbinfo = au_sbi(sb); ++ switch (opt->type) { ++ case Opt_udba: ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= opt->udba; ++ opts->given_udba |= opt->udba; ++ break; ++ ++ case Opt_plink: ++ au_opt_set(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_noplink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_opt_clr(sbinfo->si_mntflags, PLINK); ++ break; ++ case Opt_list_plink: ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_list(sb); ++ break; ++ ++ case Opt_dio: ++ au_opt_set(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ case Opt_nodio: ++ au_opt_clr(sbinfo->si_mntflags, DIO); ++ au_fset_opts(opts->flags, REFRESH_DYAOP); ++ break; ++ ++ case Opt_diropq_a: ++ au_opt_set(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ case Opt_diropq_w: ++ au_opt_clr(sbinfo->si_mntflags, ALWAYS_DIROPQ); ++ break; ++ ++ case Opt_warn_perm: ++ au_opt_set(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ case Opt_nowarn_perm: ++ au_opt_clr(sbinfo->si_mntflags, WARN_PERM); ++ break; ++ ++ case Opt_refrof: ++ au_opt_set(sbinfo->si_mntflags, REFROF); ++ break; ++ case Opt_norefrof: ++ au_opt_clr(sbinfo->si_mntflags, REFROF); ++ break; ++ ++ case Opt_verbose: ++ au_opt_set(sbinfo->si_mntflags, VERBOSE); ++ break; ++ case Opt_noverbose: ++ au_opt_clr(sbinfo->si_mntflags, VERBOSE); ++ break; ++ ++ case Opt_sum: ++ au_opt_set(sbinfo->si_mntflags, SUM); ++ break; ++ case Opt_wsum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_set(sbinfo->si_mntflags, SUM_W); ++ case Opt_nosum: ++ au_opt_clr(sbinfo->si_mntflags, SUM); ++ au_opt_clr(sbinfo->si_mntflags, SUM_W); ++ break; ++ ++ case Opt_wbr_create: ++ err = au_opt_wbr_create(sb, &opt->wbr_create); ++ break; ++ case Opt_wbr_copyup: ++ sbinfo->si_wbr_copyup = opt->wbr_copyup; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + opt->wbr_copyup; ++ break; ++ ++ case Opt_dirwh: ++ sbinfo->si_dirwh = opt->dirwh; ++ break; ++ ++ case Opt_rdcache: ++ sbinfo->si_rdcache ++ = msecs_to_jiffies(opt->rdcache * MSEC_PER_SEC); ++ break; ++ case Opt_rdblk: ++ sbinfo->si_rdblk = opt->rdblk; ++ break; ++ case Opt_rdblk_def: ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ break; ++ case Opt_rdhash: ++ sbinfo->si_rdhash = opt->rdhash; ++ break; ++ case Opt_rdhash_def: ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ break; ++ ++ case Opt_shwh: ++ au_opt_set(sbinfo->si_mntflags, SHWH); ++ break; ++ case Opt_noshwh: ++ au_opt_clr(sbinfo->si_mntflags, SHWH); ++ break; ++ ++ case Opt_trunc_xino: ++ au_opt_set(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ case Opt_notrunc_xino: ++ au_opt_clr(sbinfo->si_mntflags, TRUNC_XINO); ++ break; ++ ++ case Opt_trunc_xino_path: ++ case Opt_itrunc_xino: ++ err = au_xino_trunc(sb, opt->xino_itrunc.bindex); ++ if (!err) ++ err = 1; ++ break; ++ ++ case Opt_trunc_xib: ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ break; ++ case Opt_notrunc_xib: ++ au_fclr_opts(opts->flags, TRUNC_XIB); ++ break; ++ ++ default: ++ err = 0; ++ break; ++ } ++ ++ 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; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_append: ++ opt->add.bindex = au_sbend(sb) + 1; ++ if (opt->add.bindex < 0) ++ opt->add.bindex = 0; ++ goto add; ++ case Opt_prepend: ++ opt->add.bindex = 0; ++ add: ++ case Opt_add: ++ err = au_br_add(sb, &opt->add, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_del: ++ case Opt_idel: ++ err = au_br_del(sb, &opt->del, ++ au_ftest_opts(opts->flags, REMOUNT)); ++ if (!err) { ++ err = 1; ++ au_fset_opts(opts->flags, TRUNC_XIB); ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ ++ case Opt_mod: ++ case Opt_imod: ++ err = au_br_mod(sb, &opt->mod, ++ au_ftest_opts(opts->flags, REMOUNT), ++ &do_refresh); ++ if (!err) { ++ err = 1; ++ if (do_refresh) ++ au_fset_opts(opts->flags, REFRESH); ++ } ++ break; ++ } ++ ++ 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; ++ aufs_bindex_t bend, bindex; ++ struct dentry *root, *parent, *h_root; ++ ++ err = 0; ++ switch (opt->type) { ++ case Opt_xino: ++ err = au_xino_set(sb, &opt->xino, ++ !!au_ftest_opts(opts->flags, REMOUNT)); ++ if (unlikely(err)) ++ break; ++ ++ *opt_xino = &opt->xino; ++ au_xino_brid_set(sb, -1); ++ ++ /* safe d_parent access */ ++ parent = opt->xino.file->f_dentry->d_parent; ++ root = sb->s_root; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ h_root = au_h_dptr(root, bindex); ++ if (h_root == parent) { ++ au_xino_brid_set(sb, au_sbr_id(sb, bindex)); ++ break; ++ } ++ } ++ break; ++ ++ case Opt_noxino: ++ au_xino_clr(sb); ++ au_xino_brid_set(sb, -1); ++ *opt_xino = (void *)-1; ++ break; ++ } ++ ++ return err; ++} ++ ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ unsigned char do_plink, skip, do_free; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *root; ++ struct inode *dir, *h_dir; ++ struct au_sbinfo *sbinfo; ++ struct au_hinode *hdir; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!(sbinfo->si_mntflags & AuOptMask_UDBA)); ++ ++ if (!(sb_flags & MS_RDONLY)) { ++ if (unlikely(!au_br_writable(au_sbr_perm(sb, 0)))) ++ pr_warning("first branch should be rw\n"); ++ if (unlikely(au_opt_test(sbinfo->si_mntflags, SHWH))) ++ pr_warning("shwh should be used with ro\n"); ++ } ++ ++ if (au_opt_test((sbinfo->si_mntflags | pending), UDBA_HNOTIFY) ++ && !au_opt_test(sbinfo->si_mntflags, XINO)) ++ pr_warning("udba=*notify requires xino\n"); ++ ++ err = 0; ++ root = sb->s_root; ++ dir = root->d_inode; ++ do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK); ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ skip = 0; ++ h_dir = au_h_iptr(dir, bindex); ++ br = au_sbr(sb, bindex); ++ do_free = 0; ++ ++ wbr = br->br_wbr; ++ if (wbr) ++ wbr_wh_read_lock(wbr); ++ ++ switch (br->br_perm) { ++ case AuBrPerm_RO: ++ case AuBrPerm_ROWH: ++ case AuBrPerm_RR: ++ case AuBrPerm_RRWH: ++ do_free = !!wbr; ++ skip = (!wbr ++ || (!wbr->wbr_whbase ++ && !wbr->wbr_plink ++ && !wbr->wbr_orph)); ++ break; ++ ++ case AuBrPerm_RWNoLinkWH: ++ /* skip = (!br->br_whbase && !br->br_orph); */ ++ skip = (!wbr || !wbr->wbr_whbase); ++ if (skip && wbr) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ break; ++ ++ case AuBrPerm_RW: ++ /* skip = (br->br_whbase && br->br_ohph); */ ++ skip = (wbr && wbr->wbr_whbase); ++ if (skip) { ++ if (do_plink) ++ skip = !!wbr->wbr_plink; ++ else ++ skip = !wbr->wbr_plink; ++ } ++ break; ++ ++ default: ++ BUG(); ++ } ++ if (wbr) ++ wbr_wh_read_unlock(wbr); ++ ++ if (skip) ++ continue; ++ ++ hdir = au_hi(dir, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ if (wbr) ++ wbr_wh_write_lock(wbr); ++ err = au_wh_init(au_h_dptr(root, bindex), br, sb); ++ if (wbr) ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ ++ if (!err && do_free) { ++ kfree(wbr); ++ br->br_wbr = NULL; ++ } ++ } ++ ++ return err; ++} ++ ++int au_opts_mount(struct super_block *sb, struct au_opts *opts) ++{ ++ int err; ++ unsigned int tmp; ++ aufs_bindex_t bindex, bend; ++ struct au_opt *opt; ++ struct au_opt_xino *opt_xino, xino; ++ struct au_sbinfo *sbinfo; ++ struct au_branch *br; ++ ++ SiMustWriteLock(sb); ++ ++ 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 and udba temporary */ ++ sbinfo = au_sbi(sb); ++ tmp = sbinfo->si_mntflags; ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_REVAL); ++ ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) ++ err = au_opt_br(sb, opt++, opts); ++ if (err > 0) ++ err = 0; ++ else if (unlikely(err < 0)) ++ goto out; ++ ++ bend = au_sbend(sb); ++ if (unlikely(bend < 0)) { ++ err = -EINVAL; ++ pr_err("no branches\n"); ++ goto out; ++ } ++ ++ if (au_opt_test(tmp, XINO)) ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ opt = opts->opt; ++ while (!err && opt->type != Opt_tail) ++ err = au_opt_xino(sb, opt++, &opt_xino, opts); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_opts_verify(sb, sb->s_flags, tmp); ++ if (unlikely(err)) ++ goto out; ++ ++ /* restore xino */ ++ if (au_opt_test(tmp, XINO) && !opt_xino) { ++ xino.file = au_xino_def(sb); ++ err = PTR_ERR(xino.file); ++ if (IS_ERR(xino.file)) ++ goto out; ++ ++ err = au_xino_set(sb, &xino, /*remount*/0); ++ fput(xino.file); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ /* restore udba */ ++ tmp &= AuOptMask_UDBA; ++ sbinfo->si_mntflags &= ~AuOptMask_UDBA; ++ sbinfo->si_mntflags |= tmp; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(tmp, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ if (au_opt_test(tmp, UDBA_HNOTIFY)) { ++ struct inode *dir = sb->s_root->d_inode; ++ au_hn_reset(dir, au_hi_flags(dir, /*isdir*/1) & ~AuHi_XINO); ++ } ++ ++out: ++ 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; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ dir = sb->s_root->d_inode; ++ sbinfo = au_sbi(sb); ++ err = 0; ++ opt_xino = NULL; ++ opt = opts->opt; ++ while (err >= 0 && opt->type != Opt_tail) { ++ err = au_opt_simple(sb, opt, opts); ++ if (!err) ++ err = au_opt_br(sb, opt, opts); ++ if (!err) ++ err = au_opt_xino(sb, opt, &opt_xino, opts); ++ opt++; ++ } ++ if (err > 0) ++ err = 0; ++ AuTraceErr(err); ++ /* go on even err */ ++ ++ rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ ++ if (au_ftest_opts(opts->flags, TRUNC_XIB)) { ++ rerr = au_xib_trunc(sb); ++ if (unlikely(rerr && !err)) ++ err = rerr; ++ } ++ ++ /* will be handled by the caller */ ++ if (!au_ftest_opts(opts->flags, REFRESH) ++ && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO))) ++ au_fset_opts(opts->flags, REFRESH); ++ ++ AuDbg("status 0x%x\n", opts->flags); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_opt_udba(struct super_block *sb) ++{ ++ return au_mntflags(sb) & AuOptMask_UDBA; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/opts.h linux-2.6.36/fs/aufs/opts.h +--- linux-2.6.36.orig/fs/aufs/opts.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/opts.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,210 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * mount options/flags ++ */ ++ ++#ifndef __AUFS_OPTS_H__ ++#define __AUFS_OPTS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++ ++struct file; ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* mount flags */ ++#define AuOpt_XINO 1 /* external inode number bitmap ++ and translation table */ ++#define AuOpt_TRUNC_XINO (1 << 1) /* truncate xino files */ ++#define AuOpt_UDBA_NONE (1 << 2) /* users direct branch access */ ++#define AuOpt_UDBA_REVAL (1 << 3) ++#define AuOpt_UDBA_HNOTIFY (1 << 4) ++#define AuOpt_SHWH (1 << 5) /* show whiteout */ ++#define AuOpt_PLINK (1 << 6) /* pseudo-link */ ++#define AuOpt_DIRPERM1 (1 << 7) /* unimplemented */ ++#define AuOpt_REFROF (1 << 8) /* unimplemented */ ++#define AuOpt_ALWAYS_DIROPQ (1 << 9) /* policy to creating diropq */ ++#define AuOpt_SUM (1 << 10) /* summation for statfs(2) */ ++#define AuOpt_SUM_W (1 << 11) /* unimplemented */ ++#define AuOpt_WARN_PERM (1 << 12) /* warn when add-branch */ ++#define AuOpt_VERBOSE (1 << 13) /* busy inode when del-branch */ ++#define AuOpt_DIO (1 << 14) /* direct io */ ++ ++#ifndef CONFIG_AUFS_HNOTIFY ++#undef AuOpt_UDBA_HNOTIFY ++#define AuOpt_UDBA_HNOTIFY 0 ++#endif ++#ifndef CONFIG_AUFS_SHWH ++#undef AuOpt_SHWH ++#define AuOpt_SHWH 0 ++#endif ++ ++#define AuOpt_Def (AuOpt_XINO \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_PLINK \ ++ /* | AuOpt_DIRPERM1 */ \ ++ | AuOpt_WARN_PERM) ++#define AuOptMask_UDBA (AuOpt_UDBA_NONE \ ++ | AuOpt_UDBA_REVAL \ ++ | AuOpt_UDBA_HNOTIFY) ++ ++#define au_opt_test(flags, name) (flags & AuOpt_##name) ++#define au_opt_set(flags, name) do { \ ++ BUILD_BUG_ON(AuOpt_##name & AuOptMask_UDBA); \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_set_udba(flags, name) do { \ ++ (flags) &= ~AuOptMask_UDBA; \ ++ ((flags) |= AuOpt_##name); \ ++} while (0) ++#define au_opt_clr(flags, name) do { \ ++ ((flags) &= ~AuOpt_##name); \ ++} while (0) ++ ++static inline unsigned int au_opts_plink(unsigned int mntflags) ++{ ++#ifdef CONFIG_PROC_FS ++ return mntflags; ++#else ++ return mntflags & ~AuOpt_PLINK; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies to select one among multiple writable branches */ ++enum { ++ AuWbrCreate_TDP, /* top down parent */ ++ AuWbrCreate_RR, /* round robin */ ++ AuWbrCreate_MFS, /* most free space */ ++ AuWbrCreate_MFSV, /* mfs with seconds */ ++ AuWbrCreate_MFSRR, /* mfs then rr */ ++ AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ ++ AuWbrCreate_PMFS, /* parent and mfs */ ++ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ ++ ++ AuWbrCreate_Def = AuWbrCreate_TDP ++}; ++ ++enum { ++ AuWbrCopyup_TDP, /* top down parent */ ++ AuWbrCopyup_BUP, /* bottom up parent */ ++ AuWbrCopyup_BU, /* bottom up */ ++ ++ AuWbrCopyup_Def = AuWbrCopyup_TDP ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_opt_add { ++ aufs_bindex_t bindex; ++ char *pathname; ++ int perm; ++ struct path path; ++}; ++ ++struct au_opt_del { ++ char *pathname; ++ struct path h_path; ++}; ++ ++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_wbr_create { ++ int wbr_create; ++ int mfs_second; ++ unsigned long long 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; ++ unsigned int rdblk; ++ unsigned int rdhash; ++ int udba; ++ struct au_opt_wbr_create wbr_create; ++ int wbr_copyup; ++ }; ++}; ++ ++/* opts flags */ ++#define AuOpts_REMOUNT 1 ++#define AuOpts_REFRESH (1 << 1) ++#define AuOpts_TRUNC_XIB (1 << 2) ++#define AuOpts_REFRESH_DYAOP (1 << 3) ++#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name) ++#define au_fset_opts(flags, name) \ ++ do { (flags) |= AuOpts_##name; } while (0) ++#define au_fclr_opts(flags, name) \ ++ do { (flags) &= ~AuOpts_##name; } while (0) ++ ++struct au_opts { ++ struct au_opt *opt; ++ int max_opt; ++ ++ unsigned int given_udba; ++ unsigned int flags; ++ unsigned long sb_flags; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++const char *au_optstr_br_perm(int brperm); ++const char *au_optstr_udba(int udba); ++const char *au_optstr_wbr_copyup(int wbr_copyup); ++const char *au_optstr_wbr_create(int wbr_create); ++ ++void au_opts_free(struct au_opts *opts); ++int au_opts_parse(struct super_block *sb, char *str, struct au_opts *opts); ++int au_opts_verify(struct super_block *sb, unsigned long sb_flags, ++ unsigned int pending); ++int au_opts_mount(struct super_block *sb, struct au_opts *opts); ++int au_opts_remount(struct super_block *sb, struct au_opts *opts); ++ ++unsigned int au_opt_udba(struct super_block *sb); ++ ++/* ---------------------------------------------------------------------- */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_OPTS_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/plink.c linux-2.6.36/fs/aufs/plink.c +--- linux-2.6.36.orig/fs/aufs/plink.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/plink.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,515 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * pseudo-link ++ */ ++ ++#include "aufs.h" ++ ++/* ++ * the pseudo-link maintenance mode. ++ * during a user process maintains the pseudo-links, ++ * prohibit adding a new plink and branch manipulation. ++ * ++ * Flags ++ * NOPLM: ++ * For entry functions which will handle plink, and i_mutex is already held ++ * in VFS. ++ * They cannot wait and should return an error at once. ++ * Callers has to check the error. ++ * NOPLMW: ++ * For entry functions which will handle plink, but i_mutex is not held ++ * in VFS. ++ * They can wait the plink maintenance mode to finish. ++ * ++ * They behave like F_SETLK and F_SETLKW. ++ * If the caller never handle plink, then both flags are unnecessary. ++ */ ++ ++int au_plink_maint(struct super_block *sb, int flags) ++{ ++ int err; ++ pid_t pid, ppid; ++ struct au_sbinfo *sbi; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (!au_opt_test(au_mntflags(sb), PLINK)) ++ goto out; ++ ++ sbi = au_sbi(sb); ++ pid = sbi->si_plink_maint_pid; ++ if (!pid || pid == current->pid) ++ goto out; ++ ++ /* todo: it highly depends upon /sbin/mount.aufs */ ++ rcu_read_lock(); ++ ppid = task_pid_vnr(rcu_dereference(current->real_parent)); ++ rcu_read_unlock(); ++ if (pid == ppid) ++ goto out; ++ ++ if (au_ftest_lock(flags, NOPLMW)) { ++ /* if there is no i_mutex lock in VFS, we don't need to wait */ ++ /* AuDebugOn(!lockdep_depth(current)); */ ++ while (sbi->si_plink_maint_pid) { ++ si_read_unlock(sb); ++ /* gave up wake_up_bit() */ ++ wait_event(sbi->si_plink_wq, !sbi->si_plink_maint_pid); ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&sbi->si_nowait); ++ si_noflush_read_lock(sb); ++ } ++ } else if (au_ftest_lock(flags, NOPLM)) { ++ AuDbg("ppid %d, pid %d\n", ppid, pid); ++ err = -EAGAIN; ++ } ++ ++out: ++ return err; ++} ++ ++void au_plink_maint_leave(struct au_sbinfo *sbinfo) ++{ ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ sbinfo->si_plink_maint_pid = 0; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ wake_up_all(&sbinfo->si_plink_wq); ++} ++ ++int au_plink_maint_enter(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ /* make sure i am the only one in this fs */ ++ si_write_lock(sb, AuLock_FLUSH); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ spin_lock(&sbinfo->si_plink_maint_lock); ++ if (!sbinfo->si_plink_maint_pid) ++ sbinfo->si_plink_maint_pid = current->pid; ++ else ++ err = -EBUSY; ++ spin_unlock(&sbinfo->si_plink_maint_lock); ++ } ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct pseudo_link { ++ union { ++ struct list_head list; ++ struct rcu_head rcu; ++ }; ++ struct inode *inode; ++}; ++ ++#ifdef CONFIG_AUFS_DEBUG ++void au_plink_list(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink; ++ ++ SiMustAnyLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(plink, plink_list, list) ++ AuDbg("%lu\n", plink->inode->i_ino); ++ rcu_read_unlock(); ++} ++#endif ++ ++/* is the inode pseudo-linked? */ ++int au_plink_test(struct inode *inode) ++{ ++ int found; ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink; ++ ++ sbinfo = au_sbi(inode->i_sb); ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ AuDebugOn(!au_opt_test(au_mntflags(inode->i_sb), PLINK)); ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ found = 0; ++ plink_list = &sbinfo->si_plink.head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(plink, plink_list, list) ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ rcu_read_unlock(); ++ return found; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * generate a name for plink. ++ * the file will be stored under AUFS_WH_PLINKDIR. ++ */ ++/* 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; ++ ++ h_inode = au_h_iptr(inode, bindex); ++ rlen = snprintf(name, len, "%lu.%lu", inode->i_ino, h_inode->i_ino); ++ return rlen; ++} ++ ++struct au_do_plink_lkup_args { ++ struct dentry **errp; ++ struct qstr *tgtname; ++ struct dentry *h_parent; ++ struct au_branch *br; ++}; ++ ++static struct dentry *au_do_plink_lkup(struct qstr *tgtname, ++ struct dentry *h_parent, ++ struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ struct mutex *h_mtx; ++ ++ h_mtx = &h_parent->d_inode->i_mutex; ++ mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); ++ h_dentry = au_lkup_one(tgtname, h_parent, br, /*nd*/NULL); ++ mutex_unlock(h_mtx); ++ return h_dentry; ++} ++ ++static void au_call_do_plink_lkup(void *args) ++{ ++ struct au_do_plink_lkup_args *a = args; ++ *a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br); ++} ++ ++/* lookup the plink-ed @inode under the branch at @bindex */ ++struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex) ++{ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct inode *h_dir; ++ int wkq_err; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = { ++ .name = a ++ }; ++ ++ AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); ++ ++ br = au_sbr(inode->i_sb, bindex); ++ h_parent = br->br_wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ if (current_fsuid()) { ++ struct au_do_plink_lkup_args args = { ++ .errp = &h_dentry, ++ .tgtname = &tgtname, ++ .h_parent = h_parent, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args); ++ if (unlikely(wkq_err)) ++ h_dentry = ERR_PTR(wkq_err); ++ } else ++ h_dentry = au_do_plink_lkup(&tgtname, h_parent, br); ++ ++ return h_dentry; ++} ++ ++/* create a pseudo-link */ ++static int do_whplink(struct qstr *tgt, struct dentry *h_parent, ++ struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ ++ h_dir = h_parent->d_inode; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_CHILD2); ++again: ++ h_path.dentry = au_lkup_one(tgt, h_parent, br, /*nd*/NULL); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ /* wh.plink dir is not monitored */ ++ /* todo: is it really safe? */ ++ if (h_path.dentry->d_inode ++ && h_path.dentry->d_inode != h_dentry->d_inode) { ++ err = vfsub_unlink(h_dir, &h_path, /*force*/0); ++ dput(h_path.dentry); ++ h_path.dentry = NULL; ++ if (!err) ++ goto again; ++ } ++ if (!err && !h_path.dentry->d_inode) ++ err = vfsub_link(h_dentry, h_dir, &h_path); ++ dput(h_path.dentry); ++ ++out: ++ mutex_unlock(&h_dir->i_mutex); ++ return err; ++} ++ ++struct do_whplink_args { ++ int *errp; ++ struct qstr *tgt; ++ struct dentry *h_parent; ++ struct dentry *h_dentry; ++ struct au_branch *br; ++}; ++ ++static void call_do_whplink(void *args) ++{ ++ struct do_whplink_args *a = args; ++ *a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br); ++} ++ ++static int whplink(struct dentry *h_dentry, struct inode *inode, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err, wkq_err; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ char a[PLINK_NAME_LEN]; ++ struct qstr tgtname = { ++ .name = a ++ }; ++ ++ wbr = au_sbr(inode->i_sb, bindex)->br_wbr; ++ h_parent = wbr->wbr_plink; ++ h_dir = h_parent->d_inode; ++ tgtname.len = plink_name(a, sizeof(a), inode, bindex); ++ ++ /* always superio. */ ++ if (current_fsuid()) { ++ struct do_whplink_args args = { ++ .errp = &err, ++ .tgt = &tgtname, ++ .h_parent = h_parent, ++ .h_dentry = h_dentry, ++ .br = br ++ }; ++ wkq_err = au_wkq_wait(call_do_whplink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } else ++ err = do_whplink(&tgtname, h_parent, h_dentry, br); ++ ++ return err; ++} ++ ++/* free a single plink */ ++static void do_put_plink(struct pseudo_link *plink, int do_del) ++{ ++ if (do_del) ++ list_del(&plink->list); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++static void do_put_plink_rcu(struct rcu_head *rcu) ++{ ++ struct pseudo_link *plink; ++ ++ plink = container_of(rcu, struct pseudo_link, rcu); ++ iput(plink->inode); ++ kfree(plink); ++} ++ ++/* ++ * create a new pseudo-link for @h_dentry on @bindex. ++ * the linked inode is held in aufs @inode. ++ */ ++void au_plink_append(struct inode *inode, aufs_bindex_t bindex, ++ struct dentry *h_dentry) ++{ ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink, *tmp; ++ int found, err, cnt; ++ ++ sb = inode->i_sb; ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ cnt = 0; ++ found = 0; ++ plink_list = &sbinfo->si_plink.head; ++ rcu_read_lock(); ++ list_for_each_entry_rcu(plink, plink_list, list) { ++ cnt++; ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ } ++ rcu_read_unlock(); ++ if (found) ++ return; ++ ++ tmp = kmalloc(sizeof(*plink), GFP_NOFS); ++ if (tmp) ++ tmp->inode = au_igrab(inode); ++ else { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ spin_lock(&sbinfo->si_plink.spin); ++ list_for_each_entry(plink, plink_list, list) { ++ if (plink->inode == inode) { ++ found = 1; ++ break; ++ } ++ } ++ if (!found) ++ list_add_rcu(&tmp->list, plink_list); ++ spin_unlock(&sbinfo->si_plink.spin); ++ if (!found) { ++ cnt++; ++ WARN_ONCE(cnt > AUFS_PLINK_WARN, ++ "unexpectedly many pseudo links, %d\n", cnt); ++ err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); ++ } else { ++ do_put_plink(tmp, 0); ++ return; ++ } ++ ++out: ++ if (unlikely(err)) { ++ pr_warning("err %d, damaged pseudo link.\n", err); ++ if (tmp) { ++ au_spl_del_rcu(&tmp->list, &sbinfo->si_plink); ++ call_rcu(&tmp->rcu, do_put_plink_rcu); ++ } ++ } ++} ++ ++/* free all plinks */ ++void au_plink_put(struct super_block *sb, int verbose) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink, *tmp; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ /* no spin_lock since sbinfo is write-locked */ ++ WARN(verbose && !list_empty(plink_list), "pseudo-link is not flushed"); ++ list_for_each_entry_safe(plink, tmp, plink_list, list) ++ do_put_plink(plink, 0); ++ INIT_LIST_HEAD(plink_list); ++} ++ ++void au_plink_clean(struct super_block *sb, int verbose) ++{ ++ struct dentry *root; ++ ++ root = sb->s_root; ++ aufs_write_lock(root); ++ if (au_opt_test(au_mntflags(sb), PLINK)) ++ au_plink_put(sb, verbose); ++ aufs_write_unlock(root); ++} ++ ++/* free the plinks on a branch specified by @br_id */ ++void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) ++{ ++ struct au_sbinfo *sbinfo; ++ struct list_head *plink_list; ++ struct pseudo_link *plink, *tmp; ++ struct inode *inode; ++ aufs_bindex_t bstart, bend, bindex; ++ unsigned char do_put; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); ++ AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); ++ ++ plink_list = &sbinfo->si_plink.head; ++ /* no spin_lock since sbinfo is write-locked */ ++ list_for_each_entry_safe(plink, tmp, plink_list, list) { ++ do_put = 0; ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ bstart = au_ibstart(inode); ++ bend = au_ibend(inode); ++ if (bstart >= 0) { ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ if (!au_h_iptr(inode, bindex) ++ || au_ii_br_id(inode, bindex) != br_id) ++ continue; ++ au_set_h_iptr(inode, bindex, NULL, 0); ++ do_put = 1; ++ break; ++ } ++ } else ++ do_put_plink(plink, 1); ++ ++ if (do_put) { ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ if (do_put) ++ do_put_plink(plink, 1); ++ } ++ ii_write_unlock(inode); ++ iput(inode); ++ } ++} +diff -Nur linux-2.6.36.orig/fs/aufs/poll.c linux-2.6.36/fs/aufs/poll.c +--- linux-2.6.36.orig/fs/aufs/poll.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/poll.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * poll operation ++ * There is only one filesystem which implements ->poll operation, currently. ++ */ ++ ++#include "aufs.h" ++ ++unsigned int aufs_poll(struct file *file, poll_table *wait) ++{ ++ unsigned int mask; ++ int err; ++ struct file *h_file; ++ struct dentry *dentry; ++ struct super_block *sb; ++ ++ /* We should pretend an error happened. */ ++ mask = POLLERR /* | POLLIN | POLLOUT */; ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0); ++ if (unlikely(err)) ++ goto out; ++ ++ /* it is not an error if h_file has no operation */ ++ mask = DEFAULT_POLLMASK; ++ h_file = au_hf_top(file); ++ if (h_file->f_op && h_file->f_op->poll) ++ mask = h_file->f_op->poll(h_file, wait); ++ ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++out: ++ si_read_unlock(sb); ++ AuTraceErr((int)mask); ++ return mask; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/procfs.c linux-2.6.36/fs/aufs/procfs.c +--- linux-2.6.36.orig/fs/aufs/procfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/procfs.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,169 @@ ++/* ++ * Copyright (C) 2010-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * procfs interfaces ++ */ ++ ++#include ++#include "aufs.h" ++ ++static int au_procfs_plm_release(struct inode *inode, struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) { ++ au_plink_maint_leave(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++ } ++ ++ return 0; ++} ++ ++static void au_procfs_plm_write_clean(struct file *file) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = file->private_data; ++ if (sbinfo) ++ au_plink_clean(sbinfo->si_sb, /*verbose*/0); ++} ++ ++static int au_procfs_plm_write_si(struct file *file, unsigned long id) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ err = -EBUSY; ++ if (unlikely(file->private_data)) ++ goto out; ++ ++ sb = NULL; ++ spin_lock(&au_sbilist.spin); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ if (id == sysaufs_si_id(sbinfo)) { ++ kobject_get(&sbinfo->si_kobj); ++ sb = sbinfo->si_sb; ++ break; ++ } ++ spin_unlock(&au_sbilist.spin); ++ ++ err = -EINVAL; ++ if (unlikely(!sb)) ++ goto out; ++ ++ err = au_plink_maint_enter(sb); ++ if (!err) ++ /* keep kobject_get() */ ++ file->private_data = sbinfo; ++ else ++ kobject_put(&sbinfo->si_kobj); ++out: ++ return err; ++} ++ ++/* ++ * Accept a valid "si=xxxx" only. ++ * Once it is accepted successfully, accept "clean" too. ++ */ ++static ssize_t au_procfs_plm_write(struct file *file, const char __user *ubuf, ++ size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ unsigned long id; ++ /* last newline is allowed */ ++ char buf[3 + sizeof(unsigned long) * 2 + 1]; ++ ++ err = -EACCES; ++ if (unlikely(!capable(CAP_SYS_ADMIN))) ++ goto out; ++ ++ err = -EINVAL; ++ if (unlikely(count > sizeof(buf))) ++ goto out; ++ ++ err = copy_from_user(buf, ubuf, count); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ goto out; ++ } ++ buf[count] = 0; ++ ++ err = -EINVAL; ++ if (!strcmp("clean", buf)) { ++ au_procfs_plm_write_clean(file); ++ goto out_success; ++ } else if (unlikely(strncmp("si=", buf, 3))) ++ goto out; ++ ++ err = strict_strtoul(buf + 3, 16, &id); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_procfs_plm_write_si(file, id); ++ if (unlikely(err)) ++ goto out; ++ ++out_success: ++ err = count; /* success */ ++out: ++ return err; ++} ++ ++static const struct file_operations au_procfs_plm_fop = { ++ .write = au_procfs_plm_write, ++ .release = au_procfs_plm_release, ++ .owner = THIS_MODULE ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct proc_dir_entry *au_procfs_dir; ++ ++void au_procfs_fin(void) ++{ ++ remove_proc_entry(AUFS_PLINK_MAINT_NAME, au_procfs_dir); ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++} ++ ++int __init au_procfs_init(void) ++{ ++ int err; ++ struct proc_dir_entry *entry; ++ ++ err = -ENOMEM; ++ au_procfs_dir = proc_mkdir(AUFS_PLINK_MAINT_DIR, NULL); ++ if (unlikely(!au_procfs_dir)) ++ goto out; ++ ++ entry = proc_create(AUFS_PLINK_MAINT_NAME, S_IFREG | S_IWUSR, ++ au_procfs_dir, &au_procfs_plm_fop); ++ if (unlikely(!entry)) ++ goto out_dir; ++ ++ err = 0; ++ goto out; /* success */ ++ ++ ++out_dir: ++ remove_proc_entry(AUFS_PLINK_MAINT_DIR, NULL); ++out: ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/rdu.c linux-2.6.36/fs/aufs/rdu.c +--- linux-2.6.36.orig/fs/aufs/rdu.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/rdu.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,383 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * readdir in userspace. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* bits for struct aufs_rdu.flags */ ++#define AuRdu_CALLED 1 ++#define AuRdu_CONT (1 << 1) ++#define AuRdu_FULL (1 << 2) ++#define au_ftest_rdu(flags, name) ((flags) & AuRdu_##name) ++#define au_fset_rdu(flags, name) \ ++ do { (flags) |= AuRdu_##name; } while (0) ++#define au_fclr_rdu(flags, name) \ ++ do { (flags) &= ~AuRdu_##name; } while (0) ++ ++struct au_rdu_arg { ++ struct aufs_rdu *rdu; ++ union au_rdu_ent_ul ent; ++ unsigned long end; ++ ++ struct super_block *sb; ++ int err; ++}; ++ ++static int au_rdu_fill(void *__arg, const char *name, int nlen, ++ loff_t offset, u64 h_ino, unsigned int d_type) ++{ ++ int err, len; ++ struct au_rdu_arg *arg = __arg; ++ struct aufs_rdu *rdu = arg->rdu; ++ struct au_rdu_ent ent; ++ ++ err = 0; ++ arg->err = 0; ++ au_fset_rdu(rdu->cookie.flags, CALLED); ++ len = au_rdu_len(nlen); ++ if (arg->ent.ul + len < arg->end) { ++ ent.ino = h_ino; ++ ent.bindex = rdu->cookie.bindex; ++ ent.type = d_type; ++ ent.nlen = nlen; ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ ent.type = DT_UNKNOWN; ++ ++ err = -EFAULT; ++ if (copy_to_user(arg->ent.e, &ent, sizeof(ent))) ++ goto out; ++ if (copy_to_user(arg->ent.e->name, name, nlen)) ++ goto out; ++ /* the terminating NULL */ ++ if (__put_user(0, arg->ent.e->name + nlen)) ++ goto out; ++ err = 0; ++ /* AuDbg("%p, %.*s\n", arg->ent.p, nlen, name); */ ++ arg->ent.ul += len; ++ rdu->rent++; ++ } else { ++ err = -EFAULT; ++ au_fset_rdu(rdu->cookie.flags, FULL); ++ rdu->full = 1; ++ rdu->tail = arg->ent; ++ } ++ ++out: ++ /* AuTraceErr(err); */ ++ return err; ++} ++ ++static int au_rdu_do(struct file *h_file, struct au_rdu_arg *arg) ++{ ++ int err; ++ loff_t offset; ++ struct au_rdu_cookie *cookie = &arg->rdu->cookie; ++ ++ offset = vfsub_llseek(h_file, cookie->h_pos, SEEK_SET); ++ err = offset; ++ if (unlikely(offset != cookie->h_pos)) ++ goto out; ++ ++ err = 0; ++ do { ++ arg->err = 0; ++ au_fclr_rdu(cookie->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_readdir(h_file, au_rdu_fill, arg); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err ++ && au_ftest_rdu(cookie->flags, CALLED) ++ && !au_ftest_rdu(cookie->flags, FULL)); ++ cookie->h_pos = h_file->f_pos; ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ aufs_bindex_t bend; ++ struct au_rdu_arg arg; ++ struct dentry *dentry; ++ struct inode *inode; ++ struct file *h_file; ++ struct au_rdu_cookie *cookie = &rdu->cookie; ++ ++ err = !access_ok(VERIFY_WRITE, rdu->ent.e, rdu->sz); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu->rent = 0; ++ rdu->tail = rdu->ent; ++ rdu->full = 0; ++ arg.rdu = rdu; ++ arg.ent = rdu->ent; ++ arg.end = arg.ent.ul; ++ arg.end += rdu->sz; ++ ++ err = -ENOTDIR; ++ if (unlikely(!file->f_op || !file->f_op->readdir)) ++ goto out; ++ ++ err = security_file_permission(file, MAY_READ); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++ ++ dentry = file->f_dentry; ++ inode = dentry->d_inode; ++#if 1 ++ mutex_lock(&inode->i_mutex); ++#else ++ err = mutex_lock_killable(&inode->i_mutex); ++ AuTraceErr(err); ++ if (unlikely(err)) ++ goto out; ++#endif ++ ++ arg.sb = inode->i_sb; ++ err = si_read_lock(arg.sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_si; ++ /* todo: reval? */ ++ fi_read_lock(file); ++ ++ err = -EAGAIN; ++ if (unlikely(au_ftest_rdu(cookie->flags, CONT) ++ && cookie->generation != au_figen(file))) ++ goto out_unlock; ++ ++ err = 0; ++ if (!rdu->blk) { ++ rdu->blk = au_sbi(arg.sb)->si_rdblk; ++ if (!rdu->blk) ++ rdu->blk = au_dir_size(file, /*dentry*/NULL); ++ } ++ bend = au_fbstart(file); ++ if (cookie->bindex < bend) ++ cookie->bindex = bend; ++ bend = au_fbend_dir(file); ++ /* AuDbg("b%d, b%d\n", cookie->bindex, bend); */ ++ for (; !err && cookie->bindex <= bend; ++ cookie->bindex++, cookie->h_pos = 0) { ++ h_file = au_hf_dir(file, cookie->bindex); ++ if (!h_file) ++ continue; ++ ++ au_fclr_rdu(cookie->flags, FULL); ++ err = au_rdu_do(h_file, &arg); ++ AuTraceErr(err); ++ if (unlikely(au_ftest_rdu(cookie->flags, FULL) || err)) ++ break; ++ } ++ AuDbg("rent %llu\n", rdu->rent); ++ ++ if (!err && !au_ftest_rdu(cookie->flags, CONT)) { ++ rdu->shwh = !!au_opt_test(au_sbi(arg.sb)->si_mntflags, SHWH); ++ au_fset_rdu(cookie->flags, CONT); ++ cookie->generation = au_figen(file); ++ } ++ ++ ii_read_lock_child(inode); ++ fsstack_copy_attr_atime(inode, au_h_iptr(inode, au_ibstart(inode))); ++ ii_read_unlock(inode); ++ ++out_unlock: ++ fi_read_unlock(file); ++out_si: ++ si_read_unlock(arg.sb); ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_rdu_ino(struct file *file, struct aufs_rdu *rdu) ++{ ++ int err; ++ ino_t ino; ++ unsigned long long nent; ++ union au_rdu_ent_ul *u; ++ struct au_rdu_ent ent; ++ struct super_block *sb; ++ ++ err = 0; ++ nent = rdu->nent; ++ u = &rdu->ent; ++ sb = file->f_dentry->d_sb; ++ si_read_lock(sb, AuLock_FLUSH); ++ while (nent-- > 0) { ++ err = copy_from_user(&ent, u->e, sizeof(ent)); ++ if (!err) ++ err = !access_ok(VERIFY_WRITE, &u->e->ino, sizeof(ino)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ ++ /* AuDbg("b%d, i%llu\n", ent.bindex, ent.ino); */ ++ if (!ent.wh) ++ err = au_ino(sb, ent.bindex, ent.ino, ent.type, &ino); ++ else ++ err = au_wh_ino(sb, ent.bindex, ent.ino, ent.type, ++ &ino); ++ if (unlikely(err)) { ++ AuTraceErr(err); ++ break; ++ } ++ ++ err = __put_user(ino, &u->e->ino); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ break; ++ } ++ u->ul += au_rdu_len(ent.nlen); ++ } ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_rdu_verify(struct aufs_rdu *rdu) ++{ ++ AuDbg("rdu{%llu, %p, %u | %u | %llu, %u, %u | " ++ "%llu, b%d, 0x%x, g%u}\n", ++ rdu->sz, rdu->ent.e, rdu->verify[AufsCtlRduV_SZ], ++ rdu->blk, ++ rdu->rent, rdu->shwh, rdu->full, ++ rdu->cookie.h_pos, rdu->cookie.bindex, rdu->cookie.flags, ++ rdu->cookie.generation); ++ ++ if (rdu->verify[AufsCtlRduV_SZ] == sizeof(*rdu)) ++ return 0; ++ ++ AuDbg("%u:%u\n", ++ rdu->verify[AufsCtlRduV_SZ], (unsigned int)sizeof(*rdu)); ++ return -EINVAL; ++} ++ ++long au_rdu_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = (void __user *)arg; ++ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++#ifdef CONFIG_COMPAT ++long au_rdu_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ long err, e; ++ struct aufs_rdu rdu; ++ void __user *p = compat_ptr(arg); ++ ++ /* todo: get_user()? */ ++ err = copy_from_user(&rdu, p, sizeof(rdu)); ++ if (unlikely(err)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ goto out; ++ } ++ rdu.ent.e = compat_ptr(rdu.ent.ul); ++ err = au_rdu_verify(&rdu); ++ if (unlikely(err)) ++ goto out; ++ ++ switch (cmd) { ++ case AUFS_CTL_RDU: ++ err = au_rdu(file, &rdu); ++ if (unlikely(err)) ++ break; ++ ++ rdu.ent.ul = ptr_to_compat(rdu.ent.e); ++ rdu.tail.ul = ptr_to_compat(rdu.tail.e); ++ e = copy_to_user(p, &rdu, sizeof(rdu)); ++ if (unlikely(e)) { ++ err = -EFAULT; ++ AuTraceErr(err); ++ } ++ break; ++ case AUFS_CTL_RDU_INO: ++ err = au_rdu_ino(file, &rdu); ++ break; ++ ++ default: ++ /* err = -ENOTTY; */ ++ err = -EINVAL; ++ } ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++#endif +diff -Nur linux-2.6.36.orig/fs/aufs/rwsem.h linux-2.6.36/fs/aufs/rwsem.h +--- linux-2.6.36.orig/fs/aufs/rwsem.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/rwsem.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,189 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * simple read-write semaphore wrappers ++ */ ++ ++#ifndef __AUFS_RWSEM_H__ ++#define __AUFS_RWSEM_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "debug.h" ++ ++struct au_rwsem { ++ struct rw_semaphore rwsem; ++#ifdef CONFIG_AUFS_DEBUG ++ /* just for debugging, not almighty counter */ ++ atomic_t rcnt, wcnt; ++#endif ++}; ++ ++#ifdef CONFIG_AUFS_DEBUG ++#define AuDbgCntInit(rw) do { \ ++ atomic_set(&(rw)->rcnt, 0); \ ++ atomic_set(&(rw)->wcnt, 0); \ ++ smp_mb(); /* atomic set */ \ ++} while (0) ++ ++#define AuDbgRcntInc(rw) atomic_inc(&(rw)->rcnt) ++#define AuDbgRcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->rcnt) < 0) ++#define AuDbgWcntInc(rw) atomic_inc(&(rw)->wcnt) ++#define AuDbgWcntDec(rw) WARN_ON(atomic_dec_return(&(rw)->wcnt) < 0) ++#else ++#define AuDbgCntInit(rw) do {} while (0) ++#define AuDbgRcntInc(rw) do {} while (0) ++#define AuDbgRcntDec(rw) do {} while (0) ++#define AuDbgWcntInc(rw) do {} while (0) ++#define AuDbgWcntDec(rw) do {} while (0) ++#endif /* CONFIG_AUFS_DEBUG */ ++ ++/* to debug easier, do not make them inlined functions */ ++#define AuRwMustNoWaiters(rw) AuDebugOn(!list_empty(&(rw)->rwsem.wait_list)) ++/* rwsem_is_locked() is unusable */ ++#define AuRwMustReadLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0) ++#define AuRwMustWriteLock(rw) AuDebugOn(atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwMustAnyLock(rw) AuDebugOn(atomic_read(&(rw)->rcnt) <= 0 \ ++ && atomic_read(&(rw)->wcnt) <= 0) ++#define AuRwDestroy(rw) AuDebugOn(atomic_read(&(rw)->rcnt) \ ++ || atomic_read(&(rw)->wcnt)) ++ ++#define au_rw_class(rw, key) lockdep_set_class(&(rw)->rwsem, key) ++ ++static inline void au_rw_init(struct au_rwsem *rw) ++{ ++ AuDbgCntInit(rw); ++ init_rwsem(&rw->rwsem); ++} ++ ++static inline void au_rw_init_wlock(struct au_rwsem *rw) ++{ ++ au_rw_init(rw); ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_init_wlock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ au_rw_init(rw); ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_read_lock(struct au_rwsem *rw) ++{ ++ down_read(&rw->rwsem); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc) ++{ ++ down_read_nested(&rw->rwsem, lsc); ++ AuDbgRcntInc(rw); ++} ++ ++static inline void au_rw_read_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustReadLock(rw); ++ AuDbgRcntDec(rw); ++ up_read(&rw->rwsem); ++} ++ ++static inline void au_rw_dgrade_lock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgRcntInc(rw); ++ AuDbgWcntDec(rw); ++ downgrade_write(&rw->rwsem); ++} ++ ++static inline void au_rw_write_lock(struct au_rwsem *rw) ++{ ++ down_write(&rw->rwsem); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_lock_nested(struct au_rwsem *rw, ++ unsigned int lsc) ++{ ++ down_write_nested(&rw->rwsem, lsc); ++ AuDbgWcntInc(rw); ++} ++ ++static inline void au_rw_write_unlock(struct au_rwsem *rw) ++{ ++ AuRwMustWriteLock(rw); ++ AuDbgWcntDec(rw); ++ up_write(&rw->rwsem); ++} ++ ++/* why is not _nested version defined */ ++static inline int au_rw_read_trylock(struct au_rwsem *rw) ++{ ++ int ret = down_read_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgRcntInc(rw); ++ return ret; ++} ++ ++static inline int au_rw_write_trylock(struct au_rwsem *rw) ++{ ++ int ret = down_write_trylock(&rw->rwsem); ++ if (ret) ++ AuDbgWcntInc(rw); ++ return ret; ++} ++ ++#undef AuDbgCntInit ++#undef AuDbgRcntInc ++#undef AuDbgRcntDec ++#undef AuDbgWcntInc ++#undef AuDbgWcntDec ++ ++#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_lock(param) \ ++{ au_rw_read_lock(rwsem); } \ ++static inline void prefix##_write_lock(param) \ ++{ au_rw_write_lock(rwsem); } \ ++static inline int prefix##_read_trylock(param) \ ++{ return au_rw_read_trylock(rwsem); } \ ++static inline int prefix##_write_trylock(param) \ ++{ return au_rw_write_trylock(rwsem); } ++/* why is not _nested version defined */ ++/* static inline void prefix##_read_trylock_nested(param, lsc) ++{ au_rw_read_trylock_nested(rwsem, lsc)); } ++static inline void prefix##_write_trylock_nestd(param, lsc) ++{ au_rw_write_trylock_nested(rwsem, lsc); } */ ++ ++#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \ ++static inline void prefix##_read_unlock(param) \ ++{ au_rw_read_unlock(rwsem); } \ ++static inline void prefix##_write_unlock(param) \ ++{ au_rw_write_unlock(rwsem); } \ ++static inline void prefix##_downgrade_lock(param) \ ++{ au_rw_dgrade_lock(rwsem); } ++ ++#define AuSimpleRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleLockRwsemFuncs(prefix, param, rwsem) \ ++ AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_RWSEM_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/sbinfo.c linux-2.6.36/fs/aufs/sbinfo.c +--- linux-2.6.36.orig/fs/aufs/sbinfo.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/sbinfo.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,345 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * superblock private data ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* ++ * they are necessary regardless sysfs is disabled. ++ */ ++void au_si_free(struct kobject *kobj) ++{ ++ struct au_sbinfo *sbinfo; ++ char *locked __maybe_unused; /* debug only */ ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ AuDebugOn(!list_empty(&sbinfo->si_plink.head)); ++ AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); ++ ++ au_rw_write_lock(&sbinfo->si_rwsem); ++ au_br_free(sbinfo); ++ au_rw_write_unlock(&sbinfo->si_rwsem); ++ ++ AuDebugOn(radix_tree_gang_lookup ++ (&sbinfo->au_si_pid.tree, (void **)&locked, ++ /*first_index*/PID_MAX_DEFAULT - 1, ++ /*max_items*/sizeof(locked)/sizeof(*locked))); ++ ++ kfree(sbinfo->si_branch); ++ kfree(sbinfo->au_si_pid.bitmap); ++ mutex_destroy(&sbinfo->si_xib_mtx); ++ AuRwDestroy(&sbinfo->si_rwsem); ++ ++ kfree(sbinfo); ++} ++ ++int au_si_alloc(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ static struct lock_class_key aufs_si; ++ ++ err = -ENOMEM; ++ sbinfo = kzalloc(sizeof(*sbinfo), GFP_NOFS); ++ if (unlikely(!sbinfo)) ++ goto out; ++ ++ BUILD_BUG_ON(sizeof(unsigned long) != ++ sizeof(*sbinfo->au_si_pid.bitmap)); ++ sbinfo->au_si_pid.bitmap = kcalloc(BITS_TO_LONGS(PID_MAX_DEFAULT), ++ sizeof(*sbinfo->au_si_pid.bitmap), ++ GFP_NOFS); ++ if (unlikely(!sbinfo->au_si_pid.bitmap)) ++ goto out_sbinfo; ++ ++ /* will be reallocated separately */ ++ sbinfo->si_branch = kzalloc(sizeof(*sbinfo->si_branch), GFP_NOFS); ++ if (unlikely(!sbinfo->si_branch)) ++ goto out_pidmap; ++ ++ err = sysaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ goto out_br; ++ ++ au_nwt_init(&sbinfo->si_nowait); ++ au_rw_init_wlock(&sbinfo->si_rwsem); ++ au_rw_class(&sbinfo->si_rwsem, &aufs_si); ++ spin_lock_init(&sbinfo->au_si_pid.tree_lock); ++ INIT_RADIX_TREE(&sbinfo->au_si_pid.tree, GFP_ATOMIC | __GFP_NOFAIL); ++ ++ atomic_long_set(&sbinfo->si_ninodes, 0); ++ atomic_long_set(&sbinfo->si_nfiles, 0); ++ ++ sbinfo->si_bend = -1; ++ ++ sbinfo->si_wbr_copyup = AuWbrCopyup_Def; ++ sbinfo->si_wbr_create = AuWbrCreate_Def; ++ sbinfo->si_wbr_copyup_ops = au_wbr_copyup_ops + sbinfo->si_wbr_copyup; ++ sbinfo->si_wbr_create_ops = au_wbr_create_ops + sbinfo->si_wbr_create; ++ ++ sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); ++ ++ mutex_init(&sbinfo->si_xib_mtx); ++ sbinfo->si_xino_brid = -1; ++ /* leave si_xib_last_pindex and si_xib_next_bit */ ++ ++ sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC); ++ sbinfo->si_rdblk = AUFS_RDBLK_DEF; ++ sbinfo->si_rdhash = AUFS_RDHASH_DEF; ++ sbinfo->si_dirwh = AUFS_DIRWH_DEF; ++ ++ au_spl_init(&sbinfo->si_plink); ++ init_waitqueue_head(&sbinfo->si_plink_wq); ++ spin_lock_init(&sbinfo->si_plink_maint_lock); ++ ++ /* leave other members for sysaufs and si_mnt. */ ++ sbinfo->si_sb = sb; ++ sb->s_fs_info = sbinfo; ++ si_pid_set(sb); ++ au_debug_sbinfo_init(sbinfo); ++ return 0; /* success */ ++ ++out_br: ++ kfree(sbinfo->si_branch); ++out_pidmap: ++ kfree(sbinfo->au_si_pid.bitmap); ++out_sbinfo: ++ kfree(sbinfo); ++out: ++ return err; ++} ++ ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr) ++{ ++ int err, sz; ++ struct au_branch **brp; ++ ++ AuRwMustWriteLock(&sbinfo->si_rwsem); ++ ++ err = -ENOMEM; ++ sz = sizeof(*brp) * (sbinfo->si_bend + 1); ++ if (unlikely(!sz)) ++ sz = sizeof(*brp); ++ brp = au_kzrealloc(sbinfo->si_branch, sz, sizeof(*brp) * nbr, GFP_NOFS); ++ if (brp) { ++ sbinfo->si_branch = brp; ++ err = 0; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++unsigned int au_sigen_inc(struct super_block *sb) ++{ ++ unsigned int gen; ++ ++ SiMustWriteLock(sb); ++ ++ gen = ++au_sbi(sb)->si_generation; ++ au_update_digen(sb->s_root); ++ au_update_iigen(sb->s_root->d_inode); ++ sb->s_root->d_inode->i_version++; ++ return gen; ++} ++ ++aufs_bindex_t au_new_br_id(struct super_block *sb) ++{ ++ aufs_bindex_t br_id; ++ int i; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ for (i = 0; i <= AUFS_BRANCH_MAX; i++) { ++ br_id = ++sbinfo->si_last_br_id; ++ AuDebugOn(br_id < 0); ++ if (br_id && au_br_index(sb, br_id) < 0) ++ return br_id; ++ } ++ ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* it is ok that new 'nwt' tasks are appended while we are sleeping */ ++int si_read_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ err = 0; ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_read_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_read_unlock(sb); ++ ++ return err; ++} ++ ++int si_write_lock(struct super_block *sb, int flags) ++{ ++ int err; ++ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ ++ si_noflush_write_lock(sb); ++ err = au_plink_maint(sb, flags); ++ if (unlikely(err)) ++ si_write_unlock(sb); ++ ++ return err; ++} ++ ++/* dentry and super_block lock. call at entry point */ ++int aufs_read_lock(struct dentry *dentry, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ ++ sb = dentry->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_ftest_lock(flags, DW)) ++ di_write_lock_child(dentry); ++ else ++ di_read_lock_child(dentry, flags); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ err = au_digen_test(dentry, au_sigen(sb)); ++ AuDebugOn(!err && au_dbrange_test(dentry)); ++ if (unlikely(err)) ++ aufs_read_unlock(dentry, flags); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_unlock(struct dentry *dentry, int flags) ++{ ++ if (au_ftest_lock(flags, DW)) ++ di_write_unlock(dentry); ++ else ++ di_read_unlock(dentry, flags); ++ si_read_unlock(dentry->d_sb); ++} ++ ++void aufs_write_lock(struct dentry *dentry) ++{ ++ si_write_lock(dentry->d_sb, AuLock_FLUSH | AuLock_NOPLMW); ++ di_write_lock_child(dentry); ++} ++ ++void aufs_write_unlock(struct dentry *dentry) ++{ ++ di_write_unlock(dentry); ++ si_write_unlock(dentry->d_sb); ++} ++ ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags) ++{ ++ int err; ++ unsigned int sigen; ++ struct super_block *sb; ++ ++ sb = d1->d_sb; ++ err = si_read_lock(sb, flags); ++ if (unlikely(err)) ++ goto out; ++ ++ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR)); ++ ++ if (au_ftest_lock(flags, GEN)) { ++ sigen = au_sigen(sb); ++ err = au_digen_test(d1, sigen); ++ AuDebugOn(!err && au_dbrange_test(d1)); ++ if (!err) { ++ err = au_digen_test(d2, sigen); ++ AuDebugOn(!err && au_dbrange_test(d2)); ++ } ++ if (unlikely(err)) ++ aufs_read_and_write_unlock2(d1, d2); ++ } ++ ++out: ++ return err; ++} ++ ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2) ++{ ++ di_write_unlock2(d1, d2); ++ si_read_unlock(d1->d_sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int si_pid_test_slow(struct super_block *sb) ++{ ++ void *p; ++ ++ rcu_read_lock(); ++ p = radix_tree_lookup(&au_sbi(sb)->au_si_pid.tree, current->pid); ++ rcu_read_unlock(); ++ ++ return (long)p; ++} ++ ++void si_pid_set_slow(struct super_block *sb) ++{ ++ int err; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ err = radix_tree_preload(GFP_NOFS | __GFP_NOFAIL); ++ AuDebugOn(err); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ err = radix_tree_insert(&sbinfo->au_si_pid.tree, current->pid, ++ (void *)1); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++ AuDebugOn(err); ++ radix_tree_preload_end(); ++} ++ ++void si_pid_clr_slow(struct super_block *sb) ++{ ++ void *p; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(!si_pid_test_slow(sb)); ++ ++ sbinfo = au_sbi(sb); ++ spin_lock(&sbinfo->au_si_pid.tree_lock); ++ p = radix_tree_delete(&sbinfo->au_si_pid.tree, current->pid); ++ spin_unlock(&sbinfo->au_si_pid.tree_lock); ++ AuDebugOn(1 != (long)p); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/spl.h linux-2.6.36/fs/aufs/spl.h +--- linux-2.6.36.orig/fs/aufs/spl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/spl.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,66 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++/* ++ * simple list protected by a spinlock ++ */ ++ ++#ifndef __AUFS_SPL_H__ ++#define __AUFS_SPL_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct au_splhead { ++ spinlock_t spin; ++ struct list_head head; ++}; ++ ++static inline void au_spl_init(struct au_splhead *spl) ++{ ++ spin_lock_init(&spl->spin); ++ INIT_LIST_HEAD(&spl->head); ++} ++ ++static inline void au_spl_add(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_add(list, &spl->head); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del(struct list_head *list, struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del(list); ++ spin_unlock(&spl->spin); ++} ++ ++static inline void au_spl_del_rcu(struct list_head *list, ++ struct au_splhead *spl) ++{ ++ spin_lock(&spl->spin); ++ list_del_rcu(list); ++ spin_unlock(&spl->spin); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SPL_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/super.c linux-2.6.36/fs/aufs/super.c +--- linux-2.6.36.orig/fs/aufs/super.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/super.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,913 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * mount and super_block operations ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++/* ++ * super_operations ++ */ ++static struct inode *aufs_alloc_inode(struct super_block *sb __maybe_unused) ++{ ++ struct au_icntnr *c; ++ ++ c = au_cache_alloc_icntnr(); ++ if (c) { ++ au_icntnr_init(c); ++ 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) ++{ ++ au_iinfo_fin(inode); ++ au_cache_free_icntnr(container_of(inode, struct au_icntnr, vfs_inode)); ++} ++ ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino) ++{ ++ struct inode *inode; ++ int err; ++ ++ inode = iget_locked(sb, ino); ++ if (unlikely(!inode)) { ++ inode = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ if (!(inode->i_state & I_NEW)) ++ goto out; ++ ++ err = au_xigen_new(inode); ++ if (!err) ++ err = au_iinfo_init(inode); ++ if (!err) ++ inode->i_version++; ++ else { ++ iget_failed(inode); ++ inode = ERR_PTR(err); ++ } ++ ++out: ++ /* never return NULL */ ++ AuDebugOn(!inode); ++ AuTraceErrPtr(inode); ++ return inode; ++} ++ ++/* lock free root dinfo */ ++static int au_show_brs(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ struct path path; ++ struct au_hdentry *hdp; ++ struct au_branch *br; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ for (bindex = 0; !err && bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ path.mnt = br->br_mnt; ++ path.dentry = hdp[bindex].hd_dentry; ++ err = au_seq_path(seq, &path); ++ if (err > 0) ++ err = seq_printf(seq, "=%s", ++ au_optstr_br_perm(br->br_perm)); ++ if (!err && bindex != bend) ++ err = seq_putc(seq, ':'); ++ } ++ ++ return err; ++} ++ ++static void au_show_wbr_create(struct seq_file *m, int v, ++ struct au_sbinfo *sbinfo) ++{ ++ const char *pat; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ seq_printf(m, ",create="); ++ pat = au_optstr_wbr_create(v); ++ switch (v) { ++ case AuWbrCreate_TDP: ++ case AuWbrCreate_RR: ++ case AuWbrCreate_MFS: ++ case AuWbrCreate_PMFS: ++ seq_printf(m, pat); ++ break; ++ case AuWbrCreate_MFSV: ++ seq_printf(m, /*pat*/"mfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_PMFSV: ++ seq_printf(m, /*pat*/"pmfs:%lu", ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ case AuWbrCreate_MFSRR: ++ seq_printf(m, /*pat*/"mfsrr:%llu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark); ++ break; ++ case AuWbrCreate_MFSRRV: ++ seq_printf(m, /*pat*/"mfsrr:%llu:%lu", ++ sbinfo->si_wbr_mfs.mfsrr_watermark, ++ jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) ++ / MSEC_PER_SEC); ++ break; ++ } ++} ++ ++static int au_show_xino(struct seq_file *seq, struct vfsmount *mnt) ++{ ++#ifdef CONFIG_SYSFS ++ return 0; ++#else ++ int err; ++ const int len = sizeof(AUFS_XINO_FNAME) - 1; ++ aufs_bindex_t bindex, brid; ++ struct super_block *sb; ++ struct qstr *name; ++ struct file *f; ++ struct dentry *d, *h_root; ++ struct au_hdentry *hdp; ++ ++ AuRwMustAnyLock(&sbinfo->si_rwsem); ++ ++ err = 0; ++ sb = mnt->mnt_sb; ++ f = au_sbi(sb)->si_xib; ++ if (!f) ++ goto out; ++ ++ /* stop printing the default xino path on the first writable branch */ ++ h_root = NULL; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) { ++ bindex = au_br_index(sb, brid); ++ hdp = au_di(sb->s_root)->di_hdentry; ++ h_root = hdp[0 + bindex].hd_dentry; ++ } ++ d = f->f_dentry; ++ name = &d->d_name; ++ /* safe ->d_parent because the file is unlinked */ ++ if (d->d_parent == h_root ++ && name->len == len ++ && !memcmp(name->name, AUFS_XINO_FNAME, len)) ++ goto out; ++ ++ seq_puts(seq, ",xino="); ++ err = au_xino_path(seq, f); ++ ++out: ++ return err; ++#endif ++} ++ ++/* seq_file will re-call me in case of too long string */ ++static int aufs_show_options(struct seq_file *m, struct vfsmount *mnt) ++{ ++ int err; ++ unsigned int mnt_flags, v; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++#define AuBool(name, str) do { \ ++ v = au_opt_test(mnt_flags, name); \ ++ if (v != au_opt_test(AuOpt_Def, name)) \ ++ seq_printf(m, ",%s" #str, v ? "" : "no"); \ ++} while (0) ++ ++#define AuStr(name, str) do { \ ++ v = mnt_flags & AuOptMask_##name; \ ++ if (v != (AuOpt_Def & AuOptMask_##name)) \ ++ seq_printf(m, "," #str "=%s", au_optstr_##str(v)); \ ++} while (0) ++ ++#define AuUInt(name, str, val) do { \ ++ if (val != AUFS_##name##_DEF) \ ++ seq_printf(m, "," #str "=%u", val); \ ++} while (0) ++ ++ /* lock free root dinfo */ ++ sb = mnt->mnt_sb; ++ si_noflush_read_lock(sb); ++ sbinfo = au_sbi(sb); ++ seq_printf(m, ",si=%lx", sysaufs_si_id(sbinfo)); ++ ++ mnt_flags = au_mntflags(sb); ++ if (au_opt_test(mnt_flags, XINO)) { ++ err = au_show_xino(m, mnt); ++ if (unlikely(err)) ++ goto out; ++ } else ++ seq_puts(m, ",noxino"); ++ ++ AuBool(TRUNC_XINO, trunc_xino); ++ AuStr(UDBA, udba); ++ AuBool(SHWH, shwh); ++ AuBool(PLINK, plink); ++ AuBool(DIO, dio); ++ /* AuBool(DIRPERM1, dirperm1); */ ++ /* AuBool(REFROF, refrof); */ ++ ++ v = sbinfo->si_wbr_create; ++ if (v != AuWbrCreate_Def) ++ au_show_wbr_create(m, v, sbinfo); ++ ++ v = sbinfo->si_wbr_copyup; ++ if (v != AuWbrCopyup_Def) ++ seq_printf(m, ",cpup=%s", au_optstr_wbr_copyup(v)); ++ ++ v = au_opt_test(mnt_flags, ALWAYS_DIROPQ); ++ if (v != au_opt_test(AuOpt_Def, ALWAYS_DIROPQ)) ++ seq_printf(m, ",diropq=%c", v ? 'a' : 'w'); ++ ++ AuUInt(DIRWH, dirwh, sbinfo->si_dirwh); ++ ++ v = jiffies_to_msecs(sbinfo->si_rdcache) / MSEC_PER_SEC; ++ AuUInt(RDCACHE, rdcache, v); ++ ++ AuUInt(RDBLK, rdblk, sbinfo->si_rdblk); ++ AuUInt(RDHASH, rdhash, sbinfo->si_rdhash); ++ ++ AuBool(SUM, sum); ++ /* AuBool(SUM_W, wsum); */ ++ AuBool(WARN_PERM, warn_perm); ++ AuBool(VERBOSE, verbose); ++ ++out: ++ /* be sure to print "br:" last */ ++ if (!sysaufs_brs) { ++ seq_puts(m, ",br:"); ++ au_show_brs(m, sb); ++ } ++ si_read_unlock(sb); ++ return 0; ++ ++#undef AuBool ++#undef AuStr ++#undef AuUInt ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sum mode which returns the summation for statfs(2) */ ++ ++static u64 au_add_till_max(u64 a, u64 b) ++{ ++ u64 old; ++ ++ old = a; ++ a += b; ++ if (old < a) ++ return a; ++ return ULLONG_MAX; ++} ++ ++static int au_statfs_sum(struct super_block *sb, struct kstatfs *buf) ++{ ++ int err; ++ u64 blocks, bfree, bavail, files, ffree; ++ aufs_bindex_t bend, bindex, i; ++ unsigned char shared; ++ struct path h_path; ++ struct super_block *h_sb; ++ ++ blocks = 0; ++ bfree = 0; ++ bavail = 0; ++ files = 0; ++ ffree = 0; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = bend; bindex >= 0; bindex--) { ++ h_path.mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path.mnt->mnt_sb; ++ shared = 0; ++ for (i = bindex + 1; !shared && i <= bend; i++) ++ shared = (au_sbr_sb(sb, i) == h_sb); ++ if (shared) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ if (unlikely(err)) ++ goto out; ++ ++ blocks = au_add_till_max(blocks, buf->f_blocks); ++ bfree = au_add_till_max(bfree, buf->f_bfree); ++ bavail = au_add_till_max(bavail, buf->f_bavail); ++ files = au_add_till_max(files, buf->f_files); ++ ffree = au_add_till_max(ffree, buf->f_ffree); ++ } ++ ++ buf->f_blocks = blocks; ++ buf->f_bfree = bfree; ++ buf->f_bavail = bavail; ++ buf->f_files = files; ++ buf->f_ffree = ffree; ++ ++out: ++ return err; ++} ++ ++static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ int err; ++ struct path h_path; ++ struct super_block *sb; ++ ++ /* lock free root dinfo */ ++ sb = dentry->d_sb; ++ si_noflush_read_lock(sb); ++ if (!au_opt_test(au_mntflags(sb), SUM)) { ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = au_sbr_mnt(sb, 0); ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, buf); ++ } else ++ err = au_statfs_sum(sb, buf); ++ si_read_unlock(sb); ++ ++ if (!err) { ++ buf->f_type = AUFS_SUPER_MAGIC; ++ buf->f_namelen = AUFS_MAX_NAMELEN; ++ memset(&buf->f_fsid, 0, sizeof(buf->f_fsid)); ++ } ++ /* buf->f_bsize = buf->f_blocks = buf->f_bfree = buf->f_bavail = -1; */ ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* final actions when unmounting a file system */ ++static void aufs_put_super(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (!sbinfo) ++ return; ++ ++ dbgaufs_si_fin(sbinfo); ++ kobject_put(&sbinfo->si_kobj); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_array_free(void *array) ++{ ++ if (array) { ++ if (!is_vmalloc_addr(array)) ++ kfree(array); ++ else ++ vfree(array); ++ } ++} ++ ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg) ++{ ++ void *array; ++ unsigned long long n; ++ ++ array = NULL; ++ n = 0; ++ if (!*hint) ++ goto out; ++ ++ if (*hint > ULLONG_MAX / sizeof(array)) { ++ array = ERR_PTR(-EMFILE); ++ pr_err("hint %llu\n", *hint); ++ goto out; ++ } ++ ++ array = kmalloc(sizeof(array) * *hint, GFP_NOFS); ++ if (unlikely(!array)) ++ array = vmalloc(sizeof(array) * *hint); ++ if (unlikely(!array)) { ++ array = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ n = cb(array, *hint, arg); ++ AuDebugOn(n > *hint); ++ ++out: ++ *hint = n; ++ return array; ++} ++ ++static unsigned long long au_iarray_cb(void *a, ++ unsigned long long max __maybe_unused, ++ void *arg) ++{ ++ unsigned long long n; ++ struct inode **p, *inode; ++ struct list_head *head; ++ ++ n = 0; ++ p = a; ++ head = arg; ++ spin_lock(&inode_lock); ++ list_for_each_entry(inode, head, i_sb_list) { ++ if (!is_bad_inode(inode) ++ && au_ii(inode)->ii_bstart >= 0) { ++ au_igrab(inode); ++ *p++ = inode; ++ n++; ++ AuDebugOn(n > max); ++ } ++ } ++ spin_unlock(&inode_lock); ++ ++ return n; ++} ++ ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max) ++{ ++ *max = atomic_long_read(&au_sbi(sb)->si_ninodes); ++ return au_array_alloc(max, au_iarray_cb, &sb->s_inodes); ++} ++ ++void au_iarray_free(struct inode **a, unsigned long long max) ++{ ++ unsigned long long ull; ++ ++ for (ull = 0; ull < max; ull++) ++ iput(a[ull]); ++ au_array_free(a); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * refresh dentry and inode at remount time. ++ */ ++/* todo: consolidate with simple_reval_dpath() and au_reval_for_attr() */ ++static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags, ++ struct dentry *parent) ++{ ++ int err; ++ ++ di_write_lock_child(dentry); ++ di_read_lock_parent(parent, AuLock_IR); ++ err = au_refresh_dentry(dentry, parent); ++ if (!err && dir_flags) ++ au_hn_reset(dentry->d_inode, dir_flags); ++ di_read_unlock(parent, AuLock_IR); ++ di_write_unlock(dentry); ++ ++ return err; ++} ++ ++static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen, ++ struct au_sbinfo *sbinfo, ++ const unsigned int dir_flags) ++{ ++ int err; ++ struct dentry *parent; ++ struct inode *inode; ++ ++ err = 0; ++ parent = dget_parent(dentry); ++ if (!au_digen_test(parent, sigen) && au_digen_test(dentry, sigen)) { ++ inode = dentry->d_inode; ++ if (inode) { ++ if (!S_ISDIR(inode->i_mode)) ++ err = au_do_refresh(dentry, /*dir_flags*/0, ++ parent); ++ else { ++ err = au_do_refresh(dentry, dir_flags, parent); ++ if (unlikely(err)) ++ au_fset_si(sbinfo, FAILED_REFRESH_DIR); ++ } ++ } else ++ err = au_do_refresh(dentry, /*dir_flags*/0, parent); ++ AuDbgDentry(dentry); ++ } ++ dput(parent); ++ ++ AuTraceErr(err); ++ return err; ++} ++ ++static int au_refresh_d(struct super_block *sb) ++{ ++ int err, i, j, ndentry, e; ++ unsigned int sigen; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *d; ++ struct au_sbinfo *sbinfo; ++ struct dentry *root = sb->s_root; ++ const unsigned int dir_flags = au_hi_flags(root->d_inode, /*isdir*/1); ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_dcsub_pages(&dpages, root, NULL, NULL); ++ if (unlikely(err)) ++ goto out_dpages; ++ ++ sigen = au_sigen(sb); ++ sbinfo = au_sbi(sb); ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags); ++ if (unlikely(e && !err)) ++ err = e; ++ /* go on even err */ ++ } ++ } ++ ++out_dpages: ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_refresh_i(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int sigen; ++ unsigned long long max, ull; ++ struct inode *inode, **array; ++ ++ array = au_iarray_alloc(sb, &max); ++ err = PTR_ERR(array); ++ if (IS_ERR(array)) ++ goto out; ++ ++ err = 0; ++ sigen = au_sigen(sb); ++ for (ull = 0; ull < max; ull++) { ++ inode = array[ull]; ++ if (au_iigen(inode) != sigen) { ++ ii_write_lock_child(inode); ++ e = au_refresh_hinode_self(inode); ++ ii_write_unlock(inode); ++ if (unlikely(e)) { ++ pr_err("error %d, i%lu\n", e, inode->i_ino); ++ if (!err) ++ err = e; ++ /* go on even if err */ ++ } ++ } ++ } ++ ++ au_iarray_free(array, max); ++ ++out: ++ return err; ++} ++ ++static void au_remount_refresh(struct super_block *sb) ++{ ++ int err, e; ++ unsigned int udba; ++ aufs_bindex_t bindex, bend; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_branch *br; ++ ++ au_sigen_inc(sb); ++ au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR); ++ ++ root = sb->s_root; ++ DiMustNoWaiters(root); ++ inode = root->d_inode; ++ IiMustNoWaiters(inode); ++ ++ udba = au_opt_udba(sb); ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ err = au_hnotify_reset_br(udba, br, br->br_perm); ++ if (unlikely(err)) ++ AuIOErr("hnotify failed on br %d, %d, ignored\n", ++ bindex, err); ++ /* go on even if err */ ++ } ++ au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1)); ++ ++ di_write_unlock(root); ++ err = au_refresh_d(sb); ++ e = au_refresh_i(sb); ++ if (unlikely(e && !err)) ++ err = e; ++ /* aufs_write_lock() calls ..._child() */ ++ di_write_lock_child(root); ++ ++ au_cpup_attr_all(inode, /*force*/1); ++ ++ if (unlikely(err)) ++ AuIOErr("refresh failed, ignored, %d\n", 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; ++} ++ ++static int aufs_remount_fs(struct super_block *sb, int *flags, char *data) ++{ ++ int err, do_dx; ++ unsigned int mntflags; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ struct au_sbinfo *sbinfo; ++ ++ err = 0; ++ root = sb->s_root; ++ if (!data || !*data) { ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (!err) { ++ di_write_lock_child(root); ++ err = au_opts_verify(sb, *flags, /*pending*/0); ++ aufs_write_unlock(root); ++ } ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.flags = AuOpts_REMOUNT; ++ opts.sb_flags = *flags; ++ ++ /* parse it before aufs lock */ ++ err = au_opts_parse(sb, data, &opts); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ sbinfo = au_sbi(sb); ++ inode = root->d_inode; ++ mutex_lock(&inode->i_mutex); ++ err = si_write_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ if (unlikely(err)) ++ goto out_mtx; ++ di_write_lock_child(root); ++ ++ /* au_opts_remount() may return an error */ ++ err = au_opts_remount(sb, &opts); ++ au_opts_free(&opts); ++ ++ if (au_ftest_opts(opts.flags, REFRESH)) ++ au_remount_refresh(sb); ++ ++ if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) { ++ mntflags = au_mntflags(sb); ++ do_dx = !!au_opt_test(mntflags, DIO); ++ au_dy_arefresh(do_dx); ++ } ++ ++ aufs_write_unlock(root); ++ ++out_mtx: ++ mutex_unlock(&inode->i_mutex); ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++static const struct super_operations aufs_sop = { ++ .alloc_inode = aufs_alloc_inode, ++ .destroy_inode = aufs_destroy_inode, ++ /* always deleting, no clearing */ ++ .drop_inode = generic_delete_inode, ++ .show_options = aufs_show_options, ++ .statfs = aufs_statfs, ++ .put_super = aufs_put_super, ++ .remount_fs = aufs_remount_fs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int alloc_root(struct super_block *sb) ++{ ++ int err; ++ struct inode *inode; ++ struct dentry *root; ++ ++ err = -ENOMEM; ++ inode = au_iget_locked(sb, AUFS_ROOT_INO); ++ err = PTR_ERR(inode); ++ if (IS_ERR(inode)) ++ goto out; ++ ++ inode->i_op = &aufs_dir_iop; ++ inode->i_fop = &aufs_dir_fop; ++ inode->i_mode = S_IFDIR; ++ inode->i_nlink = 2; ++ unlock_new_inode(inode); ++ ++ root = d_alloc_root(inode); ++ if (unlikely(!root)) ++ goto out_iput; ++ err = PTR_ERR(root); ++ if (IS_ERR(root)) ++ goto out_iput; ++ ++ err = au_di_init(root); ++ if (!err) { ++ sb->s_root = root; ++ return 0; /* success */ ++ } ++ dput(root); ++ goto out; /* do not iput */ ++ ++out_iput: ++ iget_failed(inode); ++out: ++ return err; ++ ++} ++ ++static int aufs_fill_super(struct super_block *sb, void *raw_data, ++ int silent __maybe_unused) ++{ ++ int err; ++ struct au_opts opts; ++ struct dentry *root; ++ struct inode *inode; ++ char *arg = raw_data; ++ ++ if (unlikely(!arg || !*arg)) { ++ err = -EINVAL; ++ pr_err("no arg\n"); ++ goto out; ++ } ++ ++ err = -ENOMEM; ++ memset(&opts, 0, sizeof(opts)); ++ opts.opt = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!opts.opt)) ++ goto out; ++ opts.max_opt = PAGE_SIZE / sizeof(*opts.opt); ++ opts.sb_flags = sb->s_flags; ++ ++ err = au_si_alloc(sb); ++ if (unlikely(err)) ++ goto out_opts; ++ ++ /* 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; ++ sb->s_maxbytes = 0; ++ au_export_init(sb); ++ ++ err = alloc_root(sb); ++ if (unlikely(err)) { ++ si_write_unlock(sb); ++ goto out_info; ++ } ++ root = sb->s_root; ++ inode = root->d_inode; ++ ++ /* ++ * 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 (unlikely(err)) ++ goto out_root; ++ ++ /* lock vfs_inode first, then aufs. */ ++ mutex_lock(&inode->i_mutex); ++ aufs_write_lock(root); ++ err = au_opts_mount(sb, &opts); ++ au_opts_free(&opts); ++ aufs_write_unlock(root); ++ mutex_unlock(&inode->i_mutex); ++ if (!err) ++ goto out_opts; /* success */ ++ ++out_root: ++ dput(root); ++ sb->s_root = NULL; ++out_info: ++ kobject_put(&au_sbi(sb)->si_kobj); ++ sb->s_fs_info = NULL; ++out_opts: ++ free_page((unsigned long)opts.opt); ++out: ++ AuTraceErr(err); ++ err = cvt_err(err); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_get_sb(struct file_system_type *fs_type, int flags, ++ const char *dev_name __maybe_unused, void *raw_data, ++ struct vfsmount *mnt) ++{ ++ int err; ++ struct super_block *sb; ++ ++ /* 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) { ++ sb = mnt->mnt_sb; ++ si_write_lock(sb, !AuLock_FLUSH); ++ sysaufs_brs_add(sb, 0); ++ si_write_unlock(sb); ++ au_sbilist_add(sb); ++ } ++ return err; ++} ++ ++static void aufs_kill_sb(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ sbinfo = au_sbi(sb); ++ if (sbinfo) { ++ au_sbilist_del(sb); ++ aufs_write_lock(sb->s_root); ++ if (sbinfo->si_wbr_create_ops->fin) ++ sbinfo->si_wbr_create_ops->fin(sb); ++ if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) { ++ au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE); ++ au_remount_refresh(sb); ++ } ++ if (au_opt_test(sbinfo->si_mntflags, PLINK)) ++ au_plink_put(sb, /*verbose*/1); ++ au_xino_clr(sb); ++ aufs_write_unlock(sb->s_root); ++ au_nwt_flush(&sbinfo->si_nowait); ++ } ++ generic_shutdown_super(sb); ++} ++ ++struct file_system_type aufs_fs_type = { ++ .name = AUFS_FSTYPE, ++ .fs_flags = ++ FS_RENAME_DOES_D_MOVE /* a race between rename and others */ ++ | FS_REVAL_DOT, /* for NFS branch and udba */ ++ .get_sb = aufs_get_sb, ++ .kill_sb = aufs_kill_sb, ++ /* no need to __module_get() and module_put(). */ ++ .owner = THIS_MODULE, ++}; +diff -Nur linux-2.6.36.orig/fs/aufs/super.h linux-2.6.36/fs/aufs/super.h +--- linux-2.6.36.orig/fs/aufs/super.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/super.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,527 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * super_block operations ++ */ ++ ++#ifndef __AUFS_SUPER_H__ ++#define __AUFS_SUPER_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "rwsem.h" ++#include "spl.h" ++#include "wkq.h" ++ ++typedef ssize_t (*au_readf_t)(struct file *, char __user *, size_t, loff_t *); ++typedef ssize_t (*au_writef_t)(struct file *, const char __user *, size_t, ++ loff_t *); ++ ++/* policies to select one among multiple writable branches */ ++struct au_wbr_copyup_operations { ++ int (*copyup)(struct dentry *dentry); ++}; ++ ++struct au_wbr_create_operations { ++ int (*create)(struct dentry *dentry, int isdir); ++ int (*init)(struct super_block *sb); ++ int (*fin)(struct super_block *sb); ++}; ++ ++struct au_wbr_mfs { ++ struct mutex mfs_lock; /* protect this structure */ ++ unsigned long mfs_jiffy; ++ unsigned long mfs_expire; ++ aufs_bindex_t mfs_bindex; ++ ++ unsigned long long mfsrr_bytes; ++ unsigned long long mfsrr_watermark; ++}; ++ ++struct au_branch; ++struct au_sbinfo { ++ /* nowait tasks in the system-wide workqueue */ ++ struct au_nowait_tasks si_nowait; ++ ++ /* ++ * tried sb->s_umount, but failed due to the dependecy between i_mutex. ++ * rwsem for au_sbinfo is necessary. ++ */ ++ struct au_rwsem si_rwsem; ++ ++ /* prevent recursive locking in deleting inode */ ++ struct { ++ unsigned long *bitmap; ++ spinlock_t tree_lock; ++ struct radix_tree_root tree; ++ } au_si_pid; ++ ++ /* ++ * dirty approach to protect sb->sb_inodes and ->s_files from remount. ++ */ ++ atomic_long_t si_ninodes, si_nfiles; ++ ++ /* branch management */ ++ unsigned int si_generation; ++ ++ /* see above flags */ ++ unsigned char au_si_status; ++ ++ aufs_bindex_t si_bend; ++ ++ /* dirty trick to keep br_id plus */ ++ unsigned int si_last_br_id : ++ sizeof(aufs_bindex_t) * BITS_PER_BYTE - 1; ++ struct au_branch **si_branch; ++ ++ /* policy to select a writable branch */ ++ unsigned char si_wbr_copyup; ++ unsigned char si_wbr_create; ++ struct au_wbr_copyup_operations *si_wbr_copyup_ops; ++ struct au_wbr_create_operations *si_wbr_create_ops; ++ ++ /* round robin */ ++ atomic_t si_wbr_rr_next; ++ ++ /* most free space */ ++ struct au_wbr_mfs si_wbr_mfs; ++ ++ /* mount flags */ ++ /* include/asm-ia64/siginfo.h defines a macro named si_flags */ ++ unsigned int si_mntflags; ++ ++ /* external inode number (bitmap and translation table) */ ++ au_readf_t si_xread; ++ au_writef_t si_xwrite; ++ struct file *si_xib; ++ struct mutex si_xib_mtx; /* protect xib members */ ++ unsigned long *si_xib_buf; ++ unsigned long si_xib_last_pindex; ++ int si_xib_next_bit; ++ aufs_bindex_t si_xino_brid; ++ /* reserved for future use */ ++ /* unsigned long long si_xib_limit; */ /* Max xib file size */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++ /* i_generation */ ++ struct file *si_xigen; ++ atomic_t si_xigen_next; ++#endif ++ ++ /* vdir parameters */ ++ unsigned long si_rdcache; /* max cache time in jiffies */ ++ unsigned int si_rdblk; /* deblk size */ ++ unsigned int si_rdhash; /* hash size */ ++ ++ /* ++ * If the number of whiteouts are larger than si_dirwh, leave all of ++ * them after au_whtmp_ren to reduce the cost of rmdir(2). ++ * future fsck.aufs or kernel thread will remove them later. ++ * Otherwise, remove all whiteouts and the dir in rmdir(2). ++ */ ++ unsigned int si_dirwh; ++ ++ /* ++ * rename(2) a directory with all children. ++ */ ++ /* reserved for future use */ ++ /* int si_rendir; */ ++ ++ /* pseudo_link list */ ++ struct au_splhead si_plink; ++ wait_queue_head_t si_plink_wq; ++ spinlock_t si_plink_maint_lock; ++ pid_t si_plink_maint_pid; ++ ++ /* ++ * sysfs and lifetime management. ++ * this is not a small structure and it may be a waste of memory in case ++ * of sysfs is disabled, particulary when many aufs-es are mounted. ++ * but using sysfs is majority. ++ */ ++ struct kobject si_kobj; ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *si_dbgaufs, *si_dbgaufs_xib; ++#ifdef CONFIG_AUFS_EXPORT ++ struct dentry *si_dbgaufs_xigen; ++#endif ++#endif ++ ++#ifdef CONFIG_AUFS_SBILIST ++ struct list_head si_list; ++#endif ++ ++ /* dirty, necessary for unmounting, sysfs and sysrq */ ++ struct super_block *si_sb; ++}; ++ ++/* sbinfo status flags */ ++/* ++ * set true when refresh_dirs() failed at remount time. ++ * then try refreshing dirs at access time again. ++ * if it is false, refreshing dirs at access time is unnecesary ++ */ ++#define AuSi_FAILED_REFRESH_DIR 1 ++static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi, ++ unsigned int flag) ++{ ++ AuRwMustAnyLock(&sbi->si_rwsem); ++ return sbi->au_si_status & flag; ++} ++#define au_ftest_si(sbinfo, name) au_do_ftest_si(sbinfo, AuSi_##name) ++#define au_fset_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status |= AuSi_##name; \ ++} while (0) ++#define au_fclr_si(sbinfo, name) do { \ ++ AuRwMustWriteLock(&(sbinfo)->si_rwsem); \ ++ (sbinfo)->au_si_status &= ~AuSi_##name; \ ++} while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policy to select one among writable branches */ ++#define AuWbrCopyup(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_copyup_ops->copyup(__VA_ARGS__)) ++#define AuWbrCreate(sbinfo, ...) \ ++ ((sbinfo)->si_wbr_create_ops->create(__VA_ARGS__)) ++ ++/* flags for si_read_lock()/aufs_read_lock()/di_read_lock() */ ++#define AuLock_DW 1 /* write-lock dentry */ ++#define AuLock_IR (1 << 1) /* read-lock inode */ ++#define AuLock_IW (1 << 2) /* write-lock inode */ ++#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */ ++#define AuLock_DIR (1 << 4) /* target is a dir */ ++#define AuLock_NOPLM (1 << 5) /* return err in plm mode */ ++#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */ ++#define AuLock_GEN (1 << 7) /* test digen/iigen */ ++#define au_ftest_lock(flags, name) ((flags) & AuLock_##name) ++#define au_fset_lock(flags, name) \ ++ do { (flags) |= AuLock_##name; } while (0) ++#define au_fclr_lock(flags, name) \ ++ do { (flags) &= ~AuLock_##name; } while (0) ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* super.c */ ++extern struct file_system_type aufs_fs_type; ++struct inode *au_iget_locked(struct super_block *sb, ino_t ino); ++typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max, ++ void *arg); ++void au_array_free(void *array); ++void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg); ++struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max); ++void au_iarray_free(struct inode **a, unsigned long long max); ++ ++/* sbinfo.c */ ++void au_si_free(struct kobject *kobj); ++int au_si_alloc(struct super_block *sb); ++int au_sbr_realloc(struct au_sbinfo *sbinfo, int nbr); ++ ++unsigned int au_sigen_inc(struct super_block *sb); ++aufs_bindex_t au_new_br_id(struct super_block *sb); ++ ++int si_read_lock(struct super_block *sb, int flags); ++int si_write_lock(struct super_block *sb, int flags); ++int 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); ++int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags); ++void aufs_read_and_write_unlock2(struct dentry *d1, struct dentry *d2); ++ ++int si_pid_test_slow(struct super_block *sb); ++void si_pid_set_slow(struct super_block *sb); ++void si_pid_clr_slow(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); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct au_sbinfo *au_sbi(struct super_block *sb) ++{ ++ return sb->s_fs_info; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_EXPORT ++void au_export_init(struct super_block *sb); ++ ++static inline int au_test_nfsd(void) ++{ ++ struct task_struct *tsk = current; ++ ++ return (tsk->flags & PF_KTHREAD) ++ && !strcmp(tsk->comm, "nfsd"); ++} ++ ++void au_xigen_inc(struct inode *inode); ++int au_xigen_new(struct inode *inode); ++int au_xigen_set(struct super_block *sb, struct file *base); ++void au_xigen_clr(struct super_block *sb); ++ ++static inline int au_busy_or_stale(void) ++{ ++ if (!au_test_nfsd()) ++ return -EBUSY; ++ return -ESTALE; ++} ++#else ++AuStubVoid(au_export_init, struct super_block *sb) ++AuStubInt0(au_test_nfsd, void) ++AuStubVoid(au_xigen_inc, struct inode *inode) ++AuStubInt0(au_xigen_new, struct inode *inode) ++AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) ++AuStubVoid(au_xigen_clr, struct super_block *sb) ++static inline int au_busy_or_stale(void) ++{ ++ return -EBUSY; ++} ++#endif /* CONFIG_AUFS_EXPORT */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#ifdef CONFIG_AUFS_SBILIST ++/* module.c */ ++extern struct au_splhead au_sbilist; ++ ++static inline void au_sbilist_init(void) ++{ ++ au_spl_init(&au_sbilist); ++} ++ ++static inline void au_sbilist_add(struct super_block *sb) ++{ ++ au_spl_add(&au_sbi(sb)->si_list, &au_sbilist); ++} ++ ++static inline void au_sbilist_del(struct super_block *sb) ++{ ++ au_spl_del(&au_sbi(sb)->si_list, &au_sbilist); ++} ++#else ++AuStubVoid(au_sbilist_init, void) ++AuStubVoid(au_sbilist_add, struct super_block*) ++AuStubVoid(au_sbilist_del, struct super_block*) ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) ++{ ++ /* ++ * This function is a dynamic '__init' fucntion actually, ++ * so the tiny check for si_rwsem is unnecessary. ++ */ ++ /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ ++#ifdef CONFIG_DEBUG_FS ++ sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_xib = NULL; ++#ifdef CONFIG_AUFS_EXPORT ++ sbinfo->si_dbgaufs_xigen = NULL; ++#endif ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline pid_t si_pid_bit(void) ++{ ++ /* the origin of pid is 1, but the bitmap's is 0 */ ++ return current->pid - 1; ++} ++ ++static inline int si_pid_test(struct super_block *sb) ++{ ++ pid_t bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) ++ return test_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ else ++ return si_pid_test_slow(sb); ++} ++ ++static inline void si_pid_set(struct super_block *sb) ++{ ++ pid_t bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ set_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_set_slow(sb); ++} ++ ++static inline void si_pid_clr(struct super_block *sb) ++{ ++ pid_t bit = si_pid_bit(); ++ if (bit < PID_MAX_DEFAULT) { ++ AuDebugOn(!test_bit(bit, au_sbi(sb)->au_si_pid.bitmap)); ++ clear_bit(bit, au_sbi(sb)->au_si_pid.bitmap); ++ /* smp_mb(); */ ++ } else ++ si_pid_clr_slow(sb); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock superblock. mainly for entry point functions */ ++/* ++ * __si_read_lock, __si_write_lock, ++ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock ++ */ ++AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem); ++ ++#define SiMustNoWaiters(sb) AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem) ++#define SiMustAnyLock(sb) AuRwMustAnyLock(&au_sbi(sb)->si_rwsem) ++#define SiMustWriteLock(sb) AuRwMustWriteLock(&au_sbi(sb)->si_rwsem) ++ ++static inline void si_noflush_read_lock(struct super_block *sb) ++{ ++ __si_read_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_read_trylock(struct super_block *sb) ++{ ++ int locked = __si_read_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++static inline void si_noflush_write_lock(struct super_block *sb) ++{ ++ __si_write_lock(sb); ++ si_pid_set(sb); ++} ++ ++static inline int si_noflush_write_trylock(struct super_block *sb) ++{ ++ int locked = __si_write_trylock(sb); ++ if (locked) ++ si_pid_set(sb); ++ return locked; ++} ++ ++#if 0 /* unused */ ++static inline int si_read_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_read_trylock(sb); ++} ++#endif ++ ++static inline void si_read_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_read_unlock(sb); ++} ++ ++#if 0 /* unused */ ++static inline int si_write_trylock(struct super_block *sb, int flags) ++{ ++ if (au_ftest_lock(flags, FLUSH)) ++ au_nwt_flush(&au_sbi(sb)->si_nowait); ++ return si_noflush_write_trylock(sb); ++} ++#endif ++ ++static inline void si_write_unlock(struct super_block *sb) ++{ ++ si_pid_clr(sb); ++ __si_write_unlock(sb); ++} ++ ++#if 0 /* unused */ ++static inline void si_downgrade_lock(struct super_block *sb) ++{ ++ __si_downgrade_lock(sb); ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline aufs_bindex_t au_sbend(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_bend; ++} ++ ++static inline unsigned int au_mntflags(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_mntflags; ++} ++ ++static inline unsigned int au_sigen(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_generation; ++} ++ ++static inline void au_ninodes_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_ninodes_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_ninodes)); ++ atomic_long_dec(&au_sbi(sb)->si_ninodes); ++} ++ ++static inline void au_nfiles_inc(struct super_block *sb) ++{ ++ atomic_long_inc(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline void au_nfiles_dec(struct super_block *sb) ++{ ++ AuDebugOn(!atomic_long_read(&au_sbi(sb)->si_nfiles)); ++ atomic_long_dec(&au_sbi(sb)->si_nfiles); ++} ++ ++static inline struct au_branch *au_sbr(struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_branch[0 + bindex]; ++} ++ ++static inline void au_xino_brid_set(struct super_block *sb, aufs_bindex_t brid) ++{ ++ SiMustWriteLock(sb); ++ au_sbi(sb)->si_xino_brid = brid; ++} ++ ++static inline aufs_bindex_t au_xino_brid(struct super_block *sb) ++{ ++ SiMustAnyLock(sb); ++ return au_sbi(sb)->si_xino_brid; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_SUPER_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/sysaufs.c linux-2.6.36/fs/aufs/sysaufs.c +--- linux-2.6.36.orig/fs/aufs/sysaufs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/sysaufs.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,107 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sysfs interface and lifetime management ++ * they are necessary regardless sysfs is disabled. ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++unsigned long sysaufs_si_mask; ++struct kset *sysaufs_kset; ++ ++#define AuSiAttr(_name) { \ ++ .attr = { .name = __stringify(_name), .mode = 0444 }, \ ++ .show = sysaufs_si_##_name, \ ++} ++ ++static struct sysaufs_si_attr sysaufs_si_attr_xi_path = AuSiAttr(xi_path); ++struct attribute *sysaufs_si_attrs[] = { ++ &sysaufs_si_attr_xi_path.attr, ++ NULL, ++}; ++ ++static const struct sysfs_ops au_sbi_ops = { ++ .show = sysaufs_si_show ++}; ++ ++static struct kobj_type au_sbi_ktype = { ++ .release = au_si_free, ++ .sysfs_ops = &au_sbi_ops, ++ .default_attrs = sysaufs_si_attrs ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_init(struct au_sbinfo *sbinfo) ++{ ++ int err; ++ ++ sbinfo->si_kobj.kset = sysaufs_kset; ++ /* cf. sysaufs_name() */ ++ err = kobject_init_and_add ++ (&sbinfo->si_kobj, &au_sbi_ktype, /*&sysaufs_kset->kobj*/NULL, ++ SysaufsSiNamePrefix "%lx", sysaufs_si_id(sbinfo)); ++ ++ dbgaufs_si_null(sbinfo); ++ if (!err) { ++ err = dbgaufs_si_init(sbinfo); ++ if (unlikely(err)) ++ kobject_put(&sbinfo->si_kobj); ++ } ++ return err; ++} ++ ++void sysaufs_fin(void) ++{ ++ dbgaufs_fin(); ++ sysfs_remove_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ kset_unregister(sysaufs_kset); ++} ++ ++int __init sysaufs_init(void) ++{ ++ int err; ++ ++ do { ++ get_random_bytes(&sysaufs_si_mask, sizeof(sysaufs_si_mask)); ++ } while (!sysaufs_si_mask); ++ ++ err = -EINVAL; ++ sysaufs_kset = kset_create_and_add(AUFS_NAME, NULL, fs_kobj); ++ if (unlikely(!sysaufs_kset)) ++ goto out; ++ err = PTR_ERR(sysaufs_kset); ++ if (IS_ERR(sysaufs_kset)) ++ goto out; ++ err = sysfs_create_group(&sysaufs_kset->kobj, sysaufs_attr_group); ++ if (unlikely(err)) { ++ kset_unregister(sysaufs_kset); ++ goto out; ++ } ++ ++ err = dbgaufs_init(); ++ if (unlikely(err)) ++ sysaufs_fin(); ++out: ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/sysaufs.h linux-2.6.36/fs/aufs/sysaufs.h +--- linux-2.6.36.orig/fs/aufs/sysaufs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/sysaufs.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sysfs interface and mount lifetime management ++ */ ++ ++#ifndef __SYSAUFS_H__ ++#define __SYSAUFS_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "module.h" ++ ++struct super_block; ++struct au_sbinfo; ++ ++struct sysaufs_si_attr { ++ struct attribute attr; ++ int (*show)(struct seq_file *seq, struct super_block *sb); ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* sysaufs.c */ ++extern unsigned long sysaufs_si_mask; ++extern struct kset *sysaufs_kset; ++extern struct attribute *sysaufs_si_attrs[]; ++int sysaufs_si_init(struct au_sbinfo *sbinfo); ++int __init sysaufs_init(void); ++void sysaufs_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* some people doesn't like to show a pointer in kernel */ ++static inline unsigned long sysaufs_si_id(struct au_sbinfo *sbinfo) ++{ ++ return sysaufs_si_mask ^ (unsigned long)sbinfo; ++} ++ ++#define SysaufsSiNamePrefix "si_" ++#define SysaufsSiNameLen (sizeof(SysaufsSiNamePrefix) + 16) ++static inline void sysaufs_name(struct au_sbinfo *sbinfo, char *name) ++{ ++ snprintf(name, SysaufsSiNameLen, SysaufsSiNamePrefix "%lx", ++ sysaufs_si_id(sbinfo)); ++} ++ ++struct au_branch; ++#ifdef CONFIG_SYSFS ++/* sysfs.c */ ++extern struct attribute_group *sysaufs_attr_group; ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb); ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf); ++ ++void sysaufs_br_init(struct au_branch *br); ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex); ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex); ++ ++#define sysaufs_brs_init() do {} while (0) ++ ++#else ++#define sysaufs_attr_group NULL ++ ++AuStubInt0(sysaufs_si_xi_path, struct seq_file *seq, struct super_block *sb) ++ ++static inline ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ return 0; ++} ++ ++AuStubVoid(sysaufs_br_init, struct au_branch *br) ++AuStubVoid(sysaufs_brs_add, struct super_block *sb, aufs_bindex_t bindex) ++AuStubVoid(sysaufs_brs_del, struct super_block *sb, aufs_bindex_t bindex) ++ ++static inline void sysaufs_brs_init(void) ++{ ++ sysaufs_brs = 0; ++} ++ ++#endif /* CONFIG_SYSFS */ ++ ++#endif /* __KERNEL__ */ ++#endif /* __SYSAUFS_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/sysfs.c linux-2.6.36/fs/aufs/sysfs.c +--- linux-2.6.36.orig/fs/aufs/sysfs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/sysfs.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,250 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sysfs interface ++ */ ++ ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++#ifdef CONFIG_AUFS_FS_MODULE ++/* this entry violates the "one line per file" policy of sysfs */ ++static ssize_t config_show(struct kobject *kobj, struct kobj_attribute *attr, ++ char *buf) ++{ ++ ssize_t err; ++ static char *conf = ++/* this file is generated at compiling */ ++#include "conf.str" ++ ; ++ ++ err = snprintf(buf, PAGE_SIZE, conf); ++ if (unlikely(err >= PAGE_SIZE)) ++ err = -EFBIG; ++ return err; ++} ++ ++static struct kobj_attribute au_config_attr = __ATTR_RO(config); ++#endif ++ ++static struct attribute *au_attr[] = { ++#ifdef CONFIG_AUFS_FS_MODULE ++ &au_config_attr.attr, ++#endif ++ NULL, /* need to NULL terminate the list of attributes */ ++}; ++ ++static struct attribute_group sysaufs_attr_group_body = { ++ .attrs = au_attr ++}; ++ ++struct attribute_group *sysaufs_attr_group = &sysaufs_attr_group_body; ++ ++/* ---------------------------------------------------------------------- */ ++ ++int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) ++{ ++ int err; ++ ++ SiMustAnyLock(sb); ++ ++ err = 0; ++ if (au_opt_test(au_mntflags(sb), XINO)) { ++ err = au_xino_path(seq, au_sbi(sb)->si_xib); ++ seq_putc(seq, '\n'); ++ } ++ return err; ++} ++ ++/* ++ * the lifetime of branch is independent from the entry under sysfs. ++ * sysfs handles the lifetime of the entry, and never call ->show() after it is ++ * unlinked. ++ */ ++static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, ++ aufs_bindex_t bindex) ++{ ++ struct path path; ++ struct dentry *root; ++ struct au_branch *br; ++ ++ AuDbg("b%d\n", bindex); ++ ++ root = sb->s_root; ++ di_read_lock_parent(root, !AuLock_IR); ++ br = au_sbr(sb, bindex); ++ path.mnt = br->br_mnt; ++ path.dentry = au_h_dptr(root, bindex); ++ au_seq_path(seq, &path); ++ di_read_unlock(root, !AuLock_IR); ++ seq_printf(seq, "=%s\n", au_optstr_br_perm(br->br_perm)); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static struct seq_file *au_seq(char *p, ssize_t len) ++{ ++ struct seq_file *seq; ++ ++ seq = kzalloc(sizeof(*seq), GFP_NOFS); ++ if (seq) { ++ /* mutex_init(&seq.lock); */ ++ seq->buf = p; ++ seq->size = len; ++ return seq; /* success */ ++ } ++ ++ seq = ERR_PTR(-ENOMEM); ++ return seq; ++} ++ ++#define SysaufsBr_PREFIX "br" ++ ++/* todo: file size may exceed PAGE_SIZE */ ++ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, ++ char *buf) ++{ ++ ssize_t err; ++ long l; ++ aufs_bindex_t bend; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct seq_file *seq; ++ char *name; ++ struct attribute **cattr; ++ ++ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); ++ sb = sbinfo->si_sb; ++ ++ /* ++ * prevent a race condition between sysfs and aufs. ++ * for instance, sysfs_file_read() calls sysfs_get_active_two() which ++ * prohibits maintaining the sysfs entries. ++ * hew we acquire read lock after sysfs_get_active_two(). ++ * on the other hand, the remount process may maintain the sysfs/aufs ++ * entries after acquiring write lock. ++ * it can cause a deadlock. ++ * simply we gave up processing read here. ++ */ ++ err = -EBUSY; ++ if (unlikely(!si_noflush_read_trylock(sb))) ++ goto out; ++ ++ seq = au_seq(buf, PAGE_SIZE); ++ err = PTR_ERR(seq); ++ if (IS_ERR(seq)) ++ goto out_unlock; ++ ++ name = (void *)attr->name; ++ cattr = sysaufs_si_attrs; ++ while (*cattr) { ++ if (!strcmp(name, (*cattr)->name)) { ++ err = container_of(*cattr, struct sysaufs_si_attr, attr) ++ ->show(seq, sb); ++ goto out_seq; ++ } ++ cattr++; ++ } ++ ++ bend = au_sbend(sb); ++ if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) { ++ name += sizeof(SysaufsBr_PREFIX) - 1; ++ err = strict_strtol(name, 10, &l); ++ if (!err) { ++ if (l <= bend) ++ err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l); ++ else ++ err = -ENOENT; ++ } ++ goto out_seq; ++ } ++ BUG(); ++ ++out_seq: ++ if (!err) { ++ err = seq->count; ++ /* sysfs limit */ ++ if (unlikely(err == PAGE_SIZE)) ++ err = -EFBIG; ++ } ++ kfree(seq); ++out_unlock: ++ si_read_unlock(sb); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void sysaufs_br_init(struct au_branch *br) ++{ ++ struct attribute *attr = &br->br_attr; ++ ++ sysfs_attr_init(attr); ++ attr->name = br->br_name; ++ attr->mode = S_IRUGO; ++} ++ ++void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ struct au_branch *br; ++ struct kobject *kobj; ++ aufs_bindex_t bend; ++ ++ dbgaufs_brs_del(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ sysfs_remove_file(kobj, &br->br_attr); ++ } ++} ++ ++void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ aufs_bindex_t bend; ++ struct kobject *kobj; ++ struct au_branch *br; ++ ++ dbgaufs_brs_add(sb, bindex); ++ ++ if (!sysaufs_brs) ++ return; ++ ++ kobj = &au_sbi(sb)->si_kobj; ++ bend = au_sbend(sb); ++ for (; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX ++ "%d", bindex); ++ err = sysfs_create_file(kobj, &br->br_attr); ++ if (unlikely(err)) ++ pr_warning("failed %s under sysfs(%d)\n", ++ br->br_name, err); ++ } ++} +diff -Nur linux-2.6.36.orig/fs/aufs/sysrq.c linux-2.6.36/fs/aufs/sysrq.c +--- linux-2.6.36.orig/fs/aufs/sysrq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/sysrq.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,148 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * magic sysrq hanlder ++ */ ++ ++#include ++#include ++#include ++/* #include */ ++#include ++#include "aufs.h" ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void sysrq_sb(struct super_block *sb) ++{ ++ char *plevel; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ plevel = au_plevel; ++ au_plevel = KERN_WARNING; ++ ++ sbinfo = au_sbi(sb); ++ /* since we define pr_fmt, call printk directly */ ++ printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); ++ printk(KERN_WARNING AUFS_NAME ": superblock\n"); ++ au_dpri_sb(sb); ++ ++#if 0 ++ printk(KERN_WARNING AUFS_NAME ": root dentry\n"); ++ au_dpri_dentry(sb->s_root); ++ printk(KERN_WARNING AUFS_NAME ": root inode\n"); ++ au_dpri_inode(sb->s_root->d_inode); ++#endif ++ ++#if 0 ++ do { ++ int err, i, j, ndentry; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ ++ err = au_dpages_init(&dpages, GFP_ATOMIC); ++ if (unlikely(err)) ++ break; ++ err = au_dcsub_pages(&dpages, sb->s_root, NULL, NULL); ++ if (!err) ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) ++ au_dpri_dentry(dpage->dentries[j]); ++ } ++ au_dpages_free(&dpages); ++ } while (0); ++#endif ++ ++#if 1 ++ { ++ struct inode *i; ++ printk(KERN_WARNING AUFS_NAME ": isolated inode\n"); ++ spin_lock(&inode_lock); ++ list_for_each_entry(i, &sb->s_inodes, i_sb_list) ++ if (1 || list_empty(&i->i_dentry)) ++ au_dpri_inode(i); ++ spin_unlock(&inode_lock); ++ } ++#endif ++ printk(KERN_WARNING AUFS_NAME ": files\n"); ++ lg_global_lock(files_lglock); ++ do_file_list_for_each_entry(sb, file) { ++ umode_t mode; ++ mode = file->f_dentry->d_inode->i_mode; ++ if (!special_file(mode) || au_special_file(mode)) ++ au_dpri_file(file); ++ } while_file_list_for_each_entry; ++ lg_global_unlock(files_lglock); ++ printk(KERN_WARNING AUFS_NAME ": done\n"); ++ ++ au_plevel = plevel; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* module parameter */ ++static char *aufs_sysrq_key = "a"; ++module_param_named(sysrq, aufs_sysrq_key, charp, S_IRUGO); ++MODULE_PARM_DESC(sysrq, "MagicSysRq key for " AUFS_NAME); ++ ++static void au_sysrq(int key __maybe_unused) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ lockdep_off(); ++ spin_lock(&au_sbilist.spin); ++ list_for_each_entry(sbinfo, &au_sbilist.head, si_list) ++ sysrq_sb(sbinfo->si_sb); ++ spin_unlock(&au_sbilist.spin); ++ lockdep_on(); ++} ++ ++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)) ++ pr_err("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)) ++ pr_err("err %d (ignored)\n", err); ++} +diff -Nur linux-2.6.36.orig/fs/aufs/vdir.c linux-2.6.36/fs/aufs/vdir.c +--- linux-2.6.36.orig/fs/aufs/vdir.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/vdir.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,886 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * virtual or vertical directory ++ */ ++ ++#include ++#include "aufs.h" ++ ++static unsigned int calc_size(int nlen) ++{ ++ return ALIGN(sizeof(struct au_vdir_de) + nlen, sizeof(ino_t)); ++} ++ ++static int set_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) { ++ p->de->de_str.len = 0; ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -1; /* error */ ++} ++ ++/* returns true or false */ ++static int is_deblk_end(union au_vdir_deblk_p *p, ++ union au_vdir_deblk_p *deblk_end) ++{ ++ if (calc_size(0) <= deblk_end->deblk - p->deblk) ++ return !p->de->de_str.len; ++ return 1; ++} ++ ++static unsigned char *last_deblk(struct au_vdir *vdir) ++{ ++ return vdir->vd_deblk[vdir->vd_nblk - 1]; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* estimate the apropriate size for name hash table */ ++unsigned int au_rdhash_est(loff_t sz) ++{ ++ unsigned int n; ++ ++ n = UINT_MAX; ++ sz >>= 10; ++ if (sz < n) ++ n = sz; ++ if (sz < AUFS_RDHASH_DEF) ++ n = AUFS_RDHASH_DEF; ++ /* pr_info("n %u\n", n); */ ++ return n; ++} ++ ++/* ++ * the allocated memory has to be freed by ++ * au_nhash_wh_free() or au_nhash_de_free(). ++ */ ++int au_nhash_alloc(struct au_nhash *nhash, unsigned int num_hash, gfp_t gfp) ++{ ++ struct hlist_head *head; ++ unsigned int u; ++ ++ head = kmalloc(sizeof(*nhash->nh_head) * num_hash, gfp); ++ if (head) { ++ nhash->nh_num = num_hash; ++ nhash->nh_head = head; ++ for (u = 0; u < num_hash; u++) ++ INIT_HLIST_HEAD(head++); ++ return 0; /* success */ ++ } ++ ++ return -ENOMEM; ++} ++ ++static void nhash_count(struct hlist_head *head) ++{ ++#if 0 ++ unsigned long n; ++ struct hlist_node *pos; ++ ++ n = 0; ++ hlist_for_each(pos, head) ++ n++; ++ pr_info("%lu\n", n); ++#endif ++} ++ ++static void au_nhash_wh_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos, *node; ++ ++ hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) { ++ /* hlist_del(pos); */ ++ kfree(tpos); ++ } ++} ++ ++static void au_nhash_de_do_free(struct hlist_head *head) ++{ ++ struct au_vdir_dehstr *tpos; ++ struct hlist_node *pos, *node; ++ ++ hlist_for_each_entry_safe(tpos, pos, node, head, hash) { ++ /* hlist_del(pos); */ ++ au_cache_free_vdir_dehstr(tpos); ++ } ++} ++ ++static void au_nhash_do_free(struct au_nhash *nhash, ++ void (*free)(struct hlist_head *head)) ++{ ++ unsigned int n; ++ struct hlist_head *head; ++ ++ n = nhash->nh_num; ++ if (!n) ++ return; ++ ++ head = nhash->nh_head; ++ while (n-- > 0) { ++ nhash_count(head); ++ free(head++); ++ } ++ kfree(nhash->nh_head); ++} ++ ++void au_nhash_wh_free(struct au_nhash *whlist) ++{ ++ au_nhash_do_free(whlist, au_nhash_wh_do_free); ++} ++ ++static void au_nhash_de_free(struct au_nhash *delist) ++{ ++ au_nhash_do_free(delist, au_nhash_de_do_free); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_test_longer_wh(struct au_nhash *whlist, aufs_bindex_t btgt, ++ int limit) ++{ ++ int num; ++ unsigned int u, n; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ ++ num = 0; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (u = 0; u < n; u++, head++) ++ hlist_for_each_entry(tpos, pos, head, wh_hash) ++ if (tpos->wh_bindex == btgt && ++num > limit) ++ return 1; ++ return 0; ++} ++ ++static struct hlist_head *au_name_hash(struct au_nhash *nhash, ++ unsigned char *name, ++ unsigned int len) ++{ ++ unsigned int v; ++ /* const unsigned int magic_bit = 12; */ ++ ++ AuDebugOn(!nhash->nh_num || !nhash->nh_head); ++ ++ v = 0; ++ while (len--) ++ v += *name++; ++ /* v = hash_long(v, magic_bit); */ ++ v %= nhash->nh_num; ++ return nhash->nh_head + v; ++} ++ ++static int au_nhash_test_name(struct au_vdir_destr *str, const char *name, ++ int nlen) ++{ ++ return str->len == nlen && !memcmp(str->name, name, nlen); ++} ++ ++/* returns found or not */ ++int au_nhash_test_known_wh(struct au_nhash *whlist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(whlist, name, nlen); ++ hlist_for_each_entry(tpos, pos, head, wh_hash) { ++ str = &tpos->wh_str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++/* returns found(true) or not */ ++static int test_known(struct au_nhash *delist, char *name, int nlen) ++{ ++ struct hlist_head *head; ++ struct au_vdir_dehstr *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ head = au_name_hash(delist, name, nlen); ++ hlist_for_each_entry(tpos, pos, head, hash) { ++ str = tpos->str; ++ AuDbg("%.*s\n", str->len, str->name); ++ if (au_nhash_test_name(str, name, nlen)) ++ return 1; ++ } ++ return 0; ++} ++ ++static void au_shwh_init_wh(struct au_vdir_wh *wh, ino_t ino, ++ unsigned char d_type) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ wh->wh_ino = ino; ++ wh->wh_type = d_type; ++#endif ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino, ++ unsigned int d_type, aufs_bindex_t bindex, ++ unsigned char shwh) ++{ ++ int err; ++ struct au_vdir_destr *str; ++ struct au_vdir_wh *wh; ++ ++ AuDbg("%.*s\n", nlen, name); ++ AuDebugOn(!whlist->nh_num || !whlist->nh_head); ++ ++ err = -ENOMEM; ++ wh = kmalloc(sizeof(*wh) + nlen, GFP_NOFS); ++ if (unlikely(!wh)) ++ goto out; ++ ++ err = 0; ++ wh->wh_bindex = bindex; ++ if (shwh) ++ au_shwh_init_wh(wh, ino, d_type); ++ str = &wh->wh_str; ++ str->len = nlen; ++ memcpy(str->name, name, nlen); ++ hlist_add_head(&wh->wh_hash, au_name_hash(whlist, name, nlen)); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++static int append_deblk(struct au_vdir *vdir) ++{ ++ int err; ++ unsigned long ul; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, deblk_end; ++ unsigned char **o; ++ ++ err = -ENOMEM; ++ o = krealloc(vdir->vd_deblk, sizeof(*o) * (vdir->vd_nblk + 1), ++ GFP_NOFS); ++ if (unlikely(!o)) ++ goto out; ++ ++ vdir->vd_deblk = o; ++ p.deblk = kmalloc(deblk_sz, GFP_NOFS); ++ if (p.deblk) { ++ ul = vdir->vd_nblk++; ++ vdir->vd_deblk[ul] = p.deblk; ++ vdir->vd_last.ul = ul; ++ vdir->vd_last.p.deblk = p.deblk; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ } ++ ++out: ++ return err; ++} ++ ++static int append_de(struct au_vdir *vdir, char *name, int nlen, ino_t ino, ++ unsigned int d_type, struct au_nhash *delist) ++{ ++ int err; ++ unsigned int sz; ++ const unsigned int deblk_sz = vdir->vd_deblk_sz; ++ union au_vdir_deblk_p p, *room, deblk_end; ++ struct au_vdir_dehstr *dehstr; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ room = &vdir->vd_last.p; ++ AuDebugOn(room->deblk < p.deblk || deblk_end.deblk <= room->deblk ++ || !is_deblk_end(room, &deblk_end)); ++ ++ sz = calc_size(nlen); ++ if (unlikely(sz > deblk_end.deblk - room->deblk)) { ++ err = append_deblk(vdir); ++ if (unlikely(err)) ++ goto out; ++ ++ p.deblk = last_deblk(vdir); ++ deblk_end.deblk = p.deblk + deblk_sz; ++ /* smp_mb(); */ ++ AuDebugOn(room->deblk != p.deblk); ++ } ++ ++ err = -ENOMEM; ++ dehstr = au_cache_alloc_vdir_dehstr(); ++ if (unlikely(!dehstr)) ++ goto out; ++ ++ dehstr->str = &room->de->de_str; ++ hlist_add_head(&dehstr->hash, au_name_hash(delist, name, nlen)); ++ room->de->de_ino = ino; ++ room->de->de_type = d_type; ++ room->de->de_str.len = nlen; ++ memcpy(room->de->de_str.name, name, nlen); ++ ++ err = 0; ++ room->deblk += sz; ++ if (unlikely(set_deblk_end(room, &deblk_end))) ++ err = append_deblk(vdir); ++ /* smp_mb(); */ ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_vdir_free(struct au_vdir *vdir) ++{ ++ unsigned char **deblk; ++ ++ deblk = vdir->vd_deblk; ++ while (vdir->vd_nblk--) ++ kfree(*deblk++); ++ kfree(vdir->vd_deblk); ++ au_cache_free_vdir(vdir); ++} ++ ++static struct au_vdir *alloc_vdir(struct file *file) ++{ ++ struct au_vdir *vdir; ++ struct super_block *sb; ++ int err; ++ ++ sb = file->f_dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ err = -ENOMEM; ++ vdir = au_cache_alloc_vdir(); ++ if (unlikely(!vdir)) ++ goto out; ++ ++ vdir->vd_deblk = kzalloc(sizeof(*vdir->vd_deblk), GFP_NOFS); ++ if (unlikely(!vdir->vd_deblk)) ++ goto out_free; ++ ++ vdir->vd_deblk_sz = au_sbi(sb)->si_rdblk; ++ if (!vdir->vd_deblk_sz) { ++ /* estimate the apropriate size for deblk */ ++ vdir->vd_deblk_sz = au_dir_size(file, /*dentry*/NULL); ++ /* pr_info("vd_deblk_sz %u\n", vdir->vd_deblk_sz); */ ++ } ++ 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); ++ return vdir; ++} ++ ++static int reinit_vdir(struct au_vdir *vdir) ++{ ++ int err; ++ union au_vdir_deblk_p p, deblk_end; ++ ++ 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 + vdir->vd_deblk_sz; ++ err = set_deblk_end(&p, &deblk_end); ++ /* keep vd_dblk_sz */ ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ vdir->vd_version = 0; ++ vdir->vd_jiffy = 0; ++ /* smp_mb(); */ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++#define AuFillVdir_CALLED 1 ++#define AuFillVdir_WHABLE (1 << 1) ++#define AuFillVdir_SHWH (1 << 2) ++#define au_ftest_fillvdir(flags, name) ((flags) & AuFillVdir_##name) ++#define au_fset_fillvdir(flags, name) \ ++ do { (flags) |= AuFillVdir_##name; } while (0) ++#define au_fclr_fillvdir(flags, name) \ ++ do { (flags) &= ~AuFillVdir_##name; } while (0) ++ ++#ifndef CONFIG_AUFS_SHWH ++#undef AuFillVdir_SHWH ++#define AuFillVdir_SHWH 0 ++#endif ++ ++struct fillvdir_arg { ++ struct file *file; ++ struct au_vdir *vdir; ++ struct au_nhash delist; ++ struct au_nhash whlist; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++ int err; ++}; ++ ++static int fillvdir(void *__arg, const char *__name, int nlen, ++ loff_t offset __maybe_unused, u64 h_ino, ++ unsigned int d_type) ++{ ++ struct fillvdir_arg *arg = __arg; ++ char *name = (void *)__name; ++ struct super_block *sb; ++ ino_t ino; ++ const unsigned char shwh = !!au_ftest_fillvdir(arg->flags, SHWH); ++ ++ arg->err = 0; ++ sb = arg->file->f_dentry->d_sb; ++ au_fset_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ if (nlen <= AUFS_WH_PFX_LEN ++ || memcmp(name, AUFS_WH_PFX, AUFS_WH_PFX_LEN)) { ++ if (test_known(&arg->delist, name, nlen) ++ || au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already exists or whiteouted */ ++ ++ sb = arg->file->f_dentry->d_sb; ++ arg->err = au_ino(sb, arg->bindex, h_ino, d_type, &ino); ++ if (!arg->err) { ++ if (unlikely(nlen > AUFS_MAX_NAMELEN)) ++ d_type = DT_UNKNOWN; ++ arg->err = append_de(arg->vdir, name, nlen, ino, ++ d_type, &arg->delist); ++ } ++ } else if (au_ftest_fillvdir(arg->flags, WHABLE)) { ++ name += AUFS_WH_PFX_LEN; ++ nlen -= AUFS_WH_PFX_LEN; ++ if (au_nhash_test_known_wh(&arg->whlist, name, nlen)) ++ goto out; /* already whiteouted */ ++ ++ if (shwh) ++ arg->err = au_wh_ino(sb, arg->bindex, h_ino, d_type, ++ &ino); ++ if (!arg->err) { ++ if (nlen <= AUFS_MAX_NAMELEN + AUFS_WH_PFX_LEN) ++ d_type = DT_UNKNOWN; ++ arg->err = au_nhash_append_wh ++ (&arg->whlist, name, nlen, ino, d_type, ++ arg->bindex, shwh); ++ } ++ } ++ ++out: ++ if (!arg->err) ++ arg->vdir->vd_jiffy = jiffies; ++ /* smp_mb(); */ ++ AuTraceErr(arg->err); ++ return arg->err; ++} ++ ++static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, ++ struct au_nhash *whlist, struct au_nhash *delist) ++{ ++#ifdef CONFIG_AUFS_SHWH ++ int err; ++ unsigned int nh, u; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos, *n; ++ char *p, *o; ++ struct au_vdir_destr *destr; ++ ++ AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); ++ ++ err = -ENOMEM; ++ o = p = __getname_gfp(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = 0; ++ nh = whlist->nh_num; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ for (u = 0; u < nh; u++) { ++ head = whlist->nh_head + u; ++ hlist_for_each_entry_safe(tpos, pos, n, head, wh_hash) { ++ destr = &tpos->wh_str; ++ memcpy(p, destr->name, destr->len); ++ err = append_de(vdir, o, destr->len + AUFS_WH_PFX_LEN, ++ tpos->wh_ino, tpos->wh_type, delist); ++ if (unlikely(err)) ++ break; ++ } ++ } ++ ++ __putname(o); ++ ++out: ++ AuTraceErr(err); ++ return err; ++#else ++ return 0; ++#endif ++} ++ ++static int au_do_read_vdir(struct fillvdir_arg *arg) ++{ ++ int err; ++ unsigned int rdhash; ++ loff_t offset; ++ aufs_bindex_t bend, bindex, bstart; ++ unsigned char shwh; ++ struct file *hf, *file; ++ struct super_block *sb; ++ ++ file = arg->file; ++ sb = file->f_dentry->d_sb; ++ SiMustAnyLock(sb); ++ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = au_rdhash_est(au_dir_size(file, /*dentry*/NULL)); ++ err = au_nhash_alloc(&arg->delist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ err = au_nhash_alloc(&arg->whlist, rdhash, GFP_NOFS); ++ if (unlikely(err)) ++ goto out_delist; ++ ++ err = 0; ++ arg->flags = 0; ++ shwh = 0; ++ if (au_opt_test(au_mntflags(sb), SHWH)) { ++ shwh = 1; ++ au_fset_fillvdir(arg->flags, SHWH); ++ } ++ bstart = au_fbstart(file); ++ bend = au_fbend_dir(file); ++ for (bindex = bstart; !err && bindex <= bend; bindex++) { ++ hf = au_hf_dir(file, bindex); ++ if (!hf) ++ continue; ++ ++ offset = vfsub_llseek(hf, 0, SEEK_SET); ++ err = offset; ++ if (unlikely(offset)) ++ break; ++ ++ arg->bindex = bindex; ++ au_fclr_fillvdir(arg->flags, WHABLE); ++ if (shwh ++ || (bindex != bend ++ && au_br_whable(au_sbr_perm(sb, bindex)))) ++ au_fset_fillvdir(arg->flags, WHABLE); ++ do { ++ arg->err = 0; ++ au_fclr_fillvdir(arg->flags, CALLED); ++ /* smp_mb(); */ ++ err = vfsub_readdir(hf, fillvdir, arg); ++ if (err >= 0) ++ err = arg->err; ++ } while (!err && au_ftest_fillvdir(arg->flags, CALLED)); ++ } ++ ++ if (!err && shwh) ++ err = au_handle_shwh(sb, arg->vdir, &arg->whlist, &arg->delist); ++ ++ au_nhash_wh_free(&arg->whlist); ++ ++out_delist: ++ au_nhash_de_free(&arg->delist); ++out: ++ return err; ++} ++ ++static int read_vdir(struct file *file, int may_read) ++{ ++ int err; ++ unsigned long expire; ++ unsigned char do_read; ++ struct fillvdir_arg arg; ++ struct inode *inode; ++ struct au_vdir *vdir, *allocated; ++ ++ err = 0; ++ inode = file->f_dentry->d_inode; ++ IMustLock(inode); ++ SiMustAnyLock(inode->i_sb); ++ ++ allocated = NULL; ++ do_read = 0; ++ expire = au_sbi(inode->i_sb)->si_rdcache; ++ vdir = au_ivdir(inode); ++ if (!vdir) { ++ do_read = 1; ++ vdir = alloc_vdir(file); ++ 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))) { ++ do_read = 1; ++ err = reinit_vdir(vdir); ++ if (unlikely(err)) ++ goto out; ++ } ++ ++ if (!do_read) ++ return 0; /* success */ ++ ++ arg.file = file; ++ arg.vdir = vdir; ++ err = au_do_read_vdir(&arg); ++ if (!err) { ++ /* file->f_pos = 0; */ ++ vdir->vd_version = inode->i_version; ++ vdir->vd_last.ul = 0; ++ vdir->vd_last.p.deblk = vdir->vd_deblk[0]; ++ if (allocated) ++ au_set_ivdir(inode, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static int copy_vdir(struct au_vdir *tgt, struct au_vdir *src) ++{ ++ int err, rerr; ++ unsigned long ul, n; ++ const unsigned int deblk_sz = src->vd_deblk_sz; ++ ++ AuDebugOn(tgt->vd_nblk != 1); ++ ++ err = -ENOMEM; ++ if (tgt->vd_nblk < src->vd_nblk) { ++ unsigned char **p; ++ ++ p = krealloc(tgt->vd_deblk, sizeof(*p) * src->vd_nblk, ++ GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk = p; ++ } ++ ++ if (tgt->vd_deblk_sz != deblk_sz) { ++ unsigned char *p; ++ ++ tgt->vd_deblk_sz = deblk_sz; ++ p = krealloc(tgt->vd_deblk[0], deblk_sz, GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ tgt->vd_deblk[0] = p; ++ } ++ memcpy(tgt->vd_deblk[0], src->vd_deblk[0], deblk_sz); ++ tgt->vd_version = src->vd_version; ++ tgt->vd_jiffy = src->vd_jiffy; ++ ++ n = src->vd_nblk; ++ for (ul = 1; ul < n; ul++) { ++ tgt->vd_deblk[ul] = kmemdup(src->vd_deblk[ul], deblk_sz, ++ GFP_NOFS); ++ if (unlikely(!tgt->vd_deblk[ul])) ++ goto out; ++ tgt->vd_nblk++; ++ } ++ tgt->vd_nblk = n; ++ tgt->vd_last.ul = tgt->vd_last.ul; ++ tgt->vd_last.p.deblk = tgt->vd_deblk[tgt->vd_last.ul]; ++ tgt->vd_last.p.deblk += src->vd_last.p.deblk ++ - src->vd_deblk[src->vd_last.ul]; ++ /* smp_mb(); */ ++ return 0; /* success */ ++ ++out: ++ rerr = reinit_vdir(tgt); ++ BUG_ON(rerr); ++ return err; ++} ++ ++int au_vdir_init(struct file *file) ++{ ++ int err; ++ struct inode *inode; ++ struct au_vdir *vdir_cache, *allocated; ++ ++ err = read_vdir(file, !file->f_pos); ++ if (unlikely(err)) ++ goto out; ++ ++ allocated = NULL; ++ vdir_cache = au_fvdir_cache(file); ++ if (!vdir_cache) { ++ vdir_cache = alloc_vdir(file); ++ 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 */ ++ ++ inode = file->f_dentry->d_inode; ++ err = copy_vdir(vdir_cache, au_ivdir(inode)); ++ if (!err) { ++ file->f_version = inode->i_version; ++ if (allocated) ++ au_set_fvdir_cache(file, allocated); ++ } else if (allocated) ++ au_vdir_free(allocated); ++ ++out: ++ return err; ++} ++ ++static loff_t calc_offset(struct au_vdir *vdir) ++{ ++ loff_t offset; ++ union au_vdir_deblk_p p; ++ ++ p.deblk = vdir->vd_deblk[vdir->vd_last.ul]; ++ offset = vdir->vd_last.p.deblk - p.deblk; ++ offset += vdir->vd_deblk_sz * vdir->vd_last.ul; ++ return offset; ++} ++ ++/* returns true or false */ ++static int seek_vdir(struct file *file) ++{ ++ int valid; ++ unsigned int deblk_sz; ++ unsigned long ul, n; ++ loff_t offset; ++ union au_vdir_deblk_p p, deblk_end; ++ struct au_vdir *vdir_cache; ++ ++ valid = 1; ++ vdir_cache = au_fvdir_cache(file); ++ offset = calc_offset(vdir_cache); ++ AuDbg("offset %lld\n", offset); ++ if (file->f_pos == offset) ++ goto out; ++ ++ vdir_cache->vd_last.ul = 0; ++ vdir_cache->vd_last.p.deblk = vdir_cache->vd_deblk[0]; ++ if (!file->f_pos) ++ goto out; ++ ++ valid = 0; ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ ul = div64_u64(file->f_pos, deblk_sz); ++ AuDbg("ul %lu\n", ul); ++ if (ul >= vdir_cache->vd_nblk) ++ goto out; ++ ++ n = vdir_cache->vd_nblk; ++ for (; ul < n; ul++) { ++ p.deblk = vdir_cache->vd_deblk[ul]; ++ deblk_end.deblk = p.deblk + deblk_sz; ++ offset = ul; ++ offset *= deblk_sz; ++ while (!is_deblk_end(&p, &deblk_end) && offset < file->f_pos) { ++ unsigned int l; ++ ++ l = calc_size(p.de->de_str.len); ++ offset += l; ++ p.deblk += l; ++ } ++ if (!is_deblk_end(&p, &deblk_end)) { ++ valid = 1; ++ vdir_cache->vd_last.ul = ul; ++ vdir_cache->vd_last.p = p; ++ break; ++ } ++ } ++ ++out: ++ /* smp_mb(); */ ++ AuTraceErr(!valid); ++ return valid; ++} ++ ++int au_vdir_fill_de(struct file *file, void *dirent, filldir_t filldir) ++{ ++ int err; ++ unsigned int l, deblk_sz; ++ union au_vdir_deblk_p deblk_end; ++ struct au_vdir *vdir_cache; ++ struct au_vdir_de *de; ++ ++ vdir_cache = au_fvdir_cache(file); ++ if (!seek_vdir(file)) ++ return 0; ++ ++ deblk_sz = vdir_cache->vd_deblk_sz; ++ while (1) { ++ deblk_end.deblk = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ deblk_end.deblk += deblk_sz; ++ while (!is_deblk_end(&vdir_cache->vd_last.p, &deblk_end)) { ++ de = vdir_cache->vd_last.p.de; ++ AuDbg("%.*s, off%lld, i%lu, dt%d\n", ++ de->de_str.len, de->de_str.name, file->f_pos, ++ (unsigned long)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); ++ /* todo: ignore the error caused by udba? */ ++ /* return err; */ ++ return 0; ++ } ++ ++ l = calc_size(de->de_str.len); ++ vdir_cache->vd_last.p.deblk += l; ++ file->f_pos += l; ++ } ++ if (vdir_cache->vd_last.ul < vdir_cache->vd_nblk - 1) { ++ vdir_cache->vd_last.ul++; ++ vdir_cache->vd_last.p.deblk ++ = vdir_cache->vd_deblk[vdir_cache->vd_last.ul]; ++ file->f_pos = deblk_sz * vdir_cache->vd_last.ul; ++ continue; ++ } ++ break; ++ } ++ ++ /* smp_mb(); */ ++ return 0; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/vfsub.c linux-2.6.36/fs/aufs/vfsub.c +--- linux-2.6.36.orig/fs/aufs/vfsub.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/vfsub.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,790 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "aufs.h" ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did) ++{ ++ int err; ++ struct kstat st; ++ struct super_block *h_sb; ++ ++ /* for remote fs, leave work for its getattr or d_revalidate */ ++ /* for bad i_attr fs, handle them in aufs_getattr() */ ++ /* still some fs may acquire i_mutex. we need to skip them */ ++ err = 0; ++ if (!did) ++ did = &err; ++ h_sb = h_path->dentry->d_sb; ++ *did = (!au_test_fs_remote(h_sb) && au_test_fs_refresh_iattr(h_sb)); ++ if (*did) ++ err = vfs_getattr(h_path->mnt, h_path->dentry, &st); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_conv_oflags(int flags) ++{ ++ int mask = 0; ++ ++#ifdef CONFIG_IMA ++ fmode_t fmode; ++ ++ /* mask = MAY_OPEN; */ ++ fmode = OPEN_FMODE(flags); ++ if (fmode & FMODE_READ) ++ mask |= MAY_READ; ++ if ((fmode & FMODE_WRITE) ++ || (flags & O_TRUNC)) ++ mask |= MAY_WRITE; ++ /* ++ * if (flags & O_APPEND) ++ * mask |= MAY_APPEND; ++ */ ++ if (flags & vfsub_fmode_to_uint(FMODE_EXEC)) ++ mask |= MAY_EXEC; ++ ++ AuDbg("flags 0x%x, mask 0x%x\n", flags, mask); ++#endif ++ ++ return mask; ++} ++ ++struct file *vfsub_dentry_open(struct path *path, int flags) ++{ ++ struct file *file; ++ int err; ++ ++ path_get(path); ++ file = dentry_open(path->dentry, path->mnt, ++ flags /* | vfsub_fmode_to_uint(FMODE_NONOTIFY) */, ++ current_cred()); ++ if (IS_ERR(file)) ++ goto out; ++ ++ err = ima_file_check(file, au_conv_oflags(flags)); ++ if (unlikely(err)) { ++ fput(file); ++ file = ERR_PTR(err); ++ } ++out: ++ return file; ++} ++ ++struct file *vfsub_filp_open(const char *path, int oflags, int mode) ++{ ++ struct file *file; ++ ++ file = filp_open(path, ++ oflags /* | vfsub_fmode_to_uint(FMODE_NONOTIFY) */, ++ mode); ++ if (IS_ERR(file)) ++ goto out; ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ ++out: ++ return file; ++} ++ ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path) ++{ ++ int err; ++ ++ err = kern_path(name, flags, path); ++ if (!err && path->dentry->d_inode) ++ vfsub_update_h_iattr(path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len) ++{ ++ struct path path = { ++ .mnt = NULL ++ }; ++ ++ /* VFS checks it too, but by WARN_ON_ONCE() */ ++ IMustLock(parent->d_inode); ++ ++ path.dentry = lookup_one_len(name, parent, len); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (path.dentry->d_inode) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ ++struct dentry *vfsub_lookup_hash(struct nameidata *nd) ++{ ++ struct path path = { ++ .mnt = nd->path.mnt ++ }; ++ ++ IMustLock(nd->path.dentry->d_inode); ++ ++ path.dentry = lookup_hash(nd); ++ if (IS_ERR(path.dentry)) ++ goto out; ++ if (path.dentry->d_inode) ++ vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/ ++ ++out: ++ AuTraceErrPtr(path.dentry); ++ return path.dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ struct dentry *d; ++ ++ d = lock_rename(d1, d2); ++ au_hn_suspend(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_suspend(hdir2); ++ ++ return d; ++} ++ ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2) ++{ ++ au_hn_resume(hdir1); ++ if (hdir1 != hdir2) ++ au_hn_resume(hdir2); ++ unlock_rename(d1, d2); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, 0); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ if (au_test_fs_null_nd(dir->i_sb)) ++ err = vfs_create(dir, path->dentry, mode, NULL); ++ else { ++ struct nameidata h_nd; ++ ++ memset(&h_nd, 0, sizeof(h_nd)); ++ h_nd.flags = LOOKUP_CREATE; ++ h_nd.intent.open.flags = O_CREAT ++ | vfsub_fmode_to_uint(FMODE_READ); ++ h_nd.intent.open.create_mode = mode; ++ h_nd.path.dentry = path->dentry->d_parent; ++ h_nd.path.mnt = path->mnt; ++ path_get(&h_nd.path); ++ err = vfs_create(dir, path->dentry, mode, &h_nd); ++ path_put(&h_nd.path); ++ } ++ ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_symlink(struct inode *dir, struct path *path, const char *symname) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_symlink(path, d, symname); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_symlink(dir, path->dentry, symname); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mknod(path, d, mode, dev); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_mknod(dir, path->dentry, mode, dev); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++static int au_test_nlink(struct inode *inode) ++{ ++ const unsigned int link_max = UINT_MAX >> 1; /* rough margin */ ++ ++ if (!au_test_fs_no_limit_nlink(inode->i_sb) ++ || inode->i_nlink < link_max) ++ return 0; ++ return -EMLINK; ++} ++ ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ err = au_test_nlink(src_dentry->d_inode); ++ if (unlikely(err)) ++ return err; ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_link(src_dentry, path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_link(src_dentry, dir, path->dentry); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ /* fuse has different memory inode for the same inumber */ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rename(struct inode *src_dir, struct dentry *src_dentry, ++ struct inode *dir, struct path *path) ++{ ++ int err; ++ struct path tmp = { ++ .mnt = path->mnt ++ }; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ IMustLock(src_dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ tmp.dentry = src_dentry->d_parent; ++ err = security_path_rename(&tmp, src_dentry, path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_rename(src_dir, src_dentry, dir, path->dentry); ++ if (!err) { ++ int did; ++ ++ tmp.dentry = d->d_parent; ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = src_dentry; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ tmp.dentry = src_dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_mkdir(path, d, mode); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_mkdir(dir, path->dentry, mode); ++ if (!err) { ++ struct path tmp = *path; ++ int did; ++ ++ vfsub_update_h_iattr(&tmp, &did); ++ if (did) { ++ tmp.dentry = path->dentry->d_parent; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); ++ } ++ /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++int vfsub_rmdir(struct inode *dir, struct path *path) ++{ ++ int err; ++ struct dentry *d; ++ ++ IMustLock(dir); ++ ++ d = path->dentry; ++ path->dentry = d->d_parent; ++ err = security_path_rmdir(path, d); ++ path->dentry = d; ++ if (unlikely(err)) ++ goto out; ++ ++ err = vfs_rmdir(dir, path->dentry); ++ if (!err) { ++ struct path tmp = { ++ .dentry = path->dentry->d_parent, ++ .mnt = path->mnt ++ }; ++ ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ err = vfs_read(file, ubuf, count, ppos); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* todo: kernel_read()? */ ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_read_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos) ++{ ++ ssize_t err; ++ ++ err = vfs_write(file, ubuf, count, ppos); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, loff_t *ppos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ err = vfsub_write_u(file, buf.u, count, ppos); ++ set_fs(oldfs); ++ return err; ++} ++ ++int vfsub_flush(struct file *file, fl_owner_t id) ++{ ++ int err; ++ ++ err = 0; ++ if (file->f_op && file->f_op->flush) { ++ err = file->f_op->flush(file, id); ++ if (!err) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); ++ /*ignore*/ ++ } ++ return err; ++} ++ ++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg) ++{ ++ int err; ++ ++ err = vfs_readdir(file, filldir, arg); ++ if (err >= 0) ++ vfsub_update_h_iattr(&file->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ long err; ++ ++ err = do_splice_to(in, ppos, pipe, len, flags); ++ file_accessed(in); ++ if (err >= 0) ++ vfsub_update_h_iattr(&in->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ long err; ++ ++ err = do_splice_from(pipe, out, ppos, len, flags); ++ if (err >= 0) ++ vfsub_update_h_iattr(&out->f_path, /*did*/NULL); /*ignore*/ ++ return err; ++} ++ ++/* cf. open.c:do_sys_truncate() and do_sys_ftruncate() */ ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file) ++{ ++ int err; ++ struct inode *h_inode; ++ ++ h_inode = h_path->dentry->d_inode; ++ if (!h_file) { ++ err = mnt_want_write(h_path->mnt); ++ if (err) ++ goto out; ++ err = inode_permission(h_inode, MAY_WRITE); ++ if (err) ++ goto out_mnt; ++ err = get_write_access(h_inode); ++ if (err) ++ goto out_mnt; ++ err = break_lease(h_inode, O_WRONLY); ++ if (err) ++ goto out_inode; ++ } ++ ++ err = locks_verify_truncate(h_inode, h_file, length); ++ if (!err) ++ err = security_path_truncate(h_path); ++ if (!err) ++ err = do_truncate(h_path->dentry, length, attr, h_file); ++ ++out_inode: ++ if (!h_file) ++ put_write_access(h_inode); ++out_mnt: ++ if (!h_file) ++ mnt_drop_write(h_path->mnt); ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_vfsub_mkdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++ int mode; ++}; ++ ++static void au_call_vfsub_mkdir(void *args) ++{ ++ struct au_vfsub_mkdir_args *a = args; ++ *a->errp = vfsub_mkdir(a->dir, a->path, a->mode); ++} ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) ++ err = vfsub_mkdir(dir, path, mode); ++ else { ++ struct au_vfsub_mkdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path, ++ .mode = mode ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_mkdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++struct au_vfsub_rmdir_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void au_call_vfsub_rmdir(void *args) ++{ ++ struct au_vfsub_rmdir_args *a = args; ++ *a->errp = vfsub_rmdir(a->dir, a->path); ++} ++ ++int vfsub_sio_rmdir(struct inode *dir, struct path *path) ++{ ++ int err, do_sio, wkq_err; ++ ++ do_sio = au_test_h_perm_sio(dir, MAY_EXEC | MAY_WRITE); ++ if (!do_sio) ++ err = vfsub_rmdir(dir, path); ++ else { ++ struct au_vfsub_rmdir_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ wkq_err = au_wkq_wait(au_call_vfsub_rmdir, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct notify_change_args { ++ int *errp; ++ struct path *path; ++ struct iattr *ia; ++}; ++ ++static void call_notify_change(void *args) ++{ ++ struct notify_change_args *a = args; ++ struct inode *h_inode; ++ ++ h_inode = a->path->dentry->d_inode; ++ IMustLock(h_inode); ++ ++ *a->errp = -EPERM; ++ if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) { ++ *a->errp = notify_change(a->path->dentry, a->ia); ++ if (!*a->errp) ++ vfsub_update_h_iattr(a->path, /*did*/NULL); /*ignore*/ ++ } ++ AuTraceErr(*a->errp); ++} ++ ++int vfsub_notify_change(struct path *path, struct iattr *ia) ++{ ++ int err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia ++ }; ++ ++ call_notify_change(&args); ++ ++ return err; ++} ++ ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia) ++{ ++ int err, wkq_err; ++ struct notify_change_args args = { ++ .errp = &err, ++ .path = path, ++ .ia = ia ++ }; ++ ++ wkq_err = au_wkq_wait(call_notify_change, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct unlink_args { ++ int *errp; ++ struct inode *dir; ++ struct path *path; ++}; ++ ++static void call_unlink(void *args) ++{ ++ struct unlink_args *a = args; ++ struct dentry *d = a->path->dentry; ++ struct inode *h_inode; ++ const int stop_sillyrename = (au_test_nfs(d->d_sb) ++ && atomic_read(&d->d_count) == 1); ++ ++ IMustLock(a->dir); ++ ++ a->path->dentry = d->d_parent; ++ *a->errp = security_path_unlink(a->path, d); ++ a->path->dentry = d; ++ if (unlikely(*a->errp)) ++ return; ++ ++ if (!stop_sillyrename) ++ dget(d); ++ h_inode = d->d_inode; ++ if (h_inode) ++ atomic_inc(&h_inode->i_count); ++ ++ *a->errp = vfs_unlink(a->dir, d); ++ if (!*a->errp) { ++ struct path tmp = { ++ .dentry = d->d_parent, ++ .mnt = a->path->mnt ++ }; ++ vfsub_update_h_iattr(&tmp, /*did*/NULL); /*ignore*/ ++ } ++ ++ if (!stop_sillyrename) ++ dput(d); ++ if (h_inode) ++ iput(h_inode); ++ ++ AuTraceErr(*a->errp); ++} ++ ++/* ++ * @dir: must be locked. ++ * @dentry: target dentry. ++ */ ++int vfsub_unlink(struct inode *dir, struct path *path, int force) ++{ ++ int err; ++ struct unlink_args args = { ++ .errp = &err, ++ .dir = dir, ++ .path = path ++ }; ++ ++ if (!force) ++ call_unlink(&args); ++ else { ++ int wkq_err; ++ ++ wkq_err = au_wkq_wait(call_unlink, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/vfsub.h linux-2.6.36/fs/aufs/vfsub.h +--- linux-2.6.36.orig/fs/aufs/vfsub.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/vfsub.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,226 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * sub-routines for VFS ++ */ ++ ++#ifndef __AUFS_VFSUB_H__ ++#define __AUFS_VFSUB_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include "debug.h" ++ ++/* copied from linux/fs/internal.h */ ++DECLARE_BRLOCK(vfsmount_lock); ++extern void file_sb_list_del(struct file *f); ++ ++/* copied from linux/fs/file_table.c */ ++DECLARE_LGLOCK(files_lglock); ++#ifdef CONFIG_SMP ++/* ++ * These macros iterate all files on all CPUs for a given superblock. ++ * files_lglock must be held globally. ++ */ ++#define do_file_list_for_each_entry(__sb, __file) \ ++{ \ ++ int i; \ ++ for_each_possible_cpu(i) { \ ++ struct list_head *list; \ ++ list = per_cpu_ptr((__sb)->s_files, i); \ ++ list_for_each_entry((__file), list, f_u.fu_list) ++ ++#define while_file_list_for_each_entry \ ++ } \ ++} ++ ++#else ++ ++#define do_file_list_for_each_entry(__sb, __file) \ ++{ \ ++ struct list_head *list; \ ++ list = &(sb)->s_files; \ ++ list_for_each_entry((__file), list, f_u.fu_list) ++ ++#define while_file_list_for_each_entry \ ++} ++#endif ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* lock subclass for lower inode */ ++/* default MAX_LOCKDEP_SUBCLASSES(8) is not enough */ ++/* reduce? gave up. */ ++enum { ++ AuLsc_I_Begin = I_MUTEX_QUOTA, /* 4 */ ++ AuLsc_I_PARENT, /* lower inode, parent first */ ++ AuLsc_I_PARENT2, /* copyup dirs */ ++ AuLsc_I_PARENT3, /* copyup wh */ ++ AuLsc_I_CHILD, ++ AuLsc_I_CHILD2, ++ AuLsc_I_End ++}; ++ ++/* to debug easier, do not make them inlined functions */ ++#define MtxMustLock(mtx) AuDebugOn(!mutex_is_locked(mtx)) ++#define IMustLock(i) MtxMustLock(&(i)->i_mutex) ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline void vfsub_drop_nlink(struct inode *inode) ++{ ++ AuDebugOn(!inode->i_nlink); ++ drop_nlink(inode); ++} ++ ++static inline void vfsub_dead_dir(struct inode *inode) ++{ ++ AuDebugOn(!S_ISDIR(inode->i_mode)); ++ inode->i_flags |= S_DEAD; ++ clear_nlink(inode); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_update_h_iattr(struct path *h_path, int *did); ++struct file *vfsub_dentry_open(struct path *path, int flags); ++struct file *vfsub_filp_open(const char *path, int oflags, int mode); ++int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); ++struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, ++ int len); ++struct dentry *vfsub_lookup_hash(struct nameidata *nd); ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_hinode; ++struct dentry *vfsub_lock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++void vfsub_unlock_rename(struct dentry *d1, struct au_hinode *hdir1, ++ struct dentry *d2, struct au_hinode *hdir2); ++ ++int vfsub_create(struct inode *dir, struct path *path, int mode); ++int vfsub_symlink(struct inode *dir, struct path *path, ++ const char *symname); ++int vfsub_mknod(struct inode *dir, struct path *path, int mode, dev_t dev); ++int vfsub_link(struct dentry *src_dentry, struct inode *dir, ++ struct path *path); ++int vfsub_rename(struct inode *src_hdir, struct dentry *src_dentry, ++ struct inode *hdir, struct path *path); ++int vfsub_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_rmdir(struct inode *dir, struct path *path); ++ ++/* ---------------------------------------------------------------------- */ ++ ++ssize_t vfsub_read_u(struct file *file, char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_read_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_u(struct file *file, const char __user *ubuf, size_t count, ++ loff_t *ppos); ++ssize_t vfsub_write_k(struct file *file, void *kbuf, size_t count, ++ loff_t *ppos); ++int vfsub_flush(struct file *file, fl_owner_t id); ++int vfsub_readdir(struct file *file, filldir_t filldir, void *arg); ++ ++static inline unsigned int vfsub_file_flags(struct file *file) ++{ ++ unsigned int flags; ++ ++ spin_lock(&file->f_lock); ++ flags = file->f_flags; ++ spin_unlock(&file->f_lock); ++ ++ return flags; ++} ++ ++static inline void vfsub_file_accessed(struct file *h_file) ++{ ++ file_accessed(h_file); ++ vfsub_update_h_iattr(&h_file->f_path, /*did*/NULL); /*ignore*/ ++} ++ ++static inline void vfsub_touch_atime(struct vfsmount *h_mnt, ++ struct dentry *h_dentry) ++{ ++ struct path h_path = { ++ .dentry = h_dentry, ++ .mnt = h_mnt ++ }; ++ touch_atime(h_mnt, h_dentry); ++ vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ ++} ++ ++long vfsub_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, ++ struct file *h_file); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline loff_t vfsub_llseek(struct file *file, loff_t offset, int origin) ++{ ++ loff_t err; ++ ++ err = vfs_llseek(file, offset, origin); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* dirty workaround for strict type of fmode_t */ ++union vfsub_fmu { ++ fmode_t fm; ++ unsigned int ui; ++}; ++ ++static inline unsigned int vfsub_fmode_to_uint(fmode_t fm) ++{ ++ union vfsub_fmu u = { ++ .fm = fm ++ }; ++ ++ BUILD_BUG_ON(sizeof(u.fm) != sizeof(u.ui)); ++ ++ return u.ui; ++} ++ ++static inline fmode_t vfsub_uint_to_fmode(unsigned int ui) ++{ ++ union vfsub_fmu u = { ++ .ui = ui ++ }; ++ ++ return u.fm; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int vfsub_sio_mkdir(struct inode *dir, struct path *path, int mode); ++int vfsub_sio_rmdir(struct inode *dir, struct path *path); ++int vfsub_sio_notify_change(struct path *path, struct iattr *ia); ++int vfsub_notify_change(struct path *path, struct iattr *ia); ++int vfsub_unlink(struct inode *dir, struct path *path, int force); ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_VFSUB_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/wbr_policy.c linux-2.6.36/fs/aufs/wbr_policy.c +--- linux-2.6.36.orig/fs/aufs/wbr_policy.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/wbr_policy.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,700 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * policies for selecting one among multiple writable branches ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* subset of cpup_attr() */ ++static noinline_for_stack ++int au_cpdown_attr(struct path *h_path, struct dentry *h_src) ++{ ++ int err, sbits; ++ struct iattr ia; ++ struct inode *h_isrc; ++ ++ h_isrc = h_src->d_inode; ++ 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)); ++ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc); ++ err = vfsub_sio_notify_change(h_path, &ia); ++ ++ /* is this nfs only? */ ++ if (!err && sbits && au_test_nfs(h_path->dentry->d_sb)) { ++ ia.ia_valid = ATTR_FORCE | ATTR_MODE; ++ ia.ia_mode = h_isrc->i_mode; ++ err = vfsub_sio_notify_change(h_path, &ia); ++ } ++ ++ return err; ++} ++ ++#define AuCpdown_PARENT_OPQ 1 ++#define AuCpdown_WHED (1 << 1) ++#define AuCpdown_MADE_DIR (1 << 2) ++#define AuCpdown_DIROPQ (1 << 3) ++#define au_ftest_cpdown(flags, name) ((flags) & AuCpdown_##name) ++#define au_fset_cpdown(flags, name) \ ++ do { (flags) |= AuCpdown_##name; } while (0) ++#define au_fclr_cpdown(flags, name) \ ++ do { (flags) &= ~AuCpdown_##name; } while (0) ++ ++struct au_cpdown_dir_args { ++ struct dentry *parent; ++ unsigned int flags; ++}; ++ ++static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_cpdown_dir_args *a) ++{ ++ int err; ++ struct dentry *opq_dentry; ++ ++ opq_dentry = au_diropq_create(dentry, bdst); ++ err = PTR_ERR(opq_dentry); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ dput(opq_dentry); ++ au_fset_cpdown(a->flags, DIROPQ); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, ++ struct inode *dir, aufs_bindex_t bdst) ++{ ++ int err; ++ struct path h_path; ++ struct au_branch *br; ++ ++ br = au_sbr(dentry->d_sb, bdst); ++ h_path.dentry = au_wh_lkup(h_parent, &dentry->d_name, br); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ err = 0; ++ if (h_path.dentry->d_inode) { ++ h_path.mnt = br->br_mnt; ++ err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, ++ dentry); ++ } ++ dput(h_path.dentry); ++ ++out: ++ return err; ++} ++ ++static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct dentry *h_parent, void *arg) ++{ ++ int err, rerr; ++ aufs_bindex_t bopq, bstart; ++ struct path h_path; ++ struct dentry *parent; ++ struct inode *h_dir, *h_inode, *inode, *dir; ++ struct au_cpdown_dir_args *args = arg; ++ ++ bstart = au_dbstart(dentry); ++ /* dentry is di-locked */ ++ parent = dget_parent(dentry); ++ dir = parent->d_inode; ++ h_dir = h_parent->d_inode; ++ AuDebugOn(h_dir != au_h_iptr(dir, bdst)); ++ IMustLock(h_dir); ++ ++ err = au_lkup_neg(dentry, bdst); ++ if (unlikely(err < 0)) ++ goto out; ++ h_path.dentry = au_h_dptr(dentry, bdst); ++ h_path.mnt = au_sbr_mnt(dentry->d_sb, bdst); ++ err = vfsub_sio_mkdir(au_h_iptr(dir, bdst), &h_path, ++ S_IRWXU | S_IRUGO | S_IXUGO); ++ if (unlikely(err)) ++ goto out_put; ++ au_fset_cpdown(args->flags, MADE_DIR); ++ ++ bopq = au_dbdiropq(dentry); ++ au_fclr_cpdown(args->flags, WHED); ++ au_fclr_cpdown(args->flags, DIROPQ); ++ if (au_dbwh(dentry) == bdst) ++ au_fset_cpdown(args->flags, WHED); ++ if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst) ++ au_fset_cpdown(args->flags, PARENT_OPQ); ++ h_inode = h_path.dentry->d_inode; ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ if (au_ftest_cpdown(args->flags, WHED)) { ++ err = au_cpdown_dir_opq(dentry, bdst, args); ++ if (unlikely(err)) { ++ mutex_unlock(&h_inode->i_mutex); ++ goto out_dir; ++ } ++ } ++ ++ err = au_cpdown_attr(&h_path, au_h_dptr(dentry, bstart)); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_opq; ++ ++ if (au_ftest_cpdown(args->flags, WHED)) { ++ err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); ++ if (unlikely(err)) ++ goto out_opq; ++ } ++ ++ inode = dentry->d_inode; ++ if (au_ibend(inode) < bdst) ++ au_set_ibend(inode, bdst); ++ au_set_h_iptr(inode, bdst, au_igrab(h_inode), ++ au_hi_flags(inode, /*isdir*/1)); ++ goto out; /* success */ ++ ++ /* revert */ ++out_opq: ++ if (au_ftest_cpdown(args->flags, DIROPQ)) { ++ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); ++ rerr = au_diropq_remove(dentry, bdst); ++ mutex_unlock(&h_inode->i_mutex); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing diropq for %.*s b%d (%d)\n", ++ AuDLNPair(dentry), bdst, rerr); ++ err = -EIO; ++ goto out; ++ } ++ } ++out_dir: ++ if (au_ftest_cpdown(args->flags, MADE_DIR)) { ++ rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); ++ if (unlikely(rerr)) { ++ AuIOErr("failed removing %.*s b%d (%d)\n", ++ AuDLNPair(dentry), bdst, rerr); ++ err = -EIO; ++ } ++ } ++out_put: ++ au_set_h_dptr(dentry, bdst, NULL); ++ if (au_dbend(dentry) == bdst) ++ au_update_dbend(dentry); ++out: ++ dput(parent); ++ return err; ++} ++ ++int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) ++{ ++ int err; ++ struct au_cpdown_dir_args args = { ++ .parent = dget_parent(dentry), ++ .flags = 0 ++ }; ++ ++ err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args); ++ dput(args.parent); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* policies for create */ ++ ++static int au_wbr_nonopq(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ int err, i, j, ndentry; ++ aufs_bindex_t bopq; ++ struct au_dcsub_pages dpages; ++ struct au_dpage *dpage; ++ struct dentry **dentries, *parent, *d; ++ ++ err = au_dpages_init(&dpages, GFP_NOFS); ++ if (unlikely(err)) ++ goto out; ++ parent = dget_parent(dentry); ++ err = au_dcsub_pages_rev_aufs(&dpages, parent, /*do_include*/0); ++ if (unlikely(err)) ++ goto out_free; ++ ++ err = bindex; ++ for (i = 0; i < dpages.ndpage; i++) { ++ dpage = dpages.dpages + i; ++ dentries = dpage->dentries; ++ ndentry = dpage->ndentry; ++ for (j = 0; j < ndentry; j++) { ++ d = dentries[j]; ++ di_read_lock_parent2(d, !AuLock_IR); ++ bopq = au_dbdiropq(d); ++ di_read_unlock(d, !AuLock_IR); ++ if (bopq >= 0 && bopq < err) ++ err = bopq; ++ } ++ } ++ ++out_free: ++ dput(parent); ++ au_dpages_free(&dpages); ++out: ++ return err; ++} ++ ++static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ for (; bindex >= 0; bindex--) ++ if (!au_br_rdonly(au_sbr(sb, bindex))) ++ return bindex; ++ return -EROFS; ++} ++ ++/* top down parent */ ++static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused) ++{ ++ int err; ++ aufs_bindex_t bstart, bindex; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ ++ sb = dentry->d_sb; ++ bstart = au_dbstart(dentry); ++ err = bstart; ++ if (!au_br_rdonly(au_sbr(sb, bstart))) ++ goto out; ++ ++ err = -EROFS; ++ parent = dget_parent(dentry); ++ for (bindex = au_dbstart(parent); bindex < bstart; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) { ++ err = au_wbr_bu(sb, bstart - 1); ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ } ++ ++out: ++ AuDbg("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; ++ aufs_bindex_t bwh, bdiropq; ++ struct dentry *parent; ++ ++ err = -1; ++ bwh = au_dbwh(dentry); ++ parent = dget_parent(dentry); ++ bdiropq = au_dbdiropq(parent); ++ if (bwh >= 0) { ++ if (bdiropq >= 0) ++ err = min(bdiropq, bwh); ++ else ++ err = bwh; ++ AuDbg("%d\n", err); ++ } else if (bdiropq >= 0) { ++ err = bdiropq; ++ AuDbg("%d\n", err); ++ } ++ dput(parent); ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++ if (err >= 0 && au_br_rdonly(au_sbr(dentry->d_sb, err))) ++ err = -1; ++ ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* round robin */ ++static int au_wbr_create_init_rr(struct super_block *sb) ++{ ++ int err; ++ ++ err = au_wbr_bu(sb, au_sbend(sb)); ++ atomic_set(&au_sbi(sb)->si_wbr_rr_next, -err); /* less important */ ++ /* smp_mb(); */ ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_rr(struct dentry *dentry, int isdir) ++{ ++ int err, nbr; ++ unsigned int u; ++ aufs_bindex_t bindex, bend; ++ struct super_block *sb; ++ atomic_t *next; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ next = &au_sbi(sb)->si_wbr_rr_next; ++ bend = au_sbend(sb); ++ nbr = bend + 1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ if (!isdir) { ++ err = atomic_dec_return(next) + 1; ++ /* modulo for 0 is meaningless */ ++ if (unlikely(!err)) ++ err = atomic_dec_return(next) + 1; ++ } else ++ err = atomic_read(next); ++ AuDbg("%d\n", err); ++ u = err; ++ err = u % nbr; ++ AuDbg("%d\n", err); ++ if (!au_br_rdonly(au_sbr(sb, err))) ++ break; ++ err = -EROFS; ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("%d\n", err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space */ ++static void au_mfs(struct dentry *dentry) ++{ ++ struct super_block *sb; ++ struct au_branch *br; ++ struct au_wbr_mfs *mfs; ++ aufs_bindex_t bindex, bend; ++ int err; ++ unsigned long long b, bavail; ++ struct path h_path; ++ /* reduce the stack usage */ ++ struct kstatfs *st; ++ ++ st = kmalloc(sizeof(*st), GFP_NOFS); ++ if (unlikely(!st)) { ++ AuWarn1("failed updating mfs(%d), ignored\n", -ENOMEM); ++ return; ++ } ++ ++ bavail = 0; ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ MtxMustLock(&mfs->mfs_lock); ++ mfs->mfs_bindex = -EROFS; ++ mfs->mfsrr_bytes = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_rdonly(br)) ++ continue; ++ ++ /* sb->s_root for NFS is unreliable */ ++ h_path.mnt = br->br_mnt; ++ h_path.dentry = h_path.mnt->mnt_root; ++ err = vfs_statfs(&h_path, st); ++ if (unlikely(err)) { ++ AuWarn1("failed statfs, b%d, %d\n", bindex, err); ++ continue; ++ } ++ ++ /* when the available size is equal, select the lower one */ ++ BUILD_BUG_ON(sizeof(b) < sizeof(st->f_bavail) ++ || sizeof(b) < sizeof(st->f_bsize)); ++ b = st->f_bavail * st->f_bsize; ++ br->br_wbr->wbr_bytes = b; ++ if (b >= bavail) { ++ bavail = b; ++ mfs->mfs_bindex = bindex; ++ mfs->mfs_jiffy = jiffies; ++ } ++ } ++ ++ mfs->mfsrr_bytes = bavail; ++ AuDbg("b%d\n", mfs->mfs_bindex); ++ kfree(st); ++} ++ ++static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused) ++{ ++ int err; ++ struct super_block *sb; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_exp(dentry); ++ if (err >= 0) ++ goto out; ++ ++ sb = dentry->d_sb; ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) ++ || mfs->mfs_bindex < 0 ++ || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) ++ au_mfs(dentry); ++ mutex_unlock(&mfs->mfs_lock); ++ err = mfs->mfs_bindex; ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out: ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfs(struct super_block *sb) ++{ ++ struct au_wbr_mfs *mfs; ++ ++ mfs = &au_sbi(sb)->si_wbr_mfs; ++ 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 __maybe_unused) ++{ ++ mutex_destroy(&au_sbi(sb)->si_wbr_mfs.mfs_lock); ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* most free space and then round robin */ ++static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir) ++{ ++ int err; ++ struct au_wbr_mfs *mfs; ++ ++ err = au_wbr_create_mfs(dentry, isdir); ++ if (err >= 0) { ++ mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; ++ mutex_lock(&mfs->mfs_lock); ++ if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) ++ err = au_wbr_create_rr(dentry, isdir); ++ mutex_unlock(&mfs->mfs_lock); ++ } ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++static int au_wbr_create_init_mfsrr(struct super_block *sb) ++{ ++ int err; ++ ++ au_wbr_create_init_mfs(sb); /* ignore */ ++ err = au_wbr_create_init_rr(sb); ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* top down parent and most free space */ ++static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) ++{ ++ int err, e2; ++ unsigned long long b; ++ aufs_bindex_t bindex, bstart, bend; ++ struct super_block *sb; ++ struct dentry *parent, *h_parent; ++ struct au_branch *br; ++ ++ err = au_wbr_create_tdp(dentry, isdir); ++ if (unlikely(err < 0)) ++ goto out; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ bend = au_dbtaildir(parent); ++ if (bstart == bend) ++ goto out_parent; /* success */ ++ ++ e2 = au_wbr_create_mfs(dentry, isdir); ++ if (e2 < 0) ++ goto out_parent; /* success */ ++ ++ /* when the available size is equal, select upper one */ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, err); ++ b = br->br_wbr->wbr_bytes; ++ AuDbg("b%d, %llu\n", err, b); ++ ++ for (bindex = bstart; bindex <= bend; bindex++) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ br = au_sbr(sb, bindex); ++ if (!au_br_rdonly(br) && br->br_wbr->wbr_bytes > b) { ++ b = br->br_wbr->wbr_bytes; ++ err = bindex; ++ AuDbg("b%d, %llu\n", err, b); ++ } ++ } ++ ++ if (err >= 0) ++ err = au_wbr_nonopq(dentry, err); ++ ++out_parent: ++ dput(parent); ++out: ++ AuDbg("b%d\n", err); ++ 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; ++ aufs_bindex_t bindex, bstart; ++ struct dentry *parent, *h_parent; ++ struct super_block *sb; ++ ++ err = -EROFS; ++ sb = dentry->d_sb; ++ parent = dget_parent(dentry); ++ bstart = au_dbstart(parent); ++ for (bindex = au_dbstart(dentry); bindex >= bstart; bindex--) { ++ h_parent = au_h_dptr(parent, bindex); ++ if (!h_parent || !h_parent->d_inode) ++ continue; ++ ++ if (!au_br_rdonly(au_sbr(sb, bindex))) { ++ err = bindex; ++ break; ++ } ++ } ++ dput(parent); ++ ++ /* bottom up here */ ++ if (unlikely(err < 0)) ++ err = au_wbr_bu(sb, bstart - 1); ++ ++ AuDbg("b%d\n", err); ++ return err; ++} ++ ++/* bottom up */ ++static int au_wbr_copyup_bu(struct dentry *dentry) ++{ ++ int err; ++ aufs_bindex_t bstart; ++ ++ bstart = au_dbstart(dentry); ++ err = au_wbr_bu(dentry->d_sb, bstart); ++ AuDbg("b%d\n", err); ++ if (err > bstart) ++ err = au_wbr_nonopq(dentry, err); ++ ++ AuDbg("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 -Nur linux-2.6.36.orig/fs/aufs/whout.c linux-2.6.36/fs/aufs/whout.c +--- linux-2.6.36.orig/fs/aufs/whout.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/whout.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1062 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#include ++#include "aufs.h" ++ ++#define WH_MASK S_IRUGO ++ ++/* ++ * If a directory contains this file, then it is opaque. We start with the ++ * .wh. flag so that it is blocked by lookup. ++ */ ++static struct qstr diropq_name = { ++ .name = AUFS_WH_DIROPQ, ++ .len = sizeof(AUFS_WH_DIROPQ) - 1 ++}; ++ ++/* ++ * generate whiteout name, which is NOT terminated by NULL. ++ * @name: original d_name.name ++ * @len: original d_name.len ++ * @wh: whiteout qstr ++ * returns zero when succeeds, otherwise error. ++ * succeeded value as wh->name should be freed by kfree(). ++ */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name) ++{ ++ char *p; ++ ++ if (unlikely(name->len > PATH_MAX - AUFS_WH_PFX_LEN)) ++ return -ENAMETOOLONG; ++ ++ wh->len = name->len + AUFS_WH_PFX_LEN; ++ p = kmalloc(wh->len, GFP_NOFS); ++ wh->name = p; ++ if (p) { ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ memcpy(p + AUFS_WH_PFX_LEN, name->name, name->len); ++ /* smp_mb(); */ ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * test if the @wh_name exists under @h_parent. ++ * @try_sio specifies the necessary of super-io. ++ */ ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, ++ struct au_branch *br, int try_sio) ++{ ++ int err; ++ struct dentry *wh_dentry; ++ ++ if (!try_sio) ++ wh_dentry = au_lkup_one(wh_name, h_parent, br, /*nd*/NULL); ++ else ++ wh_dentry = au_sio_lkup_one(wh_name, h_parent, br); ++ 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: ++ return err; ++} ++ ++/* ++ * test if the @h_dentry sets opaque or not. ++ */ ++int au_diropq_test(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_dentry->d_inode; ++ err = au_wh_test(h_dentry, &diropq_name, br, ++ au_test_h_perm_sio(h_dir, MAY_EXEC)); ++ return err; ++} ++ ++/* ++ * returns a negative dentry whose name is unique and temporary. ++ */ ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix) ++{ ++ struct dentry *dentry; ++ int i; ++ char defname[NAME_MAX - AUFS_MAX_NAMELEN + DNAME_INLINE_LEN_MIN + 1], ++ *name, *p; ++ /* strict atomic_t is unnecessary here */ ++ static unsigned short cnt; ++ struct qstr qs; ++ ++ BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN); ++ ++ name = defname; ++ qs.len = sizeof(defname) - DNAME_INLINE_LEN_MIN + prefix->len - 1; ++ if (unlikely(prefix->len > DNAME_INLINE_LEN_MIN)) { ++ dentry = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(qs.len > NAME_MAX)) ++ goto out; ++ dentry = ERR_PTR(-ENOMEM); ++ name = kmalloc(qs.len + 1, GFP_NOFS); ++ 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 + qs.len + 1 - p <= AUFS_WH_TMP_LEN); ++ ++ qs.name = name; ++ for (i = 0; i < 3; i++) { ++ sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++); ++ dentry = au_sio_lkup_one(&qs, h_parent, br); ++ if (IS_ERR(dentry) || !dentry->d_inode) ++ goto out_name; ++ dput(dentry); ++ } ++ /* pr_warning("could not get random name\n"); */ ++ dentry = ERR_PTR(-EEXIST); ++ AuDbg("%.*s\n", AuLNPair(&qs)); ++ BUG(); ++ ++out_name: ++ if (name != defname) ++ kfree(name); ++out: ++ AuTraceErrPtr(dentry); ++ return dentry; ++} ++ ++/* ++ * rename the @h_dentry on @br to the whiteouted temporary name. ++ */ ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ struct dentry *h_parent; ++ ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ h_path.dentry = au_whtmp_lkup(h_parent, br, &h_dentry->d_name); ++ err = PTR_ERR(h_path.dentry); ++ if (IS_ERR(h_path.dentry)) ++ goto out; ++ ++ /* under the same dir, no need to lock_rename() */ ++ err = vfsub_rename(h_dir, h_dentry, h_dir, &h_path); ++ AuTraceErr(err); ++ dput(h_path.dentry); ++ ++out: ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * functions for removing a whiteout ++ */ ++ ++static int do_unlink_wh(struct inode *h_dir, struct path *h_path) ++{ ++ int force; ++ ++ /* ++ * forces superio when the dir has a sticky bit. ++ * this may be a violation of unix fs semantics. ++ */ ++ force = (h_dir->i_mode & S_ISVTX) ++ && h_path->dentry->d_inode->i_uid != current_fsuid(); ++ return vfsub_unlink(h_dir, h_path, force); ++} ++ ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry) ++{ ++ int err; ++ ++ err = do_unlink_wh(h_dir, h_path); ++ if (!err && dentry) ++ au_set_dbwh(dentry, -1); ++ ++ return err; ++} ++ ++static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, ++ struct au_branch *br) ++{ ++ int err; ++ struct path h_path = { ++ .mnt = br->br_mnt ++ }; ++ ++ err = 0; ++ h_path.dentry = au_lkup_one(wh, h_parent, br, /*nd*/NULL); ++ if (IS_ERR(h_path.dentry)) ++ err = PTR_ERR(h_path.dentry); ++ else { ++ if (h_path.dentry->d_inode ++ && S_ISREG(h_path.dentry->d_inode->i_mode)) ++ err = do_unlink_wh(h_parent->d_inode, &h_path); ++ dput(h_path.dentry); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * initialize/clean whiteout for a branch ++ */ ++ ++static void au_wh_clean(struct inode *h_dir, struct path *whpath, ++ const int isdir) ++{ ++ int err; ++ ++ if (!whpath->dentry->d_inode) ++ return; ++ ++ err = mnt_want_write(whpath->mnt); ++ if (!err) { ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else ++ err = vfsub_unlink(h_dir, whpath, /*force*/0); ++ mnt_drop_write(whpath->mnt); ++ } ++ if (unlikely(err)) ++ pr_warning("failed removing %.*s (%d), ignored.\n", ++ AuDLNPair(whpath->dentry), err); ++} ++ ++static int test_linkable(struct dentry *h_root) ++{ ++ struct inode *h_dir = h_root->d_inode; ++ ++ if (h_dir->i_op->link) ++ return 0; ++ ++ pr_err("%.*s (%s) doesn't support link(2), use noplink and rw+nolwh\n", ++ AuDLNPair(h_root), au_sbtype(h_root->d_sb)); ++ return -ENOSYS; ++} ++ ++/* todo: should this mkdir be done in /sbin/mount.aufs helper? */ ++static int au_whdir(struct inode *h_dir, struct path *path) ++{ ++ int err; ++ ++ err = -EEXIST; ++ if (!path->dentry->d_inode) { ++ int mode = S_IRWXU; ++ ++ if (au_test_nfs(path->dentry->d_sb)) ++ mode |= S_IXUGO; ++ err = mnt_want_write(path->mnt); ++ if (!err) { ++ err = vfsub_mkdir(h_dir, path, mode); ++ mnt_drop_write(path->mnt); ++ } ++ } else if (S_ISDIR(path->dentry->d_inode->i_mode)) ++ err = 0; ++ else ++ pr_err("unknown %.*s exists\n", AuDLNPair(path->dentry)); ++ ++ return err; ++} ++ ++struct au_wh_base { ++ const struct qstr *name; ++ struct dentry *dentry; ++}; ++ ++static void au_wh_init_ro(struct inode *h_dir, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++} ++ ++/* ++ * returns tri-state, ++ * minus: error, caller should print the mesage ++ * zero: succuess ++ * plus: error, caller should NOT print the mesage ++ */ ++static int au_wh_init_rw_nolink(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ h_dir = h_root->d_inode; ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ au_wh_clean(h_dir, h_path, /*isdir*/0); ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * 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. ++ * ++ * returns tri-state, see above. ++ */ ++static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, ++ int do_plink, struct au_wh_base base[], ++ struct path *h_path) ++{ ++ int err; ++ struct inode *h_dir; ++ ++ WbrWhMustWriteLock(wbr); ++ ++ err = test_linkable(h_root); ++ if (unlikely(err)) { ++ err = 1; ++ goto out; ++ } ++ ++ /* ++ * todo: should this create be done in /sbin/mount.aufs helper? ++ */ ++ err = -EEXIST; ++ h_dir = h_root->d_inode; ++ if (!base[AuBrWh_BASE].dentry->d_inode) { ++ err = mnt_want_write(h_path->mnt); ++ if (!err) { ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK); ++ mnt_drop_write(h_path->mnt); ++ } ++ } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) ++ err = 0; ++ else ++ pr_err("unknown %.*s/%.*s exists\n", ++ AuDLNPair(h_root), AuDLNPair(base[AuBrWh_BASE].dentry)); ++ if (unlikely(err)) ++ goto out; ++ ++ h_path->dentry = base[AuBrWh_PLINK].dentry; ++ if (do_plink) { ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_plink = dget(base[AuBrWh_PLINK].dentry); ++ } else ++ au_wh_clean(h_dir, h_path, /*isdir*/1); ++ wbr->wbr_whbase = dget(base[AuBrWh_BASE].dentry); ++ ++ h_path->dentry = base[AuBrWh_ORPH].dentry; ++ err = au_whdir(h_dir, h_path); ++ if (unlikely(err)) ++ goto out; ++ wbr->wbr_orph = dget(base[AuBrWh_ORPH].dentry); ++ ++out: ++ return err; ++} ++ ++/* ++ * initialize the whiteout base file/dir for @br. ++ */ ++int au_wh_init(struct dentry *h_root, struct au_branch *br, ++ struct super_block *sb) ++{ ++ int err, i; ++ const unsigned char do_plink ++ = !!au_opt_test(au_mntflags(sb), PLINK); ++ struct path path = { ++ .mnt = br->br_mnt ++ }; ++ struct inode *h_dir; ++ struct au_wbr *wbr = br->br_wbr; ++ static const struct qstr base_name[] = { ++ [AuBrWh_BASE] = { ++ .name = AUFS_BASE_NAME, ++ .len = sizeof(AUFS_BASE_NAME) - 1 ++ }, ++ [AuBrWh_PLINK] = { ++ .name = AUFS_PLINKDIR_NAME, ++ .len = sizeof(AUFS_PLINKDIR_NAME) - 1 ++ }, ++ [AuBrWh_ORPH] = { ++ .name = AUFS_ORPHDIR_NAME, ++ .len = sizeof(AUFS_ORPHDIR_NAME) - 1 ++ } ++ }; ++ struct au_wh_base base[] = { ++ [AuBrWh_BASE] = { ++ .name = base_name + AuBrWh_BASE, ++ .dentry = NULL ++ }, ++ [AuBrWh_PLINK] = { ++ .name = base_name + AuBrWh_PLINK, ++ .dentry = NULL ++ }, ++ [AuBrWh_ORPH] = { ++ .name = base_name + AuBrWh_ORPH, ++ .dentry = NULL ++ } ++ }; ++ ++ if (wbr) ++ WbrWhMustWriteLock(wbr); ++ ++ for (i = 0; i < AuBrWh_Last; i++) { ++ /* doubly whiteouted */ ++ struct dentry *d; ++ ++ d = au_wh_lkup(h_root, (void *)base[i].name, br); ++ err = PTR_ERR(d); ++ if (IS_ERR(d)) ++ goto out; ++ ++ base[i].dentry = d; ++ AuDebugOn(wbr ++ && wbr->wbr_wh[i] ++ && wbr->wbr_wh[i] != base[i].dentry); ++ } ++ ++ if (wbr) ++ for (i = 0; i < AuBrWh_Last; i++) { ++ dput(wbr->wbr_wh[i]); ++ wbr->wbr_wh[i] = NULL; ++ } ++ ++ err = 0; ++ switch (br->br_perm) { ++ case AuBrPerm_RO: ++ case AuBrPerm_ROWH: ++ case AuBrPerm_RR: ++ case AuBrPerm_RRWH: ++ h_dir = h_root->d_inode; ++ au_wh_init_ro(h_dir, base, &path); ++ break; ++ ++ case AuBrPerm_RWNoLinkWH: ++ err = au_wh_init_rw_nolink(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ break; ++ ++ case AuBrPerm_RW: ++ err = au_wh_init_rw(h_root, wbr, do_plink, base, &path); ++ if (err > 0) ++ goto out; ++ else if (err) ++ goto out_err; ++ break; ++ ++ default: ++ BUG(); ++ } ++ goto out; /* success */ ++ ++out_err: ++ pr_err("an error(%d) on the writable branch %.*s(%s)\n", ++ err, AuDLNPair(h_root), au_sbtype(h_root->d_sb)); ++out: ++ for (i = 0; i < AuBrWh_Last; i++) ++ dput(base[i].dentry); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * whiteouts are all hard-linked usually. ++ * when its link count reaches a ceiling, we create a new whiteout base ++ * asynchronously. ++ */ ++ ++struct reinit_br_wh { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void reinit_br_wh(void *arg) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct path h_path; ++ struct reinit_br_wh *a = arg; ++ struct au_wbr *wbr; ++ struct inode *dir; ++ struct dentry *h_root; ++ struct au_hinode *hdir; ++ ++ err = 0; ++ wbr = a->br->br_wbr; ++ /* big aufs lock */ ++ si_noflush_write_lock(a->sb); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(a->sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ di_read_lock_parent(a->sb->s_root, AuLock_IR); ++ dir = a->sb->s_root->d_inode; ++ hdir = au_hi(dir, bindex); ++ h_root = au_h_dptr(a->sb->s_root, bindex); ++ ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ wbr_wh_write_lock(wbr); ++ err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, ++ h_root, a->br); ++ if (!err) { ++ err = mnt_want_write(a->br->br_mnt); ++ if (!err) { ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = a->br->br_mnt; ++ err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); ++ mnt_drop_write(a->br->br_mnt); ++ } ++ } else { ++ pr_warning("%.*s is moved, ignored\n", ++ AuDLNPair(wbr->wbr_whbase)); ++ err = 0; ++ } ++ dput(wbr->wbr_whbase); ++ wbr->wbr_whbase = NULL; ++ if (!err) ++ err = au_wh_init(h_root, a->br, a->sb); ++ wbr_wh_write_unlock(wbr); ++ au_hn_imtx_unlock(hdir); ++ di_read_unlock(a->sb->s_root, AuLock_IR); ++ ++out: ++ if (wbr) ++ atomic_dec(&wbr->wbr_wh_running); ++ atomic_dec(&a->br->br_count); ++ si_write_unlock(a->sb); ++ au_nwt_done(&au_sbi(a->sb)->si_nowait); ++ kfree(arg); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++static void kick_reinit_br_wh(struct super_block *sb, struct au_branch *br) ++{ ++ int do_dec, wkq_err; ++ struct reinit_br_wh *arg; ++ ++ do_dec = 1; ++ if (atomic_inc_return(&br->br_wbr->wbr_wh_running) != 1) ++ goto out; ++ ++ /* ignore ENOMEM */ ++ arg = kmalloc(sizeof(*arg), GFP_NOFS); ++ if (arg) { ++ /* ++ * dec(wh_running), kfree(arg) and dec(br_count) ++ * in reinit function ++ */ ++ arg->sb = sb; ++ arg->br = br; ++ atomic_inc(&br->br_count); ++ wkq_err = au_wkq_nowait(reinit_br_wh, arg, sb); ++ if (unlikely(wkq_err)) { ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++ atomic_dec(&br->br_count); ++ kfree(arg); ++ } ++ do_dec = 0; ++ } ++ ++out: ++ if (do_dec) ++ atomic_dec(&br->br_wbr->wbr_wh_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create the whiteout @wh. ++ */ ++static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, ++ struct dentry *wh) ++{ ++ int err; ++ struct path h_path = { ++ .dentry = wh ++ }; ++ struct au_branch *br; ++ struct au_wbr *wbr; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ ++ h_parent = wh->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ ++ br = au_sbr(sb, bindex); ++ h_path.mnt = br->br_mnt; ++ wbr = br->br_wbr; ++ wbr_wh_read_lock(wbr); ++ if (wbr->wbr_whbase) { ++ err = vfsub_link(wbr->wbr_whbase, h_dir, &h_path); ++ if (!err || err != -EMLINK) ++ goto out; ++ ++ /* link count full. re-initialize br_whbase. */ ++ kick_reinit_br_wh(sb, br); ++ } ++ ++ /* return this error in this context */ ++ err = vfsub_create(h_dir, &h_path, WH_MASK); ++ ++out: ++ wbr_wh_read_unlock(wbr); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create or remove the diropq. ++ */ ++static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *opq_dentry, *h_dentry; ++ struct super_block *sb; ++ struct au_branch *br; ++ int err; ++ ++ sb = dentry->d_sb; ++ br = au_sbr(sb, bindex); ++ h_dentry = au_h_dptr(dentry, bindex); ++ opq_dentry = au_lkup_one(&diropq_name, h_dentry, br, /*nd*/NULL); ++ if (IS_ERR(opq_dentry)) ++ goto out; ++ ++ if (au_ftest_diropq(flags, CREATE)) { ++ err = link_or_create_wh(sb, bindex, opq_dentry); ++ if (!err) { ++ au_set_dbdiropq(dentry, bindex); ++ goto out; /* success */ ++ } ++ } else { ++ struct path tmp = { ++ .dentry = opq_dentry, ++ .mnt = br->br_mnt ++ }; ++ err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); ++ if (!err) ++ au_set_dbdiropq(dentry, -1); ++ } ++ dput(opq_dentry); ++ opq_dentry = ERR_PTR(err); ++ ++out: ++ return opq_dentry; ++} ++ ++struct do_diropq_args { ++ struct dentry **errp; ++ struct dentry *dentry; ++ aufs_bindex_t bindex; ++ unsigned int flags; ++}; ++ ++static void call_do_diropq(void *args) ++{ ++ struct do_diropq_args *a = args; ++ *a->errp = do_diropq(a->dentry, a->bindex, a->flags); ++} ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags) ++{ ++ struct dentry *diropq, *h_dentry; ++ ++ h_dentry = au_h_dptr(dentry, bindex); ++ if (!au_test_h_perm_sio(h_dentry->d_inode, MAY_EXEC | MAY_WRITE)) ++ 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); ++ if (unlikely(wkq_err)) ++ diropq = ERR_PTR(wkq_err); ++ } ++ ++ return diropq; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * lookup whiteout dentry. ++ * @h_parent: lower parent dentry which must exist and be locked ++ * @base_name: name of dentry which will be whiteouted ++ * returns dentry for whiteout. ++ */ ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br) ++{ ++ int err; ++ struct qstr wh_name; ++ struct dentry *wh_dentry; ++ ++ err = au_wh_name_alloc(&wh_name, base_name); ++ wh_dentry = ERR_PTR(err); ++ if (!err) { ++ wh_dentry = au_lkup_one(&wh_name, h_parent, br, /*nd*/NULL); ++ kfree(wh_name.name); ++ } ++ return wh_dentry; ++} ++ ++/* ++ * link/create a whiteout for @dentry on @bindex. ++ */ ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent) ++{ ++ struct dentry *wh_dentry; ++ struct super_block *sb; ++ int err; ++ ++ sb = dentry->d_sb; ++ wh_dentry = au_wh_lkup(h_parent, &dentry->d_name, au_sbr(sb, bindex)); ++ if (!IS_ERR(wh_dentry) && !wh_dentry->d_inode) { ++ err = link_or_create_wh(sb, bindex, wh_dentry); ++ if (!err) ++ au_set_dbwh(dentry, bindex); ++ else { ++ dput(wh_dentry); ++ wh_dentry = ERR_PTR(err); ++ } ++ } ++ ++ return wh_dentry; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* Delete all whiteouts in this directory on branch bindex. */ ++static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist, ++ aufs_bindex_t bindex, struct au_branch *br) ++{ ++ int err; ++ unsigned long ul, n; ++ struct qstr wh_name; ++ char *p; ++ struct hlist_head *head; ++ struct au_vdir_wh *tpos; ++ struct hlist_node *pos; ++ struct au_vdir_destr *str; ++ ++ err = -ENOMEM; ++ p = __getname_gfp(GFP_NOFS); ++ wh_name.name = p; ++ if (unlikely(!wh_name.name)) ++ goto out; ++ ++ err = 0; ++ memcpy(p, AUFS_WH_PFX, AUFS_WH_PFX_LEN); ++ p += AUFS_WH_PFX_LEN; ++ n = whlist->nh_num; ++ head = whlist->nh_head; ++ for (ul = 0; !err && ul < n; ul++, head++) { ++ 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_dentry, &wh_name, br); ++ if (!err) ++ continue; ++ break; ++ } ++ AuIOErr("whiteout name too long %.*s\n", ++ str->len, str->name); ++ err = -EIO; ++ break; ++ } ++ } ++ __putname(wh_name.name); ++ ++out: ++ return err; ++} ++ ++struct del_wh_children_args { ++ int *errp; ++ struct dentry *h_dentry; ++ struct au_nhash *whlist; ++ aufs_bindex_t bindex; ++ struct au_branch *br; ++}; ++ ++static void call_del_wh_children(void *args) ++{ ++ struct del_wh_children_args *a = args; ++ *a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp) ++{ ++ struct au_whtmp_rmdir *whtmp; ++ int err; ++ unsigned int rdhash; ++ ++ SiMustAnyLock(sb); ++ ++ whtmp = kmalloc(sizeof(*whtmp), gfp); ++ if (unlikely(!whtmp)) { ++ whtmp = ERR_PTR(-ENOMEM); ++ goto out; ++ } ++ ++ whtmp->dir = NULL; ++ whtmp->br = NULL; ++ whtmp->wh_dentry = NULL; ++ /* no estimation for dir size */ ++ rdhash = au_sbi(sb)->si_rdhash; ++ if (!rdhash) ++ rdhash = AUFS_RDHASH_DEF; ++ err = au_nhash_alloc(&whtmp->whlist, rdhash, gfp); ++ if (unlikely(err)) { ++ kfree(whtmp); ++ whtmp = ERR_PTR(err); ++ } ++ ++out: ++ return whtmp; ++} ++ ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp) ++{ ++ if (whtmp->br) ++ atomic_dec(&whtmp->br->br_count); ++ dput(whtmp->wh_dentry); ++ iput(whtmp->dir); ++ au_nhash_wh_free(&whtmp->whlist); ++ kfree(whtmp); ++} ++ ++/* ++ * rmdir the whiteouted temporary named dir @h_dentry. ++ * @whlist: whiteouted children. ++ */ ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist) ++{ ++ int err; ++ struct path h_tmp; ++ struct inode *wh_inode, *h_dir; ++ struct au_branch *br; ++ ++ h_dir = wh_dentry->d_parent->d_inode; /* dir inode is locked */ ++ IMustLock(h_dir); ++ ++ br = au_sbr(dir->i_sb, bindex); ++ wh_inode = wh_dentry->d_inode; ++ mutex_lock_nested(&wh_inode->i_mutex, AuLsc_I_CHILD); ++ ++ /* ++ * someone else might change some whiteouts while we were sleeping. ++ * it means this whlist may have an obsoleted entry. ++ */ ++ if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE)) ++ err = del_wh_children(wh_dentry, whlist, bindex, br); ++ else { ++ int wkq_err; ++ struct del_wh_children_args args = { ++ .errp = &err, ++ .h_dentry = wh_dentry, ++ .whlist = whlist, ++ .bindex = bindex, ++ .br = br ++ }; ++ ++ wkq_err = au_wkq_wait(call_del_wh_children, &args); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ mutex_unlock(&wh_inode->i_mutex); ++ ++ if (!err) { ++ h_tmp.dentry = wh_dentry; ++ h_tmp.mnt = br->br_mnt; ++ err = vfsub_rmdir(h_dir, &h_tmp); ++ } ++ ++ if (!err) { ++ if (au_ibstart(dir) == bindex) { ++ /* todo: dir->i_mutex is necessary */ ++ au_cpup_attr_timesizes(dir); ++ vfsub_drop_nlink(dir); ++ } ++ return 0; /* success */ ++ } ++ ++ pr_warning("failed removing %.*s(%d), ignored\n", ++ AuDLNPair(wh_dentry), err); ++ return err; ++} ++ ++static void call_rmdir_whtmp(void *args) ++{ ++ int err; ++ aufs_bindex_t bindex; ++ struct au_whtmp_rmdir *a = args; ++ struct super_block *sb; ++ struct dentry *h_parent; ++ struct inode *h_dir; ++ struct au_hinode *hdir; ++ ++ /* rmdir by nfsd may cause deadlock with this i_mutex */ ++ /* mutex_lock(&a->dir->i_mutex); */ ++ err = -EROFS; ++ sb = a->dir->i_sb; ++ si_read_lock(sb, !AuLock_FLUSH); ++ if (!au_br_writable(a->br->br_perm)) ++ goto out; ++ bindex = au_br_index(sb, a->br->br_id); ++ if (unlikely(bindex < 0)) ++ goto out; ++ ++ err = -EIO; ++ ii_write_lock_parent(a->dir); ++ h_parent = dget_parent(a->wh_dentry); ++ h_dir = h_parent->d_inode; ++ hdir = au_hi(a->dir, bindex); ++ au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); ++ err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, ++ a->br); ++ if (!err) { ++ err = mnt_want_write(a->br->br_mnt); ++ if (!err) { ++ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, ++ &a->whlist); ++ mnt_drop_write(a->br->br_mnt); ++ } ++ } ++ au_hn_imtx_unlock(hdir); ++ dput(h_parent); ++ ii_write_unlock(a->dir); ++ ++out: ++ /* mutex_unlock(&a->dir->i_mutex); */ ++ au_whtmp_rmdir_free(a); ++ si_read_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ if (unlikely(err)) ++ AuIOErr("err %d\n", err); ++} ++ ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args) ++{ ++ int wkq_err; ++ struct super_block *sb; ++ ++ IMustLock(dir); ++ ++ /* all post-process will be done in do_rmdir_whtmp(). */ ++ sb = dir->i_sb; ++ args->dir = au_igrab(dir); ++ args->br = au_sbr(sb, bindex); ++ atomic_inc(&args->br->br_count); ++ args->wh_dentry = dget(wh_dentry); ++ wkq_err = au_wkq_nowait(call_rmdir_whtmp, args, sb); ++ if (unlikely(wkq_err)) { ++ pr_warning("rmdir error %.*s (%d), ignored\n", ++ AuDLNPair(wh_dentry), wkq_err); ++ au_whtmp_rmdir_free(args); ++ } ++} +diff -Nur linux-2.6.36.orig/fs/aufs/whout.h linux-2.6.36/fs/aufs/whout.h +--- linux-2.6.36.orig/fs/aufs/whout.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/whout.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,89 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * whiteout for logical deletion and opaque directory ++ */ ++ ++#ifndef __AUFS_WHOUT_H__ ++#define __AUFS_WHOUT_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include "dir.h" ++ ++/* whout.c */ ++int au_wh_name_alloc(struct qstr *wh, const struct qstr *name); ++struct au_branch; ++int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, ++ struct au_branch *br, int try_sio); ++int au_diropq_test(struct dentry *h_dentry, struct au_branch *br); ++struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, ++ struct qstr *prefix); ++int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); ++int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, ++ struct dentry *dentry); ++int au_wh_init(struct dentry *h_parent, struct au_branch *br, ++ struct super_block *sb); ++ ++/* diropq flags */ ++#define AuDiropq_CREATE 1 ++#define au_ftest_diropq(flags, name) ((flags) & AuDiropq_##name) ++#define au_fset_diropq(flags, name) \ ++ do { (flags) |= AuDiropq_##name; } while (0) ++#define au_fclr_diropq(flags, name) \ ++ do { (flags) &= ~AuDiropq_##name; } while (0) ++ ++struct dentry *au_diropq_sio(struct dentry *dentry, aufs_bindex_t bindex, ++ unsigned int flags); ++struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name, ++ struct au_branch *br); ++struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex, ++ struct dentry *h_parent); ++ ++/* real rmdir for the whiteout-ed dir */ ++struct au_whtmp_rmdir { ++ struct inode *dir; ++ struct au_branch *br; ++ struct dentry *wh_dentry; ++ struct au_nhash whlist; ++}; ++ ++struct au_whtmp_rmdir *au_whtmp_rmdir_alloc(struct super_block *sb, gfp_t gfp); ++void au_whtmp_rmdir_free(struct au_whtmp_rmdir *whtmp); ++int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_nhash *whlist); ++void au_whtmp_kick_rmdir(struct inode *dir, aufs_bindex_t bindex, ++ struct dentry *wh_dentry, struct au_whtmp_rmdir *args); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline struct dentry *au_diropq_create(struct dentry *dentry, ++ aufs_bindex_t bindex) ++{ ++ return au_diropq_sio(dentry, bindex, AuDiropq_CREATE); ++} ++ ++static inline int au_diropq_remove(struct dentry *dentry, aufs_bindex_t bindex) ++{ ++ return PTR_ERR(au_diropq_sio(dentry, bindex, !AuDiropq_CREATE)); ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WHOUT_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/wkq.c linux-2.6.36/fs/aufs/wkq.c +--- linux-2.6.36.orig/fs/aufs/wkq.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/wkq.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,236 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new dredential scheme ++ */ ++ ++#include ++#include "aufs.h" ++ ++/* internal workqueue named AUFS_WKQ_NAME and AUFS_WKQ_PRE_NAME */ ++enum { ++ AuWkq_INORMAL, ++ AuWkq_IPRE ++}; ++ ++static struct { ++ char *name; ++ struct workqueue_struct *wkq; ++} au_wkq[] = { ++ [AuWkq_INORMAL] = { ++ .name = AUFS_WKQ_NAME ++ }, ++ [AuWkq_IPRE] = { ++ .name = AUFS_WKQ_PRE_NAME ++ } ++}; ++ ++struct au_wkinfo { ++ struct work_struct wk; ++ struct kobject *kobj; ++ ++ unsigned int flags; /* see wkq.h */ ++ ++ au_wkq_func_t func; ++ void *args; ++ ++ struct completion *comp; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void wkq_func(struct work_struct *wk) ++{ ++ struct au_wkinfo *wkinfo = container_of(wk, struct au_wkinfo, wk); ++ ++ AuDebugOn(current_fsuid()); ++ AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY); ++ ++ wkinfo->func(wkinfo->args); ++ if (au_ftest_wkq(wkinfo->flags, WAIT)) ++ complete(wkinfo->comp); ++ else { ++ kobject_put(wkinfo->kobj); ++ module_put(THIS_MODULE); ++ kfree(wkinfo); ++ } ++} ++ ++/* ++ * Since struct completion is large, try allocating it dynamically. ++ */ ++#if defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) ++#define AuWkqCompDeclare(name) struct completion *comp = NULL ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ *comp = kmalloc(sizeof(**comp), GFP_NOFS); ++ if (*comp) { ++ init_completion(*comp); ++ wkinfo->comp = *comp; ++ return 0; ++ } ++ return -ENOMEM; ++} ++ ++static void au_wkq_comp_free(struct completion *comp) ++{ ++ kfree(comp); ++} ++ ++#else ++ ++/* no braces */ ++#define AuWkqCompDeclare(name) \ ++ DECLARE_COMPLETION_ONSTACK(_ ## name); \ ++ struct completion *comp = &_ ## name ++ ++static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) ++{ ++ wkinfo->comp = *comp; ++ return 0; ++} ++ ++static void au_wkq_comp_free(struct completion *comp __maybe_unused) ++{ ++ /* empty */ ++} ++#endif /* 4KSTACKS */ ++ ++static void au_wkq_run(struct au_wkinfo *wkinfo, unsigned int flags) ++{ ++ struct workqueue_struct *wkq; ++ ++ au_dbg_verify_kthread(); ++ if (flags & AuWkq_WAIT) { ++ INIT_WORK_ON_STACK(&wkinfo->wk, wkq_func); ++ wkq = au_wkq[AuWkq_INORMAL].wkq; ++ if (flags & AuWkq_PRE) ++ wkq = au_wkq[AuWkq_IPRE].wkq; ++ queue_work(wkq, &wkinfo->wk); ++ } else { ++ INIT_WORK(&wkinfo->wk, wkq_func); ++ schedule_work(&wkinfo->wk); ++ } ++} ++ ++/* ++ * Be careful. It is easy to make deadlock happen. ++ * processA: lock, wkq and wait ++ * processB: wkq and wait, lock in wkq ++ * --> deadlock ++ */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args) ++{ ++ int err; ++ AuWkqCompDeclare(comp); ++ struct au_wkinfo wkinfo = { ++ .flags = flags, ++ .func = func, ++ .args = args ++ }; ++ ++ err = au_wkq_comp_alloc(&wkinfo, &comp); ++ if (!err) { ++ au_wkq_run(&wkinfo, flags); ++ /* no timeout, no interrupt */ ++ wait_for_completion(wkinfo.comp); ++ au_wkq_comp_free(comp); ++ destroy_work_on_stack(&wkinfo.wk); ++ } ++ ++ return err; ++ ++} ++ ++/* ++ * Note: dget/dput() in func for aufs dentries are not supported. It will be a ++ * problem in a concurrent umounting. ++ */ ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb) ++{ ++ int err; ++ struct au_wkinfo *wkinfo; ++ ++ atomic_inc(&au_sbi(sb)->si_nowait.nw_len); ++ ++ /* ++ * wkq_func() must free this wkinfo. ++ * it highly depends upon the implementation of workqueue. ++ */ ++ err = 0; ++ wkinfo = kmalloc(sizeof(*wkinfo), GFP_NOFS); ++ if (wkinfo) { ++ wkinfo->kobj = &au_sbi(sb)->si_kobj; ++ wkinfo->flags = !AuWkq_WAIT; ++ wkinfo->func = func; ++ wkinfo->args = args; ++ wkinfo->comp = NULL; ++ kobject_get(wkinfo->kobj); ++ __module_get(THIS_MODULE); ++ ++ au_wkq_run(wkinfo, !AuWkq_WAIT); ++ } else { ++ err = -ENOMEM; ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_nwt_init(struct au_nowait_tasks *nwt) ++{ ++ atomic_set(&nwt->nw_len, 0); ++ /* smp_mb(); */ /* atomic_set */ ++ init_waitqueue_head(&nwt->nw_wq); ++} ++ ++void au_wkq_fin(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(au_wkq); i++) ++ if (au_wkq[i].wkq) ++ destroy_workqueue(au_wkq[i].wkq); ++} ++ ++int __init au_wkq_init(void) ++{ ++ int err, i; ++ ++ err = 0; ++ for (i = 0; !err && i < ARRAY_SIZE(au_wkq); i++) { ++ BUILD_BUG_ON(!WQ_RESCUER); ++ au_wkq[i].wkq = alloc_workqueue(au_wkq[i].name, !WQ_RESCUER, ++ WQ_DFL_ACTIVE); ++ if (IS_ERR(au_wkq[i].wkq)) ++ err = PTR_ERR(au_wkq[i].wkq); ++ else if (!au_wkq[i].wkq) ++ err = -ENOMEM; ++ if (unlikely(err)) ++ au_wkq[i].wkq = NULL; ++ } ++ if (unlikely(err)) ++ au_wkq_fin(); ++ ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/aufs/wkq.h linux-2.6.36/fs/aufs/wkq.h +--- linux-2.6.36.orig/fs/aufs/wkq.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/wkq.h 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,90 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * workqueue for asynchronous/super-io operations ++ * todo: try new credentials management scheme ++ */ ++ ++#ifndef __AUFS_WKQ_H__ ++#define __AUFS_WKQ_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++ ++struct super_block; ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * in the next operation, wait for the 'nowait' tasks in system-wide workqueue ++ */ ++struct au_nowait_tasks { ++ atomic_t nw_len; ++ wait_queue_head_t nw_wq; ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ ++typedef void (*au_wkq_func_t)(void *args); ++ ++/* wkq flags */ ++#define AuWkq_WAIT 1 ++#define AuWkq_PRE (1 << 1) ++#define au_ftest_wkq(flags, name) ((flags) & AuWkq_##name) ++#define au_fset_wkq(flags, name) \ ++ do { (flags) |= AuWkq_##name; } while (0) ++#define au_fclr_wkq(flags, name) \ ++ do { (flags) &= ~AuWkq_##name; } while (0) ++ ++/* wkq.c */ ++int au_wkq_do_wait(unsigned int flags, au_wkq_func_t func, void *args); ++int au_wkq_nowait(au_wkq_func_t func, void *args, struct super_block *sb); ++void au_nwt_init(struct au_nowait_tasks *nwt); ++int __init au_wkq_init(void); ++void au_wkq_fin(void); ++ ++/* ---------------------------------------------------------------------- */ ++ ++static inline int au_wkq_wait_pre(au_wkq_func_t func, void *args) ++{ ++ return au_wkq_do_wait(AuWkq_WAIT | AuWkq_PRE, func, args); ++} ++ ++static inline int au_wkq_wait(au_wkq_func_t func, void *args) ++{ ++ return au_wkq_do_wait(AuWkq_WAIT, func, args); ++} ++ ++static inline void au_nwt_done(struct au_nowait_tasks *nwt) ++{ ++ if (atomic_dec_and_test(&nwt->nw_len)) ++ wake_up_all(&nwt->nw_wq); ++} ++ ++static inline int au_nwt_flush(struct au_nowait_tasks *nwt) ++{ ++ wait_event(nwt->nw_wq, !atomic_read(&nwt->nw_len)); ++ return 0; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __AUFS_WKQ_H__ */ +diff -Nur linux-2.6.36.orig/fs/aufs/xino.c linux-2.6.36/fs/aufs/xino.c +--- linux-2.6.36.orig/fs/aufs/xino.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/aufs/xino.c 2011-01-10 19:24:41.000000000 +0100 +@@ -0,0 +1,1265 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. Okajima ++ * ++ * This program, aufs is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA ++ */ ++ ++/* ++ * external inode number translation table and bitmap ++ */ ++ ++#include ++#include ++#include ++#include "aufs.h" ++ ++ssize_t xino_fread(au_readf_t func, struct file *file, void *kbuf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_access(file->f_dentry); ++#endif ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static ssize_t do_xino_fwrite(au_writef_t func, struct file *file, void *kbuf, ++ size_t size, loff_t *pos) ++{ ++ ssize_t err; ++ mm_segment_t oldfs; ++ union { ++ void *k; ++ const char __user *u; ++ } buf; ++ ++ buf.k = kbuf; ++ oldfs = get_fs(); ++ set_fs(KERNEL_DS); ++ do { ++ /* todo: signal_pending? */ ++ err = func(file, buf.u, size, pos); ++ } while (err == -EAGAIN || err == -EINTR); ++ set_fs(oldfs); ++ ++#if 0 /* reserved for future use */ ++ if (err > 0) ++ fsnotify_modify(file->f_dentry); ++#endif ++ ++ return err; ++} ++ ++struct do_xino_fwrite_args { ++ ssize_t *errp; ++ au_writef_t func; ++ struct file *file; ++ void *buf; ++ size_t size; ++ loff_t *pos; ++}; ++ ++static void call_do_xino_fwrite(void *args) ++{ ++ struct do_xino_fwrite_args *a = args; ++ *a->errp = do_xino_fwrite(a->func, a->file, a->buf, a->size, a->pos); ++} ++ ++ssize_t xino_fwrite(au_writef_t func, struct file *file, void *buf, size_t size, ++ loff_t *pos) ++{ ++ ssize_t err; ++ ++ /* todo: signal block and no wkq? */ ++ if (rlimit(RLIMIT_FSIZE) == RLIM_INFINITY) { ++ lockdep_off(); ++ err = do_xino_fwrite(func, file, buf, size, pos); ++ lockdep_on(); ++ } else { ++ /* ++ * it breaks RLIMIT_FSIZE and normal user's limit, ++ * users should care about quota and real 'filesystem full.' ++ */ ++ 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); ++ if (unlikely(wkq_err)) ++ err = wkq_err; ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a new xinofile at the same place/path as @base_file. ++ */ ++struct file *au_xino_create2(struct file *base_file, struct file *copy_src) ++{ ++ struct file *file; ++ struct dentry *base, *parent; ++ struct inode *dir; ++ struct qstr *name; ++ struct path path; ++ int err; ++ ++ base = base_file->f_dentry; ++ parent = base->d_parent; /* dir inode is locked */ ++ dir = parent->d_inode; ++ IMustLock(dir); ++ ++ file = ERR_PTR(-EINVAL); ++ name = &base->d_name; ++ path.dentry = vfsub_lookup_one_len(name->name, parent, name->len); ++ if (IS_ERR(path.dentry)) { ++ file = (void *)path.dentry; ++ pr_err("%.*s lookup err %ld\n", ++ AuLNPair(name), PTR_ERR(path.dentry)); ++ goto out; ++ } ++ ++ /* no need to mnt_want_write() since we call dentry_open() later */ ++ err = vfs_create(dir, path.dentry, S_IRUGO | S_IWUGO, NULL); ++ if (unlikely(err)) { ++ file = ERR_PTR(err); ++ pr_err("%.*s create err %d\n", AuLNPair(name), err); ++ goto out_dput; ++ } ++ ++ path.mnt = base_file->f_vfsmnt; ++ file = vfsub_dentry_open(&path, ++ O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | FMODE_NONOTIFY */); ++ if (IS_ERR(file)) { ++ pr_err("%.*s open err %ld\n", AuLNPair(name), PTR_ERR(file)); ++ goto out_dput; ++ } ++ ++ err = vfsub_unlink(dir, &file->f_path, /*force*/0); ++ if (unlikely(err)) { ++ pr_err("%.*s unlink err %d\n", AuLNPair(name), err); ++ goto out_fput; ++ } ++ ++ if (copy_src) { ++ /* no one can touch copy_src xino */ ++ err = au_copy_file(file, copy_src, ++ i_size_read(copy_src->f_dentry->d_inode)); ++ if (unlikely(err)) { ++ pr_err("%.*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(path.dentry); ++out: ++ return file; ++} ++ ++struct au_xino_lock_dir { ++ struct au_hinode *hdir; ++ struct dentry *parent; ++ struct mutex *mtx; ++}; ++ ++static void au_xino_lock_dir(struct super_block *sb, struct file *xino, ++ struct au_xino_lock_dir *ldir) ++{ ++ aufs_bindex_t brid, bindex; ++ ++ ldir->hdir = NULL; ++ bindex = -1; ++ brid = au_xino_brid(sb); ++ if (brid >= 0) ++ bindex = au_br_index(sb, brid); ++ if (bindex >= 0) { ++ ldir->hdir = au_hi(sb->s_root->d_inode, bindex); ++ au_hn_imtx_lock_nested(ldir->hdir, AuLsc_I_PARENT); ++ } else { ++ ldir->parent = dget_parent(xino->f_dentry); ++ ldir->mtx = &ldir->parent->d_inode->i_mutex; ++ mutex_lock_nested(ldir->mtx, AuLsc_I_PARENT); ++ } ++} ++ ++static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) ++{ ++ if (ldir->hdir) ++ au_hn_imtx_unlock(ldir->hdir); ++ else { ++ mutex_unlock(ldir->mtx); ++ dput(ldir->parent); ++ } ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate xino files asynchronously */ ++ ++int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) ++{ ++ int err; ++ aufs_bindex_t bi, bend; ++ struct au_branch *br; ++ struct file *new_xino, *file; ++ struct super_block *h_sb; ++ struct au_xino_lock_dir ldir; ++ ++ err = -EINVAL; ++ bend = au_sbend(sb); ++ if (unlikely(bindex < 0 || bend < bindex)) ++ goto out; ++ br = au_sbr(sb, bindex); ++ file = br->br_xino.xi_file; ++ if (!file) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ new_xino = au_xino_create2(file, file); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(new_xino); ++ if (IS_ERR(new_xino)) ++ goto out; ++ err = 0; ++ fput(file); ++ br->br_xino.xi_file = new_xino; ++ ++ h_sb = br->br_mnt->mnt_sb; ++ for (bi = 0; bi <= bend; bi++) { ++ if (unlikely(bi == bindex)) ++ continue; ++ br = au_sbr(sb, bi); ++ if (br->br_mnt->mnt_sb != h_sb) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = new_xino; ++ get_file(new_xino); ++ } ++ ++out: ++ return err; ++} ++ ++struct xino_do_trunc_args { ++ struct super_block *sb; ++ struct au_branch *br; ++}; ++ ++static void xino_do_trunc(void *_args) ++{ ++ struct xino_do_trunc_args *args = _args; ++ struct super_block *sb; ++ struct au_branch *br; ++ struct inode *dir; ++ int err; ++ aufs_bindex_t bindex; ++ ++ err = 0; ++ sb = args->sb; ++ dir = sb->s_root->d_inode; ++ br = args->br; ++ ++ si_noflush_write_lock(sb); ++ ii_read_lock_parent(dir); ++ bindex = au_br_index(sb, br->br_id); ++ err = au_xino_trunc(sb, bindex); ++ if (!err ++ && br->br_xino.xi_file->f_dentry->d_inode->i_blocks ++ >= br->br_xino_upper) ++ br->br_xino_upper += AUFS_XINO_TRUNC_STEP; ++ ++ ii_read_unlock(dir); ++ if (unlikely(err)) ++ pr_warning("err b%d, (%d)\n", bindex, err); ++ atomic_dec(&br->br_xino_running); ++ atomic_dec(&br->br_count); ++ si_write_unlock(sb); ++ au_nwt_done(&au_sbi(sb)->si_nowait); ++ kfree(args); ++} ++ ++static void xino_try_trunc(struct super_block *sb, struct au_branch *br) ++{ ++ struct xino_do_trunc_args *args; ++ int wkq_err; ++ ++ if (br->br_xino.xi_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_NOFS); ++ if (unlikely(!args)) { ++ AuErr1("no memory\n"); ++ goto out_args; ++ } ++ ++ atomic_inc(&br->br_count); ++ args->sb = sb; ++ args->br = br; ++ wkq_err = au_wkq_nowait(xino_do_trunc, args, sb); ++ if (!wkq_err) ++ return; /* success */ ++ ++ pr_err("wkq %d\n", wkq_err); ++ atomic_dec(&br->br_count); ++ ++out_args: ++ kfree(args); ++out: ++ atomic_dec(&br->br_xino_running); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int au_xino_do_write(au_writef_t write, struct file *file, ++ ino_t h_ino, ino_t ino) ++{ ++ loff_t pos; ++ ssize_t sz; ++ ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(ino); ++ sz = xino_fwrite(write, file, &ino, sizeof(ino), &pos); ++ if (sz == sizeof(ino)) ++ return 0; /* success */ ++ ++ AuIOErr("write failed (%zd)\n", sz); ++ return -EIO; ++} ++ ++/* ++ * write @ino to the xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * even if @ino is zero, it is written to the xinofile and means no entry. ++ * if the size of the xino file on a specific filesystem exceeds the watermark, ++ * try truncating it. ++ */ ++int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t ino) ++{ ++ int err; ++ unsigned int mnt_flags; ++ struct au_branch *br; ++ ++ BUILD_BUG_ON(sizeof(long long) != sizeof(au_loff_max) ++ || ((loff_t)-1) > 0); ++ SiMustAnyLock(sb); ++ ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO)) ++ return 0; ++ ++ br = au_sbr(sb, bindex); ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (!err) { ++ if (au_opt_test(mnt_flags, TRUNC_XINO) ++ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ xino_try_trunc(sb, br); ++ return 0; /* success */ ++ } ++ ++ AuIOErr("write failed (%d)\n", err); ++ return -EIO; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* aufs inode number bitmap */ ++ ++static const int page_bits = (int)PAGE_SIZE * BITS_PER_BYTE; ++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; ++ loff_t pos; ++ ssize_t sz; ++ struct au_sbinfo *sbinfo; ++ struct file *xib; ++ unsigned long *p; ++ ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ AuDebugOn(pindex > ULONG_MAX / PAGE_SIZE ++ || !au_opt_test(sbinfo->si_mntflags, XINO)); ++ ++ if (pindex == sbinfo->si_xib_last_pindex) ++ return 0; ++ ++ xib = sbinfo->si_xib; ++ p = sbinfo->si_xib_buf; ++ pos = sbinfo->si_xib_last_pindex; ++ pos *= PAGE_SIZE; ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ if (unlikely(sz != PAGE_SIZE)) ++ goto out; ++ ++ pos = pindex; ++ pos *= PAGE_SIZE; ++ if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE) ++ sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); ++ else { ++ memset(p, 0, PAGE_SIZE); ++ sz = xino_fwrite(sbinfo->si_xwrite, xib, p, PAGE_SIZE, &pos); ++ } ++ if (sz == PAGE_SIZE) { ++ sbinfo->si_xib_last_pindex = pindex; ++ return 0; /* success */ ++ } ++ ++out: ++ AuIOErr1("write failed (%zd)\n", sz); ++ err = sz; ++ if (sz >= 0) ++ err = -EIO; ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static void au_xib_clear_bit(struct inode *inode) ++{ ++ int err, bit; ++ unsigned long pindex; ++ struct super_block *sb; ++ struct au_sbinfo *sbinfo; ++ ++ AuDebugOn(inode->i_nlink); ++ ++ sb = inode->i_sb; ++ xib_calc_bit(inode->i_ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ sbinfo = au_sbi(sb); ++ 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); ++} ++ ++/* for s_op->delete_inode() */ ++void au_xino_delete_inode(struct inode *inode, const int unlinked) ++{ ++ int err; ++ unsigned int mnt_flags; ++ aufs_bindex_t bindex, bend, bi; ++ unsigned char try_trunc; ++ struct au_iinfo *iinfo; ++ struct super_block *sb; ++ struct au_hinode *hi; ++ struct inode *h_inode; ++ struct au_branch *br; ++ au_writef_t xwrite; ++ ++ sb = inode->i_sb; ++ mnt_flags = au_mntflags(sb); ++ if (!au_opt_test(mnt_flags, XINO) ++ || inode->i_ino == AUFS_ROOT_INO) ++ return; ++ ++ if (unlinked) { ++ au_xigen_inc(inode); ++ au_xib_clear_bit(inode); ++ } ++ ++ iinfo = au_ii(inode); ++ if (!iinfo) ++ return; ++ ++ bindex = iinfo->ii_bstart; ++ if (bindex < 0) ++ return; ++ ++ xwrite = au_sbi(sb)->si_xwrite; ++ try_trunc = !!au_opt_test(mnt_flags, TRUNC_XINO); ++ hi = iinfo->ii_hinode + bindex; ++ bend = iinfo->ii_bend; ++ for (; bindex <= bend; bindex++, hi++) { ++ h_inode = hi->hi_inode; ++ if (!h_inode ++ || (!unlinked && h_inode->i_nlink)) ++ continue; ++ ++ /* inode may not be revalidated */ ++ bi = au_br_index(sb, hi->hi_id); ++ if (bi < 0) ++ continue; ++ ++ br = au_sbr(sb, bi); ++ err = au_xino_do_write(xwrite, br->br_xino.xi_file, ++ h_inode->i_ino, /*ino*/0); ++ if (!err && try_trunc ++ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ xino_try_trunc(sb, br); ++ } ++} ++ ++/* get an unused inode number from bitmap */ ++ino_t au_xino_new_ino(struct super_block *sb) ++{ ++ ino_t ino; ++ unsigned long *p, pindex, ul, pend; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ int free_bit, err; ++ ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return iunique(sb, AUFS_FIRST_INO); ++ ++ sbinfo = au_sbi(sb); ++ mutex_lock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ free_bit = sbinfo->si_xib_next_bit; ++ if (free_bit < page_bits && !test_bit(free_bit, p)) ++ goto out; /* success */ ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ ++ pindex = sbinfo->si_xib_last_pindex; ++ for (ul = pindex - 1; ul < ULONG_MAX; ul--) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ ++ file = sbinfo->si_xib; ++ pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE; ++ for (ul = pindex + 1; ul <= pend; ul++) { ++ err = xib_pindex(sb, ul); ++ if (unlikely(err)) ++ goto out_err; ++ free_bit = find_first_zero_bit(p, page_bits); ++ if (free_bit < page_bits) ++ goto out; /* success */ ++ } ++ BUG(); ++ ++out: ++ set_bit(free_bit, p); ++ sbinfo->si_xib_next_bit = free_bit + 1; ++ pindex = sbinfo->si_xib_last_pindex; ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ino = xib_calc_ino(pindex, free_bit); ++ AuDbg("i%lu\n", (unsigned long)ino); ++ return ino; ++out_err: ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ AuDbg("i0\n"); ++ return 0; ++} ++ ++/* ++ * read @ino from xinofile for the specified branch{@sb, @bindex} ++ * at the position of @h_ino. ++ * if @ino does not exist and @do_new is true, get new one. ++ */ ++int au_xino_read(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, ++ ino_t *ino) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct file *file; ++ struct au_sbinfo *sbinfo; ++ ++ *ino = 0; ++ if (!au_opt_test(au_mntflags(sb), XINO)) ++ return 0; /* no xino */ ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ pos = h_ino; ++ if (unlikely(au_loff_max / sizeof(*ino) - 1 < pos)) { ++ AuIOErr1("too large hi%lu\n", (unsigned long)h_ino); ++ return -EFBIG; ++ } ++ pos *= sizeof(*ino); ++ ++ file = au_sbr(sb, bindex)->br_xino.xi_file; ++ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*ino)) ++ return 0; /* no ino */ ++ ++ sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); ++ if (sz == sizeof(*ino)) ++ return 0; /* success */ ++ ++ err = sz; ++ if (unlikely(sz >= 0)) { ++ err = -EIO; ++ AuIOErr("xino read error (%zd)\n", sz); ++ } ++ ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* create and set a new xino file */ ++ ++struct file *au_xino_create(struct super_block *sb, char *fname, int silent) ++{ ++ struct file *file; ++ struct dentry *h_parent, *d; ++ struct inode *h_dir; ++ int err; ++ ++ /* ++ * at mount-time, and the xino file is the default path, ++ * hnotify is disabled so we have no notify events to ignore. ++ * when a user specified the xino, we cannot get au_hdir to be ignored. ++ */ ++ file = vfsub_filp_open(fname, O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE ++ /* | FMODE_NONOTIFY */, ++ S_IRUGO | S_IWUGO); ++ if (IS_ERR(file)) { ++ if (!silent) ++ pr_err("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; ++ mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = vfsub_unlink(h_dir, &file->f_path, /*force*/0); ++ mutex_unlock(&h_dir->i_mutex); ++ dput(h_parent); ++ if (unlikely(err)) { ++ if (!silent) ++ pr_err("unlink %s(%d)\n", fname, err); ++ goto out; ++ } ++ ++ err = -EINVAL; ++ d = file->f_dentry; ++ if (unlikely(sb == d->d_sb)) { ++ if (!silent) ++ pr_err("%s must be outside\n", fname); ++ goto out; ++ } ++ if (unlikely(au_test_fs_bad_xino(d->d_sb))) { ++ if (!silent) ++ pr_err("xino doesn't support %s(%s)\n", ++ fname, au_sbtype(d->d_sb)); ++ goto out; ++ } ++ return file; /* success */ ++ ++out: ++ fput(file); ++ file = ERR_PTR(err); ++ return file; ++} ++ ++/* ++ * find another branch who is on the same filesystem of the specified ++ * branch{@btgt}. search until @bend. ++ */ ++static int is_sb_shared(struct super_block *sb, aufs_bindex_t btgt, ++ aufs_bindex_t bend) ++{ ++ aufs_bindex_t bindex; ++ struct super_block *tgt_sb = au_sbr_sb(sb, btgt); ++ ++ for (bindex = 0; bindex < btgt; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ for (bindex++; bindex <= bend; bindex++) ++ if (unlikely(tgt_sb == au_sbr_sb(sb, bindex))) ++ return bindex; ++ return -1; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * initialize the xinofile for the specified branch @br ++ * at the place/path where @base_file indicates. ++ * test whether another branch is on the same filesystem or not, ++ * if @do_test is true. ++ */ ++int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, ++ struct file *base_file, int do_test) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bend, bindex; ++ struct au_branch *shared_br, *b; ++ struct file *file; ++ struct super_block *tgt_sb; ++ ++ shared_br = NULL; ++ bend = au_sbend(sb); ++ if (do_test) { ++ tgt_sb = br->br_mnt->mnt_sb; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ b = au_sbr(sb, bindex); ++ if (tgt_sb == b->br_mnt->mnt_sb) { ++ shared_br = b; ++ break; ++ } ++ } ++ } ++ ++ if (!shared_br || !shared_br->br_xino.xi_file) { ++ struct au_xino_lock_dir ldir; ++ ++ au_xino_lock_dir(sb, base_file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(base_file, NULL); ++ au_xino_unlock_dir(&ldir); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ br->br_xino.xi_file = file; ++ } else { ++ br->br_xino.xi_file = shared_br->br_xino.xi_file; ++ get_file(br->br_xino.xi_file); ++ } ++ ++ ino = AUFS_ROOT_INO; ++ err = au_xino_do_write(au_sbi(sb)->si_xwrite, br->br_xino.xi_file, ++ h_ino, ino); ++ if (unlikely(err)) { ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* trucate a xino bitmap file */ ++ ++/* todo: slow */ ++static int do_xib_restore(struct super_block *sb, struct file *file, void *page) ++{ ++ int err, bit; ++ ssize_t sz; ++ unsigned long pindex; ++ loff_t pos, pend; ++ struct au_sbinfo *sbinfo; ++ au_readf_t func; ++ ino_t *ino; ++ unsigned long *p; ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ MtxMustLock(&sbinfo->si_xib_mtx); ++ p = sbinfo->si_xib_buf; ++ func = sbinfo->si_xread; ++ pend = i_size_read(file->f_dentry->d_inode); ++ 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 (ino = page; sz > 0; ino++, sz -= sizeof(ino)) { ++ if (unlikely(*ino < AUFS_FIRST_INO)) ++ continue; ++ ++ xib_calc_bit(*ino, &pindex, &bit); ++ AuDebugOn(page_bits <= bit); ++ err = xib_pindex(sb, pindex); ++ if (!err) ++ set_bit(bit, p); ++ else ++ goto out; ++ } ++ } ++ ++out: ++ return err; ++} ++ ++static int xib_restore(struct super_block *sb) ++{ ++ int err; ++ aufs_bindex_t bindex, bend; ++ void *page; ++ ++ err = -ENOMEM; ++ page = (void *)__get_free_page(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ ++ err = 0; ++ bend = au_sbend(sb); ++ for (bindex = 0; !err && bindex <= bend; bindex++) ++ if (!bindex || is_sb_shared(sb, bindex, bindex - 1) < 0) ++ err = do_xib_restore ++ (sb, au_sbr(sb, bindex)->br_xino.xi_file, page); ++ else ++ AuDbg("b%d\n", bindex); ++ free_page((unsigned long)page); ++ ++out: ++ return err; ++} ++ ++int au_xib_trunc(struct super_block *sb) ++{ ++ int err; ++ ssize_t sz; ++ loff_t pos; ++ struct au_xino_lock_dir ldir; ++ struct au_sbinfo *sbinfo; ++ unsigned long *p; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ if (!au_opt_test(sbinfo->si_mntflags, XINO)) ++ goto out; ++ ++ file = sbinfo->si_xib; ++ if (i_size_read(file->f_dentry->d_inode) <= PAGE_SIZE) ++ goto out; ++ ++ au_xino_lock_dir(sb, file, &ldir); ++ /* mnt_want_write() is unnecessary here */ ++ file = au_xino_create2(sbinfo->si_xib, NULL); ++ au_xino_unlock_dir(&ldir); ++ 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; ++ } ++ ++ mutex_lock(&sbinfo->si_xib_mtx); ++ /* mnt_want_write() is unnecessary here */ ++ err = xib_restore(sb); ++ mutex_unlock(&sbinfo->si_xib_mtx); ++ ++out: ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * xino mount option handlers ++ */ ++static au_readf_t find_readf(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop) { ++ if (fop->read) ++ return fop->read; ++ if (fop->aio_read) ++ return do_sync_read; ++ } ++ return ERR_PTR(-ENOSYS); ++} ++ ++static au_writef_t find_writef(struct file *h_file) ++{ ++ const struct file_operations *fop = h_file->f_op; ++ ++ if (fop) { ++ if (fop->write) ++ return fop->write; ++ if (fop->aio_write) ++ return do_sync_write; ++ } ++ return ERR_PTR(-ENOSYS); ++} ++ ++/* xino bitmap */ ++static void xino_clear_xib(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ sbinfo->si_xread = NULL; ++ sbinfo->si_xwrite = NULL; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = NULL; ++ free_page((unsigned long)sbinfo->si_xib_buf); ++ sbinfo->si_xib_buf = NULL; ++} ++ ++static int au_xino_set_xib(struct super_block *sb, struct file *base) ++{ ++ int err; ++ loff_t pos; ++ struct au_sbinfo *sbinfo; ++ struct file *file; ++ ++ SiMustWriteLock(sb); ++ ++ sbinfo = au_sbi(sb); ++ file = au_xino_create2(base, sbinfo->si_xib); ++ err = PTR_ERR(file); ++ if (IS_ERR(file)) ++ goto out; ++ if (sbinfo->si_xib) ++ fput(sbinfo->si_xib); ++ sbinfo->si_xib = file; ++ sbinfo->si_xread = find_readf(file); ++ sbinfo->si_xwrite = find_writef(file); ++ ++ err = -ENOMEM; ++ if (!sbinfo->si_xib_buf) ++ sbinfo->si_xib_buf = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!sbinfo->si_xib_buf)) ++ goto out_unset; ++ ++ sbinfo->si_xib_last_pindex = 0; ++ sbinfo->si_xib_next_bit = 0; ++ 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: ++ return err; ++} ++ ++/* xino for each branch */ ++static void xino_clear_br(struct super_block *sb) ++{ ++ aufs_bindex_t bindex, bend; ++ struct au_branch *br; ++ ++ bend = au_sbend(sb); ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (!br || !br->br_xino.xi_file) ++ continue; ++ ++ fput(br->br_xino.xi_file); ++ br->br_xino.xi_file = NULL; ++ } ++} ++ ++static int au_xino_set_br(struct super_block *sb, struct file *base) ++{ ++ int err; ++ ino_t ino; ++ aufs_bindex_t bindex, bend, bshared; ++ struct { ++ struct file *old, *new; ++ } *fpair, *p; ++ struct au_branch *br; ++ struct inode *inode; ++ au_writef_t writef; ++ ++ SiMustWriteLock(sb); ++ ++ err = -ENOMEM; ++ bend = au_sbend(sb); ++ fpair = kcalloc(bend + 1, sizeof(*fpair), GFP_NOFS); ++ if (unlikely(!fpair)) ++ goto out; ++ ++ inode = sb->s_root->d_inode; ++ ino = AUFS_ROOT_INO; ++ writef = au_sbi(sb)->si_xwrite; ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ bshared = is_sb_shared(sb, bindex, bindex - 1); ++ if (bshared >= 0) { ++ /* shared xino */ ++ *p = fpair[bshared]; ++ get_file(p->new); ++ } ++ ++ if (!p->new) { ++ /* new xino */ ++ p->old = br->br_xino.xi_file; ++ p->new = au_xino_create2(base, br->br_xino.xi_file); ++ err = PTR_ERR(p->new); ++ if (IS_ERR(p->new)) { ++ p->new = NULL; ++ goto out_pair; ++ } ++ } ++ ++ err = au_xino_do_write(writef, p->new, ++ au_h_iptr(inode, bindex)->i_ino, ino); ++ if (unlikely(err)) ++ goto out_pair; ++ } ++ ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) { ++ br = au_sbr(sb, bindex); ++ if (br->br_xino.xi_file) ++ fput(br->br_xino.xi_file); ++ get_file(p->new); ++ br->br_xino.xi_file = p->new; ++ } ++ ++out_pair: ++ for (bindex = 0, p = fpair; bindex <= bend; bindex++, p++) ++ if (p->new) ++ fput(p->new); ++ else ++ break; ++ kfree(fpair); ++out: ++ return err; ++} ++ ++void au_xino_clr(struct super_block *sb) ++{ ++ struct au_sbinfo *sbinfo; ++ ++ au_xigen_clr(sb); ++ xino_clear_xib(sb); ++ xino_clear_br(sb); ++ sbinfo = au_sbi(sb); ++ /* lvalue, do not call au_mntflags() */ ++ au_opt_clr(sbinfo->si_mntflags, XINO); ++} ++ ++int au_xino_set(struct super_block *sb, struct au_opt_xino *xino, int remount) ++{ ++ int err, skip; ++ struct dentry *parent, *cur_parent; ++ struct qstr *dname, *cur_name; ++ struct file *cur_xino; ++ struct inode *dir; ++ struct au_sbinfo *sbinfo; ++ ++ SiMustWriteLock(sb); ++ ++ err = 0; ++ sbinfo = au_sbi(sb); ++ parent = dget_parent(xino->file->f_dentry); ++ if (remount) { ++ skip = 0; ++ dname = &xino->file->f_dentry->d_name; ++ cur_xino = sbinfo->si_xib; ++ if (cur_xino) { ++ cur_parent = dget_parent(cur_xino->f_dentry); ++ cur_name = &cur_xino->f_dentry->d_name; ++ skip = (cur_parent == parent ++ && dname->len == cur_name->len ++ && !memcmp(dname->name, cur_name->name, ++ dname->len)); ++ dput(cur_parent); ++ } ++ if (skip) ++ goto out; ++ } ++ ++ au_opt_set(sbinfo->si_mntflags, XINO); ++ dir = parent->d_inode; ++ mutex_lock_nested(&dir->i_mutex, AuLsc_I_PARENT); ++ /* mnt_want_write() is unnecessary here */ ++ err = au_xino_set_xib(sb, xino->file); ++ if (!err) ++ err = au_xigen_set(sb, xino->file); ++ if (!err) ++ err = au_xino_set_br(sb, xino->file); ++ mutex_unlock(&dir->i_mutex); ++ if (!err) ++ goto out; /* success */ ++ ++ /* reset all */ ++ AuIOErr("failed creating xino(%d).\n", err); ++ ++out: ++ dput(parent); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * create a xinofile at the default place/path. ++ */ ++struct file *au_xino_def(struct super_block *sb) ++{ ++ struct file *file; ++ char *page, *p; ++ struct au_branch *br; ++ struct super_block *h_sb; ++ struct path path; ++ aufs_bindex_t bend, bindex, bwr; ++ ++ br = NULL; ++ bend = au_sbend(sb); ++ bwr = -1; ++ for (bindex = 0; bindex <= bend; bindex++) { ++ br = au_sbr(sb, bindex); ++ if (au_br_writable(br->br_perm) ++ && !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) { ++ bwr = bindex; ++ break; ++ } ++ } ++ ++ if (bwr >= 0) { ++ file = ERR_PTR(-ENOMEM); ++ page = __getname_gfp(GFP_NOFS); ++ if (unlikely(!page)) ++ goto out; ++ path.mnt = br->br_mnt; ++ path.dentry = au_h_dptr(sb->s_root, bwr); ++ p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); ++ file = (void *)p; ++ if (!IS_ERR(p)) { ++ strcat(p, "/" AUFS_XINO_FNAME); ++ AuDbg("%s\n", p); ++ file = au_xino_create(sb, p, /*silent*/0); ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, br->br_id); ++ } ++ __putname(page); ++ } else { ++ file = au_xino_create(sb, AUFS_XINO_DEFPATH, /*silent*/0); ++ if (IS_ERR(file)) ++ goto out; ++ h_sb = file->f_dentry->d_sb; ++ if (unlikely(au_test_fs_bad_xino(h_sb))) { ++ pr_err("xino doesn't support %s(%s)\n", ++ AUFS_XINO_DEFPATH, au_sbtype(h_sb)); ++ fput(file); ++ file = ERR_PTR(-EINVAL); ++ } ++ if (!IS_ERR(file)) ++ au_xino_brid_set(sb, -1); ++ } ++ ++out: ++ return file; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++int au_xino_path(struct seq_file *seq, struct file *file) ++{ ++ int err; ++ ++ err = au_seq_path(seq, &file->f_path); ++ if (unlikely(err < 0)) ++ goto out; ++ ++ err = 0; ++#define Deleted "\\040(deleted)" ++ seq->count -= sizeof(Deleted) - 1; ++ AuDebugOn(memcmp(seq->buf + seq->count, Deleted, ++ sizeof(Deleted) - 1)); ++#undef Deleted ++ ++out: ++ return err; ++} +diff -Nur linux-2.6.36.orig/fs/file_table.c linux-2.6.36/fs/file_table.c +--- linux-2.6.36.orig/fs/file_table.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/file_table.c 2011-01-10 19:52:38.000000000 +0100 +@@ -394,6 +394,8 @@ + } + } + ++EXPORT_SYMBOL(file_sb_list_del); ++ + #ifdef CONFIG_SMP + + /* +diff -Nur linux-2.6.36.orig/fs/inode.c linux-2.6.36/fs/inode.c +--- linux-2.6.36.orig/fs/inode.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/inode.c 2011-01-10 19:52:38.000000000 +0100 +@@ -83,6 +83,7 @@ + * the i_state of an inode while it is in use.. + */ + DEFINE_SPINLOCK(inode_lock); ++EXPORT_SYMBOL(inode_lock); + + /* + * iprune_sem provides exclusion between the kswapd or try_to_free_pages +diff -Nur linux-2.6.36.orig/fs/namei.c linux-2.6.36/fs/namei.c +--- linux-2.6.36.orig/fs/namei.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/namei.c 2011-01-10 19:52:38.000000000 +0100 +@@ -347,6 +347,7 @@ + + return 0; + } ++EXPORT_SYMBOL(deny_write_access); + + /** + * path_get - get a reference to a path +@@ -1159,7 +1160,7 @@ + * needs parent already locked. Doesn't follow mounts. + * SMP-safe. + */ +-static struct dentry *lookup_hash(struct nameidata *nd) ++struct dentry *lookup_hash(struct nameidata *nd) + { + int err; + +@@ -1168,8 +1169,9 @@ + return ERR_PTR(err); + return __lookup_hash(&nd->last, nd->path.dentry, nd); + } ++EXPORT_SYMBOL(lookup_hash); + +-static int __lookup_one_len(const char *name, struct qstr *this, ++int __lookup_one_len(const char *name, struct qstr *this, + struct dentry *base, int len) + { + unsigned long hash; +@@ -1190,6 +1192,7 @@ + this->hash = end_name_hash(hash); + return 0; + } ++EXPORT_SYMBOL(__lookup_one_len); + + /** + * lookup_one_len - filesystem helper to lookup single pathname component +diff -Nur linux-2.6.36.orig/fs/namei.c.orig linux-2.6.36/fs/namei.c.orig +--- linux-2.6.36.orig/fs/namei.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/namei.c.orig 2011-01-10 19:52:38.000000000 +0100 +@@ -0,0 +1,2921 @@ ++/* ++ * linux/fs/namei.c ++ * ++ * Copyright (C) 1991, 1992 Linus Torvalds ++ */ ++ ++/* ++ * Some corrections by tytso. ++ */ ++ ++/* [Feb 1997 T. Schoebel-Theuer] Complete rewrite of the pathname ++ * lookup logic. ++ */ ++/* [Feb-Apr 2000, AV] Rewrite to the new namespace architecture. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "internal.h" ++ ++/* [Feb-1997 T. Schoebel-Theuer] ++ * Fundamental changes in the pathname lookup mechanisms (namei) ++ * were necessary because of omirr. The reason is that omirr needs ++ * to know the _real_ pathname, not the user-supplied one, in case ++ * of symlinks (and also when transname replacements occur). ++ * ++ * The new code replaces the old recursive symlink resolution with ++ * an iterative one (in case of non-nested symlink chains). It does ++ * this with calls to _follow_link(). ++ * As a side effect, dir_namei(), _namei() and follow_link() are now ++ * replaced with a single function lookup_dentry() that can handle all ++ * the special cases of the former code. ++ * ++ * With the new dcache, the pathname is stored at each inode, at least as ++ * long as the refcount of the inode is positive. As a side effect, the ++ * size of the dcache depends on the inode cache and thus is dynamic. ++ * ++ * [29-Apr-1998 C. Scott Ananian] Updated above description of symlink ++ * resolution to correspond with current state of the code. ++ * ++ * Note that the symlink resolution is not *completely* iterative. ++ * There is still a significant amount of tail- and mid- recursion in ++ * the algorithm. Also, note that _readlink() is not used in ++ * lookup_dentry(): lookup_dentry() on the result of _readlink() ++ * may return different results than _follow_link(). Many virtual ++ * filesystems (including /proc) exhibit this behavior. ++ */ ++ ++/* [24-Feb-97 T. Schoebel-Theuer] Side effects caused by new implementation: ++ * New symlink semantics: when open() is called with flags O_CREAT | O_EXCL ++ * and the name already exists in form of a symlink, try to create the new ++ * name indicated by the symlink. The old code always complained that the ++ * name already exists, due to not following the symlink even if its target ++ * is nonexistent. The new semantics affects also mknod() and link() when ++ * the name is a symlink pointing to a non-existant name. ++ * ++ * I don't know which semantics is the right one, since I have no access ++ * to standards. But I found by trial that HP-UX 9.0 has the full "new" ++ * semantics implemented, while SunOS 4.1.1 and Solaris (SunOS 5.4) have the ++ * "old" one. Personally, I think the new semantics is much more logical. ++ * Note that "ln old new" where "new" is a symlink pointing to a non-existing ++ * file does succeed in both HP-UX and SunOs, but not in Solaris ++ * and in the old Linux semantics. ++ */ ++ ++/* [16-Dec-97 Kevin Buhr] For security reasons, we change some symlink ++ * semantics. See the comments in "open_namei" and "do_link" below. ++ * ++ * [10-Sep-98 Alan Modra] Another symlink change. ++ */ ++ ++/* [Feb-Apr 2000 AV] Complete rewrite. Rules for symlinks: ++ * inside the path - always follow. ++ * in the last component in creation/removal/renaming - never follow. ++ * if LOOKUP_FOLLOW passed - follow. ++ * if the pathname has trailing slashes - follow. ++ * otherwise - don't follow. ++ * (applied in that order). ++ * ++ * [Jun 2000 AV] Inconsistent behaviour of open() in case if flags==O_CREAT ++ * restored for 2.4. This is the last surviving part of old 4.2BSD bug. ++ * During the 2.4 we need to fix the userland stuff depending on it - ++ * hopefully we will be able to get rid of that wart in 2.5. So far only ++ * XEmacs seems to be relying on it... ++ */ ++/* ++ * [Sep 2001 AV] Single-semaphore locking scheme (kudos to David Holland) ++ * implemented. Let's see if raised priority of ->s_vfs_rename_mutex gives ++ * any extra contention... ++ */ ++ ++/* In order to reduce some races, while at the same time doing additional ++ * checking and hopefully speeding things up, we copy filenames to the ++ * kernel data space before using them.. ++ * ++ * POSIX.1 2.4: an empty pathname is invalid (ENOENT). ++ * PATH_MAX includes the nul terminator --RR. ++ */ ++static int do_getname(const char __user *filename, char *page) ++{ ++ int retval; ++ unsigned long len = PATH_MAX; ++ ++ if (!segment_eq(get_fs(), KERNEL_DS)) { ++ if ((unsigned long) filename >= TASK_SIZE) ++ return -EFAULT; ++ if (TASK_SIZE - (unsigned long) filename < PATH_MAX) ++ len = TASK_SIZE - (unsigned long) filename; ++ } ++ ++ retval = strncpy_from_user(page, filename, len); ++ if (retval > 0) { ++ if (retval < len) ++ return 0; ++ return -ENAMETOOLONG; ++ } else if (!retval) ++ retval = -ENOENT; ++ return retval; ++} ++ ++char * getname(const char __user * filename) ++{ ++ char *tmp, *result; ++ ++ result = ERR_PTR(-ENOMEM); ++ tmp = __getname(); ++ if (tmp) { ++ int retval = do_getname(filename, tmp); ++ ++ result = tmp; ++ if (retval < 0) { ++ __putname(tmp); ++ result = ERR_PTR(retval); ++ } ++ } ++ audit_getname(result); ++ return result; ++} ++ ++#ifdef CONFIG_AUDITSYSCALL ++void putname(const char *name) ++{ ++ if (unlikely(!audit_dummy_context())) ++ audit_putname(name); ++ else ++ __putname(name); ++} ++EXPORT_SYMBOL(putname); ++#endif ++ ++/* ++ * This does basic POSIX ACL permission checking ++ */ ++static int acl_permission_check(struct inode *inode, int mask, ++ int (*check_acl)(struct inode *inode, int mask)) ++{ ++ umode_t mode = inode->i_mode; ++ ++ mask &= MAY_READ | MAY_WRITE | MAY_EXEC; ++ ++ if (current_fsuid() == inode->i_uid) ++ mode >>= 6; ++ else { ++ if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { ++ int error = check_acl(inode, mask); ++ if (error != -EAGAIN) ++ return error; ++ } ++ ++ if (in_group_p(inode->i_gid)) ++ mode >>= 3; ++ } ++ ++ /* ++ * If the DACs are ok we don't need any capability check. ++ */ ++ if ((mask & ~mode) == 0) ++ return 0; ++ return -EACCES; ++} ++ ++/** ++ * generic_permission - check for access rights on a Posix-like filesystem ++ * @inode: inode to check access rights for ++ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) ++ * @check_acl: optional callback to check for Posix ACLs ++ * ++ * Used to check for read/write/execute permissions on a file. ++ * We use "fsuid" for this, letting us set arbitrary permissions ++ * for filesystem access without changing the "normal" uids which ++ * are used for other things.. ++ */ ++int generic_permission(struct inode *inode, int mask, ++ int (*check_acl)(struct inode *inode, int mask)) ++{ ++ int ret; ++ ++ /* ++ * Do the basic POSIX ACL permission checks. ++ */ ++ ret = acl_permission_check(inode, mask, check_acl); ++ if (ret != -EACCES) ++ return ret; ++ ++ /* ++ * Read/write DACs are always overridable. ++ * Executable DACs are overridable if at least one exec bit is set. ++ */ ++ if (!(mask & MAY_EXEC) || execute_ok(inode)) ++ if (capable(CAP_DAC_OVERRIDE)) ++ return 0; ++ ++ /* ++ * Searching includes executable on directories, else just read. ++ */ ++ mask &= MAY_READ | MAY_WRITE | MAY_EXEC; ++ if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE))) ++ if (capable(CAP_DAC_READ_SEARCH)) ++ return 0; ++ ++ return -EACCES; ++} ++ ++/** ++ * inode_permission - check for access rights to a given inode ++ * @inode: inode to check permission on ++ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) ++ * ++ * Used to check for read/write/execute permissions on an inode. ++ * We use "fsuid" for this, letting us set arbitrary permissions ++ * for filesystem access without changing the "normal" uids which ++ * are used for other things. ++ */ ++int inode_permission(struct inode *inode, int mask) ++{ ++ int retval; ++ ++ if (mask & MAY_WRITE) { ++ umode_t mode = inode->i_mode; ++ ++ /* ++ * Nobody gets write access to a read-only fs. ++ */ ++ if (IS_RDONLY(inode) && ++ (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) ++ return -EROFS; ++ ++ /* ++ * Nobody gets write access to an immutable file. ++ */ ++ if (IS_IMMUTABLE(inode)) ++ return -EACCES; ++ } ++ ++ if (inode->i_op->permission) ++ retval = inode->i_op->permission(inode, mask); ++ else ++ retval = generic_permission(inode, mask, inode->i_op->check_acl); ++ ++ if (retval) ++ return retval; ++ ++ retval = devcgroup_inode_permission(inode, mask); ++ if (retval) ++ return retval; ++ ++ return security_inode_permission(inode, mask); ++} ++ ++/** ++ * file_permission - check for additional access rights to a given file ++ * @file: file to check access rights for ++ * @mask: right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC) ++ * ++ * Used to check for read/write/execute permissions on an already opened ++ * file. ++ * ++ * Note: ++ * Do not use this function in new code. All access checks should ++ * be done using inode_permission(). ++ */ ++int file_permission(struct file *file, int mask) ++{ ++ return inode_permission(file->f_path.dentry->d_inode, mask); ++} ++ ++/* ++ * get_write_access() gets write permission for a file. ++ * put_write_access() releases this write permission. ++ * This is used for regular files. ++ * We cannot support write (and maybe mmap read-write shared) accesses and ++ * MAP_DENYWRITE mmappings simultaneously. The i_writecount field of an inode ++ * can have the following values: ++ * 0: no writers, no VM_DENYWRITE mappings ++ * < 0: (-i_writecount) vm_area_structs with VM_DENYWRITE set exist ++ * > 0: (i_writecount) users are writing to the file. ++ * ++ * Normally we operate on that counter with atomic_{inc,dec} and it's safe ++ * except for the cases where we don't hold i_writecount yet. Then we need to ++ * use {get,deny}_write_access() - these functions check the sign and refuse ++ * to do the change if sign is wrong. Exclusion between them is provided by ++ * the inode->i_lock spinlock. ++ */ ++ ++int get_write_access(struct inode * inode) ++{ ++ spin_lock(&inode->i_lock); ++ if (atomic_read(&inode->i_writecount) < 0) { ++ spin_unlock(&inode->i_lock); ++ return -ETXTBSY; ++ } ++ atomic_inc(&inode->i_writecount); ++ spin_unlock(&inode->i_lock); ++ ++ return 0; ++} ++ ++int deny_write_access(struct file * file) ++{ ++ struct inode *inode = file->f_path.dentry->d_inode; ++ ++ spin_lock(&inode->i_lock); ++ if (atomic_read(&inode->i_writecount) > 0) { ++ spin_unlock(&inode->i_lock); ++ return -ETXTBSY; ++ } ++ atomic_dec(&inode->i_writecount); ++ spin_unlock(&inode->i_lock); ++ ++ return 0; ++} ++EXPORT_SYMBOL(deny_write_access); ++ ++/** ++ * path_get - get a reference to a path ++ * @path: path to get the reference to ++ * ++ * Given a path increment the reference count to the dentry and the vfsmount. ++ */ ++void path_get(struct path *path) ++{ ++ mntget(path->mnt); ++ dget(path->dentry); ++} ++EXPORT_SYMBOL(path_get); ++ ++/** ++ * path_put - put a reference to a path ++ * @path: path to put the reference to ++ * ++ * Given a path decrement the reference count to the dentry and the vfsmount. ++ */ ++void path_put(struct path *path) ++{ ++ dput(path->dentry); ++ mntput(path->mnt); ++} ++EXPORT_SYMBOL(path_put); ++ ++/** ++ * release_open_intent - free up open intent resources ++ * @nd: pointer to nameidata ++ */ ++void release_open_intent(struct nameidata *nd) ++{ ++ if (nd->intent.open.file->f_path.dentry == NULL) ++ put_filp(nd->intent.open.file); ++ else ++ fput(nd->intent.open.file); ++} ++ ++static inline struct dentry * ++do_revalidate(struct dentry *dentry, struct nameidata *nd) ++{ ++ int status = dentry->d_op->d_revalidate(dentry, nd); ++ if (unlikely(status <= 0)) { ++ /* ++ * The dentry failed validation. ++ * If d_revalidate returned 0 attempt to invalidate ++ * the dentry otherwise d_revalidate is asking us ++ * to return a fail status. ++ */ ++ if (!status) { ++ if (!d_invalidate(dentry)) { ++ dput(dentry); ++ dentry = NULL; ++ } ++ } else { ++ dput(dentry); ++ dentry = ERR_PTR(status); ++ } ++ } ++ return dentry; ++} ++ ++/* ++ * force_reval_path - force revalidation of a dentry ++ * ++ * In some situations the path walking code will trust dentries without ++ * revalidating them. This causes problems for filesystems that depend on ++ * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set ++ * (which indicates that it's possible for the dentry to go stale), force ++ * a d_revalidate call before proceeding. ++ * ++ * Returns 0 if the revalidation was successful. If the revalidation fails, ++ * either return the error returned by d_revalidate or -ESTALE if the ++ * revalidation it just returned 0. If d_revalidate returns 0, we attempt to ++ * invalidate the dentry. It's up to the caller to handle putting references ++ * to the path if necessary. ++ */ ++static int ++force_reval_path(struct path *path, struct nameidata *nd) ++{ ++ int status; ++ struct dentry *dentry = path->dentry; ++ ++ /* ++ * only check on filesystems where it's possible for the dentry to ++ * become stale. It's assumed that if this flag is set then the ++ * d_revalidate op will also be defined. ++ */ ++ if (!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) ++ return 0; ++ ++ status = dentry->d_op->d_revalidate(dentry, nd); ++ if (status > 0) ++ return 0; ++ ++ if (!status) { ++ d_invalidate(dentry); ++ status = -ESTALE; ++ } ++ return status; ++} ++ ++/* ++ * Short-cut version of permission(), for calling on directories ++ * during pathname resolution. Combines parts of permission() ++ * and generic_permission(), and tests ONLY for MAY_EXEC permission. ++ * ++ * If appropriate, check DAC only. If not appropriate, or ++ * short-cut DAC fails, then call ->permission() to do more ++ * complete permission check. ++ */ ++static int exec_permission(struct inode *inode) ++{ ++ int ret; ++ ++ if (inode->i_op->permission) { ++ ret = inode->i_op->permission(inode, MAY_EXEC); ++ if (!ret) ++ goto ok; ++ return ret; ++ } ++ ret = acl_permission_check(inode, MAY_EXEC, inode->i_op->check_acl); ++ if (!ret) ++ goto ok; ++ ++ if (capable(CAP_DAC_OVERRIDE) || capable(CAP_DAC_READ_SEARCH)) ++ goto ok; ++ ++ return ret; ++ok: ++ return security_inode_permission(inode, MAY_EXEC); ++} ++ ++static __always_inline void set_root(struct nameidata *nd) ++{ ++ if (!nd->root.mnt) ++ get_fs_root(current->fs, &nd->root); ++} ++ ++static int link_path_walk(const char *, struct nameidata *); ++ ++static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) ++{ ++ if (IS_ERR(link)) ++ goto fail; ++ ++ if (*link == '/') { ++ set_root(nd); ++ path_put(&nd->path); ++ nd->path = nd->root; ++ path_get(&nd->root); ++ } ++ ++ return link_path_walk(link, nd); ++fail: ++ path_put(&nd->path); ++ return PTR_ERR(link); ++} ++ ++static void path_put_conditional(struct path *path, struct nameidata *nd) ++{ ++ dput(path->dentry); ++ if (path->mnt != nd->path.mnt) ++ mntput(path->mnt); ++} ++ ++static inline void path_to_nameidata(struct path *path, struct nameidata *nd) ++{ ++ dput(nd->path.dentry); ++ if (nd->path.mnt != path->mnt) { ++ mntput(nd->path.mnt); ++ nd->path.mnt = path->mnt; ++ } ++ nd->path.dentry = path->dentry; ++} ++ ++static __always_inline int ++__do_follow_link(struct path *path, struct nameidata *nd, void **p) ++{ ++ int error; ++ struct dentry *dentry = path->dentry; ++ ++ touch_atime(path->mnt, dentry); ++ nd_set_link(nd, NULL); ++ ++ if (path->mnt != nd->path.mnt) { ++ path_to_nameidata(path, nd); ++ dget(dentry); ++ } ++ mntget(path->mnt); ++ nd->last_type = LAST_BIND; ++ *p = dentry->d_inode->i_op->follow_link(dentry, nd); ++ error = PTR_ERR(*p); ++ if (!IS_ERR(*p)) { ++ char *s = nd_get_link(nd); ++ error = 0; ++ if (s) ++ error = __vfs_follow_link(nd, s); ++ else if (nd->last_type == LAST_BIND) { ++ error = force_reval_path(&nd->path, nd); ++ if (error) ++ path_put(&nd->path); ++ } ++ } ++ return error; ++} ++ ++/* ++ * This limits recursive symlink follows to 8, while ++ * limiting consecutive symlinks to 40. ++ * ++ * Without that kind of total limit, nasty chains of consecutive ++ * symlinks can cause almost arbitrarily long lookups. ++ */ ++static inline int do_follow_link(struct path *path, struct nameidata *nd) ++{ ++ void *cookie; ++ int err = -ELOOP; ++ if (current->link_count >= MAX_NESTED_LINKS) ++ goto loop; ++ if (current->total_link_count >= 40) ++ goto loop; ++ BUG_ON(nd->depth >= MAX_NESTED_LINKS); ++ cond_resched(); ++ err = security_inode_follow_link(path->dentry, nd); ++ if (err) ++ goto loop; ++ current->link_count++; ++ current->total_link_count++; ++ nd->depth++; ++ err = __do_follow_link(path, nd, &cookie); ++ if (!IS_ERR(cookie) && path->dentry->d_inode->i_op->put_link) ++ path->dentry->d_inode->i_op->put_link(path->dentry, nd, cookie); ++ path_put(path); ++ current->link_count--; ++ nd->depth--; ++ return err; ++loop: ++ path_put_conditional(path, nd); ++ path_put(&nd->path); ++ return err; ++} ++ ++int follow_up(struct path *path) ++{ ++ struct vfsmount *parent; ++ struct dentry *mountpoint; ++ ++ br_read_lock(vfsmount_lock); ++ parent = path->mnt->mnt_parent; ++ if (parent == path->mnt) { ++ br_read_unlock(vfsmount_lock); ++ return 0; ++ } ++ mntget(parent); ++ mountpoint = dget(path->mnt->mnt_mountpoint); ++ br_read_unlock(vfsmount_lock); ++ dput(path->dentry); ++ path->dentry = mountpoint; ++ mntput(path->mnt); ++ path->mnt = parent; ++ return 1; ++} ++ ++/* no need for dcache_lock, as serialization is taken care in ++ * namespace.c ++ */ ++static int __follow_mount(struct path *path) ++{ ++ int res = 0; ++ while (d_mountpoint(path->dentry)) { ++ struct vfsmount *mounted = lookup_mnt(path); ++ if (!mounted) ++ break; ++ dput(path->dentry); ++ if (res) ++ mntput(path->mnt); ++ path->mnt = mounted; ++ path->dentry = dget(mounted->mnt_root); ++ res = 1; ++ } ++ return res; ++} ++ ++static void follow_mount(struct path *path) ++{ ++ while (d_mountpoint(path->dentry)) { ++ struct vfsmount *mounted = lookup_mnt(path); ++ if (!mounted) ++ break; ++ dput(path->dentry); ++ mntput(path->mnt); ++ path->mnt = mounted; ++ path->dentry = dget(mounted->mnt_root); ++ } ++} ++ ++/* no need for dcache_lock, as serialization is taken care in ++ * namespace.c ++ */ ++int follow_down(struct path *path) ++{ ++ struct vfsmount *mounted; ++ ++ mounted = lookup_mnt(path); ++ if (mounted) { ++ dput(path->dentry); ++ mntput(path->mnt); ++ path->mnt = mounted; ++ path->dentry = dget(mounted->mnt_root); ++ return 1; ++ } ++ return 0; ++} ++ ++static __always_inline void follow_dotdot(struct nameidata *nd) ++{ ++ set_root(nd); ++ ++ while(1) { ++ struct dentry *old = nd->path.dentry; ++ ++ if (nd->path.dentry == nd->root.dentry && ++ nd->path.mnt == nd->root.mnt) { ++ break; ++ } ++ if (nd->path.dentry != nd->path.mnt->mnt_root) { ++ /* rare case of legitimate dget_parent()... */ ++ nd->path.dentry = dget_parent(nd->path.dentry); ++ dput(old); ++ break; ++ } ++ if (!follow_up(&nd->path)) ++ break; ++ } ++ follow_mount(&nd->path); ++} ++ ++/* ++ * Allocate a dentry with name and parent, and perform a parent ++ * directory ->lookup on it. Returns the new dentry, or ERR_PTR ++ * on error. parent->d_inode->i_mutex must be held. d_lookup must ++ * have verified that no child exists while under i_mutex. ++ */ ++static struct dentry *d_alloc_and_lookup(struct dentry *parent, ++ struct qstr *name, struct nameidata *nd) ++{ ++ struct inode *inode = parent->d_inode; ++ struct dentry *dentry; ++ struct dentry *old; ++ ++ /* Don't create child dentry for a dead directory. */ ++ if (unlikely(IS_DEADDIR(inode))) ++ return ERR_PTR(-ENOENT); ++ ++ dentry = d_alloc(parent, name); ++ if (unlikely(!dentry)) ++ return ERR_PTR(-ENOMEM); ++ ++ old = inode->i_op->lookup(inode, dentry, nd); ++ if (unlikely(old)) { ++ dput(dentry); ++ dentry = old; ++ } ++ return dentry; ++} ++ ++/* ++ * It's more convoluted than I'd like it to be, but... it's still fairly ++ * small and for now I'd prefer to have fast path as straight as possible. ++ * It _is_ time-critical. ++ */ ++static int do_lookup(struct nameidata *nd, struct qstr *name, ++ struct path *path) ++{ ++ struct vfsmount *mnt = nd->path.mnt; ++ struct dentry *dentry, *parent; ++ struct inode *dir; ++ /* ++ * See if the low-level filesystem might want ++ * to use its own hash.. ++ */ ++ if (nd->path.dentry->d_op && nd->path.dentry->d_op->d_hash) { ++ int err = nd->path.dentry->d_op->d_hash(nd->path.dentry, name); ++ if (err < 0) ++ return err; ++ } ++ ++ /* ++ * Rename seqlock is not required here because in the off chance ++ * of a false negative due to a concurrent rename, we're going to ++ * do the non-racy lookup, below. ++ */ ++ dentry = __d_lookup(nd->path.dentry, name); ++ if (!dentry) ++ goto need_lookup; ++found: ++ if (dentry->d_op && dentry->d_op->d_revalidate) ++ goto need_revalidate; ++done: ++ path->mnt = mnt; ++ path->dentry = dentry; ++ __follow_mount(path); ++ return 0; ++ ++need_lookup: ++ parent = nd->path.dentry; ++ dir = parent->d_inode; ++ ++ mutex_lock(&dir->i_mutex); ++ /* ++ * First re-do the cached lookup just in case it was created ++ * while we waited for the directory semaphore, or the first ++ * lookup failed due to an unrelated rename. ++ * ++ * This could use version numbering or similar to avoid unnecessary ++ * cache lookups, but then we'd have to do the first lookup in the ++ * non-racy way. However in the common case here, everything should ++ * be hot in cache, so would it be a big win? ++ */ ++ dentry = d_lookup(parent, name); ++ if (likely(!dentry)) { ++ dentry = d_alloc_and_lookup(parent, name, nd); ++ mutex_unlock(&dir->i_mutex); ++ if (IS_ERR(dentry)) ++ goto fail; ++ goto done; ++ } ++ /* ++ * Uhhuh! Nasty case: the cache was re-populated while ++ * we waited on the semaphore. Need to revalidate. ++ */ ++ mutex_unlock(&dir->i_mutex); ++ goto found; ++ ++need_revalidate: ++ dentry = do_revalidate(dentry, nd); ++ if (!dentry) ++ goto need_lookup; ++ if (IS_ERR(dentry)) ++ goto fail; ++ goto done; ++ ++fail: ++ return PTR_ERR(dentry); ++} ++ ++/* ++ * This is a temporary kludge to deal with "automount" symlinks; proper ++ * solution is to trigger them on follow_mount(), so that do_lookup() ++ * would DTRT. To be killed before 2.6.34-final. ++ */ ++static inline int follow_on_final(struct inode *inode, unsigned lookup_flags) ++{ ++ return inode && unlikely(inode->i_op->follow_link) && ++ ((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode)); ++} ++ ++/* ++ * Name resolution. ++ * This is the basic name resolution function, turning a pathname into ++ * the final dentry. We expect 'base' to be positive and a directory. ++ * ++ * Returns 0 and nd will have valid dentry and mnt on success. ++ * Returns error and drops reference to input namei data on failure. ++ */ ++static int link_path_walk(const char *name, struct nameidata *nd) ++{ ++ struct path next; ++ struct inode *inode; ++ int err; ++ unsigned int lookup_flags = nd->flags; ++ ++ while (*name=='/') ++ name++; ++ if (!*name) ++ goto return_reval; ++ ++ inode = nd->path.dentry->d_inode; ++ if (nd->depth) ++ lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE); ++ ++ /* At this point we know we have a real path component. */ ++ for(;;) { ++ unsigned long hash; ++ struct qstr this; ++ unsigned int c; ++ ++ nd->flags |= LOOKUP_CONTINUE; ++ err = exec_permission(inode); ++ if (err) ++ break; ++ ++ this.name = name; ++ c = *(const unsigned char *)name; ++ ++ hash = init_name_hash(); ++ do { ++ name++; ++ hash = partial_name_hash(c, hash); ++ c = *(const unsigned char *)name; ++ } while (c && (c != '/')); ++ this.len = name - (const char *) this.name; ++ this.hash = end_name_hash(hash); ++ ++ /* remove trailing slashes? */ ++ if (!c) ++ goto last_component; ++ while (*++name == '/'); ++ if (!*name) ++ goto last_with_slashes; ++ ++ /* ++ * "." and ".." are special - ".." especially so because it has ++ * to be able to know about the current root directory and ++ * parent relationships. ++ */ ++ if (this.name[0] == '.') switch (this.len) { ++ default: ++ break; ++ case 2: ++ if (this.name[1] != '.') ++ break; ++ follow_dotdot(nd); ++ inode = nd->path.dentry->d_inode; ++ /* fallthrough */ ++ case 1: ++ continue; ++ } ++ /* This does the actual lookups.. */ ++ err = do_lookup(nd, &this, &next); ++ if (err) ++ break; ++ ++ err = -ENOENT; ++ inode = next.dentry->d_inode; ++ if (!inode) ++ goto out_dput; ++ ++ if (inode->i_op->follow_link) { ++ err = do_follow_link(&next, nd); ++ if (err) ++ goto return_err; ++ err = -ENOENT; ++ inode = nd->path.dentry->d_inode; ++ if (!inode) ++ break; ++ } else ++ path_to_nameidata(&next, nd); ++ err = -ENOTDIR; ++ if (!inode->i_op->lookup) ++ break; ++ continue; ++ /* here ends the main loop */ ++ ++last_with_slashes: ++ lookup_flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY; ++last_component: ++ /* Clear LOOKUP_CONTINUE iff it was previously unset */ ++ nd->flags &= lookup_flags | ~LOOKUP_CONTINUE; ++ if (lookup_flags & LOOKUP_PARENT) ++ goto lookup_parent; ++ if (this.name[0] == '.') switch (this.len) { ++ default: ++ break; ++ case 2: ++ if (this.name[1] != '.') ++ break; ++ follow_dotdot(nd); ++ inode = nd->path.dentry->d_inode; ++ /* fallthrough */ ++ case 1: ++ goto return_reval; ++ } ++ err = do_lookup(nd, &this, &next); ++ if (err) ++ break; ++ inode = next.dentry->d_inode; ++ if (follow_on_final(inode, lookup_flags)) { ++ err = do_follow_link(&next, nd); ++ if (err) ++ goto return_err; ++ inode = nd->path.dentry->d_inode; ++ } else ++ path_to_nameidata(&next, nd); ++ err = -ENOENT; ++ if (!inode) ++ break; ++ if (lookup_flags & LOOKUP_DIRECTORY) { ++ err = -ENOTDIR; ++ if (!inode->i_op->lookup) ++ break; ++ } ++ goto return_base; ++lookup_parent: ++ nd->last = this; ++ nd->last_type = LAST_NORM; ++ if (this.name[0] != '.') ++ goto return_base; ++ if (this.len == 1) ++ nd->last_type = LAST_DOT; ++ else if (this.len == 2 && this.name[1] == '.') ++ nd->last_type = LAST_DOTDOT; ++ else ++ goto return_base; ++return_reval: ++ /* ++ * We bypassed the ordinary revalidation routines. ++ * We may need to check the cached dentry for staleness. ++ */ ++ if (nd->path.dentry && nd->path.dentry->d_sb && ++ (nd->path.dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)) { ++ err = -ESTALE; ++ /* Note: we do not d_invalidate() */ ++ if (!nd->path.dentry->d_op->d_revalidate( ++ nd->path.dentry, nd)) ++ break; ++ } ++return_base: ++ return 0; ++out_dput: ++ path_put_conditional(&next, nd); ++ break; ++ } ++ path_put(&nd->path); ++return_err: ++ return err; ++} ++ ++static int path_walk(const char *name, struct nameidata *nd) ++{ ++ struct path save = nd->path; ++ int result; ++ ++ current->total_link_count = 0; ++ ++ /* make sure the stuff we saved doesn't go away */ ++ path_get(&save); ++ ++ result = link_path_walk(name, nd); ++ if (result == -ESTALE) { ++ /* nd->path had been dropped */ ++ current->total_link_count = 0; ++ nd->path = save; ++ path_get(&nd->path); ++ nd->flags |= LOOKUP_REVAL; ++ result = link_path_walk(name, nd); ++ } ++ ++ path_put(&save); ++ ++ return result; ++} ++ ++static int path_init(int dfd, const char *name, unsigned int flags, struct nameidata *nd) ++{ ++ int retval = 0; ++ int fput_needed; ++ struct file *file; ++ ++ nd->last_type = LAST_ROOT; /* if there are only slashes... */ ++ nd->flags = flags; ++ nd->depth = 0; ++ nd->root.mnt = NULL; ++ ++ if (*name=='/') { ++ set_root(nd); ++ nd->path = nd->root; ++ path_get(&nd->root); ++ } else if (dfd == AT_FDCWD) { ++ get_fs_pwd(current->fs, &nd->path); ++ } else { ++ struct dentry *dentry; ++ ++ file = fget_light(dfd, &fput_needed); ++ retval = -EBADF; ++ if (!file) ++ goto out_fail; ++ ++ dentry = file->f_path.dentry; ++ ++ retval = -ENOTDIR; ++ if (!S_ISDIR(dentry->d_inode->i_mode)) ++ goto fput_fail; ++ ++ retval = file_permission(file, MAY_EXEC); ++ if (retval) ++ goto fput_fail; ++ ++ nd->path = file->f_path; ++ path_get(&file->f_path); ++ ++ fput_light(file, fput_needed); ++ } ++ return 0; ++ ++fput_fail: ++ fput_light(file, fput_needed); ++out_fail: ++ return retval; ++} ++ ++/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */ ++static int do_path_lookup(int dfd, const char *name, ++ unsigned int flags, struct nameidata *nd) ++{ ++ int retval = path_init(dfd, name, flags, nd); ++ if (!retval) ++ retval = path_walk(name, nd); ++ if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && ++ nd->path.dentry->d_inode)) ++ audit_inode(name, nd->path.dentry); ++ if (nd->root.mnt) { ++ path_put(&nd->root); ++ nd->root.mnt = NULL; ++ } ++ return retval; ++} ++ ++int path_lookup(const char *name, unsigned int flags, ++ struct nameidata *nd) ++{ ++ return do_path_lookup(AT_FDCWD, name, flags, nd); ++} ++ ++int kern_path(const char *name, unsigned int flags, struct path *path) ++{ ++ struct nameidata nd; ++ int res = do_path_lookup(AT_FDCWD, name, flags, &nd); ++ if (!res) ++ *path = nd.path; ++ return res; ++} ++ ++/** ++ * vfs_path_lookup - lookup a file path relative to a dentry-vfsmount pair ++ * @dentry: pointer to dentry of the base directory ++ * @mnt: pointer to vfs mount of the base directory ++ * @name: pointer to file name ++ * @flags: lookup flags ++ * @nd: pointer to nameidata ++ */ ++int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, ++ const char *name, unsigned int flags, ++ struct nameidata *nd) ++{ ++ int retval; ++ ++ /* same as do_path_lookup */ ++ nd->last_type = LAST_ROOT; ++ nd->flags = flags; ++ nd->depth = 0; ++ ++ nd->path.dentry = dentry; ++ nd->path.mnt = mnt; ++ path_get(&nd->path); ++ nd->root = nd->path; ++ path_get(&nd->root); ++ ++ retval = path_walk(name, nd); ++ if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && ++ nd->path.dentry->d_inode)) ++ audit_inode(name, nd->path.dentry); ++ ++ path_put(&nd->root); ++ nd->root.mnt = NULL; ++ ++ return retval; ++} ++ ++static struct dentry *__lookup_hash(struct qstr *name, ++ struct dentry *base, struct nameidata *nd) ++{ ++ struct dentry *dentry; ++ struct inode *inode; ++ int err; ++ ++ inode = base->d_inode; ++ ++ /* ++ * See if the low-level filesystem might want ++ * to use its own hash.. ++ */ ++ if (base->d_op && base->d_op->d_hash) { ++ err = base->d_op->d_hash(base, name); ++ dentry = ERR_PTR(err); ++ if (err < 0) ++ goto out; ++ } ++ ++ /* ++ * Don't bother with __d_lookup: callers are for creat as ++ * well as unlink, so a lot of the time it would cost ++ * a double lookup. ++ */ ++ dentry = d_lookup(base, name); ++ ++ if (dentry && dentry->d_op && dentry->d_op->d_revalidate) ++ dentry = do_revalidate(dentry, nd); ++ ++ if (!dentry) ++ dentry = d_alloc_and_lookup(base, name, nd); ++out: ++ return dentry; ++} ++ ++/* ++ * Restricted form of lookup. Doesn't follow links, single-component only, ++ * needs parent already locked. Doesn't follow mounts. ++ * SMP-safe. ++ */ ++static struct dentry *lookup_hash(struct nameidata *nd) ++{ ++ int err; ++ ++ err = exec_permission(nd->path.dentry->d_inode); ++ if (err) ++ return ERR_PTR(err); ++ return __lookup_hash(&nd->last, nd->path.dentry, nd); ++} ++EXPORT_SYMBOL(lookup_hash); ++ ++static int __lookup_one_len(const char *name, struct qstr *this, ++ struct dentry *base, int len) ++{ ++ unsigned long hash; ++ unsigned int c; ++ ++ this->name = name; ++ this->len = len; ++ if (!len) ++ return -EACCES; ++ ++ hash = init_name_hash(); ++ while (len--) { ++ c = *(const unsigned char *)name++; ++ if (c == '/' || c == '\0') ++ return -EACCES; ++ hash = partial_name_hash(c, hash); ++ } ++ this->hash = end_name_hash(hash); ++ return 0; ++} ++EXPORT_SYMBOL(__lookup_one_len); ++ ++/** ++ * lookup_one_len - filesystem helper to lookup single pathname component ++ * @name: pathname component to lookup ++ * @base: base directory to lookup from ++ * @len: maximum length @len should be interpreted to ++ * ++ * Note that this routine is purely a helper for filesystem usage and should ++ * not be called by generic code. Also note that by using this function the ++ * nameidata argument is passed to the filesystem methods and a filesystem ++ * using this helper needs to be prepared for that. ++ */ ++struct dentry *lookup_one_len(const char *name, struct dentry *base, int len) ++{ ++ int err; ++ struct qstr this; ++ ++ WARN_ON_ONCE(!mutex_is_locked(&base->d_inode->i_mutex)); ++ ++ err = __lookup_one_len(name, &this, base, len); ++ if (err) ++ return ERR_PTR(err); ++ ++ err = exec_permission(base->d_inode); ++ if (err) ++ return ERR_PTR(err); ++ return __lookup_hash(&this, base, NULL); ++} ++ ++int user_path_at(int dfd, const char __user *name, unsigned flags, ++ struct path *path) ++{ ++ struct nameidata nd; ++ char *tmp = getname(name); ++ int err = PTR_ERR(tmp); ++ if (!IS_ERR(tmp)) { ++ ++ BUG_ON(flags & LOOKUP_PARENT); ++ ++ err = do_path_lookup(dfd, tmp, flags, &nd); ++ putname(tmp); ++ if (!err) ++ *path = nd.path; ++ } ++ return err; ++} ++ ++static int user_path_parent(int dfd, const char __user *path, ++ struct nameidata *nd, char **name) ++{ ++ char *s = getname(path); ++ int error; ++ ++ if (IS_ERR(s)) ++ return PTR_ERR(s); ++ ++ error = do_path_lookup(dfd, s, LOOKUP_PARENT, nd); ++ if (error) ++ putname(s); ++ else ++ *name = s; ++ ++ return error; ++} ++ ++/* ++ * It's inline, so penalty for filesystems that don't use sticky bit is ++ * minimal. ++ */ ++static inline int check_sticky(struct inode *dir, struct inode *inode) ++{ ++ uid_t fsuid = current_fsuid(); ++ ++ if (!(dir->i_mode & S_ISVTX)) ++ return 0; ++ if (inode->i_uid == fsuid) ++ return 0; ++ if (dir->i_uid == fsuid) ++ return 0; ++ return !capable(CAP_FOWNER); ++} ++ ++/* ++ * Check whether we can remove a link victim from directory dir, check ++ * whether the type of victim is right. ++ * 1. We can't do it if dir is read-only (done in permission()) ++ * 2. We should have write and exec permissions on dir ++ * 3. We can't remove anything from append-only dir ++ * 4. We can't do anything with immutable dir (done in permission()) ++ * 5. If the sticky bit on dir is set we should either ++ * a. be owner of dir, or ++ * b. be owner of victim, or ++ * c. have CAP_FOWNER capability ++ * 6. If the victim is append-only or immutable we can't do antyhing with ++ * links pointing to it. ++ * 7. If we were asked to remove a directory and victim isn't one - ENOTDIR. ++ * 8. If we were asked to remove a non-directory and victim isn't one - EISDIR. ++ * 9. We can't remove a root or mountpoint. ++ * 10. We don't allow removal of NFS sillyrenamed files; it's handled by ++ * nfs_async_unlink(). ++ */ ++static int may_delete(struct inode *dir,struct dentry *victim,int isdir) ++{ ++ int error; ++ ++ if (!victim->d_inode) ++ return -ENOENT; ++ ++ BUG_ON(victim->d_parent->d_inode != dir); ++ audit_inode_child(victim, dir); ++ ++ error = inode_permission(dir, MAY_WRITE | MAY_EXEC); ++ if (error) ++ return error; ++ if (IS_APPEND(dir)) ++ return -EPERM; ++ if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)|| ++ IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode)) ++ return -EPERM; ++ if (isdir) { ++ if (!S_ISDIR(victim->d_inode->i_mode)) ++ return -ENOTDIR; ++ if (IS_ROOT(victim)) ++ return -EBUSY; ++ } else if (S_ISDIR(victim->d_inode->i_mode)) ++ return -EISDIR; ++ if (IS_DEADDIR(dir)) ++ return -ENOENT; ++ if (victim->d_flags & DCACHE_NFSFS_RENAMED) ++ return -EBUSY; ++ return 0; ++} ++ ++/* Check whether we can create an object with dentry child in directory ++ * dir. ++ * 1. We can't do it if child already exists (open has special treatment for ++ * this case, but since we are inlined it's OK) ++ * 2. We can't do it if dir is read-only (done in permission()) ++ * 3. We should have write and exec permissions on dir ++ * 4. We can't do it if dir is immutable (done in permission()) ++ */ ++static inline int may_create(struct inode *dir, struct dentry *child) ++{ ++ if (child->d_inode) ++ return -EEXIST; ++ if (IS_DEADDIR(dir)) ++ return -ENOENT; ++ return inode_permission(dir, MAY_WRITE | MAY_EXEC); ++} ++ ++/* ++ * p1 and p2 should be directories on the same fs. ++ */ ++struct dentry *lock_rename(struct dentry *p1, struct dentry *p2) ++{ ++ struct dentry *p; ++ ++ if (p1 == p2) { ++ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); ++ return NULL; ++ } ++ ++ mutex_lock(&p1->d_inode->i_sb->s_vfs_rename_mutex); ++ ++ p = d_ancestor(p2, p1); ++ if (p) { ++ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_PARENT); ++ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_CHILD); ++ return p; ++ } ++ ++ p = d_ancestor(p1, p2); ++ if (p) { ++ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); ++ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); ++ return p; ++ } ++ ++ mutex_lock_nested(&p1->d_inode->i_mutex, I_MUTEX_PARENT); ++ mutex_lock_nested(&p2->d_inode->i_mutex, I_MUTEX_CHILD); ++ return NULL; ++} ++ ++void unlock_rename(struct dentry *p1, struct dentry *p2) ++{ ++ mutex_unlock(&p1->d_inode->i_mutex); ++ if (p1 != p2) { ++ mutex_unlock(&p2->d_inode->i_mutex); ++ mutex_unlock(&p1->d_inode->i_sb->s_vfs_rename_mutex); ++ } ++} ++ ++int vfs_create(struct inode *dir, struct dentry *dentry, int mode, ++ struct nameidata *nd) ++{ ++ int error = may_create(dir, dentry); ++ ++ if (error) ++ return error; ++ ++ if (!dir->i_op->create) ++ return -EACCES; /* shouldn't it be ENOSYS? */ ++ mode &= S_IALLUGO; ++ mode |= S_IFREG; ++ error = security_inode_create(dir, dentry, mode); ++ if (error) ++ return error; ++ error = dir->i_op->create(dir, dentry, mode, nd); ++ if (!error) ++ fsnotify_create(dir, dentry); ++ return error; ++} ++ ++int may_open(struct path *path, int acc_mode, int flag) ++{ ++ struct dentry *dentry = path->dentry; ++ struct inode *inode = dentry->d_inode; ++ int error; ++ ++ if (!inode) ++ return -ENOENT; ++ ++ switch (inode->i_mode & S_IFMT) { ++ case S_IFLNK: ++ return -ELOOP; ++ case S_IFDIR: ++ if (acc_mode & MAY_WRITE) ++ return -EISDIR; ++ break; ++ case S_IFBLK: ++ case S_IFCHR: ++ if (path->mnt->mnt_flags & MNT_NODEV) ++ return -EACCES; ++ /*FALLTHRU*/ ++ case S_IFIFO: ++ case S_IFSOCK: ++ flag &= ~O_TRUNC; ++ break; ++ } ++ ++ error = inode_permission(inode, acc_mode); ++ if (error) ++ return error; ++ ++ /* ++ * An append-only file must be opened in append mode for writing. ++ */ ++ if (IS_APPEND(inode)) { ++ if ((flag & O_ACCMODE) != O_RDONLY && !(flag & O_APPEND)) ++ return -EPERM; ++ if (flag & O_TRUNC) ++ return -EPERM; ++ } ++ ++ /* O_NOATIME can only be set by the owner or superuser */ ++ if (flag & O_NOATIME && !is_owner_or_cap(inode)) ++ return -EPERM; ++ ++ /* ++ * Ensure there are no outstanding leases on the file. ++ */ ++ return break_lease(inode, flag); ++} ++ ++static int handle_truncate(struct path *path) ++{ ++ struct inode *inode = path->dentry->d_inode; ++ int error = get_write_access(inode); ++ if (error) ++ return error; ++ /* ++ * Refuse to truncate files with mandatory locks held on them. ++ */ ++ error = locks_verify_locked(inode); ++ if (!error) ++ error = security_path_truncate(path); ++ if (!error) { ++ error = do_truncate(path->dentry, 0, ++ ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, ++ NULL); ++ } ++ put_write_access(inode); ++ return error; ++} ++ ++/* ++ * Be careful about ever adding any more callers of this ++ * function. Its flags must be in the namei format, not ++ * what get passed to sys_open(). ++ */ ++static int __open_namei_create(struct nameidata *nd, struct path *path, ++ int open_flag, int mode) ++{ ++ int error; ++ struct dentry *dir = nd->path.dentry; ++ ++ if (!IS_POSIXACL(dir->d_inode)) ++ mode &= ~current_umask(); ++ error = security_path_mknod(&nd->path, path->dentry, mode, 0); ++ if (error) ++ goto out_unlock; ++ error = vfs_create(dir->d_inode, path->dentry, mode, nd); ++out_unlock: ++ mutex_unlock(&dir->d_inode->i_mutex); ++ dput(nd->path.dentry); ++ nd->path.dentry = path->dentry; ++ if (error) ++ return error; ++ /* Don't check for write permission, don't truncate */ ++ return may_open(&nd->path, 0, open_flag & ~O_TRUNC); ++} ++ ++/* ++ * Note that while the flag value (low two bits) for sys_open means: ++ * 00 - read-only ++ * 01 - write-only ++ * 10 - read-write ++ * 11 - special ++ * it is changed into ++ * 00 - no permissions needed ++ * 01 - read-permission ++ * 10 - write-permission ++ * 11 - read-write ++ * for the internal routines (ie open_namei()/follow_link() etc) ++ * This is more logical, and also allows the 00 "no perm needed" ++ * to be used for symlinks (where the permissions are checked ++ * later). ++ * ++*/ ++static inline int open_to_namei_flags(int flag) ++{ ++ if ((flag+1) & O_ACCMODE) ++ flag++; ++ return flag; ++} ++ ++static int open_will_truncate(int flag, struct inode *inode) ++{ ++ /* ++ * We'll never write to the fs underlying ++ * a device file. ++ */ ++ if (special_file(inode->i_mode)) ++ return 0; ++ return (flag & O_TRUNC); ++} ++ ++static struct file *finish_open(struct nameidata *nd, ++ int open_flag, int acc_mode) ++{ ++ struct file *filp; ++ int will_truncate; ++ int error; ++ ++ will_truncate = open_will_truncate(open_flag, nd->path.dentry->d_inode); ++ if (will_truncate) { ++ error = mnt_want_write(nd->path.mnt); ++ if (error) ++ goto exit; ++ } ++ error = may_open(&nd->path, acc_mode, open_flag); ++ if (error) { ++ if (will_truncate) ++ mnt_drop_write(nd->path.mnt); ++ goto exit; ++ } ++ filp = nameidata_to_filp(nd); ++ if (!IS_ERR(filp)) { ++ error = ima_file_check(filp, acc_mode); ++ if (error) { ++ fput(filp); ++ filp = ERR_PTR(error); ++ } ++ } ++ if (!IS_ERR(filp)) { ++ if (will_truncate) { ++ error = handle_truncate(&nd->path); ++ if (error) { ++ fput(filp); ++ filp = ERR_PTR(error); ++ } ++ } ++ } ++ /* ++ * It is now safe to drop the mnt write ++ * because the filp has had a write taken ++ * on its behalf. ++ */ ++ if (will_truncate) ++ mnt_drop_write(nd->path.mnt); ++ return filp; ++ ++exit: ++ if (!IS_ERR(nd->intent.open.file)) ++ release_open_intent(nd); ++ path_put(&nd->path); ++ return ERR_PTR(error); ++} ++ ++static struct file *do_last(struct nameidata *nd, struct path *path, ++ int open_flag, int acc_mode, ++ int mode, const char *pathname) ++{ ++ struct dentry *dir = nd->path.dentry; ++ struct file *filp; ++ int error = -EISDIR; ++ ++ switch (nd->last_type) { ++ case LAST_DOTDOT: ++ follow_dotdot(nd); ++ dir = nd->path.dentry; ++ case LAST_DOT: ++ if (nd->path.mnt->mnt_sb->s_type->fs_flags & FS_REVAL_DOT) { ++ if (!dir->d_op->d_revalidate(dir, nd)) { ++ error = -ESTALE; ++ goto exit; ++ } ++ } ++ /* fallthrough */ ++ case LAST_ROOT: ++ if (open_flag & O_CREAT) ++ goto exit; ++ /* fallthrough */ ++ case LAST_BIND: ++ audit_inode(pathname, dir); ++ goto ok; ++ } ++ ++ /* trailing slashes? */ ++ if (nd->last.name[nd->last.len]) { ++ if (open_flag & O_CREAT) ++ goto exit; ++ nd->flags |= LOOKUP_DIRECTORY | LOOKUP_FOLLOW; ++ } ++ ++ /* just plain open? */ ++ if (!(open_flag & O_CREAT)) { ++ error = do_lookup(nd, &nd->last, path); ++ if (error) ++ goto exit; ++ error = -ENOENT; ++ if (!path->dentry->d_inode) ++ goto exit_dput; ++ if (path->dentry->d_inode->i_op->follow_link) ++ return NULL; ++ error = -ENOTDIR; ++ if (nd->flags & LOOKUP_DIRECTORY) { ++ if (!path->dentry->d_inode->i_op->lookup) ++ goto exit_dput; ++ } ++ path_to_nameidata(path, nd); ++ audit_inode(pathname, nd->path.dentry); ++ goto ok; ++ } ++ ++ /* OK, it's O_CREAT */ ++ mutex_lock(&dir->d_inode->i_mutex); ++ ++ path->dentry = lookup_hash(nd); ++ path->mnt = nd->path.mnt; ++ ++ error = PTR_ERR(path->dentry); ++ if (IS_ERR(path->dentry)) { ++ mutex_unlock(&dir->d_inode->i_mutex); ++ goto exit; ++ } ++ ++ if (IS_ERR(nd->intent.open.file)) { ++ error = PTR_ERR(nd->intent.open.file); ++ goto exit_mutex_unlock; ++ } ++ ++ /* Negative dentry, just create the file */ ++ if (!path->dentry->d_inode) { ++ /* ++ * This write is needed to ensure that a ++ * ro->rw transition does not occur between ++ * the time when the file is created and when ++ * a permanent write count is taken through ++ * the 'struct file' in nameidata_to_filp(). ++ */ ++ error = mnt_want_write(nd->path.mnt); ++ if (error) ++ goto exit_mutex_unlock; ++ error = __open_namei_create(nd, path, open_flag, mode); ++ if (error) { ++ mnt_drop_write(nd->path.mnt); ++ goto exit; ++ } ++ filp = nameidata_to_filp(nd); ++ mnt_drop_write(nd->path.mnt); ++ if (!IS_ERR(filp)) { ++ error = ima_file_check(filp, acc_mode); ++ if (error) { ++ fput(filp); ++ filp = ERR_PTR(error); ++ } ++ } ++ return filp; ++ } ++ ++ /* ++ * It already exists. ++ */ ++ mutex_unlock(&dir->d_inode->i_mutex); ++ audit_inode(pathname, path->dentry); ++ ++ error = -EEXIST; ++ if (open_flag & O_EXCL) ++ goto exit_dput; ++ ++ if (__follow_mount(path)) { ++ error = -ELOOP; ++ if (open_flag & O_NOFOLLOW) ++ goto exit_dput; ++ } ++ ++ error = -ENOENT; ++ if (!path->dentry->d_inode) ++ goto exit_dput; ++ ++ if (path->dentry->d_inode->i_op->follow_link) ++ return NULL; ++ ++ path_to_nameidata(path, nd); ++ error = -EISDIR; ++ if (S_ISDIR(path->dentry->d_inode->i_mode)) ++ goto exit; ++ok: ++ filp = finish_open(nd, open_flag, acc_mode); ++ return filp; ++ ++exit_mutex_unlock: ++ mutex_unlock(&dir->d_inode->i_mutex); ++exit_dput: ++ path_put_conditional(path, nd); ++exit: ++ if (!IS_ERR(nd->intent.open.file)) ++ release_open_intent(nd); ++ path_put(&nd->path); ++ return ERR_PTR(error); ++} ++ ++/* ++ * Note that the low bits of the passed in "open_flag" ++ * are not the same as in the local variable "flag". See ++ * open_to_namei_flags() for more details. ++ */ ++struct file *do_filp_open(int dfd, const char *pathname, ++ int open_flag, int mode, int acc_mode) ++{ ++ struct file *filp; ++ struct nameidata nd; ++ int error; ++ struct path path; ++ int count = 0; ++ int flag = open_to_namei_flags(open_flag); ++ int force_reval = 0; ++ ++ if (!(open_flag & O_CREAT)) ++ mode = 0; ++ ++ /* ++ * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only ++ * check for O_DSYNC if the need any syncing at all we enforce it's ++ * always set instead of having to deal with possibly weird behaviour ++ * for malicious applications setting only __O_SYNC. ++ */ ++ if (open_flag & __O_SYNC) ++ open_flag |= O_DSYNC; ++ ++ if (!acc_mode) ++ acc_mode = MAY_OPEN | ACC_MODE(open_flag); ++ ++ /* O_TRUNC implies we need access checks for write permissions */ ++ if (open_flag & O_TRUNC) ++ acc_mode |= MAY_WRITE; ++ ++ /* Allow the LSM permission hook to distinguish append ++ access from general write access. */ ++ if (open_flag & O_APPEND) ++ acc_mode |= MAY_APPEND; ++ ++ /* find the parent */ ++reval: ++ error = path_init(dfd, pathname, LOOKUP_PARENT, &nd); ++ if (error) ++ return ERR_PTR(error); ++ if (force_reval) ++ nd.flags |= LOOKUP_REVAL; ++ ++ current->total_link_count = 0; ++ error = link_path_walk(pathname, &nd); ++ if (error) { ++ filp = ERR_PTR(error); ++ goto out; ++ } ++ if (unlikely(!audit_dummy_context()) && (open_flag & O_CREAT)) ++ audit_inode(pathname, nd.path.dentry); ++ ++ /* ++ * We have the parent and last component. ++ */ ++ ++ error = -ENFILE; ++ filp = get_empty_filp(); ++ if (filp == NULL) ++ goto exit_parent; ++ nd.intent.open.file = filp; ++ filp->f_flags = open_flag; ++ nd.intent.open.flags = flag; ++ nd.intent.open.create_mode = mode; ++ nd.flags &= ~LOOKUP_PARENT; ++ nd.flags |= LOOKUP_OPEN; ++ if (open_flag & O_CREAT) { ++ nd.flags |= LOOKUP_CREATE; ++ if (open_flag & O_EXCL) ++ nd.flags |= LOOKUP_EXCL; ++ } ++ if (open_flag & O_DIRECTORY) ++ nd.flags |= LOOKUP_DIRECTORY; ++ if (!(open_flag & O_NOFOLLOW)) ++ nd.flags |= LOOKUP_FOLLOW; ++ filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); ++ while (unlikely(!filp)) { /* trailing symlink */ ++ struct path holder; ++ struct inode *inode = path.dentry->d_inode; ++ void *cookie; ++ error = -ELOOP; ++ /* S_ISDIR part is a temporary automount kludge */ ++ if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(inode->i_mode)) ++ goto exit_dput; ++ if (count++ == 32) ++ goto exit_dput; ++ /* ++ * This is subtle. Instead of calling do_follow_link() we do ++ * the thing by hands. The reason is that this way we have zero ++ * link_count and path_walk() (called from ->follow_link) ++ * honoring LOOKUP_PARENT. After that we have the parent and ++ * last component, i.e. we are in the same situation as after ++ * the first path_walk(). Well, almost - if the last component ++ * is normal we get its copy stored in nd->last.name and we will ++ * have to putname() it when we are done. Procfs-like symlinks ++ * just set LAST_BIND. ++ */ ++ nd.flags |= LOOKUP_PARENT; ++ error = security_inode_follow_link(path.dentry, &nd); ++ if (error) ++ goto exit_dput; ++ error = __do_follow_link(&path, &nd, &cookie); ++ if (unlikely(error)) { ++ /* nd.path had been dropped */ ++ if (!IS_ERR(cookie) && inode->i_op->put_link) ++ inode->i_op->put_link(path.dentry, &nd, cookie); ++ path_put(&path); ++ release_open_intent(&nd); ++ filp = ERR_PTR(error); ++ goto out; ++ } ++ holder = path; ++ nd.flags &= ~LOOKUP_PARENT; ++ filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname); ++ if (inode->i_op->put_link) ++ inode->i_op->put_link(holder.dentry, &nd, cookie); ++ path_put(&holder); ++ } ++out: ++ if (nd.root.mnt) ++ path_put(&nd.root); ++ if (filp == ERR_PTR(-ESTALE) && !force_reval) { ++ force_reval = 1; ++ goto reval; ++ } ++ return filp; ++ ++exit_dput: ++ path_put_conditional(&path, &nd); ++ if (!IS_ERR(nd.intent.open.file)) ++ release_open_intent(&nd); ++exit_parent: ++ path_put(&nd.path); ++ filp = ERR_PTR(error); ++ goto out; ++} ++ ++/** ++ * filp_open - open file and return file pointer ++ * ++ * @filename: path to open ++ * @flags: open flags as per the open(2) second argument ++ * @mode: mode for the new file if O_CREAT is set, else ignored ++ * ++ * This is the helper to open a file from kernelspace if you really ++ * have to. But in generally you should not do this, so please move ++ * along, nothing to see here.. ++ */ ++struct file *filp_open(const char *filename, int flags, int mode) ++{ ++ return do_filp_open(AT_FDCWD, filename, flags, mode, 0); ++} ++EXPORT_SYMBOL(filp_open); ++ ++/** ++ * lookup_create - lookup a dentry, creating it if it doesn't exist ++ * @nd: nameidata info ++ * @is_dir: directory flag ++ * ++ * Simple function to lookup and return a dentry and create it ++ * if it doesn't exist. Is SMP-safe. ++ * ++ * Returns with nd->path.dentry->d_inode->i_mutex locked. ++ */ ++struct dentry *lookup_create(struct nameidata *nd, int is_dir) ++{ ++ struct dentry *dentry = ERR_PTR(-EEXIST); ++ ++ mutex_lock_nested(&nd->path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); ++ /* ++ * Yucky last component or no last component at all? ++ * (foo/., foo/.., /////) ++ */ ++ if (nd->last_type != LAST_NORM) ++ goto fail; ++ nd->flags &= ~LOOKUP_PARENT; ++ nd->flags |= LOOKUP_CREATE | LOOKUP_EXCL; ++ nd->intent.open.flags = O_EXCL; ++ ++ /* ++ * Do the final lookup. ++ */ ++ dentry = lookup_hash(nd); ++ if (IS_ERR(dentry)) ++ goto fail; ++ ++ if (dentry->d_inode) ++ goto eexist; ++ /* ++ * Special case - lookup gave negative, but... we had foo/bar/ ++ * From the vfs_mknod() POV we just have a negative dentry - ++ * all is fine. Let's be bastards - you had / on the end, you've ++ * been asking for (non-existent) directory. -ENOENT for you. ++ */ ++ if (unlikely(!is_dir && nd->last.name[nd->last.len])) { ++ dput(dentry); ++ dentry = ERR_PTR(-ENOENT); ++ } ++ return dentry; ++eexist: ++ dput(dentry); ++ dentry = ERR_PTR(-EEXIST); ++fail: ++ return dentry; ++} ++EXPORT_SYMBOL_GPL(lookup_create); ++ ++int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) ++{ ++ int error = may_create(dir, dentry); ++ ++ if (error) ++ return error; ++ ++ if ((S_ISCHR(mode) || S_ISBLK(mode)) && !capable(CAP_MKNOD)) ++ return -EPERM; ++ ++ if (!dir->i_op->mknod) ++ return -EPERM; ++ ++ error = devcgroup_inode_mknod(mode, dev); ++ if (error) ++ return error; ++ ++ error = security_inode_mknod(dir, dentry, mode, dev); ++ if (error) ++ return error; ++ ++ error = dir->i_op->mknod(dir, dentry, mode, dev); ++ if (!error) ++ fsnotify_create(dir, dentry); ++ return error; ++} ++ ++static int may_mknod(mode_t mode) ++{ ++ switch (mode & S_IFMT) { ++ case S_IFREG: ++ case S_IFCHR: ++ case S_IFBLK: ++ case S_IFIFO: ++ case S_IFSOCK: ++ case 0: /* zero mode translates to S_IFREG */ ++ return 0; ++ case S_IFDIR: ++ return -EPERM; ++ default: ++ return -EINVAL; ++ } ++} ++ ++SYSCALL_DEFINE4(mknodat, int, dfd, const char __user *, filename, int, mode, ++ unsigned, dev) ++{ ++ int error; ++ char *tmp; ++ struct dentry *dentry; ++ struct nameidata nd; ++ ++ if (S_ISDIR(mode)) ++ return -EPERM; ++ ++ error = user_path_parent(dfd, filename, &nd, &tmp); ++ if (error) ++ return error; ++ ++ dentry = lookup_create(&nd, 0); ++ if (IS_ERR(dentry)) { ++ error = PTR_ERR(dentry); ++ goto out_unlock; ++ } ++ if (!IS_POSIXACL(nd.path.dentry->d_inode)) ++ mode &= ~current_umask(); ++ error = may_mknod(mode); ++ if (error) ++ goto out_dput; ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto out_dput; ++ error = security_path_mknod(&nd.path, dentry, mode, dev); ++ if (error) ++ goto out_drop_write; ++ switch (mode & S_IFMT) { ++ case 0: case S_IFREG: ++ error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd); ++ break; ++ case S_IFCHR: case S_IFBLK: ++ error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode, ++ new_decode_dev(dev)); ++ break; ++ case S_IFIFO: case S_IFSOCK: ++ error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0); ++ break; ++ } ++out_drop_write: ++ mnt_drop_write(nd.path.mnt); ++out_dput: ++ dput(dentry); ++out_unlock: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ path_put(&nd.path); ++ putname(tmp); ++ ++ return error; ++} ++ ++SYSCALL_DEFINE3(mknod, const char __user *, filename, int, mode, unsigned, dev) ++{ ++ return sys_mknodat(AT_FDCWD, filename, mode, dev); ++} ++ ++int vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) ++{ ++ int error = may_create(dir, dentry); ++ ++ if (error) ++ return error; ++ ++ if (!dir->i_op->mkdir) ++ return -EPERM; ++ ++ mode &= (S_IRWXUGO|S_ISVTX); ++ error = security_inode_mkdir(dir, dentry, mode); ++ if (error) ++ return error; ++ ++ error = dir->i_op->mkdir(dir, dentry, mode); ++ if (!error) ++ fsnotify_mkdir(dir, dentry); ++ return error; ++} ++ ++SYSCALL_DEFINE3(mkdirat, int, dfd, const char __user *, pathname, int, mode) ++{ ++ int error = 0; ++ char * tmp; ++ struct dentry *dentry; ++ struct nameidata nd; ++ ++ error = user_path_parent(dfd, pathname, &nd, &tmp); ++ if (error) ++ goto out_err; ++ ++ dentry = lookup_create(&nd, 1); ++ error = PTR_ERR(dentry); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ ++ if (!IS_POSIXACL(nd.path.dentry->d_inode)) ++ mode &= ~current_umask(); ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto out_dput; ++ error = security_path_mkdir(&nd.path, dentry, mode); ++ if (error) ++ goto out_drop_write; ++ error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); ++out_drop_write: ++ mnt_drop_write(nd.path.mnt); ++out_dput: ++ dput(dentry); ++out_unlock: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ path_put(&nd.path); ++ putname(tmp); ++out_err: ++ return error; ++} ++ ++SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode) ++{ ++ return sys_mkdirat(AT_FDCWD, pathname, mode); ++} ++ ++/* ++ * We try to drop the dentry early: we should have ++ * a usage count of 2 if we're the only user of this ++ * dentry, and if that is true (possibly after pruning ++ * the dcache), then we drop the dentry now. ++ * ++ * A low-level filesystem can, if it choses, legally ++ * do a ++ * ++ * if (!d_unhashed(dentry)) ++ * return -EBUSY; ++ * ++ * if it cannot handle the case of removing a directory ++ * that is still in use by something else.. ++ */ ++void dentry_unhash(struct dentry *dentry) ++{ ++ dget(dentry); ++ shrink_dcache_parent(dentry); ++ spin_lock(&dcache_lock); ++ spin_lock(&dentry->d_lock); ++ if (atomic_read(&dentry->d_count) == 2) ++ __d_drop(dentry); ++ spin_unlock(&dentry->d_lock); ++ spin_unlock(&dcache_lock); ++} ++ ++int vfs_rmdir(struct inode *dir, struct dentry *dentry) ++{ ++ int error = may_delete(dir, dentry, 1); ++ ++ if (error) ++ return error; ++ ++ if (!dir->i_op->rmdir) ++ return -EPERM; ++ ++ mutex_lock(&dentry->d_inode->i_mutex); ++ dentry_unhash(dentry); ++ if (d_mountpoint(dentry)) ++ error = -EBUSY; ++ else { ++ error = security_inode_rmdir(dir, dentry); ++ if (!error) { ++ error = dir->i_op->rmdir(dir, dentry); ++ if (!error) { ++ dentry->d_inode->i_flags |= S_DEAD; ++ dont_mount(dentry); ++ } ++ } ++ } ++ mutex_unlock(&dentry->d_inode->i_mutex); ++ if (!error) { ++ d_delete(dentry); ++ } ++ dput(dentry); ++ ++ return error; ++} ++ ++static long do_rmdir(int dfd, const char __user *pathname) ++{ ++ int error = 0; ++ char * name; ++ struct dentry *dentry; ++ struct nameidata nd; ++ ++ error = user_path_parent(dfd, pathname, &nd, &name); ++ if (error) ++ return error; ++ ++ switch(nd.last_type) { ++ case LAST_DOTDOT: ++ error = -ENOTEMPTY; ++ goto exit1; ++ case LAST_DOT: ++ error = -EINVAL; ++ goto exit1; ++ case LAST_ROOT: ++ error = -EBUSY; ++ goto exit1; ++ } ++ ++ nd.flags &= ~LOOKUP_PARENT; ++ ++ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); ++ dentry = lookup_hash(&nd); ++ error = PTR_ERR(dentry); ++ if (IS_ERR(dentry)) ++ goto exit2; ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto exit3; ++ error = security_path_rmdir(&nd.path, dentry); ++ if (error) ++ goto exit4; ++ error = vfs_rmdir(nd.path.dentry->d_inode, dentry); ++exit4: ++ mnt_drop_write(nd.path.mnt); ++exit3: ++ dput(dentry); ++exit2: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++exit1: ++ path_put(&nd.path); ++ putname(name); ++ return error; ++} ++ ++SYSCALL_DEFINE1(rmdir, const char __user *, pathname) ++{ ++ return do_rmdir(AT_FDCWD, pathname); ++} ++ ++int vfs_unlink(struct inode *dir, struct dentry *dentry) ++{ ++ int error = may_delete(dir, dentry, 0); ++ ++ if (error) ++ return error; ++ ++ if (!dir->i_op->unlink) ++ return -EPERM; ++ ++ mutex_lock(&dentry->d_inode->i_mutex); ++ if (d_mountpoint(dentry)) ++ error = -EBUSY; ++ else { ++ error = security_inode_unlink(dir, dentry); ++ if (!error) { ++ error = dir->i_op->unlink(dir, dentry); ++ if (!error) ++ dont_mount(dentry); ++ } ++ } ++ mutex_unlock(&dentry->d_inode->i_mutex); ++ ++ /* We don't d_delete() NFS sillyrenamed files--they still exist. */ ++ if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) { ++ fsnotify_link_count(dentry->d_inode); ++ d_delete(dentry); ++ } ++ ++ return error; ++} ++ ++/* ++ * Make sure that the actual truncation of the file will occur outside its ++ * directory's i_mutex. Truncate can take a long time if there is a lot of ++ * writeout happening, and we don't want to prevent access to the directory ++ * while waiting on the I/O. ++ */ ++static long do_unlinkat(int dfd, const char __user *pathname) ++{ ++ int error; ++ char *name; ++ struct dentry *dentry; ++ struct nameidata nd; ++ struct inode *inode = NULL; ++ ++ error = user_path_parent(dfd, pathname, &nd, &name); ++ if (error) ++ return error; ++ ++ error = -EISDIR; ++ if (nd.last_type != LAST_NORM) ++ goto exit1; ++ ++ nd.flags &= ~LOOKUP_PARENT; ++ ++ mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); ++ dentry = lookup_hash(&nd); ++ error = PTR_ERR(dentry); ++ if (!IS_ERR(dentry)) { ++ /* Why not before? Because we want correct error value */ ++ if (nd.last.name[nd.last.len]) ++ goto slashes; ++ inode = dentry->d_inode; ++ if (inode) ++ atomic_inc(&inode->i_count); ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto exit2; ++ error = security_path_unlink(&nd.path, dentry); ++ if (error) ++ goto exit3; ++ error = vfs_unlink(nd.path.dentry->d_inode, dentry); ++exit3: ++ mnt_drop_write(nd.path.mnt); ++ exit2: ++ dput(dentry); ++ } ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ if (inode) ++ iput(inode); /* truncate the inode here */ ++exit1: ++ path_put(&nd.path); ++ putname(name); ++ return error; ++ ++slashes: ++ error = !dentry->d_inode ? -ENOENT : ++ S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; ++ goto exit2; ++} ++ ++SYSCALL_DEFINE3(unlinkat, int, dfd, const char __user *, pathname, int, flag) ++{ ++ if ((flag & ~AT_REMOVEDIR) != 0) ++ return -EINVAL; ++ ++ if (flag & AT_REMOVEDIR) ++ return do_rmdir(dfd, pathname); ++ ++ return do_unlinkat(dfd, pathname); ++} ++ ++SYSCALL_DEFINE1(unlink, const char __user *, pathname) ++{ ++ return do_unlinkat(AT_FDCWD, pathname); ++} ++ ++int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname) ++{ ++ int error = may_create(dir, dentry); ++ ++ if (error) ++ return error; ++ ++ if (!dir->i_op->symlink) ++ return -EPERM; ++ ++ error = security_inode_symlink(dir, dentry, oldname); ++ if (error) ++ return error; ++ ++ error = dir->i_op->symlink(dir, dentry, oldname); ++ if (!error) ++ fsnotify_create(dir, dentry); ++ return error; ++} ++ ++SYSCALL_DEFINE3(symlinkat, const char __user *, oldname, ++ int, newdfd, const char __user *, newname) ++{ ++ int error; ++ char *from; ++ char *to; ++ struct dentry *dentry; ++ struct nameidata nd; ++ ++ from = getname(oldname); ++ if (IS_ERR(from)) ++ return PTR_ERR(from); ++ ++ error = user_path_parent(newdfd, newname, &nd, &to); ++ if (error) ++ goto out_putname; ++ ++ dentry = lookup_create(&nd, 0); ++ error = PTR_ERR(dentry); ++ if (IS_ERR(dentry)) ++ goto out_unlock; ++ ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto out_dput; ++ error = security_path_symlink(&nd.path, dentry, from); ++ if (error) ++ goto out_drop_write; ++ error = vfs_symlink(nd.path.dentry->d_inode, dentry, from); ++out_drop_write: ++ mnt_drop_write(nd.path.mnt); ++out_dput: ++ dput(dentry); ++out_unlock: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++ path_put(&nd.path); ++ putname(to); ++out_putname: ++ putname(from); ++ return error; ++} ++ ++SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname) ++{ ++ return sys_symlinkat(oldname, AT_FDCWD, newname); ++} ++ ++int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) ++{ ++ struct inode *inode = old_dentry->d_inode; ++ int error; ++ ++ if (!inode) ++ return -ENOENT; ++ ++ error = may_create(dir, new_dentry); ++ if (error) ++ return error; ++ ++ if (dir->i_sb != inode->i_sb) ++ return -EXDEV; ++ ++ /* ++ * A link to an append-only or immutable file cannot be created. ++ */ ++ if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) ++ return -EPERM; ++ if (!dir->i_op->link) ++ return -EPERM; ++ if (S_ISDIR(inode->i_mode)) ++ return -EPERM; ++ ++ error = security_inode_link(old_dentry, dir, new_dentry); ++ if (error) ++ return error; ++ ++ mutex_lock(&inode->i_mutex); ++ error = dir->i_op->link(old_dentry, dir, new_dentry); ++ mutex_unlock(&inode->i_mutex); ++ if (!error) ++ fsnotify_link(dir, inode, new_dentry); ++ return error; ++} ++ ++/* ++ * Hardlinks are often used in delicate situations. We avoid ++ * security-related surprises by not following symlinks on the ++ * newname. --KAB ++ * ++ * We don't follow them on the oldname either to be compatible ++ * with linux 2.0, and to avoid hard-linking to directories ++ * and other special files. --ADM ++ */ ++SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, ++ int, newdfd, const char __user *, newname, int, flags) ++{ ++ struct dentry *new_dentry; ++ struct nameidata nd; ++ struct path old_path; ++ int error; ++ char *to; ++ ++ if ((flags & ~AT_SYMLINK_FOLLOW) != 0) ++ return -EINVAL; ++ ++ error = user_path_at(olddfd, oldname, ++ flags & AT_SYMLINK_FOLLOW ? LOOKUP_FOLLOW : 0, ++ &old_path); ++ if (error) ++ return error; ++ ++ error = user_path_parent(newdfd, newname, &nd, &to); ++ if (error) ++ goto out; ++ error = -EXDEV; ++ if (old_path.mnt != nd.path.mnt) ++ goto out_release; ++ new_dentry = lookup_create(&nd, 0); ++ error = PTR_ERR(new_dentry); ++ if (IS_ERR(new_dentry)) ++ goto out_unlock; ++ error = mnt_want_write(nd.path.mnt); ++ if (error) ++ goto out_dput; ++ error = security_path_link(old_path.dentry, &nd.path, new_dentry); ++ if (error) ++ goto out_drop_write; ++ error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); ++out_drop_write: ++ mnt_drop_write(nd.path.mnt); ++out_dput: ++ dput(new_dentry); ++out_unlock: ++ mutex_unlock(&nd.path.dentry->d_inode->i_mutex); ++out_release: ++ path_put(&nd.path); ++ putname(to); ++out: ++ path_put(&old_path); ++ ++ return error; ++} ++ ++SYSCALL_DEFINE2(link, const char __user *, oldname, const char __user *, newname) ++{ ++ return sys_linkat(AT_FDCWD, oldname, AT_FDCWD, newname, 0); ++} ++ ++/* ++ * The worst of all namespace operations - renaming directory. "Perverted" ++ * doesn't even start to describe it. Somebody in UCB had a heck of a trip... ++ * Problems: ++ * a) we can get into loop creation. Check is done in is_subdir(). ++ * b) race potential - two innocent renames can create a loop together. ++ * That's where 4.4 screws up. Current fix: serialization on ++ * sb->s_vfs_rename_mutex. We might be more accurate, but that's another ++ * story. ++ * c) we have to lock _three_ objects - parents and victim (if it exists). ++ * And that - after we got ->i_mutex on parents (until then we don't know ++ * whether the target exists). Solution: try to be smart with locking ++ * order for inodes. We rely on the fact that tree topology may change ++ * only under ->s_vfs_rename_mutex _and_ that parent of the object we ++ * move will be locked. Thus we can rank directories by the tree ++ * (ancestors first) and rank all non-directories after them. ++ * That works since everybody except rename does "lock parent, lookup, ++ * lock child" and rename is under ->s_vfs_rename_mutex. ++ * HOWEVER, it relies on the assumption that any object with ->lookup() ++ * has no more than 1 dentry. If "hybrid" objects will ever appear, ++ * we'd better make sure that there's no link(2) for them. ++ * d) some filesystems don't support opened-but-unlinked directories, ++ * either because of layout or because they are not ready to deal with ++ * all cases correctly. The latter will be fixed (taking this sort of ++ * stuff into VFS), but the former is not going away. Solution: the same ++ * trick as in rmdir(). ++ * e) conversion from fhandle to dentry may come in the wrong moment - when ++ * we are removing the target. Solution: we will have to grab ->i_mutex ++ * in the fhandle_to_dentry code. [FIXME - current nfsfh.c relies on ++ * ->i_mutex on parents, which works but leads to some truly excessive ++ * locking]. ++ */ ++static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ int error = 0; ++ struct inode *target; ++ ++ /* ++ * If we are going to change the parent - check write permissions, ++ * we'll need to flip '..'. ++ */ ++ if (new_dir != old_dir) { ++ error = inode_permission(old_dentry->d_inode, MAY_WRITE); ++ if (error) ++ return error; ++ } ++ ++ error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry); ++ if (error) ++ return error; ++ ++ target = new_dentry->d_inode; ++ if (target) ++ mutex_lock(&target->i_mutex); ++ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) ++ error = -EBUSY; ++ else { ++ if (target) ++ dentry_unhash(new_dentry); ++ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); ++ } ++ if (target) { ++ if (!error) { ++ target->i_flags |= S_DEAD; ++ dont_mount(new_dentry); ++ } ++ mutex_unlock(&target->i_mutex); ++ if (d_unhashed(new_dentry)) ++ d_rehash(new_dentry); ++ dput(new_dentry); ++ } ++ if (!error) ++ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) ++ d_move(old_dentry,new_dentry); ++ return error; ++} ++ ++static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ struct inode *target; ++ int error; ++ ++ error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry); ++ if (error) ++ return error; ++ ++ dget(new_dentry); ++ target = new_dentry->d_inode; ++ if (target) ++ mutex_lock(&target->i_mutex); ++ if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) ++ error = -EBUSY; ++ else ++ error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); ++ if (!error) { ++ if (target) ++ dont_mount(new_dentry); ++ if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) ++ d_move(old_dentry, new_dentry); ++ } ++ if (target) ++ mutex_unlock(&target->i_mutex); ++ dput(new_dentry); ++ return error; ++} ++ ++int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ++ struct inode *new_dir, struct dentry *new_dentry) ++{ ++ int error; ++ int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); ++ const unsigned char *old_name; ++ ++ if (old_dentry->d_inode == new_dentry->d_inode) ++ return 0; ++ ++ error = may_delete(old_dir, old_dentry, is_dir); ++ if (error) ++ return error; ++ ++ if (!new_dentry->d_inode) ++ error = may_create(new_dir, new_dentry); ++ else ++ error = may_delete(new_dir, new_dentry, is_dir); ++ if (error) ++ return error; ++ ++ if (!old_dir->i_op->rename) ++ return -EPERM; ++ ++ old_name = fsnotify_oldname_init(old_dentry->d_name.name); ++ ++ if (is_dir) ++ error = vfs_rename_dir(old_dir,old_dentry,new_dir,new_dentry); ++ else ++ error = vfs_rename_other(old_dir,old_dentry,new_dir,new_dentry); ++ if (!error) ++ fsnotify_move(old_dir, new_dir, old_name, is_dir, ++ new_dentry->d_inode, old_dentry); ++ fsnotify_oldname_free(old_name); ++ ++ return error; ++} ++ ++SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname, ++ int, newdfd, const char __user *, newname) ++{ ++ struct dentry *old_dir, *new_dir; ++ struct dentry *old_dentry, *new_dentry; ++ struct dentry *trap; ++ struct nameidata oldnd, newnd; ++ char *from; ++ char *to; ++ int error; ++ ++ error = user_path_parent(olddfd, oldname, &oldnd, &from); ++ if (error) ++ goto exit; ++ ++ error = user_path_parent(newdfd, newname, &newnd, &to); ++ if (error) ++ goto exit1; ++ ++ error = -EXDEV; ++ if (oldnd.path.mnt != newnd.path.mnt) ++ goto exit2; ++ ++ old_dir = oldnd.path.dentry; ++ error = -EBUSY; ++ if (oldnd.last_type != LAST_NORM) ++ goto exit2; ++ ++ new_dir = newnd.path.dentry; ++ if (newnd.last_type != LAST_NORM) ++ goto exit2; ++ ++ oldnd.flags &= ~LOOKUP_PARENT; ++ newnd.flags &= ~LOOKUP_PARENT; ++ newnd.flags |= LOOKUP_RENAME_TARGET; ++ ++ trap = lock_rename(new_dir, old_dir); ++ ++ old_dentry = lookup_hash(&oldnd); ++ error = PTR_ERR(old_dentry); ++ if (IS_ERR(old_dentry)) ++ goto exit3; ++ /* source must exist */ ++ error = -ENOENT; ++ if (!old_dentry->d_inode) ++ goto exit4; ++ /* unless the source is a directory trailing slashes give -ENOTDIR */ ++ if (!S_ISDIR(old_dentry->d_inode->i_mode)) { ++ error = -ENOTDIR; ++ if (oldnd.last.name[oldnd.last.len]) ++ goto exit4; ++ if (newnd.last.name[newnd.last.len]) ++ goto exit4; ++ } ++ /* source should not be ancestor of target */ ++ error = -EINVAL; ++ if (old_dentry == trap) ++ goto exit4; ++ new_dentry = lookup_hash(&newnd); ++ error = PTR_ERR(new_dentry); ++ if (IS_ERR(new_dentry)) ++ goto exit4; ++ /* target should not be an ancestor of source */ ++ error = -ENOTEMPTY; ++ if (new_dentry == trap) ++ goto exit5; ++ ++ error = mnt_want_write(oldnd.path.mnt); ++ if (error) ++ goto exit5; ++ error = security_path_rename(&oldnd.path, old_dentry, ++ &newnd.path, new_dentry); ++ if (error) ++ goto exit6; ++ error = vfs_rename(old_dir->d_inode, old_dentry, ++ new_dir->d_inode, new_dentry); ++exit6: ++ mnt_drop_write(oldnd.path.mnt); ++exit5: ++ dput(new_dentry); ++exit4: ++ dput(old_dentry); ++exit3: ++ unlock_rename(new_dir, old_dir); ++exit2: ++ path_put(&newnd.path); ++ putname(to); ++exit1: ++ path_put(&oldnd.path); ++ putname(from); ++exit: ++ return error; ++} ++ ++SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname) ++{ ++ return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname); ++} ++ ++int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen, const char *link) ++{ ++ int len; ++ ++ len = PTR_ERR(link); ++ if (IS_ERR(link)) ++ goto out; ++ ++ len = strlen(link); ++ if (len > (unsigned) buflen) ++ len = buflen; ++ if (copy_to_user(buffer, link, len)) ++ len = -EFAULT; ++out: ++ return len; ++} ++ ++/* ++ * A helper for ->readlink(). This should be used *ONLY* for symlinks that ++ * have ->follow_link() touching nd only in nd_set_link(). Using (or not ++ * using) it for any given inode is up to filesystem. ++ */ ++int generic_readlink(struct dentry *dentry, char __user *buffer, int buflen) ++{ ++ struct nameidata nd; ++ void *cookie; ++ int res; ++ ++ nd.depth = 0; ++ cookie = dentry->d_inode->i_op->follow_link(dentry, &nd); ++ if (IS_ERR(cookie)) ++ return PTR_ERR(cookie); ++ ++ res = vfs_readlink(dentry, buffer, buflen, nd_get_link(&nd)); ++ if (dentry->d_inode->i_op->put_link) ++ dentry->d_inode->i_op->put_link(dentry, &nd, cookie); ++ return res; ++} ++ ++int vfs_follow_link(struct nameidata *nd, const char *link) ++{ ++ return __vfs_follow_link(nd, link); ++} ++ ++/* get the link contents into pagecache */ ++static char *page_getlink(struct dentry * dentry, struct page **ppage) ++{ ++ char *kaddr; ++ struct page *page; ++ struct address_space *mapping = dentry->d_inode->i_mapping; ++ page = read_mapping_page(mapping, 0, NULL); ++ if (IS_ERR(page)) ++ return (char*)page; ++ *ppage = page; ++ kaddr = kmap(page); ++ nd_terminate_link(kaddr, dentry->d_inode->i_size, PAGE_SIZE - 1); ++ return kaddr; ++} ++ ++int page_readlink(struct dentry *dentry, char __user *buffer, int buflen) ++{ ++ struct page *page = NULL; ++ char *s = page_getlink(dentry, &page); ++ int res = vfs_readlink(dentry,buffer,buflen,s); ++ if (page) { ++ kunmap(page); ++ page_cache_release(page); ++ } ++ return res; ++} ++ ++void *page_follow_link_light(struct dentry *dentry, struct nameidata *nd) ++{ ++ struct page *page = NULL; ++ nd_set_link(nd, page_getlink(dentry, &page)); ++ return page; ++} ++ ++void page_put_link(struct dentry *dentry, struct nameidata *nd, void *cookie) ++{ ++ struct page *page = cookie; ++ ++ if (page) { ++ kunmap(page); ++ page_cache_release(page); ++ } ++} ++ ++/* ++ * The nofs argument instructs pagecache_write_begin to pass AOP_FLAG_NOFS ++ */ ++int __page_symlink(struct inode *inode, const char *symname, int len, int nofs) ++{ ++ struct address_space *mapping = inode->i_mapping; ++ struct page *page; ++ void *fsdata; ++ int err; ++ char *kaddr; ++ unsigned int flags = AOP_FLAG_UNINTERRUPTIBLE; ++ if (nofs) ++ flags |= AOP_FLAG_NOFS; ++ ++retry: ++ err = pagecache_write_begin(NULL, mapping, 0, len-1, ++ flags, &page, &fsdata); ++ if (err) ++ goto fail; ++ ++ kaddr = kmap_atomic(page, KM_USER0); ++ memcpy(kaddr, symname, len-1); ++ kunmap_atomic(kaddr, KM_USER0); ++ ++ err = pagecache_write_end(NULL, mapping, 0, len-1, len-1, ++ page, fsdata); ++ if (err < 0) ++ goto fail; ++ if (err < len-1) ++ goto retry; ++ ++ mark_inode_dirty(inode); ++ return 0; ++fail: ++ return err; ++} ++ ++int page_symlink(struct inode *inode, const char *symname, int len) ++{ ++ return __page_symlink(inode, symname, len, ++ !(mapping_gfp_mask(inode->i_mapping) & __GFP_FS)); ++} ++ ++const struct inode_operations page_symlink_inode_operations = { ++ .readlink = generic_readlink, ++ .follow_link = page_follow_link_light, ++ .put_link = page_put_link, ++}; ++ ++EXPORT_SYMBOL(user_path_at); ++EXPORT_SYMBOL(follow_down); ++EXPORT_SYMBOL(follow_up); ++EXPORT_SYMBOL(get_write_access); /* binfmt_aout */ ++EXPORT_SYMBOL(getname); ++EXPORT_SYMBOL(lock_rename); ++EXPORT_SYMBOL(lookup_one_len); ++EXPORT_SYMBOL(page_follow_link_light); ++EXPORT_SYMBOL(page_put_link); ++EXPORT_SYMBOL(page_readlink); ++EXPORT_SYMBOL(__page_symlink); ++EXPORT_SYMBOL(page_symlink); ++EXPORT_SYMBOL(page_symlink_inode_operations); ++EXPORT_SYMBOL(path_lookup); ++EXPORT_SYMBOL(kern_path); ++EXPORT_SYMBOL(vfs_path_lookup); ++EXPORT_SYMBOL(inode_permission); ++EXPORT_SYMBOL(file_permission); ++EXPORT_SYMBOL(unlock_rename); ++EXPORT_SYMBOL(vfs_create); ++EXPORT_SYMBOL(vfs_follow_link); ++EXPORT_SYMBOL(vfs_link); ++EXPORT_SYMBOL(vfs_mkdir); ++EXPORT_SYMBOL(vfs_mknod); ++EXPORT_SYMBOL(generic_permission); ++EXPORT_SYMBOL(vfs_readlink); ++EXPORT_SYMBOL(vfs_rename); ++EXPORT_SYMBOL(vfs_rmdir); ++EXPORT_SYMBOL(vfs_symlink); ++EXPORT_SYMBOL(vfs_unlink); ++EXPORT_SYMBOL(dentry_unhash); ++EXPORT_SYMBOL(generic_readlink); +diff -Nur linux-2.6.36.orig/fs/namespace.c linux-2.6.36/fs/namespace.c +--- linux-2.6.36.orig/fs/namespace.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/namespace.c 2011-01-10 19:52:38.000000000 +0100 +@@ -1322,6 +1322,7 @@ + } + return 0; + } ++EXPORT_SYMBOL(iterate_mounts); + + static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) + { +diff -Nur linux-2.6.36.orig/fs/notify/group.c linux-2.6.36/fs/notify/group.c +--- linux-2.6.36.orig/fs/notify/group.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/notify/group.c 2011-01-10 19:52:38.000000000 +0100 +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + #include + #include "fsnotify.h" +@@ -70,6 +71,7 @@ + if (atomic_dec_and_test(&group->refcnt)) + fsnotify_destroy_group(group); + } ++EXPORT_SYMBOL(fsnotify_put_group); + + /* + * Create a new fsnotify_group and hold a reference for the group returned. +@@ -102,3 +104,4 @@ + + return group; + } ++EXPORT_SYMBOL(fsnotify_alloc_group); +diff -Nur linux-2.6.36.orig/fs/notify/mark.c linux-2.6.36/fs/notify/mark.c +--- linux-2.6.36.orig/fs/notify/mark.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/notify/mark.c 2011-01-10 19:52:38.000000000 +0100 +@@ -113,6 +113,7 @@ + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); + } ++EXPORT_SYMBOL(fsnotify_put_mark); + + /* + * Any time a mark is getting freed we end up here. +@@ -190,6 +191,7 @@ + if (unlikely(atomic_dec_and_test(&group->num_marks))) + fsnotify_final_destroy_group(group); + } ++EXPORT_SYMBOL(fsnotify_destroy_mark); + + void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) + { +@@ -277,6 +279,7 @@ + + return ret; + } ++EXPORT_SYMBOL(fsnotify_add_mark); + + /* + * clear any marks in a group in which mark->flags & flags is true +@@ -332,6 +335,7 @@ + atomic_set(&mark->refcnt, 1); + mark->free_mark = free_mark; + } ++EXPORT_SYMBOL(fsnotify_init_mark); + + static int fsnotify_mark_destroy(void *ignored) + { +diff -Nur linux-2.6.36.orig/fs/open.c linux-2.6.36/fs/open.c +--- linux-2.6.36.orig/fs/open.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/open.c 2011-01-10 19:52:38.000000000 +0100 +@@ -60,6 +60,7 @@ + mutex_unlock(&dentry->d_inode->i_mutex); + return ret; + } ++EXPORT_SYMBOL(do_truncate); + + static long do_sys_truncate(const char __user *pathname, loff_t length) + { +diff -Nur linux-2.6.36.orig/fs/splice.c linux-2.6.36/fs/splice.c +--- linux-2.6.36.orig/fs/splice.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/fs/splice.c 2011-01-10 19:52:38.000000000 +0100 +@@ -1092,8 +1092,8 @@ + /* + * Attempt to initiate a splice from pipe to file. + */ +-static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +- loff_t *ppos, size_t len, unsigned int flags) ++long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) + { + ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, + loff_t *, size_t, unsigned int); +@@ -1116,13 +1116,14 @@ + + return splice_write(pipe, out, ppos, len, flags); + } ++EXPORT_SYMBOL(do_splice_from); + + /* + * Attempt to initiate a splice from a file to a pipe. + */ +-static long do_splice_to(struct file *in, loff_t *ppos, +- struct pipe_inode_info *pipe, size_t len, +- unsigned int flags) ++long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) + { + ssize_t (*splice_read)(struct file *, loff_t *, + struct pipe_inode_info *, size_t, unsigned int); +@@ -1142,6 +1143,7 @@ + + return splice_read(in, ppos, pipe, len, flags); + } ++EXPORT_SYMBOL(do_splice_to); + + /** + * splice_direct_to_actor - splices data directly between two non-pipes +diff -Nur linux-2.6.36.orig/fs/splice.c.orig linux-2.6.36/fs/splice.c.orig +--- linux-2.6.36.orig/fs/splice.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/fs/splice.c.orig 2011-01-10 19:52:38.000000000 +0100 +@@ -0,0 +1,2076 @@ ++/* ++ * "splice": joining two ropes together by interweaving their strands. ++ * ++ * This is the "extended pipe" functionality, where a pipe is used as ++ * an arbitrary in-memory buffer. Think of a pipe as a small kernel ++ * buffer that you can use to transfer data from one end to the other. ++ * ++ * The traditional unix read/write is extended with a "splice()" operation ++ * that transfers data buffers to or from a pipe buffer. ++ * ++ * Named by Larry McVoy, original implementation from Linus, extended by ++ * Jens to support splicing to files, network, direct splicing, etc and ++ * fixing lots of bugs. ++ * ++ * Copyright (C) 2005-2006 Jens Axboe ++ * Copyright (C) 2005-2006 Linus Torvalds ++ * Copyright (C) 2006 Ingo Molnar ++ * ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Attempt to steal a page from a pipe buffer. This should perhaps go into ++ * a vm helper function, it's already simplified quite a bit by the ++ * addition of remove_mapping(). If success is returned, the caller may ++ * attempt to reuse this page for another destination. ++ */ ++static int page_cache_pipe_buf_steal(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf) ++{ ++ struct page *page = buf->page; ++ struct address_space *mapping; ++ ++ lock_page(page); ++ ++ mapping = page_mapping(page); ++ if (mapping) { ++ WARN_ON(!PageUptodate(page)); ++ ++ /* ++ * At least for ext2 with nobh option, we need to wait on ++ * writeback completing on this page, since we'll remove it ++ * from the pagecache. Otherwise truncate wont wait on the ++ * page, allowing the disk blocks to be reused by someone else ++ * before we actually wrote our data to them. fs corruption ++ * ensues. ++ */ ++ wait_on_page_writeback(page); ++ ++ if (page_has_private(page) && ++ !try_to_release_page(page, GFP_KERNEL)) ++ goto out_unlock; ++ ++ /* ++ * If we succeeded in removing the mapping, set LRU flag ++ * and return good. ++ */ ++ if (remove_mapping(mapping, page)) { ++ buf->flags |= PIPE_BUF_FLAG_LRU; ++ return 0; ++ } ++ } ++ ++ /* ++ * Raced with truncate or failed to remove page from current ++ * address space, unlock and return failure. ++ */ ++out_unlock: ++ unlock_page(page); ++ return 1; ++} ++ ++static void page_cache_pipe_buf_release(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf) ++{ ++ page_cache_release(buf->page); ++ buf->flags &= ~PIPE_BUF_FLAG_LRU; ++} ++ ++/* ++ * Check whether the contents of buf is OK to access. Since the content ++ * is a page cache page, IO may be in flight. ++ */ ++static int page_cache_pipe_buf_confirm(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf) ++{ ++ struct page *page = buf->page; ++ int err; ++ ++ if (!PageUptodate(page)) { ++ lock_page(page); ++ ++ /* ++ * Page got truncated/unhashed. This will cause a 0-byte ++ * splice, if this is the first page. ++ */ ++ if (!page->mapping) { ++ err = -ENODATA; ++ goto error; ++ } ++ ++ /* ++ * Uh oh, read-error from disk. ++ */ ++ if (!PageUptodate(page)) { ++ err = -EIO; ++ goto error; ++ } ++ ++ /* ++ * Page is ok afterall, we are done. ++ */ ++ unlock_page(page); ++ } ++ ++ return 0; ++error: ++ unlock_page(page); ++ return err; ++} ++ ++static const struct pipe_buf_operations page_cache_pipe_buf_ops = { ++ .can_merge = 0, ++ .map = generic_pipe_buf_map, ++ .unmap = generic_pipe_buf_unmap, ++ .confirm = page_cache_pipe_buf_confirm, ++ .release = page_cache_pipe_buf_release, ++ .steal = page_cache_pipe_buf_steal, ++ .get = generic_pipe_buf_get, ++}; ++ ++static int user_page_pipe_buf_steal(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf) ++{ ++ if (!(buf->flags & PIPE_BUF_FLAG_GIFT)) ++ return 1; ++ ++ buf->flags |= PIPE_BUF_FLAG_LRU; ++ return generic_pipe_buf_steal(pipe, buf); ++} ++ ++static const struct pipe_buf_operations user_page_pipe_buf_ops = { ++ .can_merge = 0, ++ .map = generic_pipe_buf_map, ++ .unmap = generic_pipe_buf_unmap, ++ .confirm = generic_pipe_buf_confirm, ++ .release = page_cache_pipe_buf_release, ++ .steal = user_page_pipe_buf_steal, ++ .get = generic_pipe_buf_get, ++}; ++ ++/** ++ * splice_to_pipe - fill passed data into a pipe ++ * @pipe: pipe to fill ++ * @spd: data to fill ++ * ++ * Description: ++ * @spd contains a map of pages and len/offset tuples, along with ++ * the struct pipe_buf_operations associated with these pages. This ++ * function will link that data to the pipe. ++ * ++ */ ++ssize_t splice_to_pipe(struct pipe_inode_info *pipe, ++ struct splice_pipe_desc *spd) ++{ ++ unsigned int spd_pages = spd->nr_pages; ++ int ret, do_wakeup, page_nr; ++ ++ ret = 0; ++ do_wakeup = 0; ++ page_nr = 0; ++ ++ pipe_lock(pipe); ++ ++ for (;;) { ++ if (!pipe->readers) { ++ send_sig(SIGPIPE, current, 0); ++ if (!ret) ++ ret = -EPIPE; ++ break; ++ } ++ ++ if (pipe->nrbufs < pipe->buffers) { ++ int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1); ++ struct pipe_buffer *buf = pipe->bufs + newbuf; ++ ++ buf->page = spd->pages[page_nr]; ++ buf->offset = spd->partial[page_nr].offset; ++ buf->len = spd->partial[page_nr].len; ++ buf->private = spd->partial[page_nr].private; ++ buf->ops = spd->ops; ++ if (spd->flags & SPLICE_F_GIFT) ++ buf->flags |= PIPE_BUF_FLAG_GIFT; ++ ++ pipe->nrbufs++; ++ page_nr++; ++ ret += buf->len; ++ ++ if (pipe->inode) ++ do_wakeup = 1; ++ ++ if (!--spd->nr_pages) ++ break; ++ if (pipe->nrbufs < pipe->buffers) ++ continue; ++ ++ break; ++ } ++ ++ if (spd->flags & SPLICE_F_NONBLOCK) { ++ if (!ret) ++ ret = -EAGAIN; ++ break; ++ } ++ ++ if (signal_pending(current)) { ++ if (!ret) ++ ret = -ERESTARTSYS; ++ break; ++ } ++ ++ if (do_wakeup) { ++ smp_mb(); ++ if (waitqueue_active(&pipe->wait)) ++ wake_up_interruptible_sync(&pipe->wait); ++ kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); ++ do_wakeup = 0; ++ } ++ ++ pipe->waiting_writers++; ++ pipe_wait(pipe); ++ pipe->waiting_writers--; ++ } ++ ++ pipe_unlock(pipe); ++ ++ if (do_wakeup) { ++ smp_mb(); ++ if (waitqueue_active(&pipe->wait)) ++ wake_up_interruptible(&pipe->wait); ++ kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN); ++ } ++ ++ while (page_nr < spd_pages) ++ spd->spd_release(spd, page_nr++); ++ ++ return ret; ++} ++ ++static void spd_release_page(struct splice_pipe_desc *spd, unsigned int i) ++{ ++ page_cache_release(spd->pages[i]); ++} ++ ++/* ++ * Check if we need to grow the arrays holding pages and partial page ++ * descriptions. ++ */ ++int splice_grow_spd(struct pipe_inode_info *pipe, struct splice_pipe_desc *spd) ++{ ++ if (pipe->buffers <= PIPE_DEF_BUFFERS) ++ return 0; ++ ++ spd->pages = kmalloc(pipe->buffers * sizeof(struct page *), GFP_KERNEL); ++ spd->partial = kmalloc(pipe->buffers * sizeof(struct partial_page), GFP_KERNEL); ++ ++ if (spd->pages && spd->partial) ++ return 0; ++ ++ kfree(spd->pages); ++ kfree(spd->partial); ++ return -ENOMEM; ++} ++ ++void splice_shrink_spd(struct pipe_inode_info *pipe, ++ struct splice_pipe_desc *spd) ++{ ++ if (pipe->buffers <= PIPE_DEF_BUFFERS) ++ return; ++ ++ kfree(spd->pages); ++ kfree(spd->partial); ++} ++ ++static int ++__generic_file_splice_read(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ struct address_space *mapping = in->f_mapping; ++ unsigned int loff, nr_pages, req_pages; ++ struct page *pages[PIPE_DEF_BUFFERS]; ++ struct partial_page partial[PIPE_DEF_BUFFERS]; ++ struct page *page; ++ pgoff_t index, end_index; ++ loff_t isize; ++ int error, page_nr; ++ struct splice_pipe_desc spd = { ++ .pages = pages, ++ .partial = partial, ++ .flags = flags, ++ .ops = &page_cache_pipe_buf_ops, ++ .spd_release = spd_release_page, ++ }; ++ ++ if (splice_grow_spd(pipe, &spd)) ++ return -ENOMEM; ++ ++ index = *ppos >> PAGE_CACHE_SHIFT; ++ loff = *ppos & ~PAGE_CACHE_MASK; ++ req_pages = (len + loff + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; ++ nr_pages = min(req_pages, pipe->buffers); ++ ++ /* ++ * Lookup the (hopefully) full range of pages we need. ++ */ ++ spd.nr_pages = find_get_pages_contig(mapping, index, nr_pages, spd.pages); ++ index += spd.nr_pages; ++ ++ /* ++ * If find_get_pages_contig() returned fewer pages than we needed, ++ * readahead/allocate the rest and fill in the holes. ++ */ ++ if (spd.nr_pages < nr_pages) ++ page_cache_sync_readahead(mapping, &in->f_ra, in, ++ index, req_pages - spd.nr_pages); ++ ++ error = 0; ++ while (spd.nr_pages < nr_pages) { ++ /* ++ * Page could be there, find_get_pages_contig() breaks on ++ * the first hole. ++ */ ++ page = find_get_page(mapping, index); ++ if (!page) { ++ /* ++ * page didn't exist, allocate one. ++ */ ++ page = page_cache_alloc_cold(mapping); ++ if (!page) ++ break; ++ ++ error = add_to_page_cache_lru(page, mapping, index, ++ GFP_KERNEL); ++ if (unlikely(error)) { ++ page_cache_release(page); ++ if (error == -EEXIST) ++ continue; ++ break; ++ } ++ /* ++ * add_to_page_cache() locks the page, unlock it ++ * to avoid convoluting the logic below even more. ++ */ ++ unlock_page(page); ++ } ++ ++ spd.pages[spd.nr_pages++] = page; ++ index++; ++ } ++ ++ /* ++ * Now loop over the map and see if we need to start IO on any ++ * pages, fill in the partial map, etc. ++ */ ++ index = *ppos >> PAGE_CACHE_SHIFT; ++ nr_pages = spd.nr_pages; ++ spd.nr_pages = 0; ++ for (page_nr = 0; page_nr < nr_pages; page_nr++) { ++ unsigned int this_len; ++ ++ if (!len) ++ break; ++ ++ /* ++ * this_len is the max we'll use from this page ++ */ ++ this_len = min_t(unsigned long, len, PAGE_CACHE_SIZE - loff); ++ page = spd.pages[page_nr]; ++ ++ if (PageReadahead(page)) ++ page_cache_async_readahead(mapping, &in->f_ra, in, ++ page, index, req_pages - page_nr); ++ ++ /* ++ * If the page isn't uptodate, we may need to start io on it ++ */ ++ if (!PageUptodate(page)) { ++ lock_page(page); ++ ++ /* ++ * Page was truncated, or invalidated by the ++ * filesystem. Redo the find/create, but this time the ++ * page is kept locked, so there's no chance of another ++ * race with truncate/invalidate. ++ */ ++ if (!page->mapping) { ++ unlock_page(page); ++ page = find_or_create_page(mapping, index, ++ mapping_gfp_mask(mapping)); ++ ++ if (!page) { ++ error = -ENOMEM; ++ break; ++ } ++ page_cache_release(spd.pages[page_nr]); ++ spd.pages[page_nr] = page; ++ } ++ /* ++ * page was already under io and is now done, great ++ */ ++ if (PageUptodate(page)) { ++ unlock_page(page); ++ goto fill_it; ++ } ++ ++ /* ++ * need to read in the page ++ */ ++ error = mapping->a_ops->readpage(in, page); ++ if (unlikely(error)) { ++ /* ++ * We really should re-lookup the page here, ++ * but it complicates things a lot. Instead ++ * lets just do what we already stored, and ++ * we'll get it the next time we are called. ++ */ ++ if (error == AOP_TRUNCATED_PAGE) ++ error = 0; ++ ++ break; ++ } ++ } ++fill_it: ++ /* ++ * i_size must be checked after PageUptodate. ++ */ ++ isize = i_size_read(mapping->host); ++ end_index = (isize - 1) >> PAGE_CACHE_SHIFT; ++ if (unlikely(!isize || index > end_index)) ++ break; ++ ++ /* ++ * if this is the last page, see if we need to shrink ++ * the length and stop ++ */ ++ if (end_index == index) { ++ unsigned int plen; ++ ++ /* ++ * max good bytes in this page ++ */ ++ plen = ((isize - 1) & ~PAGE_CACHE_MASK) + 1; ++ if (plen <= loff) ++ break; ++ ++ /* ++ * force quit after adding this page ++ */ ++ this_len = min(this_len, plen - loff); ++ len = this_len; ++ } ++ ++ spd.partial[page_nr].offset = loff; ++ spd.partial[page_nr].len = this_len; ++ len -= this_len; ++ loff = 0; ++ spd.nr_pages++; ++ index++; ++ } ++ ++ /* ++ * Release any pages at the end, if we quit early. 'page_nr' is how far ++ * we got, 'nr_pages' is how many pages are in the map. ++ */ ++ while (page_nr < nr_pages) ++ page_cache_release(spd.pages[page_nr++]); ++ in->f_ra.prev_pos = (loff_t)index << PAGE_CACHE_SHIFT; ++ ++ if (spd.nr_pages) ++ error = splice_to_pipe(pipe, &spd); ++ ++ splice_shrink_spd(pipe, &spd); ++ return error; ++} ++ ++/** ++ * generic_file_splice_read - splice data from file to a pipe ++ * @in: file to splice from ++ * @ppos: position in @in ++ * @pipe: pipe to splice to ++ * @len: number of bytes to splice ++ * @flags: splice modifier flags ++ * ++ * Description: ++ * Will read pages from given file and fill them into a pipe. Can be ++ * used as long as the address_space operations for the source implements ++ * a readpage() hook. ++ * ++ */ ++ssize_t generic_file_splice_read(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ loff_t isize, left; ++ int ret; ++ ++ isize = i_size_read(in->f_mapping->host); ++ if (unlikely(*ppos >= isize)) ++ return 0; ++ ++ left = isize - *ppos; ++ if (unlikely(left < len)) ++ len = left; ++ ++ ret = __generic_file_splice_read(in, ppos, pipe, len, flags); ++ if (ret > 0) { ++ *ppos += ret; ++ file_accessed(in); ++ } ++ ++ return ret; ++} ++EXPORT_SYMBOL(generic_file_splice_read); ++ ++static const struct pipe_buf_operations default_pipe_buf_ops = { ++ .can_merge = 0, ++ .map = generic_pipe_buf_map, ++ .unmap = generic_pipe_buf_unmap, ++ .confirm = generic_pipe_buf_confirm, ++ .release = generic_pipe_buf_release, ++ .steal = generic_pipe_buf_steal, ++ .get = generic_pipe_buf_get, ++}; ++ ++static ssize_t kernel_readv(struct file *file, const struct iovec *vec, ++ unsigned long vlen, loff_t offset) ++{ ++ mm_segment_t old_fs; ++ loff_t pos = offset; ++ ssize_t res; ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ /* The cast to a user pointer is valid due to the set_fs() */ ++ res = vfs_readv(file, (const struct iovec __user *)vec, vlen, &pos); ++ set_fs(old_fs); ++ ++ return res; ++} ++ ++static ssize_t kernel_write(struct file *file, const char *buf, size_t count, ++ loff_t pos) ++{ ++ mm_segment_t old_fs; ++ ssize_t res; ++ ++ old_fs = get_fs(); ++ set_fs(get_ds()); ++ /* The cast to a user pointer is valid due to the set_fs() */ ++ res = vfs_write(file, (const char __user *)buf, count, &pos); ++ set_fs(old_fs); ++ ++ return res; ++} ++ ++ssize_t default_file_splice_read(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ unsigned int nr_pages; ++ unsigned int nr_freed; ++ size_t offset; ++ struct page *pages[PIPE_DEF_BUFFERS]; ++ struct partial_page partial[PIPE_DEF_BUFFERS]; ++ struct iovec *vec, __vec[PIPE_DEF_BUFFERS]; ++ ssize_t res; ++ size_t this_len; ++ int error; ++ int i; ++ struct splice_pipe_desc spd = { ++ .pages = pages, ++ .partial = partial, ++ .flags = flags, ++ .ops = &default_pipe_buf_ops, ++ .spd_release = spd_release_page, ++ }; ++ ++ if (splice_grow_spd(pipe, &spd)) ++ return -ENOMEM; ++ ++ res = -ENOMEM; ++ vec = __vec; ++ if (pipe->buffers > PIPE_DEF_BUFFERS) { ++ vec = kmalloc(pipe->buffers * sizeof(struct iovec), GFP_KERNEL); ++ if (!vec) ++ goto shrink_ret; ++ } ++ ++ offset = *ppos & ~PAGE_CACHE_MASK; ++ nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; ++ ++ for (i = 0; i < nr_pages && i < pipe->buffers && len; i++) { ++ struct page *page; ++ ++ page = alloc_page(GFP_USER); ++ error = -ENOMEM; ++ if (!page) ++ goto err; ++ ++ this_len = min_t(size_t, len, PAGE_CACHE_SIZE - offset); ++ vec[i].iov_base = (void __user *) page_address(page); ++ vec[i].iov_len = this_len; ++ spd.pages[i] = page; ++ spd.nr_pages++; ++ len -= this_len; ++ offset = 0; ++ } ++ ++ res = kernel_readv(in, vec, spd.nr_pages, *ppos); ++ if (res < 0) { ++ error = res; ++ goto err; ++ } ++ ++ error = 0; ++ if (!res) ++ goto err; ++ ++ nr_freed = 0; ++ for (i = 0; i < spd.nr_pages; i++) { ++ this_len = min_t(size_t, vec[i].iov_len, res); ++ spd.partial[i].offset = 0; ++ spd.partial[i].len = this_len; ++ if (!this_len) { ++ __free_page(spd.pages[i]); ++ spd.pages[i] = NULL; ++ nr_freed++; ++ } ++ res -= this_len; ++ } ++ spd.nr_pages -= nr_freed; ++ ++ res = splice_to_pipe(pipe, &spd); ++ if (res > 0) ++ *ppos += res; ++ ++shrink_ret: ++ if (vec != __vec) ++ kfree(vec); ++ splice_shrink_spd(pipe, &spd); ++ return res; ++ ++err: ++ for (i = 0; i < spd.nr_pages; i++) ++ __free_page(spd.pages[i]); ++ ++ res = error; ++ goto shrink_ret; ++} ++EXPORT_SYMBOL(default_file_splice_read); ++ ++/* ++ * Send 'sd->len' bytes to socket from 'sd->file' at position 'sd->pos' ++ * using sendpage(). Return the number of bytes sent. ++ */ ++static int pipe_to_sendpage(struct pipe_inode_info *pipe, ++ struct pipe_buffer *buf, struct splice_desc *sd) ++{ ++ struct file *file = sd->u.file; ++ loff_t pos = sd->pos; ++ int ret, more; ++ ++ ret = buf->ops->confirm(pipe, buf); ++ if (!ret) { ++ more = (sd->flags & SPLICE_F_MORE) || sd->len < sd->total_len; ++ if (file->f_op && file->f_op->sendpage) ++ ret = file->f_op->sendpage(file, buf->page, buf->offset, ++ sd->len, &pos, more); ++ else ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++/* ++ * This is a little more tricky than the file -> pipe splicing. There are ++ * basically three cases: ++ * ++ * - Destination page already exists in the address space and there ++ * are users of it. For that case we have no other option that ++ * copying the data. Tough luck. ++ * - Destination page already exists in the address space, but there ++ * are no users of it. Make sure it's uptodate, then drop it. Fall ++ * through to last case. ++ * - Destination page does not exist, we can add the pipe page to ++ * the page cache and avoid the copy. ++ * ++ * If asked to move pages to the output file (SPLICE_F_MOVE is set in ++ * sd->flags), we attempt to migrate pages from the pipe to the output ++ * file address space page cache. This is possible if no one else has ++ * the pipe page referenced outside of the pipe and page cache. If ++ * SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create ++ * a new page in the output file page cache and fill/dirty that. ++ */ ++int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ++ struct splice_desc *sd) ++{ ++ struct file *file = sd->u.file; ++ struct address_space *mapping = file->f_mapping; ++ unsigned int offset, this_len; ++ struct page *page; ++ void *fsdata; ++ int ret; ++ ++ /* ++ * make sure the data in this buffer is uptodate ++ */ ++ ret = buf->ops->confirm(pipe, buf); ++ if (unlikely(ret)) ++ return ret; ++ ++ offset = sd->pos & ~PAGE_CACHE_MASK; ++ ++ this_len = sd->len; ++ if (this_len + offset > PAGE_CACHE_SIZE) ++ this_len = PAGE_CACHE_SIZE - offset; ++ ++ ret = pagecache_write_begin(file, mapping, sd->pos, this_len, ++ AOP_FLAG_UNINTERRUPTIBLE, &page, &fsdata); ++ if (unlikely(ret)) ++ goto out; ++ ++ if (buf->page != page) { ++ /* ++ * Careful, ->map() uses KM_USER0! ++ */ ++ char *src = buf->ops->map(pipe, buf, 1); ++ char *dst = kmap_atomic(page, KM_USER1); ++ ++ memcpy(dst + offset, src + buf->offset, this_len); ++ flush_dcache_page(page); ++ kunmap_atomic(dst, KM_USER1); ++ buf->ops->unmap(pipe, buf, src); ++ } ++ ret = pagecache_write_end(file, mapping, sd->pos, this_len, this_len, ++ page, fsdata); ++out: ++ return ret; ++} ++EXPORT_SYMBOL(pipe_to_file); ++ ++static void wakeup_pipe_writers(struct pipe_inode_info *pipe) ++{ ++ smp_mb(); ++ if (waitqueue_active(&pipe->wait)) ++ wake_up_interruptible(&pipe->wait); ++ kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT); ++} ++ ++/** ++ * splice_from_pipe_feed - feed available data from a pipe to a file ++ * @pipe: pipe to splice from ++ * @sd: information to @actor ++ * @actor: handler that splices the data ++ * ++ * Description: ++ * This function loops over the pipe and calls @actor to do the ++ * actual moving of a single struct pipe_buffer to the desired ++ * destination. It returns when there's no more buffers left in ++ * the pipe or if the requested number of bytes (@sd->total_len) ++ * have been copied. It returns a positive number (one) if the ++ * pipe needs to be filled with more data, zero if the required ++ * number of bytes have been copied and -errno on error. ++ * ++ * This, together with splice_from_pipe_{begin,end,next}, may be ++ * used to implement the functionality of __splice_from_pipe() when ++ * locking is required around copying the pipe buffers to the ++ * destination. ++ */ ++int splice_from_pipe_feed(struct pipe_inode_info *pipe, struct splice_desc *sd, ++ splice_actor *actor) ++{ ++ int ret; ++ ++ while (pipe->nrbufs) { ++ struct pipe_buffer *buf = pipe->bufs + pipe->curbuf; ++ const struct pipe_buf_operations *ops = buf->ops; ++ ++ sd->len = buf->len; ++ if (sd->len > sd->total_len) ++ sd->len = sd->total_len; ++ ++ ret = actor(pipe, buf, sd); ++ if (ret <= 0) { ++ if (ret == -ENODATA) ++ ret = 0; ++ return ret; ++ } ++ buf->offset += ret; ++ buf->len -= ret; ++ ++ sd->num_spliced += ret; ++ sd->len -= ret; ++ sd->pos += ret; ++ sd->total_len -= ret; ++ ++ if (!buf->len) { ++ buf->ops = NULL; ++ ops->release(pipe, buf); ++ pipe->curbuf = (pipe->curbuf + 1) & (pipe->buffers - 1); ++ pipe->nrbufs--; ++ if (pipe->inode) ++ sd->need_wakeup = true; ++ } ++ ++ if (!sd->total_len) ++ return 0; ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL(splice_from_pipe_feed); ++ ++/** ++ * splice_from_pipe_next - wait for some data to splice from ++ * @pipe: pipe to splice from ++ * @sd: information about the splice operation ++ * ++ * Description: ++ * This function will wait for some data and return a positive ++ * value (one) if pipe buffers are available. It will return zero ++ * or -errno if no more data needs to be spliced. ++ */ ++int splice_from_pipe_next(struct pipe_inode_info *pipe, struct splice_desc *sd) ++{ ++ while (!pipe->nrbufs) { ++ if (!pipe->writers) ++ return 0; ++ ++ if (!pipe->waiting_writers && sd->num_spliced) ++ return 0; ++ ++ if (sd->flags & SPLICE_F_NONBLOCK) ++ return -EAGAIN; ++ ++ if (signal_pending(current)) ++ return -ERESTARTSYS; ++ ++ if (sd->need_wakeup) { ++ wakeup_pipe_writers(pipe); ++ sd->need_wakeup = false; ++ } ++ ++ pipe_wait(pipe); ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL(splice_from_pipe_next); ++ ++/** ++ * splice_from_pipe_begin - start splicing from pipe ++ * @sd: information about the splice operation ++ * ++ * Description: ++ * This function should be called before a loop containing ++ * splice_from_pipe_next() and splice_from_pipe_feed() to ++ * initialize the necessary fields of @sd. ++ */ ++void splice_from_pipe_begin(struct splice_desc *sd) ++{ ++ sd->num_spliced = 0; ++ sd->need_wakeup = false; ++} ++EXPORT_SYMBOL(splice_from_pipe_begin); ++ ++/** ++ * splice_from_pipe_end - finish splicing from pipe ++ * @pipe: pipe to splice from ++ * @sd: information about the splice operation ++ * ++ * Description: ++ * This function will wake up pipe writers if necessary. It should ++ * be called after a loop containing splice_from_pipe_next() and ++ * splice_from_pipe_feed(). ++ */ ++void splice_from_pipe_end(struct pipe_inode_info *pipe, struct splice_desc *sd) ++{ ++ if (sd->need_wakeup) ++ wakeup_pipe_writers(pipe); ++} ++EXPORT_SYMBOL(splice_from_pipe_end); ++ ++/** ++ * __splice_from_pipe - splice data from a pipe to given actor ++ * @pipe: pipe to splice from ++ * @sd: information to @actor ++ * @actor: handler that splices the data ++ * ++ * Description: ++ * This function does little more than loop over the pipe and call ++ * @actor to do the actual moving of a single struct pipe_buffer to ++ * the desired destination. See pipe_to_file, pipe_to_sendpage, or ++ * pipe_to_user. ++ * ++ */ ++ssize_t __splice_from_pipe(struct pipe_inode_info *pipe, struct splice_desc *sd, ++ splice_actor *actor) ++{ ++ int ret; ++ ++ splice_from_pipe_begin(sd); ++ do { ++ ret = splice_from_pipe_next(pipe, sd); ++ if (ret > 0) ++ ret = splice_from_pipe_feed(pipe, sd, actor); ++ } while (ret > 0); ++ splice_from_pipe_end(pipe, sd); ++ ++ return sd->num_spliced ? sd->num_spliced : ret; ++} ++EXPORT_SYMBOL(__splice_from_pipe); ++ ++/** ++ * splice_from_pipe - splice data from a pipe to a file ++ * @pipe: pipe to splice from ++ * @out: file to splice to ++ * @ppos: position in @out ++ * @len: how many bytes to splice ++ * @flags: splice modifier flags ++ * @actor: handler that splices the data ++ * ++ * Description: ++ * See __splice_from_pipe. This function locks the pipe inode, ++ * otherwise it's identical to __splice_from_pipe(). ++ * ++ */ ++ssize_t splice_from_pipe(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags, ++ splice_actor *actor) ++{ ++ ssize_t ret; ++ struct splice_desc sd = { ++ .total_len = len, ++ .flags = flags, ++ .pos = *ppos, ++ .u.file = out, ++ }; ++ ++ pipe_lock(pipe); ++ ret = __splice_from_pipe(pipe, &sd, actor); ++ pipe_unlock(pipe); ++ ++ return ret; ++} ++ ++/** ++ * generic_file_splice_write - splice data from a pipe to a file ++ * @pipe: pipe info ++ * @out: file to write to ++ * @ppos: position in @out ++ * @len: number of bytes to splice ++ * @flags: splice modifier flags ++ * ++ * Description: ++ * Will either move or copy pages (determined by @flags options) from ++ * the given pipe inode to the given file. ++ * ++ */ ++ssize_t ++generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ struct address_space *mapping = out->f_mapping; ++ struct inode *inode = mapping->host; ++ struct splice_desc sd = { ++ .total_len = len, ++ .flags = flags, ++ .pos = *ppos, ++ .u.file = out, ++ }; ++ ssize_t ret; ++ ++ pipe_lock(pipe); ++ ++ splice_from_pipe_begin(&sd); ++ do { ++ ret = splice_from_pipe_next(pipe, &sd); ++ if (ret <= 0) ++ break; ++ ++ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); ++ ret = file_remove_suid(out); ++ if (!ret) { ++ file_update_time(out); ++ ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file); ++ } ++ mutex_unlock(&inode->i_mutex); ++ } while (ret > 0); ++ splice_from_pipe_end(pipe, &sd); ++ ++ pipe_unlock(pipe); ++ ++ if (sd.num_spliced) ++ ret = sd.num_spliced; ++ ++ if (ret > 0) { ++ unsigned long nr_pages; ++ int err; ++ ++ nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; ++ ++ err = generic_write_sync(out, *ppos, ret); ++ if (err) ++ ret = err; ++ else ++ *ppos += ret; ++ balance_dirty_pages_ratelimited_nr(mapping, nr_pages); ++ } ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL(generic_file_splice_write); ++ ++static int write_pipe_buf(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ++ struct splice_desc *sd) ++{ ++ int ret; ++ void *data; ++ ++ ret = buf->ops->confirm(pipe, buf); ++ if (ret) ++ return ret; ++ ++ data = buf->ops->map(pipe, buf, 0); ++ ret = kernel_write(sd->u.file, data + buf->offset, sd->len, sd->pos); ++ buf->ops->unmap(pipe, buf, data); ++ ++ return ret; ++} ++ ++static ssize_t default_file_splice_write(struct pipe_inode_info *pipe, ++ struct file *out, loff_t *ppos, ++ size_t len, unsigned int flags) ++{ ++ ssize_t ret; ++ ++ ret = splice_from_pipe(pipe, out, ppos, len, flags, write_pipe_buf); ++ if (ret > 0) ++ *ppos += ret; ++ ++ return ret; ++} ++ ++/** ++ * generic_splice_sendpage - splice data from a pipe to a socket ++ * @pipe: pipe to splice from ++ * @out: socket to write to ++ * @ppos: position in @out ++ * @len: number of bytes to splice ++ * @flags: splice modifier flags ++ * ++ * Description: ++ * Will send @len bytes from the pipe to a network socket. No data copying ++ * is involved. ++ * ++ */ ++ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ return splice_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage); ++} ++ ++EXPORT_SYMBOL(generic_splice_sendpage); ++ ++/* ++ * Attempt to initiate a splice from pipe to file. ++ */ ++static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags) ++{ ++ ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, ++ loff_t *, size_t, unsigned int); ++ int ret; ++ ++ if (unlikely(!(out->f_mode & FMODE_WRITE))) ++ return -EBADF; ++ ++ if (unlikely(out->f_flags & O_APPEND)) ++ return -EINVAL; ++ ++ ret = rw_verify_area(WRITE, out, ppos, len); ++ if (unlikely(ret < 0)) ++ return ret; ++ ++ if (out->f_op && out->f_op->splice_write) ++ splice_write = out->f_op->splice_write; ++ else ++ splice_write = default_file_splice_write; ++ ++ return splice_write(pipe, out, ppos, len, flags); ++} ++EXPORT_SYMBOL(do_splice_from); ++ ++/* ++ * Attempt to initiate a splice from a file to a pipe. ++ */ ++static long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags) ++{ ++ ssize_t (*splice_read)(struct file *, loff_t *, ++ struct pipe_inode_info *, size_t, unsigned int); ++ int ret; ++ ++ if (unlikely(!(in->f_mode & FMODE_READ))) ++ return -EBADF; ++ ++ ret = rw_verify_area(READ, in, ppos, len); ++ if (unlikely(ret < 0)) ++ return ret; ++ ++ if (in->f_op && in->f_op->splice_read) ++ splice_read = in->f_op->splice_read; ++ else ++ splice_read = default_file_splice_read; ++ ++ return splice_read(in, ppos, pipe, len, flags); ++} ++EXPORT_SYMBOL(do_splice_to); ++ ++/** ++ * splice_direct_to_actor - splices data directly between two non-pipes ++ * @in: file to splice from ++ * @sd: actor information on where to splice to ++ * @actor: handles the data splicing ++ * ++ * Description: ++ * This is a special case helper to splice directly between two ++ * points, without requiring an explicit pipe. Internally an allocated ++ * pipe is cached in the process, and reused during the lifetime of ++ * that process. ++ * ++ */ ++ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd, ++ splice_direct_actor *actor) ++{ ++ struct pipe_inode_info *pipe; ++ long ret, bytes; ++ umode_t i_mode; ++ size_t len; ++ int i, flags; ++ ++ /* ++ * We require the input being a regular file, as we don't want to ++ * randomly drop data for eg socket -> socket splicing. Use the ++ * piped splicing for that! ++ */ ++ i_mode = in->f_path.dentry->d_inode->i_mode; ++ if (unlikely(!S_ISREG(i_mode) && !S_ISBLK(i_mode))) ++ return -EINVAL; ++ ++ /* ++ * neither in nor out is a pipe, setup an internal pipe attached to ++ * 'out' and transfer the wanted data from 'in' to 'out' through that ++ */ ++ pipe = current->splice_pipe; ++ if (unlikely(!pipe)) { ++ pipe = alloc_pipe_info(NULL); ++ if (!pipe) ++ return -ENOMEM; ++ ++ /* ++ * We don't have an immediate reader, but we'll read the stuff ++ * out of the pipe right after the splice_to_pipe(). So set ++ * PIPE_READERS appropriately. ++ */ ++ pipe->readers = 1; ++ ++ current->splice_pipe = pipe; ++ } ++ ++ /* ++ * Do the splice. ++ */ ++ ret = 0; ++ bytes = 0; ++ len = sd->total_len; ++ flags = sd->flags; ++ ++ /* ++ * Don't block on output, we have to drain the direct pipe. ++ */ ++ sd->flags &= ~SPLICE_F_NONBLOCK; ++ ++ while (len) { ++ size_t read_len; ++ loff_t pos = sd->pos, prev_pos = pos; ++ ++ ret = do_splice_to(in, &pos, pipe, len, flags); ++ if (unlikely(ret <= 0)) ++ goto out_release; ++ ++ read_len = ret; ++ sd->total_len = read_len; ++ ++ /* ++ * NOTE: nonblocking mode only applies to the input. We ++ * must not do the output in nonblocking mode as then we ++ * could get stuck data in the internal pipe: ++ */ ++ ret = actor(pipe, sd); ++ if (unlikely(ret <= 0)) { ++ sd->pos = prev_pos; ++ goto out_release; ++ } ++ ++ bytes += ret; ++ len -= ret; ++ sd->pos = pos; ++ ++ if (ret < read_len) { ++ sd->pos = prev_pos + ret; ++ goto out_release; ++ } ++ } ++ ++done: ++ pipe->nrbufs = pipe->curbuf = 0; ++ file_accessed(in); ++ return bytes; ++ ++out_release: ++ /* ++ * If we did an incomplete transfer we must release ++ * the pipe buffers in question: ++ */ ++ for (i = 0; i < pipe->buffers; i++) { ++ struct pipe_buffer *buf = pipe->bufs + i; ++ ++ if (buf->ops) { ++ buf->ops->release(pipe, buf); ++ buf->ops = NULL; ++ } ++ } ++ ++ if (!bytes) ++ bytes = ret; ++ ++ goto done; ++} ++EXPORT_SYMBOL(splice_direct_to_actor); ++ ++static int direct_splice_actor(struct pipe_inode_info *pipe, ++ struct splice_desc *sd) ++{ ++ struct file *file = sd->u.file; ++ ++ return do_splice_from(pipe, file, &file->f_pos, sd->total_len, ++ sd->flags); ++} ++ ++/** ++ * do_splice_direct - splices data directly between two files ++ * @in: file to splice from ++ * @ppos: input file offset ++ * @out: file to splice to ++ * @len: number of bytes to splice ++ * @flags: splice modifier flags ++ * ++ * Description: ++ * For use by do_sendfile(). splice can easily emulate sendfile, but ++ * doing it in the application would incur an extra system call ++ * (splice in + splice out, as compared to just sendfile()). So this helper ++ * can splice directly through a process-private pipe. ++ * ++ */ ++long do_splice_direct(struct file *in, loff_t *ppos, struct file *out, ++ size_t len, unsigned int flags) ++{ ++ struct splice_desc sd = { ++ .len = len, ++ .total_len = len, ++ .flags = flags, ++ .pos = *ppos, ++ .u.file = out, ++ }; ++ long ret; ++ ++ ret = splice_direct_to_actor(in, &sd, direct_splice_actor); ++ if (ret > 0) ++ *ppos = sd.pos; ++ ++ return ret; ++} ++ ++static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, ++ struct pipe_inode_info *opipe, ++ size_t len, unsigned int flags); ++/* ++ * After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same ++ * location, so checking ->i_pipe is not enough to verify that this is a ++ * pipe. ++ */ ++static inline struct pipe_inode_info *pipe_info(struct inode *inode) ++{ ++ if (S_ISFIFO(inode->i_mode)) ++ return inode->i_pipe; ++ ++ return NULL; ++} ++ ++/* ++ * Determine where to splice to/from. ++ */ ++static long do_splice(struct file *in, loff_t __user *off_in, ++ struct file *out, loff_t __user *off_out, ++ size_t len, unsigned int flags) ++{ ++ struct pipe_inode_info *ipipe; ++ struct pipe_inode_info *opipe; ++ loff_t offset, *off; ++ long ret; ++ ++ ipipe = pipe_info(in->f_path.dentry->d_inode); ++ opipe = pipe_info(out->f_path.dentry->d_inode); ++ ++ if (ipipe && opipe) { ++ if (off_in || off_out) ++ return -ESPIPE; ++ ++ if (!(in->f_mode & FMODE_READ)) ++ return -EBADF; ++ ++ if (!(out->f_mode & FMODE_WRITE)) ++ return -EBADF; ++ ++ /* Splicing to self would be fun, but... */ ++ if (ipipe == opipe) ++ return -EINVAL; ++ ++ return splice_pipe_to_pipe(ipipe, opipe, len, flags); ++ } ++ ++ if (ipipe) { ++ if (off_in) ++ return -ESPIPE; ++ if (off_out) { ++ if (!(out->f_mode & FMODE_PWRITE)) ++ return -EINVAL; ++ if (copy_from_user(&offset, off_out, sizeof(loff_t))) ++ return -EFAULT; ++ off = &offset; ++ } else ++ off = &out->f_pos; ++ ++ ret = do_splice_from(ipipe, out, off, len, flags); ++ ++ if (off_out && copy_to_user(off_out, off, sizeof(loff_t))) ++ ret = -EFAULT; ++ ++ return ret; ++ } ++ ++ if (opipe) { ++ if (off_out) ++ return -ESPIPE; ++ if (off_in) { ++ if (!(in->f_mode & FMODE_PREAD)) ++ return -EINVAL; ++ if (copy_from_user(&offset, off_in, sizeof(loff_t))) ++ return -EFAULT; ++ off = &offset; ++ } else ++ off = &in->f_pos; ++ ++ ret = do_splice_to(in, off, opipe, len, flags); ++ ++ if (off_in && copy_to_user(off_in, off, sizeof(loff_t))) ++ ret = -EFAULT; ++ ++ return ret; ++ } ++ ++ return -EINVAL; ++} ++ ++/* ++ * Map an iov into an array of pages and offset/length tupples. With the ++ * partial_page structure, we can map several non-contiguous ranges into ++ * our ones pages[] map instead of splitting that operation into pieces. ++ * Could easily be exported as a generic helper for other users, in which ++ * case one would probably want to add a 'max_nr_pages' parameter as well. ++ */ ++static int get_iovec_page_array(const struct iovec __user *iov, ++ unsigned int nr_vecs, struct page **pages, ++ struct partial_page *partial, int aligned, ++ unsigned int pipe_buffers) ++{ ++ int buffers = 0, error = 0; ++ ++ while (nr_vecs) { ++ unsigned long off, npages; ++ struct iovec entry; ++ void __user *base; ++ size_t len; ++ int i; ++ ++ error = -EFAULT; ++ if (copy_from_user(&entry, iov, sizeof(entry))) ++ break; ++ ++ base = entry.iov_base; ++ len = entry.iov_len; ++ ++ /* ++ * Sanity check this iovec. 0 read succeeds. ++ */ ++ error = 0; ++ if (unlikely(!len)) ++ break; ++ error = -EFAULT; ++ if (!access_ok(VERIFY_READ, base, len)) ++ break; ++ ++ /* ++ * Get this base offset and number of pages, then map ++ * in the user pages. ++ */ ++ off = (unsigned long) base & ~PAGE_MASK; ++ ++ /* ++ * If asked for alignment, the offset must be zero and the ++ * length a multiple of the PAGE_SIZE. ++ */ ++ error = -EINVAL; ++ if (aligned && (off || len & ~PAGE_MASK)) ++ break; ++ ++ npages = (off + len + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ if (npages > pipe_buffers - buffers) ++ npages = pipe_buffers - buffers; ++ ++ error = get_user_pages_fast((unsigned long)base, npages, ++ 0, &pages[buffers]); ++ ++ if (unlikely(error <= 0)) ++ break; ++ ++ /* ++ * Fill this contiguous range into the partial page map. ++ */ ++ for (i = 0; i < error; i++) { ++ const int plen = min_t(size_t, len, PAGE_SIZE - off); ++ ++ partial[buffers].offset = off; ++ partial[buffers].len = plen; ++ ++ off = 0; ++ len -= plen; ++ buffers++; ++ } ++ ++ /* ++ * We didn't complete this iov, stop here since it probably ++ * means we have to move some of this into a pipe to ++ * be able to continue. ++ */ ++ if (len) ++ break; ++ ++ /* ++ * Don't continue if we mapped fewer pages than we asked for, ++ * or if we mapped the max number of pages that we have ++ * room for. ++ */ ++ if (error < npages || buffers == pipe_buffers) ++ break; ++ ++ nr_vecs--; ++ iov++; ++ } ++ ++ if (buffers) ++ return buffers; ++ ++ return error; ++} ++ ++static int pipe_to_user(struct pipe_inode_info *pipe, struct pipe_buffer *buf, ++ struct splice_desc *sd) ++{ ++ char *src; ++ int ret; ++ ++ ret = buf->ops->confirm(pipe, buf); ++ if (unlikely(ret)) ++ return ret; ++ ++ /* ++ * See if we can use the atomic maps, by prefaulting in the ++ * pages and doing an atomic copy ++ */ ++ if (!fault_in_pages_writeable(sd->u.userptr, sd->len)) { ++ src = buf->ops->map(pipe, buf, 1); ++ ret = __copy_to_user_inatomic(sd->u.userptr, src + buf->offset, ++ sd->len); ++ buf->ops->unmap(pipe, buf, src); ++ if (!ret) { ++ ret = sd->len; ++ goto out; ++ } ++ } ++ ++ /* ++ * No dice, use slow non-atomic map and copy ++ */ ++ src = buf->ops->map(pipe, buf, 0); ++ ++ ret = sd->len; ++ if (copy_to_user(sd->u.userptr, src + buf->offset, sd->len)) ++ ret = -EFAULT; ++ ++ buf->ops->unmap(pipe, buf, src); ++out: ++ if (ret > 0) ++ sd->u.userptr += ret; ++ return ret; ++} ++ ++/* ++ * For lack of a better implementation, implement vmsplice() to userspace ++ * as a simple copy of the pipes pages to the user iov. ++ */ ++static long vmsplice_to_user(struct file *file, const struct iovec __user *iov, ++ unsigned long nr_segs, unsigned int flags) ++{ ++ struct pipe_inode_info *pipe; ++ struct splice_desc sd; ++ ssize_t size; ++ int error; ++ long ret; ++ ++ pipe = pipe_info(file->f_path.dentry->d_inode); ++ if (!pipe) ++ return -EBADF; ++ ++ pipe_lock(pipe); ++ ++ error = ret = 0; ++ while (nr_segs) { ++ void __user *base; ++ size_t len; ++ ++ /* ++ * Get user address base and length for this iovec. ++ */ ++ error = get_user(base, &iov->iov_base); ++ if (unlikely(error)) ++ break; ++ error = get_user(len, &iov->iov_len); ++ if (unlikely(error)) ++ break; ++ ++ /* ++ * Sanity check this iovec. 0 read succeeds. ++ */ ++ if (unlikely(!len)) ++ break; ++ if (unlikely(!base)) { ++ error = -EFAULT; ++ break; ++ } ++ ++ if (unlikely(!access_ok(VERIFY_WRITE, base, len))) { ++ error = -EFAULT; ++ break; ++ } ++ ++ sd.len = 0; ++ sd.total_len = len; ++ sd.flags = flags; ++ sd.u.userptr = base; ++ sd.pos = 0; ++ ++ size = __splice_from_pipe(pipe, &sd, pipe_to_user); ++ if (size < 0) { ++ if (!ret) ++ ret = size; ++ ++ break; ++ } ++ ++ ret += size; ++ ++ if (size < len) ++ break; ++ ++ nr_segs--; ++ iov++; ++ } ++ ++ pipe_unlock(pipe); ++ ++ if (!ret) ++ ret = error; ++ ++ return ret; ++} ++ ++/* ++ * vmsplice splices a user address range into a pipe. It can be thought of ++ * as splice-from-memory, where the regular splice is splice-from-file (or ++ * to file). In both cases the output is a pipe, naturally. ++ */ ++static long vmsplice_to_pipe(struct file *file, const struct iovec __user *iov, ++ unsigned long nr_segs, unsigned int flags) ++{ ++ struct pipe_inode_info *pipe; ++ struct page *pages[PIPE_DEF_BUFFERS]; ++ struct partial_page partial[PIPE_DEF_BUFFERS]; ++ struct splice_pipe_desc spd = { ++ .pages = pages, ++ .partial = partial, ++ .flags = flags, ++ .ops = &user_page_pipe_buf_ops, ++ .spd_release = spd_release_page, ++ }; ++ long ret; ++ ++ pipe = pipe_info(file->f_path.dentry->d_inode); ++ if (!pipe) ++ return -EBADF; ++ ++ if (splice_grow_spd(pipe, &spd)) ++ return -ENOMEM; ++ ++ spd.nr_pages = get_iovec_page_array(iov, nr_segs, spd.pages, ++ spd.partial, flags & SPLICE_F_GIFT, ++ pipe->buffers); ++ if (spd.nr_pages <= 0) ++ ret = spd.nr_pages; ++ else ++ ret = splice_to_pipe(pipe, &spd); ++ ++ splice_shrink_spd(pipe, &spd); ++ return ret; ++} ++ ++/* ++ * Note that vmsplice only really supports true splicing _from_ user memory ++ * to a pipe, not the other way around. Splicing from user memory is a simple ++ * operation that can be supported without any funky alignment restrictions ++ * or nasty vm tricks. We simply map in the user memory and fill them into ++ * a pipe. The reverse isn't quite as easy, though. There are two possible ++ * solutions for that: ++ * ++ * - memcpy() the data internally, at which point we might as well just ++ * do a regular read() on the buffer anyway. ++ * - Lots of nasty vm tricks, that are neither fast nor flexible (it ++ * has restriction limitations on both ends of the pipe). ++ * ++ * Currently we punt and implement it as a normal copy, see pipe_to_user(). ++ * ++ */ ++SYSCALL_DEFINE4(vmsplice, int, fd, const struct iovec __user *, iov, ++ unsigned long, nr_segs, unsigned int, flags) ++{ ++ struct file *file; ++ long error; ++ int fput; ++ ++ if (unlikely(nr_segs > UIO_MAXIOV)) ++ return -EINVAL; ++ else if (unlikely(!nr_segs)) ++ return 0; ++ ++ error = -EBADF; ++ file = fget_light(fd, &fput); ++ if (file) { ++ if (file->f_mode & FMODE_WRITE) ++ error = vmsplice_to_pipe(file, iov, nr_segs, flags); ++ else if (file->f_mode & FMODE_READ) ++ error = vmsplice_to_user(file, iov, nr_segs, flags); ++ ++ fput_light(file, fput); ++ } ++ ++ return error; ++} ++ ++SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in, ++ int, fd_out, loff_t __user *, off_out, ++ size_t, len, unsigned int, flags) ++{ ++ long error; ++ struct file *in, *out; ++ int fput_in, fput_out; ++ ++ if (unlikely(!len)) ++ return 0; ++ ++ error = -EBADF; ++ in = fget_light(fd_in, &fput_in); ++ if (in) { ++ if (in->f_mode & FMODE_READ) { ++ out = fget_light(fd_out, &fput_out); ++ if (out) { ++ if (out->f_mode & FMODE_WRITE) ++ error = do_splice(in, off_in, ++ out, off_out, ++ len, flags); ++ fput_light(out, fput_out); ++ } ++ } ++ ++ fput_light(in, fput_in); ++ } ++ ++ return error; ++} ++ ++/* ++ * Make sure there's data to read. Wait for input if we can, otherwise ++ * return an appropriate error. ++ */ ++static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags) ++{ ++ int ret; ++ ++ /* ++ * Check ->nrbufs without the inode lock first. This function ++ * is speculative anyways, so missing one is ok. ++ */ ++ if (pipe->nrbufs) ++ return 0; ++ ++ ret = 0; ++ pipe_lock(pipe); ++ ++ while (!pipe->nrbufs) { ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ if (!pipe->writers) ++ break; ++ if (!pipe->waiting_writers) { ++ if (flags & SPLICE_F_NONBLOCK) { ++ ret = -EAGAIN; ++ break; ++ } ++ } ++ pipe_wait(pipe); ++ } ++ ++ pipe_unlock(pipe); ++ return ret; ++} ++ ++/* ++ * Make sure there's writeable room. Wait for room if we can, otherwise ++ * return an appropriate error. ++ */ ++static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags) ++{ ++ int ret; ++ ++ /* ++ * Check ->nrbufs without the inode lock first. This function ++ * is speculative anyways, so missing one is ok. ++ */ ++ if (pipe->nrbufs < pipe->buffers) ++ return 0; ++ ++ ret = 0; ++ pipe_lock(pipe); ++ ++ while (pipe->nrbufs >= pipe->buffers) { ++ if (!pipe->readers) { ++ send_sig(SIGPIPE, current, 0); ++ ret = -EPIPE; ++ break; ++ } ++ if (flags & SPLICE_F_NONBLOCK) { ++ ret = -EAGAIN; ++ break; ++ } ++ if (signal_pending(current)) { ++ ret = -ERESTARTSYS; ++ break; ++ } ++ pipe->waiting_writers++; ++ pipe_wait(pipe); ++ pipe->waiting_writers--; ++ } ++ ++ pipe_unlock(pipe); ++ return ret; ++} ++ ++/* ++ * Splice contents of ipipe to opipe. ++ */ ++static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe, ++ struct pipe_inode_info *opipe, ++ size_t len, unsigned int flags) ++{ ++ struct pipe_buffer *ibuf, *obuf; ++ int ret = 0, nbuf; ++ bool input_wakeup = false; ++ ++ ++retry: ++ ret = ipipe_prep(ipipe, flags); ++ if (ret) ++ return ret; ++ ++ ret = opipe_prep(opipe, flags); ++ if (ret) ++ return ret; ++ ++ /* ++ * Potential ABBA deadlock, work around it by ordering lock ++ * grabbing by pipe info address. Otherwise two different processes ++ * could deadlock (one doing tee from A -> B, the other from B -> A). ++ */ ++ pipe_double_lock(ipipe, opipe); ++ ++ do { ++ if (!opipe->readers) { ++ send_sig(SIGPIPE, current, 0); ++ if (!ret) ++ ret = -EPIPE; ++ break; ++ } ++ ++ if (!ipipe->nrbufs && !ipipe->writers) ++ break; ++ ++ /* ++ * Cannot make any progress, because either the input ++ * pipe is empty or the output pipe is full. ++ */ ++ if (!ipipe->nrbufs || opipe->nrbufs >= opipe->buffers) { ++ /* Already processed some buffers, break */ ++ if (ret) ++ break; ++ ++ if (flags & SPLICE_F_NONBLOCK) { ++ ret = -EAGAIN; ++ break; ++ } ++ ++ /* ++ * We raced with another reader/writer and haven't ++ * managed to process any buffers. A zero return ++ * value means EOF, so retry instead. ++ */ ++ pipe_unlock(ipipe); ++ pipe_unlock(opipe); ++ goto retry; ++ } ++ ++ ibuf = ipipe->bufs + ipipe->curbuf; ++ nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1); ++ obuf = opipe->bufs + nbuf; ++ ++ if (len >= ibuf->len) { ++ /* ++ * Simply move the whole buffer from ipipe to opipe ++ */ ++ *obuf = *ibuf; ++ ibuf->ops = NULL; ++ opipe->nrbufs++; ++ ipipe->curbuf = (ipipe->curbuf + 1) & (ipipe->buffers - 1); ++ ipipe->nrbufs--; ++ input_wakeup = true; ++ } else { ++ /* ++ * Get a reference to this pipe buffer, ++ * so we can copy the contents over. ++ */ ++ ibuf->ops->get(ipipe, ibuf); ++ *obuf = *ibuf; ++ ++ /* ++ * Don't inherit the gift flag, we need to ++ * prevent multiple steals of this page. ++ */ ++ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; ++ ++ obuf->len = len; ++ opipe->nrbufs++; ++ ibuf->offset += obuf->len; ++ ibuf->len -= obuf->len; ++ } ++ ret += obuf->len; ++ len -= obuf->len; ++ } while (len); ++ ++ pipe_unlock(ipipe); ++ pipe_unlock(opipe); ++ ++ /* ++ * If we put data in the output pipe, wakeup any potential readers. ++ */ ++ if (ret > 0) { ++ smp_mb(); ++ if (waitqueue_active(&opipe->wait)) ++ wake_up_interruptible(&opipe->wait); ++ kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN); ++ } ++ if (input_wakeup) ++ wakeup_pipe_writers(ipipe); ++ ++ return ret; ++} ++ ++/* ++ * Link contents of ipipe to opipe. ++ */ ++static int link_pipe(struct pipe_inode_info *ipipe, ++ struct pipe_inode_info *opipe, ++ size_t len, unsigned int flags) ++{ ++ struct pipe_buffer *ibuf, *obuf; ++ int ret = 0, i = 0, nbuf; ++ ++ /* ++ * Potential ABBA deadlock, work around it by ordering lock ++ * grabbing by pipe info address. Otherwise two different processes ++ * could deadlock (one doing tee from A -> B, the other from B -> A). ++ */ ++ pipe_double_lock(ipipe, opipe); ++ ++ do { ++ if (!opipe->readers) { ++ send_sig(SIGPIPE, current, 0); ++ if (!ret) ++ ret = -EPIPE; ++ break; ++ } ++ ++ /* ++ * If we have iterated all input buffers or ran out of ++ * output room, break. ++ */ ++ if (i >= ipipe->nrbufs || opipe->nrbufs >= opipe->buffers) ++ break; ++ ++ ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (ipipe->buffers-1)); ++ nbuf = (opipe->curbuf + opipe->nrbufs) & (opipe->buffers - 1); ++ ++ /* ++ * Get a reference to this pipe buffer, ++ * so we can copy the contents over. ++ */ ++ ibuf->ops->get(ipipe, ibuf); ++ ++ obuf = opipe->bufs + nbuf; ++ *obuf = *ibuf; ++ ++ /* ++ * Don't inherit the gift flag, we need to ++ * prevent multiple steals of this page. ++ */ ++ obuf->flags &= ~PIPE_BUF_FLAG_GIFT; ++ ++ if (obuf->len > len) ++ obuf->len = len; ++ ++ opipe->nrbufs++; ++ ret += obuf->len; ++ len -= obuf->len; ++ i++; ++ } while (len); ++ ++ /* ++ * return EAGAIN if we have the potential of some data in the ++ * future, otherwise just return 0 ++ */ ++ if (!ret && ipipe->waiting_writers && (flags & SPLICE_F_NONBLOCK)) ++ ret = -EAGAIN; ++ ++ pipe_unlock(ipipe); ++ pipe_unlock(opipe); ++ ++ /* ++ * If we put data in the output pipe, wakeup any potential readers. ++ */ ++ if (ret > 0) { ++ smp_mb(); ++ if (waitqueue_active(&opipe->wait)) ++ wake_up_interruptible(&opipe->wait); ++ kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN); ++ } ++ ++ return ret; ++} ++ ++/* ++ * This is a tee(1) implementation that works on pipes. It doesn't copy ++ * any data, it simply references the 'in' pages on the 'out' pipe. ++ * The 'flags' used are the SPLICE_F_* variants, currently the only ++ * applicable one is SPLICE_F_NONBLOCK. ++ */ ++static long do_tee(struct file *in, struct file *out, size_t len, ++ unsigned int flags) ++{ ++ struct pipe_inode_info *ipipe = pipe_info(in->f_path.dentry->d_inode); ++ struct pipe_inode_info *opipe = pipe_info(out->f_path.dentry->d_inode); ++ int ret = -EINVAL; ++ ++ /* ++ * Duplicate the contents of ipipe to opipe without actually ++ * copying the data. ++ */ ++ if (ipipe && opipe && ipipe != opipe) { ++ /* ++ * Keep going, unless we encounter an error. The ipipe/opipe ++ * ordering doesn't really matter. ++ */ ++ ret = ipipe_prep(ipipe, flags); ++ if (!ret) { ++ ret = opipe_prep(opipe, flags); ++ if (!ret) ++ ret = link_pipe(ipipe, opipe, len, flags); ++ } ++ } ++ ++ return ret; ++} ++ ++SYSCALL_DEFINE4(tee, int, fdin, int, fdout, size_t, len, unsigned int, flags) ++{ ++ struct file *in; ++ int error, fput_in; ++ ++ if (unlikely(!len)) ++ return 0; ++ ++ error = -EBADF; ++ in = fget_light(fdin, &fput_in); ++ if (in) { ++ if (in->f_mode & FMODE_READ) { ++ int fput_out; ++ struct file *out = fget_light(fdout, &fput_out); ++ ++ if (out) { ++ if (out->f_mode & FMODE_WRITE) ++ error = do_tee(in, out, len, flags); ++ fput_light(out, fput_out); ++ } ++ } ++ fput_light(in, fput_in); ++ } ++ ++ return error; ++} +diff -Nur linux-2.6.36.orig/security/commoncap.c linux-2.6.36/security/commoncap.c +--- linux-2.6.36.orig/security/commoncap.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/security/commoncap.c 2011-01-10 19:52:38.000000000 +0100 +@@ -951,3 +951,4 @@ + } + return ret; + } ++EXPORT_SYMBOL(cap_file_mmap); +diff -Nur linux-2.6.36.orig/security/device_cgroup.c linux-2.6.36/security/device_cgroup.c +--- linux-2.6.36.orig/security/device_cgroup.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/security/device_cgroup.c 2011-01-10 19:52:38.000000000 +0100 +@@ -515,6 +515,7 @@ + + return -EPERM; + } ++EXPORT_SYMBOL(devcgroup_inode_permission); + + int devcgroup_inode_mknod(int mode, dev_t dev) + { +diff -Nur linux-2.6.36.orig/security/security.c linux-2.6.36/security/security.c +--- linux-2.6.36.orig/security/security.c 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/security/security.c 2011-01-10 19:52:38.000000000 +0100 +@@ -376,6 +376,7 @@ + return 0; + return security_ops->path_mkdir(dir, dentry, mode); + } ++EXPORT_SYMBOL(security_path_mkdir); + + int security_path_rmdir(struct path *dir, struct dentry *dentry) + { +@@ -383,6 +384,7 @@ + return 0; + return security_ops->path_rmdir(dir, dentry); + } ++EXPORT_SYMBOL(security_path_rmdir); + + int security_path_unlink(struct path *dir, struct dentry *dentry) + { +@@ -390,6 +392,7 @@ + return 0; + return security_ops->path_unlink(dir, dentry); + } ++EXPORT_SYMBOL(security_path_unlink); + + int security_path_symlink(struct path *dir, struct dentry *dentry, + const char *old_name) +@@ -398,6 +401,7 @@ + return 0; + return security_ops->path_symlink(dir, dentry, old_name); + } ++EXPORT_SYMBOL(security_path_symlink); + + int security_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +@@ -406,6 +410,7 @@ + return 0; + return security_ops->path_link(old_dentry, new_dir, new_dentry); + } ++EXPORT_SYMBOL(security_path_link); + + int security_path_rename(struct path *old_dir, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry) +@@ -416,6 +421,7 @@ + return security_ops->path_rename(old_dir, old_dentry, new_dir, + new_dentry); + } ++EXPORT_SYMBOL(security_path_rename); + + int security_path_truncate(struct path *path) + { +@@ -423,6 +429,7 @@ + return 0; + return security_ops->path_truncate(path); + } ++EXPORT_SYMBOL(security_path_truncate); + + int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, + mode_t mode) +@@ -431,6 +438,7 @@ + return 0; + return security_ops->path_chmod(dentry, mnt, mode); + } ++EXPORT_SYMBOL(security_path_chmod); + + int security_path_chown(struct path *path, uid_t uid, gid_t gid) + { +@@ -438,6 +446,7 @@ + return 0; + return security_ops->path_chown(path, uid, gid); + } ++EXPORT_SYMBOL(security_path_chown); + + int security_path_chroot(struct path *path) + { +@@ -514,6 +523,7 @@ + return 0; + return security_ops->inode_readlink(dentry); + } ++EXPORT_SYMBOL(security_inode_readlink); + + int security_inode_follow_link(struct dentry *dentry, struct nameidata *nd) + { +@@ -528,6 +538,7 @@ + return 0; + return security_ops->inode_permission(inode, mask); + } ++EXPORT_SYMBOL(security_inode_permission); + + int security_inode_setattr(struct dentry *dentry, struct iattr *attr) + { +@@ -627,6 +638,7 @@ + + return fsnotify_perm(file, mask); + } ++EXPORT_SYMBOL(security_file_permission); + + int security_file_alloc(struct file *file) + { +@@ -654,6 +666,7 @@ + return ret; + return ima_file_mmap(file, prot); + } ++EXPORT_SYMBOL(security_file_mmap); + + int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, + unsigned long prot) diff --git a/toolchain/kernel-headers/patches/2.6.36/aufs2.patch b/toolchain/kernel-headers/patches/2.6.36/aufs2.patch new file mode 100644 index 000000000..eafbe6814 --- /dev/null +++ b/toolchain/kernel-headers/patches/2.6.36/aufs2.patch @@ -0,0 +1,240 @@ +diff -Nur linux-2.6.36.orig/include/linux/Kbuild linux-2.6.36/include/linux/Kbuild +--- linux-2.6.36.orig/include/linux/Kbuild 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/include/linux/Kbuild 2011-01-10 19:52:38.000000000 +0100 +@@ -60,6 +60,7 @@ + header-y += atmsap.h + header-y += atmsvc.h + header-y += audit.h ++header-y += aufs_type.h + header-y += auto_fs.h + header-y += auto_fs4.h + header-y += auxvec.h +diff -Nur linux-2.6.36.orig/include/linux/aufs_type.h linux-2.6.36/include/linux/aufs_type.h +--- linux-2.6.36.orig/include/linux/aufs_type.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.36/include/linux/aufs_type.h 2011-01-10 19:54:22.000000000 +0100 +@@ -0,0 +1,197 @@ ++/* ++ * Copyright (C) 2005-2011 Junjiro R. 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 ++ */ ++ ++#ifndef __AUFS_TYPE_H__ ++#define __AUFS_TYPE_H__ ++ ++#include ++#include ++#include ++#include ++ ++#define AUFS_VERSION "2.1-standalone.tree-36-20110110" ++ ++/* todo? 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 ++typedef __s8 aufs_bindex_t; ++#define AUFS_BRANCH_MAX 127 ++#else ++typedef __s16 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 ++#endif ++#endif ++ ++#ifdef __KERNEL__ ++#ifndef AUFS_BRANCH_MAX ++#error unknown CONFIG_AUFS_BRANCH_MAX value ++#endif ++#endif /* __KERNEL__ */ ++ ++/* ---------------------------------------------------------------------- */ ++ ++#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_WH_TMP_LEN 4 ++/* a limit for rmdir/rename a dir */ ++#define AUFS_MAX_NAMELEN (NAME_MAX \ ++ - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ ++ - 1 /* dot */\ ++ - AUFS_WH_TMP_LEN) /* hex */ ++#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_RDCACHE_MAX 3600 /* seconds */ ++#define AUFS_RDBLK_DEF 512 /* bytes */ ++#define AUFS_RDHASH_DEF 32 ++#define AUFS_WKQ_NAME AUFS_NAME "d" ++#define AUFS_WKQ_PRE_NAME AUFS_WKQ_NAME "_pre" ++#define AUFS_MFS_DEF_SEC 30 /* seconds */ ++#define AUFS_MFS_MAX_SEC 3600 /* seconds */ ++#define AUFS_PLINK_WARN 100 /* number of plinks */ ++ ++/* pseudo-link maintenace under /proc */ ++#define AUFS_PLINK_MAINT_NAME "plink_maint" ++#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME ++#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME ++ ++#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ ++#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME ++ ++#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME ++#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" ++#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" ++ ++/* doubly whiteouted */ ++#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME ++#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME ++#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME ++ ++/* branch permission */ ++#define AUFS_BRPERM_RW "rw" ++#define AUFS_BRPERM_RO "ro" ++#define AUFS_BRPERM_RR "rr" ++#define AUFS_BRPERM_WH "wh" ++#define AUFS_BRPERM_NLWH "nolwh" ++#define AUFS_BRPERM_ROWH AUFS_BRPERM_RO "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RRWH AUFS_BRPERM_RR "+" AUFS_BRPERM_WH ++#define AUFS_BRPERM_RWNLWH AUFS_BRPERM_RW "+" AUFS_BRPERM_NLWH ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ioctl */ ++enum { ++ /* readdir in userspace */ ++ AuCtl_RDU, ++ AuCtl_RDU_INO, ++ ++ /* pathconf wrapper */ ++ AuCtl_WBR_FD ++}; ++ ++/* borrowed from linux/include/linux/kernel.h */ ++#ifndef ALIGN ++#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) ++#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) ++#endif ++ ++/* borrowed from linux/include/linux/compiler-gcc3.h */ ++#ifndef __aligned ++#define __aligned(x) __attribute__((aligned(x))) ++#define __packed __attribute__((packed)) ++#endif ++ ++struct au_rdu_cookie { ++ __u64 h_pos; ++ __s16 bindex; ++ __u8 flags; ++ __u8 pad; ++ __u32 generation; ++} __aligned(8); ++ ++struct au_rdu_ent { ++ __u64 ino; ++ __s16 bindex; ++ __u8 type; ++ __u8 nlen; ++ __u8 wh; ++ char name[0]; ++} __aligned(8); ++ ++static inline int au_rdu_len(int nlen) ++{ ++ /* include the terminating NULL */ ++ return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, ++ sizeof(__u64)); ++} ++ ++union au_rdu_ent_ul { ++ struct au_rdu_ent __user *e; ++ __u64 ul; ++}; ++ ++enum { ++ AufsCtlRduV_SZ, ++ AufsCtlRduV_End ++}; ++ ++struct aufs_rdu { ++ /* input */ ++ union { ++ __u64 sz; /* AuCtl_RDU */ ++ __u64 nent; /* AuCtl_RDU_INO */ ++ }; ++ union au_rdu_ent_ul ent; ++ __u16 verify[AufsCtlRduV_End]; ++ ++ /* input/output */ ++ __u32 blk; ++ ++ /* output */ ++ union au_rdu_ent_ul tail; ++ /* number of entries which were added in a single call */ ++ __u64 rent; ++ __u8 full; ++ __u8 shwh; ++ ++ struct au_rdu_cookie cookie; ++} __aligned(8); ++ ++#define AuCtlType 'A' ++#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) ++#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) ++#define AUFS_CTL_WBR_FD _IO(AuCtlType, AuCtl_WBR_FD) ++ ++#endif /* __AUFS_TYPE_H__ */ +diff -Nur linux-2.6.36.orig/include/linux/namei.h linux-2.6.36/include/linux/namei.h +--- linux-2.6.36.orig/include/linux/namei.h 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/include/linux/namei.h 2011-01-10 19:52:38.000000000 +0100 +@@ -73,6 +73,9 @@ + extern struct file *lookup_instantiate_filp(struct nameidata *nd, struct dentry *dentry, + int (*open)(struct inode *, struct file *)); + ++extern struct dentry *lookup_hash(struct nameidata *nd); ++extern int __lookup_one_len(const char *name, struct qstr *this, ++ struct dentry *base, int len); + extern struct dentry *lookup_one_len(const char *, struct dentry *, int); + + extern int follow_down(struct path *); +diff -Nur linux-2.6.36.orig/include/linux/splice.h linux-2.6.36/include/linux/splice.h +--- linux-2.6.36.orig/include/linux/splice.h 2010-10-20 22:30:22.000000000 +0200 ++++ linux-2.6.36/include/linux/splice.h 2011-01-10 19:52:38.000000000 +0100 +@@ -89,4 +89,10 @@ + extern void splice_shrink_spd(struct pipe_inode_info *, + struct splice_pipe_desc *); + ++extern long do_splice_from(struct pipe_inode_info *pipe, struct file *out, ++ loff_t *ppos, size_t len, unsigned int flags); ++extern long do_splice_to(struct file *in, loff_t *ppos, ++ struct pipe_inode_info *pipe, size_t len, ++ unsigned int flags); ++ + #endif + -- cgit v1.2.3 From cbd0e559e6d11662e8cc685a53ff15a4ee2b1236 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:12:29 +0100 Subject: remove from TODO list, enable aufs2-util on bulk builds. --- TODO | 1 - mk/build.mk | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO b/TODO index 56959ebbe..2cd33d20f 100644 --- a/TODO +++ b/TODO @@ -7,7 +7,6 @@ - finish qemu package - fix autoreconf usage in opensc - fix timezone stuff in glibc -- add back aufs2 patches for kernel - openssl ocf support check - locales support - new package minidlna diff --git a/mk/build.mk b/mk/build.mk index 92a5e947a..1cd50ea9b 100644 --- a/mk/build.mk +++ b/mk/build.mk @@ -16,7 +16,6 @@ DEFCONFIG= ADK_DEBUG=n \ ADK_MAKE_JOBS=4 \ ADK_PACKAGE_BZR=n \ ADK_PACKAGE_GRUB=n \ - ADK_PACKAGE_AUFS2_UTIL=n \ ADK_PACKAGE_BASE_FILES=y \ ADK_PACKAGE_MGETTY=n \ ADK_COMPILE_HEIMDAL=n \ @@ -553,7 +552,7 @@ bulkallmod: done <${TOPDIR}/target/arch.lst ;\ done -${TOPDIR}/bin/tools/pkgmaker: +${TOPDIR}/bin/tools/pkgmaker: tools/adk/pkgmaker.c tools/adk/sortfile.c tools/adk/strmap.c @mkdir -p $(TOPDIR)/bin/tools @$(HOSTCC) -Wall -g -o $@ tools/adk/pkgmaker.c tools/adk/sortfile.c tools/adk/strmap.c -- cgit v1.2.3 From 59f1e5ee4443e6210b148c58af56bef3e9a75f2b Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:12:49 +0100 Subject: fix target system names --- mk/modules.mk | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/mk/modules.mk b/mk/modules.mk index 94a1d235a..27b02bbf7 100644 --- a/mk/modules.mk +++ b/mk/modules.mk @@ -926,11 +926,13 @@ $(eval $(call KMOD_template,CRYPTO_FCRYPT,crypto-fcrypt,\ ,11)) ZLIB:=lib/zlib_deflate/zlib_deflate -ifeq ($(ADK_LINUX_CRIS_FOXBOARD),) -ifeq ($(ADK_LINUX_MIPS_AG241),) +ifeq ($(ADK_TARGET_SYSTEM_FOXBOARD_LX832),y) +ifeq ($(ADK_TARGET_SYSTEM_FOXBOARD_LX416),y) +ifeq ($(ADK_TARGET_SYSTEM_LINKSYS_AG241),y) ZLIB+=lib/zlib_inflate/zlib_inflate endif endif +endif $(eval $(call KMOD_template,CRYPTO_DEFLATE,crypto-deflate,\ $(foreach mod, $(ZLIB),$(MODULES_DIR)/kernel/$(mod)) \ @@ -1177,11 +1179,13 @@ $(eval $(call KMOD_template,INPUT_EVDEV,input-evdev,\ # USB # -ifeq ($(ADK_LINUX_CRIS_FOXBOARD),) +ifeq ($(ADK_TARGET_SYSTEM_FOXBOARD_LX832),) +ifeq ($(ADK_TARGET_SYSTEM_FOXBOARD_LX416),) $(eval $(call KMOD_template,USB,usb,\ $(MODULES_DIR)/kernel/drivers/usb/core/usbcore \ ,50)) endif +endif $(eval $(call KMOD_template,USB_EHCI_HCD,usb-ehci-hcd,\ $(MODULES_DIR)/kernel/drivers/usb/host/ehci-hcd \ -- cgit v1.2.3 From e435ddd4ee81da12e3e3e3d0433a90d539901839 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:16:55 +0100 Subject: add support for C++ packages, which need G++ compiler --- package/dansguardian/Makefile | 1 + package/gpsd/Makefile | 1 + package/id3lib/Makefile | 1 + package/iperf/Makefile | 1 + package/mrd6/Makefile | 1 + package/nmap/Makefile | 1 + package/uclibc++/Config.in.manual | 4 ++++ package/uclibc++/Makefile | 1 + tools/adk/pkgmaker.c | 15 ++++++++++----- 9 files changed, 21 insertions(+), 5 deletions(-) diff --git a/package/dansguardian/Makefile b/package/dansguardian/Makefile index 5cc1d08e5..e262bbca6 100644 --- a/package/dansguardian/Makefile +++ b/package/dansguardian/Makefile @@ -13,6 +13,7 @@ PKG_DEPENDS:= libpcre zlib PKG_BUILDDEP:= pcre zlib PKG_URL:= http://dansguardian.org/ PKG_SITES:= http://dansguardian.org/downloads/2/Stable/ +PKG_NEED_CXX:= 1 PKG_CXX:= DANSGUARDIAN include ${TOPDIR}/mk/package.mk diff --git a/package/gpsd/Makefile b/package/gpsd/Makefile index 83fac860b..794d6074b 100644 --- a/package/gpsd/Makefile +++ b/package/gpsd/Makefile @@ -13,6 +13,7 @@ PKG_DEPENDS:= libpthread PKG_BUILDDEP:= ncurses PKG_SITES:= http://download.berlios.de/gpsd/ PKG_URL:= http://gpsd.berlios.de/ +PKG_NEED_CXX:= 1 PKG_CXX:= GPSD PKG_SUBPKGS:= GPSD GPSD_CLIENTS diff --git a/package/id3lib/Makefile b/package/id3lib/Makefile index 17c92c1be..63b31c5a3 100644 --- a/package/id3lib/Makefile +++ b/package/id3lib/Makefile @@ -13,6 +13,7 @@ PKG_DEPENDS:= zlib libiconv PKG_BUILDDEP:= zlib libiconv PKG_URL:= http://id3lib.sourceforge.net/ PKG_SITES:= ${MASTER_SITE_SOURCEFORGE:=id3lib/} +PKG_NEED_CXX:= 1 PKG_CXX:= ID3LIB include ${TOPDIR}/mk/package.mk diff --git a/package/iperf/Makefile b/package/iperf/Makefile index 7a4f70994..57eda553b 100644 --- a/package/iperf/Makefile +++ b/package/iperf/Makefile @@ -12,6 +12,7 @@ PKG_SECTION:= net PKG_DEPENDS:= libpthread PKG_URL:= http://sourceforge.net/projects/iperf PKG_SITES:= ${MASTER_SITE_SOURCEFORGE:=iperf/} +PKG_NEED_CXX:= 1 PKG_CXX:= IPERF include ${TOPDIR}/mk/package.mk diff --git a/package/mrd6/Makefile b/package/mrd6/Makefile index 745718a2a..5833d1fc3 100644 --- a/package/mrd6/Makefile +++ b/package/mrd6/Makefile @@ -11,6 +11,7 @@ PKG_DESCR:= IPv6 multicast routing daemon PKG_SECTION:= ipv6 PKG_DEPENDS:= kmod-ipv6 PKG_SITES:= http://www.openadk.org/distfiles/ +PKG_NEED_CXX:= 1 PKG_CXX:= MRD6 WRKBUILD= ${WRKSRC}/src diff --git a/package/nmap/Makefile b/package/nmap/Makefile index 57f986ea4..0d645ff92 100644 --- a/package/nmap/Makefile +++ b/package/nmap/Makefile @@ -13,6 +13,7 @@ PKG_DEPENDS:= libdnet libpcap libpcre PKG_BUILDDEP:= libdnet libpcap pcre PKG_URL:= http://nmap.org/ PKG_SITES:= http://download.insecure.org/nmap/dist/ +PKG_NEED_CXX:= 1 PKG_CXX:= NMAP DISTFILES:= ${PKG_NAME}-${PKG_VERSION}.tar.bz2 diff --git a/package/uclibc++/Config.in.manual b/package/uclibc++/Config.in.manual index 50729fcaa..b645e03a4 100644 --- a/package/uclibc++/Config.in.manual +++ b/package/uclibc++/Config.in.manual @@ -1,7 +1,11 @@ +comment "uClibc++... C++ library for embedded systems (disabled, c++ missing)" +depends on !ADK_TOOLCHAIN_GCC_CXX + config ADK_PACKAGE_UCLIBCXX prompt "uClibc++............... C++ library for embedded systems" tristate depends on ADK_TARGET_LIB_UCLIBC + depends on ADK_TOOLCHAIN_GCC_CXX default n help A standard C++ library for embedded systems diff --git a/package/uclibc++/Makefile b/package/uclibc++/Makefile index 760258bdd..e718b20bc 100644 --- a/package/uclibc++/Makefile +++ b/package/uclibc++/Makefile @@ -11,6 +11,7 @@ PKG_DESCR:= A standard c++ library for embedded systems PKG_SECTION:= libs PKG_URL:= http://cxx.uclibc.org/ PKG_SITES:= http://cxx.uclibc.org/src/ +PKG_NEED_CXX:= 1 DISTFILES:= ${PKG_NAME}-${PKG_VERSION}.tar.bz2 diff --git a/tools/adk/pkgmaker.c b/tools/adk/pkgmaker.c index eeba00304..e2b6e6e99 100644 --- a/tools/adk/pkgmaker.c +++ b/tools/adk/pkgmaker.c @@ -508,8 +508,12 @@ int main() { if (cfg == NULL) perror("Can not open Config.in file"); + if (pkg_need_cxx != NULL) { + fprintf(cfg, "comment \"%s... %s (disabled, c++ missing)\"\n", token, pkg_descr); + fprintf(cfg, "depends on !ADK_TOOLCHAIN_GCC_CXX\n\n"); + } fprintf(cfg, "config ADK_PACKAGE_%s\n", toupperstr(token)); - fprintf(cfg, "\tprompt \"%s... %s\"\n", pseudo_name, pkg_descr); + fprintf(cfg, "\tprompt \"%s. %s\"\n", pseudo_name, pkg_descr); fprintf(cfg, "\ttristate\n"); if (pkg_multi != NULL) if (strncmp(pkg_multi, "1", 1) == 0) @@ -592,10 +596,11 @@ int main() { } if (pkg_need_cxx != NULL) { - fprintf(cfg, "\tdepends on ADK_TOOLCHAIN_GCC_CXX"); + fprintf(cfg, "\tdepends on ADK_TOOLCHAIN_GCC_CXX\n"); } if (pkg_need_java != NULL) { - fprintf(cfg, "\tdepends on ADK_TOOLCHAIN_GCC_JAVA"); + fprintf(cfg, "\tdepends on ADK_TOOLCHAIN_GCC_JAVA\n"); + pkg_need_java = NULL; } fprintf(cfg, "\tselect ADK_COMPILE_%s\n", toupperstr(pkgdirp->d_name)); @@ -712,6 +717,8 @@ int main() { free(packages); packages = NULL; + pkg_need_cxx = NULL; + pkg_need_java = NULL; /* reset flags, free memory */ free(pkg_name); free(pkg_descr); @@ -724,8 +731,6 @@ int main() { free(pkg_arch_depends); free(pkg_host_depends); free(pkg_cxx); - free(pkg_need_cxx); - free(pkg_need_java); free(pkg_dflt); free(pkg_cfline); free(pkg_multi); -- cgit v1.2.3 From 55d3db867cddc1b2917a60cda577a7865f49eda8 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:18:32 +0100 Subject: fix patch, after aufs2 addition --- target/linux/config/Config.in.fs | 2 +- target/linux/patches/2.6.36/yaffs2.patch | 103 ++++++++++++++++--------------- 2 files changed, 54 insertions(+), 51 deletions(-) diff --git a/target/linux/config/Config.in.fs b/target/linux/config/Config.in.fs index 6ce885677..f6da0d980 100644 --- a/target/linux/config/Config.in.fs +++ b/target/linux/config/Config.in.fs @@ -151,7 +151,7 @@ config ADK_KPACKAGE_KMOD_NTFS_FS Kernel modules for NTFS support config ADK_KPACKAGE_KMOD_VFAT_FS - prompt "kmod-vfat-fs....................... VFAT filesystem support" + prompt "kmod-vfat-fs...................... VFAT filesystem support" tristate select ADK_KPACKAGE_KMOD_NLS if !ADK_KERNEL_NLS select ADK_KPACKAGE_KMOD_NLS_CODEPAGE_850 diff --git a/target/linux/patches/2.6.36/yaffs2.patch b/target/linux/patches/2.6.36/yaffs2.patch index ab8924e0f..4a52d3f10 100644 --- a/target/linux/patches/2.6.36/yaffs2.patch +++ b/target/linux/patches/2.6.36/yaffs2.patch @@ -1,6 +1,6 @@ diff -Nur linux-2.6.36.orig/fs/Kconfig linux-2.6.36/fs/Kconfig ---- linux-2.6.36.orig/fs/Kconfig 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/fs/Kconfig 2010-11-18 18:15:51.000000000 +0100 +--- linux-2.6.36.orig/fs/Kconfig 2011-01-10 19:28:45.000000000 +0100 ++++ linux-2.6.36/fs/Kconfig 2011-01-10 19:29:29.000000000 +0100 @@ -174,6 +174,7 @@ source "fs/befs/Kconfig" source "fs/bfs/Kconfig" @@ -10,16 +10,19 @@ diff -Nur linux-2.6.36.orig/fs/Kconfig linux-2.6.36/fs/Kconfig # UBIFS File system configuration source "fs/ubifs/Kconfig" diff -Nur linux-2.6.36.orig/fs/Makefile linux-2.6.36/fs/Makefile ---- linux-2.6.36.orig/fs/Makefile 2010-10-20 22:30:22.000000000 +0200 -+++ linux-2.6.36/fs/Makefile 2010-11-18 18:15:51.000000000 +0100 -@@ -126,3 +126,4 @@ +--- linux-2.6.36.orig/fs/Makefile 2011-01-10 19:28:45.000000000 +0100 ++++ linux-2.6.36/fs/Makefile 2011-01-10 19:30:04.000000000 +0100 +@@ -124,6 +124,7 @@ + obj-$(CONFIG_OCFS2_FS) += ocfs2/ + obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ ++obj-$(CONFIG_YAFFS_FS) += yaffs2/ obj-$(CONFIG_EXOFS_FS) += exofs/ obj-$(CONFIG_CEPH_FS) += ceph/ -+obj-$(CONFIG_YAFFS_FS) += yaffs2/ + obj-$(CONFIG_AUFS_FS) += aufs/ diff -Nur linux-2.6.36.orig/fs/yaffs2/Kconfig linux-2.6.36/fs/yaffs2/Kconfig --- linux-2.6.36.orig/fs/yaffs2/Kconfig 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/Kconfig 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/Kconfig 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,161 @@ +# +# YAFFS file system configurations @@ -184,7 +187,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/Kconfig linux-2.6.36/fs/yaffs2/Kconfig + If unsure, say Y. diff -Nur linux-2.6.36.orig/fs/yaffs2/Makefile linux-2.6.36/fs/yaffs2/Makefile --- linux-2.6.36.orig/fs/yaffs2/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/Makefile 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/Makefile 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,17 @@ +# +# Makefile for the linux YAFFS filesystem routines. @@ -205,7 +208,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/Makefile linux-2.6.36/fs/yaffs2/Makefile + diff -Nur linux-2.6.36.orig/fs/yaffs2/moduleconfig.h linux-2.6.36/fs/yaffs2/moduleconfig.h --- linux-2.6.36.orig/fs/yaffs2/moduleconfig.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/moduleconfig.h 2010-11-18 18:49:02.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/moduleconfig.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,81 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -290,7 +293,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/moduleconfig.h linux-2.6.36/fs/yaffs2/modu +#endif /* __YAFFS_CONFIG_H__ */ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.c linux-2.6.36/fs/yaffs2/yaffs_allocator.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_allocator.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_allocator.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,397 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -691,7 +694,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.c linux-2.6.36/fs/yaffs2/y +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.h linux-2.6.36/fs/yaffs2/yaffs_allocator.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_allocator.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_allocator.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,30 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -725,7 +728,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_allocator.h linux-2.6.36/fs/yaffs2/y +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.c linux-2.6.36/fs/yaffs2/yaffs_attribs.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_attribs.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_attribs.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,124 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -853,7 +856,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.c linux-2.6.36/fs/yaffs2/yaf +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.h linux-2.6.36/fs/yaffs2/yaffs_attribs.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_attribs.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_attribs.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -885,7 +888,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_attribs.h linux-2.6.36/fs/yaffs2/yaf +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.c linux-2.6.36/fs/yaffs2/yaffs_bitmap.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_bitmap.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_bitmap.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,104 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -993,7 +996,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.c linux-2.6.36/fs/yaffs2/yaff +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.h linux-2.6.36/fs/yaffs2/yaffs_bitmap.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_bitmap.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_bitmap.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -1030,7 +1033,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_bitmap.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.c linux-2.6.36/fs/yaffs2/yaffs_checkptrw.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_checkptrw.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_checkptrw.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,420 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -1454,7 +1457,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.c linux-2.6.36/fs/yaffs2/y +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.h linux-2.6.36/fs/yaffs2/yaffs_checkptrw.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_checkptrw.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_checkptrw.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,33 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -1491,7 +1494,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_checkptrw.h linux-2.6.36/fs/yaffs2/y +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.c linux-2.6.36/fs/yaffs2/yaffs_ecc.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_ecc.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_ecc.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,322 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -1817,7 +1820,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.c linux-2.6.36/fs/yaffs2/yaffs_e +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.h linux-2.6.36/fs/yaffs2/yaffs_ecc.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_ecc.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_ecc.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,44 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -1865,7 +1868,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_ecc.h linux-2.6.36/fs/yaffs2/yaffs_e +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_getblockinfo.h linux-2.6.36/fs/yaffs2/yaffs_getblockinfo.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_getblockinfo.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_getblockinfo.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_getblockinfo.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,36 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -1905,7 +1908,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_getblockinfo.h linux-2.6.36/fs/yaffs +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_guts.c linux-2.6.36/fs/yaffs2/yaffs_guts.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_guts.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_guts.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_guts.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,5227 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -7136,7 +7139,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_guts.c linux-2.6.36/fs/yaffs2/yaffs_ +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_guts.h linux-2.6.36/fs/yaffs2/yaffs_guts.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_guts.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_guts.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_guts.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,914 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -8054,7 +8057,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_guts.h linux-2.6.36/fs/yaffs2/yaffs_ +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_linux.h linux-2.6.36/fs/yaffs2/yaffs_linux.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_linux.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_linux.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_linux.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,41 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -8099,7 +8102,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_linux.h linux-2.6.36/fs/yaffs2/yaffs +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.c linux-2.6.36/fs/yaffs2/yaffs_mtdif.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,54 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -8157,7 +8160,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.c linux-2.6.36/fs/yaffs2/yaffs +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.h linux-2.6.36/fs/yaffs2/yaffs_mtdif.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,23 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -8184,7 +8187,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif.h linux-2.6.36/fs/yaffs2/yaffs +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.c linux-2.6.36/fs/yaffs2/yaffs_mtdif1.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif1.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif1.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,365 @@ +/* + * YAFFS: Yet another FFS. A NAND-flash specific file system. @@ -8553,7 +8556,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.c linux-2.6.36/fs/yaffs2/yaff +#endif /*MTD_VERSION */ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.h linux-2.6.36/fs/yaffs2/yaffs_mtdif1.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif1.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif1.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * YAFFS: Yet another Flash File System. A NAND-flash specific file system. @@ -8586,7 +8589,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif1.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.c linux-2.6.36/fs/yaffs2/yaffs_mtdif2.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif2.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif2.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,261 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -8851,7 +8854,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.c linux-2.6.36/fs/yaffs2/yaff +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.h linux-2.6.36/fs/yaffs2/yaffs_mtdif2.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_mtdif2.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_mtdif2.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -8884,7 +8887,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_mtdif2.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.c linux-2.6.36/fs/yaffs2/yaffs_nameval.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_nameval.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_nameval.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,201 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -9089,7 +9092,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.c linux-2.6.36/fs/yaffs2/yaf +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.h linux-2.6.36/fs/yaffs2/yaffs_nameval.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_nameval.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_nameval.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,28 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -9121,7 +9124,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nameval.h linux-2.6.36/fs/yaffs2/yaf +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nand.c linux-2.6.36/fs/yaffs2/yaffs_nand.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_nand.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_nand.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_nand.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,128 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -9253,7 +9256,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nand.c linux-2.6.36/fs/yaffs2/yaffs_ +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nand.h linux-2.6.36/fs/yaffs2/yaffs_nand.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_nand.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_nand.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_nand.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,38 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -9295,7 +9298,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_nand.h linux-2.6.36/fs/yaffs2/yaffs_ +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.c linux-2.6.36/fs/yaffs2/yaffs_packedtags1.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_packedtags1.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_packedtags1.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,53 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -9352,7 +9355,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.c linux-2.6.36/fs/yaffs2 +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.h linux-2.6.36/fs/yaffs2/yaffs_packedtags1.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_packedtags1.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_packedtags1.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -9395,7 +9398,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags1.h linux-2.6.36/fs/yaffs2 +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.c linux-2.6.36/fs/yaffs2/yaffs_packedtags2.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_packedtags2.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_packedtags2.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,197 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -9596,7 +9599,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.c linux-2.6.36/fs/yaffs2 +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.h linux-2.6.36/fs/yaffs2/yaffs_packedtags2.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_packedtags2.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_packedtags2.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,47 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -9647,7 +9650,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_packedtags2.h linux-2.6.36/fs/yaffs2 +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.c linux-2.6.36/fs/yaffs2/yaffs_tagscompat.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_tagscompat.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_tagscompat.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,454 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -10105,7 +10108,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.c linux-2.6.36/fs/yaffs2/ +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.h linux-2.6.36/fs/yaffs2/yaffs_tagscompat.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_tagscompat.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_tagscompat.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,36 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -10145,7 +10148,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagscompat.h linux-2.6.36/fs/yaffs2/ +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.c linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,27 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -10176,7 +10179,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.c linux-2.6.36/fs/yaffs +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.h linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_tagsvalidity.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,23 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -10203,7 +10206,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_tagsvalidity.h linux-2.6.36/fs/yaffs +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_trace.h linux-2.6.36/fs/yaffs2/yaffs_trace.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_trace.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_trace.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_trace.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,59 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -10266,7 +10269,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_trace.h linux-2.6.36/fs/yaffs2/yaffs +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_verify.c linux-2.6.36/fs/yaffs2/yaffs_verify.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_verify.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_verify.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_verify.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,546 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -10816,7 +10819,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_verify.c linux-2.6.36/fs/yaffs2/yaff +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_verify.h linux-2.6.36/fs/yaffs2/yaffs_verify.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_verify.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_verify.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_verify.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,43 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -10863,7 +10866,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_verify.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_vfs.c linux-2.6.36/fs/yaffs2/yaffs_vfs.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_vfs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_vfs.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_vfs.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,3565 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -14432,7 +14435,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_vfs.c linux-2.6.36/fs/yaffs2/yaffs_v +MODULE_LICENSE("GPL"); diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.c linux-2.6.36/fs/yaffs2/yaffs_yaffs1.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_yaffs1.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_yaffs1.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,437 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -14873,7 +14876,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.c linux-2.6.36/fs/yaffs2/yaff +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.h linux-2.6.36/fs/yaffs2/yaffs_yaffs1.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_yaffs1.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_yaffs1.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,22 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -14899,7 +14902,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs1.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.c linux-2.6.36/fs/yaffs2/yaffs_yaffs2.c --- linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_yaffs2.c 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_yaffs2.c 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,1620 @@ +/* + * YAFFS: Yet Another Flash File System. A NAND-flash specific file system. @@ -16523,7 +16526,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.c linux-2.6.36/fs/yaffs2/yaff +} diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.h linux-2.6.36/fs/yaffs2/yaffs_yaffs2.h --- linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yaffs_yaffs2.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yaffs_yaffs2.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,39 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. @@ -16566,7 +16569,7 @@ diff -Nur linux-2.6.36.orig/fs/yaffs2/yaffs_yaffs2.h linux-2.6.36/fs/yaffs2/yaff +#endif diff -Nur linux-2.6.36.orig/fs/yaffs2/yportenv.h linux-2.6.36/fs/yaffs2/yportenv.h --- linux-2.6.36.orig/fs/yaffs2/yportenv.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.6.36/fs/yaffs2/yportenv.h 2010-11-18 18:15:51.000000000 +0100 ++++ linux-2.6.36/fs/yaffs2/yportenv.h 2011-01-10 19:29:29.000000000 +0100 @@ -0,0 +1,339 @@ +/* + * YAFFS: Yet another Flash File System . A NAND-flash specific file system. -- cgit v1.2.3 From b608c5a7dc38765319cc5f7cb32d35fcb0701d08 Mon Sep 17 00:00:00 2001 From: Waldemar Brodkorb Date: Tue, 11 Jan 2011 18:19:44 +0100 Subject: fix misusage of CFLAGS, found via -fhonour-copts --- package/arpd/Makefile | 2 +- package/axtls/Makefile | 4 +- package/axtls/files/config | 4 + package/binutils/patches/patch-ld_Makefile_in | 20 + package/e2fsprogs/patches/patch-e2fsck_Makefile_in | 11 +- package/exmap/Makefile | 1 + package/exmap/patches/autotool.patch | 11256 +++++++++++++++++++ package/exmap/patches/patch-Makefile_in | 12 - package/gpm/patches/patch-src_Makefile_in | 51 + package/ipcad/patches/patch-Makefile_in | 17 + package/libxslt/patches/patch-libexslt_Makefile_in | 11 + package/libxslt/patches/patch-xsltproc_Makefile_in | 11 + package/lvm/Makefile | 13 +- package/lvm/patches/patch-make_tmpl_in | 13 +- 14 files changed, 11402 insertions(+), 24 deletions(-) create mode 100644 package/binutils/patches/patch-ld_Makefile_in create mode 100644 package/exmap/patches/autotool.patch delete mode 100644 package/exmap/patches/patch-Makefile_in create mode 100644 package/gpm/patches/patch-src_Makefile_in create mode 100644 package/ipcad/patches/patch-Makefile_in create mode 100644 package/libxslt/patches/patch-libexslt_Makefile_in create mode 100644 package/libxslt/patches/patch-xsltproc_Makefile_in diff --git a/package/arpd/Makefile b/package/arpd/Makefile index 3494c2115..722faae9d 100644 --- a/package/arpd/Makefile +++ b/package/arpd/Makefile @@ -23,7 +23,7 @@ $(eval $(call PKG_template,ARPD,${PKG_NAME},${PKG_VERSION}-${PKG_RELEASE},${PKG_ CONFIGURE_ARGS+= --with-libdnet=${STAGING_TARGET_DIR}/usr \ --with-libevent=${STAGING_TARGET_DIR}/usr \ --with-libpcap=${STAGING_TARGET_DIR}/usr -MAKE_FLAGS+= CCOPT="${TARGET_CFLAGS}" \ +MAKE_FLAGS+= CFLAGS="${TCFLAGS}" \ INCLS="-I. -I${STAGING_TARGET_DIR}/usr/include" \ LIBS="-L${STAGING_TARGET_DIR}/usr/lib -lpcap -ldnet -levent" diff --git a/package/axtls/Makefile b/package/axtls/Makefile index c3c659666..62dbb7226 100644 --- a/package/axtls/Makefile +++ b/package/axtls/Makefile @@ -4,9 +4,9 @@ include ${TOPDIR}/rules.mk PKG_NAME:= axtls -PKG_VERSION:= 1.2.2 +PKG_VERSION:= 1.3.0 PKG_RELEASE:= 1 -PKG_MD5SUM:= 03471b5a5874e2ce86025f24b3fa0958 +PKG_MD5SUM:= 4a06dd5b34489f11fbf2b4249a9dd303 PKG_DESCR:= small embedded webserver PKG_SECTION:= net/security PKG_DEPENDS:= libaxtls diff --git a/package/axtls/files/config b/package/axtls/files/config index ed4e64806..a37878b3a 100644 --- a/package/axtls/files/config +++ b/package/axtls/files/config @@ -50,6 +50,7 @@ CONFIG_USE_DEV_URANDOM=y # CONFIG_OPENSSL_COMPATIBLE is not set # CONFIG_PERFORMANCE_TESTING is not set # CONFIG_SSL_TEST is not set +# CONFIG_AXTLSWRAP is not set CONFIG_AXHTTPD=y # @@ -118,3 +119,6 @@ SQU_KARATSUBA_THRESH=0 # CONFIG_BIGINT_SLIDING_WINDOW is not set # CONFIG_BIGINT_SQUARE is not set # CONFIG_BIGINT_CHECK_ON is not set +CONFIG_INTEGER_32BIT=y +# CONFIG_INTEGER_16BIT is not set +# CONFIG_INTEGER_8BIT is not set diff --git a/package/binutils/patches/patch-ld_Makefile_in b/package/binutils/patches/patch-ld_Makefile_in new file mode 100644 index 000000000..ff1eeb88f --- /dev/null +++ b/package/binutils/patches/patch-ld_Makefile_in @@ -0,0 +1,20 @@ +--- binutils-2.21.orig/ld/Makefile.in 2010-11-05 11:34:22.000000000 +0100 ++++ binutils-2.21/ld/Makefile.in 2011-01-10 21:57:49.000000000 +0100 +@@ -422,7 +422,7 @@ TEXI2DVI = texi2dvi -I $(srcdir) -I $(BF + -I $(top_srcdir)/../libiberty + + AM_CPPFLAGS = -I. -I$(srcdir) -I../bfd -I$(BFDDIR) -I$(INCDIR) \ +- @INCINTL@ $(HDEFINES) $(CFLAGS) $(PLUGIN_CFLAGS) \ ++ @INCINTL@ $(HDEFINES) $(PLUGIN_CFLAGS) \ + -DLOCALEDIR="\"$(datadir)/locale\"" + + BFDLIB = ../bfd/libbfd.la +@@ -850,7 +850,7 @@ ld_new_LDADD = $(EMULATION_OFILES) $(EMU + # + @ENABLE_PLUGINS_TRUE@noinst_LTLIBRARIES = libldtestplug.la + @ENABLE_PLUGINS_TRUE@libldtestplug_la_SOURCES = testplug.c +-@ENABLE_PLUGINS_TRUE@libldtestplug_la_CFLAGS = -g -O2 ++@ENABLE_PLUGINS_TRUE@libldtestplug_la_CFLAGS = + @ENABLE_PLUGINS_TRUE@libldtestplug_la_LDFLAGS = -no-undefined -rpath /nowhere + MAINTAINERCLEANFILES = configdoc.texi ld.1 ld.info + diff --git a/package/e2fsprogs/patches/patch-e2fsck_Makefile_in b/package/e2fsprogs/patches/patch-e2fsck_Makefile_in index 2b43d8cec..ac7dc5e86 100644 --- a/package/e2fsprogs/patches/patch-e2fsck_Makefile_in +++ b/package/e2fsprogs/patches/patch-e2fsck_Makefile_in @@ -1,5 +1,5 @@ --- e2fsprogs-1.41.14.orig/e2fsck/Makefile.in 2010-12-22 16:49:20.000000000 +0100 -+++ e2fsprogs-1.41.14/e2fsck/Makefile.in 2011-01-06 23:10:49.000000000 +0100 ++++ e2fsprogs-1.41.14/e2fsck/Makefile.in 2011-01-11 00:34:19.000000000 +0100 @@ -16,7 +16,7 @@ MANPAGES= e2fsck.8 FMANPAGES= e2fsck.conf.5 XTRA_CFLAGS= -DRESOURCE_TRACK -I. @@ -9,3 +9,12 @@ DEPLIBS= $(LIBEXT2FS) $(DEPLIBCOM_ERR) $(DEPLIBBLKID) $(DEPLIBUUID) \ $(DEPLIBE2P) +@@ -119,7 +119,7 @@ e2fsck: $(OBJS) $(DEPLIBS) + + e2fsck.static: $(OBJS) $(STATIC_DEPLIBS) + $(E) " LD $@" +- $(Q) $(LD) $(LDFLAGS_STATIC) -o e2fsck.static $(OBJS) $(STATIC_LIBS) ++ $(LD) $(LDFLAGS_STATIC) -o e2fsck.static $(OBJS) $(STATIC_LIBS) + + e2fsck.profiled: $(PROFILED_OBJS) $(PROFILED_DEPLIBS) + $(E) " LD $@" diff --git a/package/exmap/Makefile b/package/exmap/Makefile index 536249af9..9b3c57740 100644 --- a/package/exmap/Makefile +++ b/package/exmap/Makefile @@ -33,6 +33,7 @@ CONFIGURE_ARGS+= --disable-doc CONFIGURE_ENV+= LIBS="-lncurses" pre-build: + GCC_HONOUR_COPTS=s \ KERNEL_PATH=${LINUX_DIR} \ CROSS_COMPILE="${TARGET_CROSS}" \ V=1 ARCH="${ARCH}" KERNELVERSION="2.6" \ diff --git a/package/exmap/patches/autotool.patch b/package/exmap/patches/autotool.patch new file mode 100644 index 000000000..73ae711f4 --- /dev/null +++ b/package/exmap/patches/autotool.patch @@ -0,0 +1,11256 @@ +diff -Nur exmap-console-0.4.1.orig/INSTALL exmap-console-0.4.1/INSTALL +--- exmap-console-0.4.1.orig/INSTALL 2006-11-03 09:36:03.000000000 +0100 ++++ exmap-console-0.4.1/INSTALL 2011-01-11 12:44:16.000000000 +0100 +@@ -1,16 +1,25 @@ + Installation Instructions + ************************* + +-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free +-Software Foundation, Inc. ++Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, ++2006, 2007, 2008, 2009 Free Software Foundation, Inc. + +-This file is free documentation; the Free Software Foundation gives +-unlimited permission to copy, distribute and modify it. ++ Copying and distribution of this file, with or without modification, ++are permitted in any medium without royalty provided the copyright ++notice and this notice are preserved. This file is offered as-is, ++without warranty of any kind. + + Basic Installation + ================== + +-These are generic installation instructions. ++ Briefly, the shell commands `./configure; make; make install' should ++configure, build, and install this package. The following ++more-detailed instructions are generic; see the `README' file for ++instructions specific to this package. Some packages provide this ++`INSTALL' file but do not implement all of the features documented ++below. The lack of an optional feature in a given package is not ++necessarily a bug. More recommendations for GNU packages can be found ++in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for + various system-dependent variables used during compilation. It uses +@@ -23,9 +32,9 @@ + + It can also use an optional file (typically called `config.cache' + and enabled with `--cache-file=config.cache' or simply `-C') that saves +-the results of its tests to speed up reconfiguring. (Caching is ++the results of its tests to speed up reconfiguring. Caching is + disabled by default to prevent problems with accidental use of stale +-cache files.) ++cache files. + + If you need to do unusual things to compile the package, please try + to figure out how `configure' could check whether to do them, and mail +@@ -35,30 +44,37 @@ + may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +-`configure' by a program called `autoconf'. You only need +-`configure.ac' if you want to change it or regenerate `configure' using +-a newer version of `autoconf'. ++`configure' by a program called `autoconf'. You need `configure.ac' if ++you want to change it or regenerate `configure' using a newer version ++of `autoconf'. + +-The simplest way to compile this package is: ++ The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type +- `./configure' to configure the package for your system. If you're +- using `csh' on an old version of System V, you might need to type +- `sh ./configure' instead to prevent `csh' from trying to execute +- `configure' itself. ++ `./configure' to configure the package for your system. + +- Running `configure' takes awhile. While running, it prints some +- messages telling which features it is checking for. ++ Running `configure' might take a while. While running, it prints ++ some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with +- the package. ++ the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and +- documentation. ++ documentation. When installing into a prefix owned by root, it is ++ recommended that the package be configured and built as a regular ++ user, and only the `make install' phase executed with root ++ privileges. ++ ++ 5. Optionally, type `make installcheck' to repeat any self-tests, but ++ this time using the binaries in their final installed location. ++ This target does not install anything. Running this target as a ++ regular user, particularly if the prior `make install' required ++ root privileges, verifies that the installation completed ++ correctly. + +- 5. You can remove the program binaries and object files from the ++ 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is +@@ -67,45 +83,69 @@ + all sorts of other programs in order to regenerate files that came + with the distribution. + ++ 7. Often, you can also type `make uninstall' to remove the installed ++ files again. In practice, not all packages have tested that ++ uninstallation works correctly, even though it is required by the ++ GNU Coding Standards. ++ ++ 8. Some packages, particularly those that use Automake, provide `make ++ distcheck', which can by used by developers to test that all other ++ targets like `make install' and `make uninstall' work correctly. ++ This target is generally not run by end users. ++ + Compilers and Options + ===================== + +-Some systems require unusual options for compilation or linking that the +-`configure' script does not know about. Run `./configure --help' for +-details on some of the pertinent environment variables. ++ Some systems require unusual options for compilation or linking that ++the `configure' script does not know about. Run `./configure --help' ++for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters + by setting variables in the command line or in the environment. Here + is an example: + +- ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix ++ ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + + Compiling For Multiple Architectures + ==================================== + +-You can compile the package for more than one kind of computer at the ++ You can compile the package for more than one kind of computer at the + same time, by placing the object files for each architecture in their +-own directory. To do this, you must use a version of `make' that +-supports the `VPATH' variable, such as GNU `make'. `cd' to the ++own directory. To do this, you can use GNU `make'. `cd' to the + directory where you want the object files and executables to go and run + the `configure' script. `configure' automatically checks for the +-source code in the directory that `configure' is in and in `..'. ++source code in the directory that `configure' is in and in `..'. This ++is known as a "VPATH" build. + +- If you have to use a `make' that does not support the `VPATH' +-variable, you have to compile the package for one architecture at a +-time in the source code directory. After you have installed the +-package for one architecture, use `make distclean' before reconfiguring +-for another architecture. ++ With a non-GNU `make', it is safer to compile the package for one ++architecture at a time in the source code directory. After you have ++installed the package for one architecture, use `make distclean' before ++reconfiguring for another architecture. ++ ++ On MacOS X 10.5 and later systems, you can create libraries and ++executables that work on multiple system types--known as "fat" or ++"universal" binaries--by specifying multiple `-arch' options to the ++compiler but only a single `-arch' option to the preprocessor. Like ++this: ++ ++ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ ++ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ ++ CPP="gcc -E" CXXCPP="g++ -E" ++ ++ This is not guaranteed to produce working output in all cases, you ++may have to build one architecture at a time and combine the results ++using the `lipo' tool if you have problems. + + Installation Names + ================== + +-By default, `make install' installs the package's commands under ++ By default, `make install' installs the package's commands under + `/usr/local/bin', include files under `/usr/local/include', etc. You + can specify an installation prefix other than `/usr/local' by giving +-`configure' the option `--prefix=PREFIX'. ++`configure' the option `--prefix=PREFIX', where PREFIX must be an ++absolute file name. + + You can specify separate installation prefixes for + architecture-specific files and architecture-independent files. If you +@@ -116,16 +156,47 @@ + In addition, if you use an unusual directory layout you can give + options like `--bindir=DIR' to specify different values for particular + kinds of files. Run `configure --help' for a list of the directories +-you can set and what kinds of files go in them. ++you can set and what kinds of files go in them. In general, the ++default for these options is expressed in terms of `${prefix}', so that ++specifying just `--prefix' will affect all of the other directory ++specifications that were not explicitly provided. ++ ++ The most portable way to affect installation locations is to pass the ++correct locations to `configure'; however, many packages provide one or ++both of the following shortcuts of passing variable assignments to the ++`make install' command line to change installation locations without ++having to reconfigure or recompile. ++ ++ The first method involves providing an override variable for each ++affected directory. For example, `make install ++prefix=/alternate/directory' will choose an alternate location for all ++directory configuration variables that were expressed in terms of ++`${prefix}'. Any directories that were specified during `configure', ++but not in terms of `${prefix}', must each be overridden at install ++time for the entire installation to be relocated. The approach of ++makefile variable overrides for each directory variable is required by ++the GNU Coding Standards, and ideally causes no recompilation. ++However, some platforms have known limitations with the semantics of ++shared libraries that end up requiring recompilation when using this ++method, particularly noticeable in packages that use GNU Libtool. ++ ++ The second method involves providing the `DESTDIR' variable. For ++example, `make install DESTDIR=/alternate/directory' will prepend ++`/alternate/directory' before all installation names. The approach of ++`DESTDIR' overrides is not required by the GNU Coding Standards, and ++does not work on platforms that have drive letters. On the other hand, ++it does better at avoiding recompilation issues, and works well even ++when some directory options were not specified in terms of `${prefix}' ++at `configure' time. ++ ++Optional Features ++================= + + If the package supports it, you can cause programs to be installed + with an extra prefix or suffix on their names by giving `configure' the + option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +-Optional Features +-================= +- +-Some packages pay attention to `--enable-FEATURE' options to ++ Some packages pay attention to `--enable-FEATURE' options to + `configure', where FEATURE indicates an optional part of the package. + They may also pay attention to `--with-PACKAGE' options, where PACKAGE + is something like `gnu-as' or `x' (for the X Window System). The +@@ -137,14 +208,53 @@ + you can use the `configure' options `--x-includes=DIR' and + `--x-libraries=DIR' to specify their locations. + ++ Some packages offer the ability to configure how verbose the ++execution of `make' will be. For these packages, running `./configure ++--enable-silent-rules' sets the default to minimal output, which can be ++overridden with `make V=1'; while running `./configure ++--disable-silent-rules' sets the default to verbose, which can be ++overridden with `make V=0'. ++ ++Particular systems ++================== ++ ++ On HP-UX, the default C compiler is not ANSI C compatible. If GNU ++CC is not installed, it is recommended to use the following options in ++order to use an ANSI C compiler: ++ ++ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" ++ ++and if that doesn't work, install pre-built binaries of GCC for HP-UX. ++ ++ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot ++parse its `' header file. The option `-nodtk' can be used as ++a workaround. If GNU CC is not installed, it is therefore recommended ++to try ++ ++ ./configure CC="cc" ++ ++and if that doesn't work, try ++ ++ ./configure CC="cc -nodtk" ++ ++ On Solaris, don't put `/usr/ucb' early in your `PATH'. This ++directory contains several dysfunctional programs; working variants of ++these programs are available in `/usr/bin'. So, if you need `/usr/ucb' ++in your `PATH', put it _after_ `/usr/bin'. ++ ++ On Haiku, software installed for all users goes in `/boot/common', ++not `/usr/local'. It is recommended to use the following options: ++ ++ ./configure --prefix=/boot/common ++ + Specifying the System Type + ========================== + +-There may be some features `configure' cannot figure out automatically, +-but needs to determine by the type of machine the package will run on. +-Usually, assuming the package is built to be run on the _same_ +-architectures, `configure' can figure that out, but if it prints a +-message saying it cannot guess the machine type, give it the ++ There may be some features `configure' cannot figure out ++automatically, but needs to determine by the type of machine the package ++will run on. Usually, assuming the package is built to be run on the ++_same_ architectures, `configure' can figure that out, but if it prints ++a message saying it cannot guess the machine type, give it the + `--build=TYPE' option. TYPE can either be a short name for the system + type, such as `sun4', or a canonical name which has the form: + +@@ -152,7 +262,8 @@ + + where SYSTEM can have one of these forms: + +- OS KERNEL-OS ++ OS ++ KERNEL-OS + + See the file `config.sub' for the possible values of each field. If + `config.sub' isn't included in this package, then this package doesn't +@@ -170,9 +281,9 @@ + Sharing Defaults + ================ + +-If you want to set default values for `configure' scripts to share, you +-can create a site shell script called `config.site' that gives default +-values for variables like `CC', `cache_file', and `prefix'. ++ If you want to set default values for `configure' scripts to share, ++you can create a site shell script called `config.site' that gives ++default values for variables like `CC', `cache_file', and `prefix'. + `configure' looks for `PREFIX/share/config.site' if it exists, then + `PREFIX/etc/config.site' if it exists. Or, you can set the + `CONFIG_SITE' environment variable to the location of the site script. +@@ -181,7 +292,7 @@ + Defining Variables + ================== + +-Variables not defined in a site shell script can be set in the ++ Variables not defined in a site shell script can be set in the + environment passed to `configure'. However, some packages may run + configure again during the build, and the customized values of these + variables may be lost. In order to avoid this problem, you should set +@@ -190,21 +301,29 @@ + ./configure CC=/usr/local2/bin/gcc + + causes the specified `gcc' to be used as the C compiler (unless it is +-overridden in the site shell script). Here is a another example: ++overridden in the site shell script). + +- /bin/bash ./configure CONFIG_SHELL=/bin/bash ++Unfortunately, this technique does not work for `CONFIG_SHELL' due to ++an Autoconf bug. Until the bug is fixed you can use this workaround: + +-Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent +-configuration-related scripts to be executed by `/bin/bash'. ++ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + + `configure' Invocation + ====================== + +-`configure' recognizes the following options to control how it operates. ++ `configure' recognizes the following options to control how it ++operates. + + `--help' + `-h' +- Print a summary of the options to `configure', and exit. ++ Print a summary of all of the options to `configure', and exit. ++ ++`--help=short' ++`--help=recursive' ++ Print a summary of the options unique to this package's ++ `configure', and exit. The `short' variant lists options used ++ only in the top level, while the `recursive' variant lists options ++ also present in any nested packages. + + `--version' + `-V' +@@ -231,6 +350,16 @@ + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + ++`--prefix=DIR' ++ Use DIR as the installation prefix. *note Installation Names:: ++ for more details, including other options available for fine-tuning ++ the installation locations. ++ ++`--no-create' ++`-n' ++ Run the configure checks, but stop before creating any output ++ files. ++ + `configure' also accepts some other, not widely useful, options. Run + `configure --help' for more details. + +diff -Nur exmap-console-0.4.1.orig/Makefile.am exmap-console-0.4.1/Makefile.am +--- exmap-console-0.4.1.orig/Makefile.am 2006-11-06 18:05:01.000000000 +0100 ++++ exmap-console-0.4.1/Makefile.am 2011-01-11 12:44:03.000000000 +0100 +@@ -1,4 +1,4 @@ +-SUBDIRS=kernel src doc ++SUBDIRS=src + + DISTCLEANFILES = *~ Makefile.in install-sh missing depcomp *.m4 config.log config.status Makefile + +diff -Nur exmap-console-0.4.1.orig/Makefile.in exmap-console-0.4.1/Makefile.in +--- exmap-console-0.4.1.orig/Makefile.in 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/Makefile.in 2011-01-11 12:44:16.000000000 +0100 +@@ -1,8 +1,9 @@ +-# Makefile.in generated by automake 1.9.6 from Makefile.am. ++# Makefile.in generated by automake 1.11.1 from Makefile.am. + # @configure_input@ + + # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005 Free Software Foundation, Inc. ++# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, ++# Inc. + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. +@@ -13,15 +14,12 @@ + # PARTICULAR PURPOSE. + + @SET_MAKE@ +-srcdir = @srcdir@ +-top_srcdir = @top_srcdir@ + VPATH = @srcdir@ + pkgdatadir = $(datadir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ + pkgincludedir = $(includedir)/@PACKAGE@ +-top_builddir = . ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ + am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-INSTALL = @INSTALL@ + install_sh_DATA = $(install_sh) -c -m 644 + install_sh_PROGRAM = $(install_sh) -c + install_sh_SCRIPT = $(install_sh) -c +@@ -42,17 +40,24 @@ + am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) + am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ +- configure.lineno configure.status.lineno ++ configure.lineno config.status.lineno + mkinstalldirs = $(install_sh) -d + CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = + SOURCES = + DIST_SOURCES = + RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ + html-recursive info-recursive install-data-recursive \ +- install-exec-recursive install-info-recursive \ +- install-recursive installcheck-recursive installdirs-recursive \ +- pdf-recursive ps-recursive uninstall-info-recursive \ +- uninstall-recursive ++ install-dvi-recursive install-exec-recursive \ ++ install-html-recursive install-info-recursive \ ++ install-pdf-recursive install-ps-recursive install-recursive \ ++ installcheck-recursive installdirs-recursive pdf-recursive \ ++ ps-recursive uninstall-recursive ++RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ ++ distclean-recursive maintainer-clean-recursive ++AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ ++ $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ ++ distdir dist dist-all distcheck + ETAGS = etags + CTAGS = ctags + DIST_SUBDIRS = $(SUBDIRS) +@@ -60,16 +65,39 @@ + distdir = $(PACKAGE)-$(VERSION) + top_distdir = $(distdir) + am__remove_distdir = \ +- { test ! -d $(distdir) \ +- || { find $(distdir) -type d ! -perm -200 -exec chmod u+w {} ';' \ +- && rm -fr $(distdir); }; } ++ { test ! -d "$(distdir)" \ ++ || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ ++ && rm -fr "$(distdir)"; }; } ++am__relativize = \ ++ dir0=`pwd`; \ ++ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ ++ sed_rest='s,^[^/]*/*,,'; \ ++ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ ++ sed_butlast='s,/*[^/]*$$,,'; \ ++ while test -n "$$dir1"; do \ ++ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ ++ if test "$$first" != "."; then \ ++ if test "$$first" = ".."; then \ ++ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ ++ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ ++ else \ ++ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ ++ if test "$$first2" = "$$first"; then \ ++ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ ++ else \ ++ dir2="../$$dir2"; \ ++ fi; \ ++ dir0="$$dir0"/"$$first"; \ ++ fi; \ ++ fi; \ ++ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ ++ done; \ ++ reldir="$$dir2" + DIST_ARCHIVES = $(distdir).tar.gz + GZIP_ENV = --best + distuninstallcheck_listfiles = find . -type f -print + distcleancheck_listfiles = find . -type f -print + ACLOCAL = @ACLOCAL@ +-AMDEP_FALSE = @AMDEP_FALSE@ +-AMDEP_TRUE = @AMDEP_TRUE@ + AMTAR = @AMTAR@ + AUTOCONF = @AUTOCONF@ + AUTOHEADER = @AUTOHEADER@ +@@ -82,8 +110,6 @@ + CYGPATH_W = @CYGPATH_W@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ +-DODOCS_FALSE = @DODOCS_FALSE@ +-DODOCS_TRUE = @DODOCS_TRUE@ + ECHO_C = @ECHO_C@ + ECHO_N = @ECHO_N@ + ECHO_T = @ECHO_T@ +@@ -91,38 +117,41 @@ + GCC_CFLAGS = @GCC_CFLAGS@ + GLIB_CFLAGS = @GLIB_CFLAGS@ + GLIB_LIBS = @GLIB_LIBS@ +-HAVE_HELPTOMAN_FALSE = @HAVE_HELPTOMAN_FALSE@ +-HAVE_HELPTOMAN_TRUE = @HAVE_HELPTOMAN_TRUE@ + HELPTOMAN = @HELPTOMAN@ ++INSTALL = @INSTALL@ + INSTALL_DATA = @INSTALL_DATA@ + INSTALL_PROGRAM = @INSTALL_PROGRAM@ + INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-INTERACTIVE_FALSE = @INTERACTIVE_FALSE@ +-INTERACTIVE_TRUE = @INTERACTIVE_TRUE@ + LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LTLIBOBJS = @LTLIBOBJS@ + MAKEINFO = @MAKEINFO@ ++MKDIR_P = @MKDIR_P@ + OBJEXT = @OBJEXT@ + PACKAGE = @PACKAGE@ + PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + PACKAGE_NAME = @PACKAGE_NAME@ + PACKAGE_STRING = @PACKAGE_STRING@ + PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ + PACKAGE_VERSION = @PACKAGE_VERSION@ + PATH_SEPARATOR = @PATH_SEPARATOR@ + PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ + READLINE_CFLAGS = @READLINE_CFLAGS@ + READLINE_LIBS = @READLINE_LIBS@ + SET_MAKE = @SET_MAKE@ + SHELL = @SHELL@ + STRIP = @STRIP@ + VERSION = @VERSION@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ + ac_ct_CC = @ac_ct_CC@ +-am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +-am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ + am__include = @am__include@ + am__leading_dot = @am__leading_dot@ + am__quote = @am__quote@ +@@ -130,6 +159,7 @@ + am__untar = @am__untar@ + bindir = @bindir@ + build_alias = @build_alias@ ++builddir = @builddir@ + datadir = @datadir@ + datarootdir = @datarootdir@ + docdir = @docdir@ +@@ -153,9 +183,13 @@ + psdir = @psdir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ +-SUBDIRS = kernel src doc ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ ++SUBDIRS = src + DISTCLEANFILES = *~ Makefile.in install-sh missing depcomp *.m4 config.log config.status Makefile + all: all-recursive + +@@ -166,15 +200,15 @@ + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ +- echo ' cd $(srcdir) && $(AUTOMAKE) --gnu '; \ +- cd $(srcdir) && $(AUTOMAKE) --gnu \ ++ echo ' cd $(srcdir) && $(AUTOMAKE) --gnu'; \ ++ $(am__cd) $(srcdir) && $(AUTOMAKE) --gnu \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ +- cd $(top_srcdir) && \ +- $(AUTOMAKE) --gnu Makefile ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --gnu Makefile + .PRECIOUS: Makefile + Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ +@@ -190,10 +224,10 @@ + $(SHELL) ./config.status --recheck + + $(top_srcdir)/configure: $(am__configure_deps) +- cd $(srcdir) && $(AUTOCONF) ++ $(am__cd) $(srcdir) && $(AUTOCONF) + $(ACLOCAL_M4): $(am__aclocal_m4_deps) +- cd $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +-uninstall-info-am: ++ $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) ++$(am__aclocal_m4_deps): + + # This directory's subdirectories are mostly independent; you can cd + # into them and run `make' without going through this Makefile. +@@ -202,7 +236,7 @@ + # (which will cause the Makefiles to be regenerated when you run `make'); + # (2) otherwise, pass the desired values on the `make' command line. + $(RECURSIVE_TARGETS): +- @failcom='exit 1'; \ ++ @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ +@@ -219,16 +253,15 @@ + else \ + local_target="$$target"; \ + fi; \ +- (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ ++ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +-mostlyclean-recursive clean-recursive distclean-recursive \ +-maintainer-clean-recursive: +- @failcom='exit 1'; \ ++$(RECURSIVE_CLEAN_TARGETS): ++ @fail= failcom='exit 1'; \ + for f in x $$MAKEFLAGS; do \ + case $$f in \ + *=* | --[!k]*);; \ +@@ -254,16 +287,16 @@ + else \ + local_target="$$target"; \ + fi; \ +- (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ ++ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done && test -z "$$fail" + tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ +- test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ ++ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + ctags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ +- test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ ++ test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ + done + + ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) +@@ -271,14 +304,14 @@ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique + tags: TAGS + + TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) +- tags=; \ ++ set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ +@@ -290,92 +323,114 @@ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ +- tags="$$tags $$include_option=$$here/$$subdir/TAGS"; \ ++ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ +- if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ ++ shift; \ ++ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ +- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ +- $$tags $$unique; \ ++ if test $$# -gt 0; then \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ "$$@" $$unique; \ ++ else \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ $$unique; \ ++ fi; \ + fi + ctags: CTAGS + CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) +- tags=; \ +- here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ +- test -z "$(CTAGS_ARGS)$$tags$$unique" \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ ++ test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ +- $$tags $$unique ++ $$unique + + GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ +- && cd $(top_srcdir) \ +- && gtags -i $(GTAGS_ARGS) $$here ++ && $(am__cd) $(top_srcdir) \ ++ && gtags -i $(GTAGS_ARGS) "$$here" + + distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + + distdir: $(DISTFILES) + $(am__remove_distdir) +- mkdir $(distdir) +- @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ +- list='$(DISTFILES)'; for file in $$list; do \ +- case $$file in \ +- $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ +- $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ +- esac; \ ++ test -d "$(distdir)" || mkdir "$(distdir)" ++ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ list='$(DISTFILES)'; \ ++ dist_files=`for file in $$list; do echo $$file; done | \ ++ sed -e "s|^$$srcdirstrip/||;t" \ ++ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ ++ case $$dist_files in \ ++ */*) $(MKDIR_P) `echo "$$dist_files" | \ ++ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ ++ sort -u` ;; \ ++ esac; \ ++ for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test "$$dir" != "$$file" && test "$$dir" != "."; then \ +- dir="/$$dir"; \ +- $(mkdir_p) "$(distdir)$$dir"; \ +- else \ +- dir=''; \ +- fi; \ + if test -d $$d/$$file; then \ ++ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ ++ if test -d "$(distdir)/$$file"; then \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ ++ fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ +- cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ +- test -f $(distdir)/$$file \ +- || cp -p $$d/$$file $(distdir)/$$file \ ++ test -f "$(distdir)/$$file" \ ++ || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +- list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ ++ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d "$(distdir)/$$subdir" \ +- || $(mkdir_p) "$(distdir)/$$subdir" \ ++ || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ +- distdir=`$(am__cd) $(distdir) && pwd`; \ +- top_distdir=`$(am__cd) $(top_distdir) && pwd`; \ +- (cd $$subdir && \ ++ fi; \ ++ done ++ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ ++ if test "$$subdir" = .; then :; else \ ++ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ ++ $(am__relativize); \ ++ new_distdir=$$reldir; \ ++ dir1=$$subdir; dir2="$(top_distdir)"; \ ++ $(am__relativize); \ ++ new_top_distdir=$$reldir; \ ++ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ ++ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ++ ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ +- top_distdir="$$top_distdir" \ +- distdir="$$distdir/$$subdir" \ ++ top_distdir="$$new_top_distdir" \ ++ distdir="$$new_distdir" \ ++ am__remove_distdir=: \ ++ am__skip_length_check=: \ ++ am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +- -find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \ ++ -test -n "$(am__skip_mode_fix)" \ ++ || find "$(distdir)" -type d ! -perm -755 \ ++ -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ +- ! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \ +- || chmod -R a+r $(distdir) ++ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ ++ || chmod -R a+r "$(distdir)" + dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz + $(am__remove_distdir) +@@ -384,6 +439,14 @@ + tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2 + $(am__remove_distdir) + ++dist-lzma: distdir ++ tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma ++ $(am__remove_distdir) ++ ++dist-xz: distdir ++ tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz ++ $(am__remove_distdir) ++ + dist-tarZ: distdir + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__remove_distdir) +@@ -407,13 +470,17 @@ + distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ +- GZIP=$(GZIP_ENV) gunzip -c $(distdir).tar.gz | $(am__untar) ;;\ ++ GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ +- bunzip2 -c $(distdir).tar.bz2 | $(am__untar) ;;\ ++ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ ++ *.tar.lzma*) \ ++ lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\ ++ *.tar.xz*) \ ++ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ +- GZIP=$(GZIP_ENV) gunzip -c $(distdir).shar.gz | unshar ;;\ ++ GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + esac +@@ -421,9 +488,11 @@ + mkdir $(distdir)/_build + mkdir $(distdir)/_inst + chmod a-w $(distdir) ++ test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ +- && cd $(distdir)/_build \ ++ && am__cwd=`pwd` \ ++ && $(am__cd) $(distdir)/_build \ + && ../configure --srcdir=.. --prefix="$$dc_install_base" \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) \ +@@ -445,13 +514,15 @@ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ +- && $(MAKE) $(AM_MAKEFLAGS) distcleancheck ++ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ ++ && cd "$$am__cwd" \ ++ || exit 1 + $(am__remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ +- sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}' ++ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' + distuninstallcheck: +- @cd $(distuninstallcheck_dir) \ ++ @$(am__cd) '$(distuninstallcheck_dir)' \ + && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ +@@ -493,6 +564,7 @@ + + distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + + maintainer-clean-generic: +@@ -510,18 +582,38 @@ + + html: html-recursive + ++html-am: ++ + info: info-recursive + + info-am: + + install-data-am: + ++install-dvi: install-dvi-recursive ++ ++install-dvi-am: ++ + install-exec-am: + ++install-html: install-html-recursive ++ ++install-html-am: ++ + install-info: install-info-recursive + ++install-info-am: ++ + install-man: + ++install-pdf: install-pdf-recursive ++ ++install-pdf-am: ++ ++install-ps: install-ps-recursive ++ ++install-ps-am: ++ + installcheck-am: + + maintainer-clean: maintainer-clean-recursive +@@ -542,29 +634,32 @@ + + ps-am: + +-uninstall-am: uninstall-info-am ++uninstall-am: + +-uninstall-info: uninstall-info-recursive ++.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ ++ install-am install-strip tags-recursive + +-.PHONY: $(RECURSIVE_TARGETS) CTAGS GTAGS all all-am am--refresh check \ +- check-am clean clean-generic clean-recursive ctags \ +- ctags-recursive dist dist-all dist-bzip2 dist-gzip dist-shar \ +- dist-tarZ dist-zip distcheck distclean distclean-generic \ +- distclean-recursive distclean-tags distcleancheck distdir \ +- distuninstallcheck dvi dvi-am html html-am info info-am \ +- install install-am install-data install-data-am install-exec \ +- install-exec-am install-info install-info-am install-man \ +- install-strip installcheck installcheck-am installdirs \ +- installdirs-am maintainer-clean maintainer-clean-generic \ +- maintainer-clean-recursive mostlyclean mostlyclean-generic \ +- mostlyclean-recursive pdf pdf-am ps ps-am tags tags-recursive \ +- uninstall uninstall-am uninstall-info-am ++.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ ++ all all-am am--refresh check check-am clean clean-generic \ ++ ctags ctags-recursive dist dist-all dist-bzip2 dist-gzip \ ++ dist-lzma dist-shar dist-tarZ dist-xz dist-zip distcheck \ ++ distclean distclean-generic distclean-tags distcleancheck \ ++ distdir distuninstallcheck dvi dvi-am html html-am info \ ++ info-am install install-am install-data install-data-am \ ++ install-dvi install-dvi-am install-exec install-exec-am \ ++ install-html install-html-am install-info install-info-am \ ++ install-man install-pdf install-pdf-am install-ps \ ++ install-ps-am install-strip installcheck installcheck-am \ ++ installdirs installdirs-am maintainer-clean \ ++ maintainer-clean-generic mostlyclean mostlyclean-generic pdf \ ++ pdf-am ps ps-am tags tags-recursive uninstall uninstall-am + + + local-distclean: + $(RM) -rf autom4te.cache + + distclean: distclean-recursive local-distclean ++ + # Tell versions [3.59,3.63) of GNU make to not export all variables. + # Otherwise a system limit (for SysV at least) may be exceeded. + .NOEXPORT: +diff -Nur exmap-console-0.4.1.orig/aclocal.m4 exmap-console-0.4.1/aclocal.m4 +--- exmap-console-0.4.1.orig/aclocal.m4 2007-02-26 12:43:59.000000000 +0100 ++++ exmap-console-0.4.1/aclocal.m4 2011-01-11 12:44:15.000000000 +0100 +@@ -1,7 +1,7 @@ +-# generated automatically by aclocal 1.9.6 -*- Autoconf -*- ++# generated automatically by aclocal 1.11.1 -*- Autoconf -*- + + # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, +-# 2005 Free Software Foundation, Inc. ++# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. +@@ -11,7 +11,16 @@ + # even the implied warranty of MERCHANTABILITY or FITNESS FOR A + # PARTICULAR PURPOSE. + ++m4_ifndef([AC_AUTOCONF_VERSION], ++ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl ++m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.68],, ++[m4_warning([this file was generated for autoconf 2.68. ++You have another version of autoconf. It may work, but is not guaranteed to. ++If you have problems, you may need to regenerate the build system entirely. ++To do so, use the procedure documented by the package, typically `autoreconf'.])]) ++ + # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- ++# serial 1 (pkg-config-0.24) + # + # Copyright © 2004 Scott James Remnant . + # +@@ -39,7 +48,10 @@ + AC_DEFUN([PKG_PROG_PKG_CONFIG], + [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) + m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) +-AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl ++AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) ++AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) ++AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) ++ + if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) + fi +@@ -52,7 +64,6 @@ + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +- + fi[]dnl + ])# PKG_PROG_PKG_CONFIG + +@@ -61,34 +72,31 @@ + # Check to see whether a particular set of modules exists. Similar + # to PKG_CHECK_MODULES(), but does not set variables or print errors. + # +-# +-# Similar to PKG_CHECK_MODULES, make sure that the first instance of +-# this or PKG_CHECK_MODULES is called, or make sure to call +-# PKG_CHECK_EXISTS manually ++# Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) ++# only at the first occurence in configure.ac, so if the first place ++# it's called might be skipped (such as if it is within an "if", you ++# have to call PKG_CHECK_EXISTS manually + # -------------------------------------------------------------- + AC_DEFUN([PKG_CHECK_EXISTS], + [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl + if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then +- m4_ifval([$2], [$2], [:]) ++ m4_default([$2], [:]) + m4_ifvaln([$3], [else + $3])dnl + fi]) + +- + # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) + # --------------------------------------------- + m4_define([_PKG_CONFIG], +-[if test -n "$PKG_CONFIG"; then +- if test -n "$$1"; then +- pkg_cv_[]$1="$$1" +- else +- PKG_CHECK_EXISTS([$3], +- [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], +- [pkg_failed=yes]) +- fi +-else +- pkg_failed=untried ++[if test -n "$$1"; then ++ pkg_cv_[]$1="$$1" ++ elif test -n "$PKG_CONFIG"; then ++ PKG_CHECK_EXISTS([$3], ++ [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], ++ [pkg_failed=yes]) ++ else ++ pkg_failed=untried + fi[]dnl + ])# _PKG_CONFIG + +@@ -130,16 +138,17 @@ + See the pkg-config man page for more details.]) + + if test $pkg_failed = yes; then ++ AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then +- $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` ++ $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "$2" 2>&1` + else +- $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` ++ $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + +- ifelse([$4], , [AC_MSG_ERROR(dnl ++ m4_default([$4], [AC_MSG_ERROR( + [Package requirements ($2) were not met: + + $$1_PKG_ERRORS +@@ -147,28 +156,28 @@ + Consider adjusting the PKG_CONFIG_PATH environment variable if you + installed software in a non-standard prefix. + +-_PKG_TEXT +-])], +- [$4]) ++_PKG_TEXT])[]dnl ++ ]) + elif test $pkg_failed = untried; then +- ifelse([$4], , [AC_MSG_FAILURE(dnl ++ AC_MSG_RESULT([no]) ++ m4_default([$4], [AC_MSG_FAILURE( + [The pkg-config script could not be found or is too old. Make sure it + is in your PATH or set the PKG_CONFIG environment variable to the full + path to pkg-config. + + _PKG_TEXT + +-To get pkg-config, see .])], +- [$4]) ++To get pkg-config, see .])dnl ++ ]) + else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) +- ifelse([$3], , :, [$3]) ++ $3 + fi[]dnl + ])# PKG_CHECK_MODULES + +-# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. ++# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -178,14 +187,31 @@ + # ---------------------------- + # Automake X.Y traces this macro to ensure aclocal.m4 has been + # generated from the m4 files accompanying Automake X.Y. +-AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) ++# (This private macro should not be called outside this file.) ++AC_DEFUN([AM_AUTOMAKE_VERSION], ++[am__api_version='1.11' ++dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to ++dnl require some minimum version. Point them to the right macro. ++m4_if([$1], [1.11.1], [], ++ [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ++]) ++ ++# _AM_AUTOCONF_VERSION(VERSION) ++# ----------------------------- ++# aclocal traces this macro to find the Autoconf version. ++# This is a private macro too. Using m4_define simplifies ++# the logic in aclocal, which can simply ignore this definition. ++m4_define([_AM_AUTOCONF_VERSION], []) + + # AM_SET_CURRENT_AUTOMAKE_VERSION + # ------------------------------- +-# Call AM_AUTOMAKE_VERSION so it can be traced. +-# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. ++# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. ++# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. + AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +- [AM_AUTOMAKE_VERSION([1.9.6])]) ++[AM_AUTOMAKE_VERSION([1.11.1])dnl ++m4_ifndef([AC_AUTOCONF_VERSION], ++ [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl ++_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + + # AM_AUX_DIR_EXPAND -*- Autoconf -*- + +@@ -242,14 +268,14 @@ + + # AM_CONDITIONAL -*- Autoconf -*- + +-# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005 ++# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 + # Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 7 ++# serial 9 + + # AM_CONDITIONAL(NAME, SHELL-CONDITION) + # ------------------------------------- +@@ -258,8 +284,11 @@ + [AC_PREREQ(2.52)dnl + ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +-AC_SUBST([$1_TRUE]) +-AC_SUBST([$1_FALSE]) ++AC_SUBST([$1_TRUE])dnl ++AC_SUBST([$1_FALSE])dnl ++_AM_SUBST_NOTMAKE([$1_TRUE])dnl ++_AM_SUBST_NOTMAKE([$1_FALSE])dnl ++m4_define([_AM_COND_VALUE_$1], [$2])dnl + if $2; then + $1_TRUE= + $1_FALSE='#' +@@ -273,15 +302,14 @@ + Usually this means the macro was only invoked conditionally.]]) + fi])]) + +- +-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 ++# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 + # Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 8 ++# serial 10 + + # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be + # written in clear, in which case automake, when reading aclocal.m4, +@@ -309,6 +337,7 @@ + ifelse([$1], CC, [depcc="$CC" am_compiler_list=], + [$1], CXX, [depcc="$CXX" am_compiler_list=], + [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'], ++ [$1], UPC, [depcc="$UPC" am_compiler_list=], + [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +@@ -337,6 +366,16 @@ + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi ++ am__universal=false ++ m4_case([$1], [CC], ++ [case " $depcc " in #( ++ *\ -arch\ *\ -arch\ *) am__universal=true ;; ++ esac], ++ [CXX], ++ [case " $depcc " in #( ++ *\ -arch\ *\ -arch\ *) am__universal=true ;; ++ esac]) ++ + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and +@@ -354,7 +393,17 @@ + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + ++ # We check with `-c' and `-o' for the sake of the "dashmstdout" ++ # mode. It turns out that the SunPro C++ compiler does not properly ++ # handle `-M -o', and we need to detect this. Also, some Intel ++ # versions had trouble with output in subdirs ++ am__obj=sub/conftest.${OBJEXT-o} ++ am__minus_obj="-o $am__obj" + case $depmode in ++ gcc) ++ # This depmode causes a compiler race in universal mode. ++ test "$am__universal" = false || continue ++ ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested +@@ -364,18 +413,23 @@ + break + fi + ;; ++ msvisualcpp | msvcmsys) ++ # This compiler won't grok `-c -o', but also, the minuso test has ++ # not run yet. These depmodes are late enough in the game, and ++ # so weak that their functioning should not be impacted. ++ am__obj=conftest.${OBJEXT-o} ++ am__minus_obj= ++ ;; + none) break ;; + esac +- # We check with `-c' and `-o' for the sake of the "dashmstdout" +- # mode. It turns out that the SunPro C++ compiler does not properly +- # handle `-M -o', and we need to detect this. + if depmode=$depmode \ +- source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ ++ source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ +- $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ ++ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && ++ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && +- grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && ++ grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message +@@ -426,61 +480,74 @@ + AMDEPBACKSLASH='\' + fi + AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +-AC_SUBST([AMDEPBACKSLASH]) ++AC_SUBST([AMDEPBACKSLASH])dnl ++_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl + ]) + + # Generate code to set up dependency tracking. -*- Autoconf -*- + +-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 ++# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 + # Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-#serial 3 ++#serial 5 + + # _AM_OUTPUT_DEPENDENCY_COMMANDS + # ------------------------------ + AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +-[for mf in $CONFIG_FILES; do +- # Strip MF so we end up with the name of the file. +- mf=`echo "$mf" | sed -e 's/:.*$//'` +- # Check whether this is an Automake generated Makefile or not. +- # We used to match only the files named `Makefile.in', but +- # some people rename them; so instead we look at the file content. +- # Grep'ing the first line is not enough: some people post-process +- # each Makefile.in and add a new line on top of each file to say so. +- # So let's grep whole file. +- if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then +- dirpart=`AS_DIRNAME("$mf")` +- else +- continue +- fi +- # Extract the definition of DEPDIR, am__include, and am__quote +- # from the Makefile without running `make'. +- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` +- test -z "$DEPDIR" && continue +- am__include=`sed -n 's/^am__include = //p' < "$mf"` +- test -z "am__include" && continue +- am__quote=`sed -n 's/^am__quote = //p' < "$mf"` +- # When using ansi2knr, U may be empty or an underscore; expand it +- U=`sed -n 's/^U = //p' < "$mf"` +- # Find all dependency output files, they are included files with +- # $(DEPDIR) in their names. We invoke sed twice because it is the +- # simplest approach to changing $(DEPDIR) to its actual value in the +- # expansion. +- for file in `sed -n " +- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ +- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do +- # Make sure the directory exists. +- test -f "$dirpart/$file" && continue +- fdir=`AS_DIRNAME(["$file"])` +- AS_MKDIR_P([$dirpart/$fdir]) +- # echo "creating $dirpart/$file" +- echo '# dummy' > "$dirpart/$file" ++[{ ++ # Autoconf 2.62 quotes --file arguments for eval, but not when files ++ # are listed without --file. Let's play safe and only enable the eval ++ # if we detect the quoting. ++ case $CONFIG_FILES in ++ *\'*) eval set x "$CONFIG_FILES" ;; ++ *) set x $CONFIG_FILES ;; ++ esac ++ shift ++ for mf ++ do ++ # Strip MF so we end up with the name of the file. ++ mf=`echo "$mf" | sed -e 's/:.*$//'` ++ # Check whether this is an Automake generated Makefile or not. ++ # We used to match only the files named `Makefile.in', but ++ # some people rename them; so instead we look at the file content. ++ # Grep'ing the first line is not enough: some people post-process ++ # each Makefile.in and add a new line on top of each file to say so. ++ # Grep'ing the whole file is not good either: AIX grep has a line ++ # limit of 2048, but all sed's we know have understand at least 4000. ++ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then ++ dirpart=`AS_DIRNAME("$mf")` ++ else ++ continue ++ fi ++ # Extract the definition of DEPDIR, am__include, and am__quote ++ # from the Makefile without running `make'. ++ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` ++ test -z "$DEPDIR" && continue ++ am__include=`sed -n 's/^am__include = //p' < "$mf"` ++ test -z "am__include" && continue ++ am__quote=`sed -n 's/^am__quote = //p' < "$mf"` ++ # When using ansi2knr, U may be empty or an underscore; expand it ++ U=`sed -n 's/^U = //p' < "$mf"` ++ # Find all dependency output files, they are included files with ++ # $(DEPDIR) in their names. We invoke sed twice because it is the ++ # simplest approach to changing $(DEPDIR) to its actual value in the ++ # expansion. ++ for file in `sed -n " ++ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ ++ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do ++ # Make sure the directory exists. ++ test -f "$dirpart/$file" && continue ++ fdir=`AS_DIRNAME(["$file"])` ++ AS_MKDIR_P([$dirpart/$fdir]) ++ # echo "creating $dirpart/$file" ++ echo '# dummy' > "$dirpart/$file" ++ done + done +-done ++} + ])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +@@ -499,14 +566,14 @@ + + # Do all the work for Automake. -*- Autoconf -*- + +-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +-# Free Software Foundation, Inc. ++# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, ++# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 12 ++# serial 16 + + # This macro actually does too much. Some checks are only needed if + # your package does certain things. But this isn't really a big deal. +@@ -523,16 +590,20 @@ + # arguments mandatory, and then we can depend on a new Autoconf + # release and drop the old call support. + AC_DEFUN([AM_INIT_AUTOMAKE], +-[AC_PREREQ([2.58])dnl ++[AC_PREREQ([2.62])dnl + dnl Autoconf wants to disallow AM_ names. We explicitly allow + dnl the ones we care about. + m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl + AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl + AC_REQUIRE([AC_PROG_INSTALL])dnl +-# test to see if srcdir already configured +-if test "`cd $srcdir && pwd`" != "`pwd`" && +- test -f $srcdir/config.status; then +- AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) ++if test "`cd $srcdir && pwd`" != "`pwd`"; then ++ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output ++ # is not polluted with repeated "-I." ++ AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl ++ # test to see if srcdir already configured ++ if test -f $srcdir/config.status; then ++ AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) ++ fi + fi + + # test whether we have cygpath +@@ -552,6 +623,9 @@ + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], + [_AM_SET_OPTIONS([$1])dnl ++dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. ++m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,, ++ [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +@@ -567,8 +641,8 @@ + AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) + AM_MISSING_PROG(AUTOHEADER, autoheader) + AM_MISSING_PROG(MAKEINFO, makeinfo) +-AM_PROG_INSTALL_SH +-AM_PROG_INSTALL_STRIP ++AC_REQUIRE([AM_PROG_INSTALL_SH])dnl ++AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl + AC_REQUIRE([AM_PROG_MKDIR_P])dnl + # We need awk for the "check" target. The system "awk" is bad on + # some platforms. +@@ -576,20 +650,37 @@ + AC_REQUIRE([AC_PROG_MAKE_SET])dnl + AC_REQUIRE([AM_SET_LEADING_DOT])dnl + _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], +- [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], +- [_AM_PROG_TAR([v7])])]) ++ [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], ++ [_AM_PROG_TAR([v7])])]) + _AM_IF_OPTION([no-dependencies],, + [AC_PROVIDE_IFELSE([AC_PROG_CC], +- [_AM_DEPENDENCIES(CC)], +- [define([AC_PROG_CC], +- defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl ++ [_AM_DEPENDENCIES(CC)], ++ [define([AC_PROG_CC], ++ defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl + AC_PROVIDE_IFELSE([AC_PROG_CXX], +- [_AM_DEPENDENCIES(CXX)], +- [define([AC_PROG_CXX], +- defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl ++ [_AM_DEPENDENCIES(CXX)], ++ [define([AC_PROG_CXX], ++ defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl ++AC_PROVIDE_IFELSE([AC_PROG_OBJC], ++ [_AM_DEPENDENCIES(OBJC)], ++ [define([AC_PROG_OBJC], ++ defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl + ]) ++_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl ++dnl The `parallel-tests' driver may need to know about EXEEXT, so add the ++dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro ++dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. ++AC_CONFIG_COMMANDS_PRE(dnl ++[m4_provide_if([_AM_COMPILER_EXEEXT], ++ [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + ]) + ++dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not ++dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further ++dnl mangled by Autoconf and run in a shell conditional statement. ++m4_define([_AC_COMPILER_EXEEXT], ++m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) ++ + + # When config.status generates a header, we must update the stamp-h file. + # This file resides in the same directory as the config header +@@ -600,18 +691,19 @@ + # our stamp files there. + AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], + [# Compute $1's index in $config_headers. ++_am_arg=$1 + _am_stamp_count=1 + for _am_header in $config_headers :; do + case $_am_header in +- $1 | $1:* ) ++ $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac + done +-echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) ++echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +-# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. ++# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -622,7 +714,14 @@ + # Define $install_sh. + AC_DEFUN([AM_PROG_INSTALL_SH], + [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +-install_sh=${install_sh-"$am_aux_dir/install-sh"} ++if test x"${install_sh}" != xset; then ++ case $am_aux_dir in ++ *\ * | *\ *) ++ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; ++ *) ++ install_sh="\${SHELL} $am_aux_dir/install-sh" ++ esac ++fi + AC_SUBST(install_sh)]) + + # Copyright (C) 2003, 2005 Free Software Foundation, Inc. +@@ -648,13 +747,13 @@ + + # Check to see how 'make' treats includes. -*- Autoconf -*- + +-# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. ++# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 3 ++# serial 4 + + # AM_MAKE_INCLUDE() + # ----------------- +@@ -663,7 +762,7 @@ + [am_make=${MAKE-make} + cat > confinc << 'END' + am__doit: +- @echo done ++ @echo this is the am__doit target + .PHONY: am__doit + END + # If we don't find an include directive, just comment out the code. +@@ -673,24 +772,24 @@ + _am_result=none + # First try GNU make style include. + echo "include confinc" > confmf +-# We grep out `Entering directory' and `Leaving directory' +-# messages which can occur if `w' ends up in MAKEFLAGS. +-# In particular we don't look at `^make:' because GNU make might +-# be invoked under some other name (usually "gmake"), in which +-# case it prints its new name instead of `make'. +-if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then +- am__include=include +- am__quote= +- _am_result=GNU +-fi ++# Ignore all kinds of additional output from `make'. ++case `$am_make -s -f confmf 2> /dev/null` in #( ++*the\ am__doit\ target*) ++ am__include=include ++ am__quote= ++ _am_result=GNU ++ ;; ++esac + # Now try BSD make style include. + if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf +- if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then +- am__include=.include +- am__quote="\"" +- _am_result=BSD +- fi ++ case `$am_make -s -f confmf 2> /dev/null` in #( ++ *the\ am__doit\ target*) ++ am__include=.include ++ am__quote="\"" ++ _am_result=BSD ++ ;; ++ esac + fi + AC_SUBST([am__include]) + AC_SUBST([am__quote]) +@@ -700,14 +799,14 @@ + + # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +-# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005 ++# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 + # Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 4 ++# serial 6 + + # AM_MISSING_PROG(NAME, PROGRAM) + # ------------------------------ +@@ -723,7 +822,15 @@ + # If it does, set am_missing_run to use it, otherwise, to nothing. + AC_DEFUN([AM_MISSING_HAS_RUN], + [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +-test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" ++AC_REQUIRE_AUX_FILE([missing])dnl ++if test x"${MISSING+set}" != xset; then ++ case $am_aux_dir in ++ *\ * | *\ *) ++ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; ++ *) ++ MISSING="\${SHELL} $am_aux_dir/missing" ;; ++ esac ++fi + # Use eval to expand $SHELL + if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " +@@ -733,7 +840,7 @@ + fi + ]) + +-# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. ++# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, +@@ -741,70 +848,33 @@ + + # AM_PROG_MKDIR_P + # --------------- +-# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise. +-# +-# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories +-# created by `make install' are always world readable, even if the +-# installer happens to have an overly restrictive umask (e.g. 077). +-# This was a mistake. There are at least two reasons why we must not +-# use `-m 0755': +-# - it causes special bits like SGID to be ignored, +-# - it may be too restrictive (some setups expect 775 directories). +-# +-# Do not use -m 0755 and let people choose whatever they expect by +-# setting umask. +-# +-# We cannot accept any implementation of `mkdir' that recognizes `-p'. +-# Some implementations (such as Solaris 8's) are not thread-safe: if a +-# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c' +-# concurrently, both version can detect that a/ is missing, but only +-# one can create it and the other will error out. Consequently we +-# restrict ourselves to GNU make (using the --version option ensures +-# this.) ++# Check for `mkdir -p'. + AC_DEFUN([AM_PROG_MKDIR_P], +-[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then +- # We used to keeping the `.' as first argument, in order to +- # allow $(mkdir_p) to be used without argument. As in +- # $(mkdir_p) $(somedir) +- # where $(somedir) is conditionally defined. However this is wrong +- # for two reasons: +- # 1. if the package is installed by a user who cannot write `.' +- # make install will fail, +- # 2. the above comment should most certainly read +- # $(mkdir_p) $(DESTDIR)$(somedir) +- # so it does not work when $(somedir) is undefined and +- # $(DESTDIR) is not. +- # To support the latter case, we have to write +- # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), +- # so the `.' trick is pointless. +- mkdir_p='mkdir -p --' +-else +- # On NextStep and OpenStep, the `mkdir' command does not +- # recognize any option. It will interpret all options as +- # directories to create, and then abort because `.' already +- # exists. +- for d in ./-p ./--version; +- do +- test -d $d && rmdir $d +- done +- # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. +- if test -f "$ac_aux_dir/mkinstalldirs"; then +- mkdir_p='$(mkinstalldirs)' +- else +- mkdir_p='$(install_sh) -d' +- fi +-fi +-AC_SUBST([mkdir_p])]) ++[AC_PREREQ([2.60])dnl ++AC_REQUIRE([AC_PROG_MKDIR_P])dnl ++dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P, ++dnl while keeping a definition of mkdir_p for backward compatibility. ++dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile. ++dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of ++dnl Makefile.ins that do not define MKDIR_P, so we do our own ++dnl adjustment using top_builddir (which is defined more often than ++dnl MKDIR_P). ++AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl ++case $mkdir_p in ++ [[\\/$]]* | ?:[[\\/]]*) ;; ++ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; ++esac ++]) + + # Helper functions for option handling. -*- Autoconf -*- + +-# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. ++# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 3 ++# serial 4 + + # _AM_MANGLE_OPTION(NAME) + # ----------------------- +@@ -821,7 +891,7 @@ + # ---------------------------------- + # OPTIONS is a space-separated list of Automake options. + AC_DEFUN([_AM_SET_OPTIONS], +-[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) ++[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + + # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) + # ------------------------------------------- +@@ -831,14 +901,14 @@ + + # Check to make sure that the build environment is sane. -*- Autoconf -*- + +-# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 ++# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 + # Free Software Foundation, Inc. + # + # This file is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. + +-# serial 4 ++# serial 5 + + # AM_SANITY_CHECK + # --------------- +@@ -847,16 +917,29 @@ + # Just in case + sleep 1 + echo timestamp > conftest.file ++# Reject unsafe characters in $srcdir or the absolute working directory ++# name. Accept space and tab only in the latter. ++am_lf=' ++' ++case `pwd` in ++ *[[\\\"\#\$\&\'\`$am_lf]]*) ++ AC_MSG_ERROR([unsafe absolute working directory name]);; ++esac ++case $srcdir in ++ *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) ++ AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; ++esac ++ + # Do `set' in a subshell so we don't clobber the current shell's + # arguments. Must try -L first in case configure is actually a + # symlink; some systems play weird games with the mod time of symlinks + # (eg FreeBSD returns the mod time of the symlink's containing + # directory). + if ( +- set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` ++ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. +- set X `ls -t $srcdir/configure conftest.file` ++ set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$[*]" != "X $srcdir/configure conftest.file" \ +@@ -906,9 +989,28 @@ + if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) + fi +-INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" ++INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + AC_SUBST([INSTALL_STRIP_PROGRAM])]) + ++# Copyright (C) 2006, 2008 Free Software Foundation, Inc. ++# ++# This file is free software; the Free Software Foundation ++# gives unlimited permission to copy and/or distribute it, ++# with or without modifications, as long as this notice is preserved. ++ ++# serial 2 ++ ++# _AM_SUBST_NOTMAKE(VARIABLE) ++# --------------------------- ++# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. ++# This macro is traced by Automake. ++AC_DEFUN([_AM_SUBST_NOTMAKE]) ++ ++# AM_SUBST_NOTMAKE(VARIABLE) ++# --------------------------- ++# Public sister of _AM_SUBST_NOTMAKE. ++AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) ++ + # Check how to create a tarball. -*- Autoconf -*- + + # Copyright (C) 2004, 2005 Free Software Foundation, Inc. +diff -Nur exmap-console-0.4.1.orig/configure exmap-console-0.4.1/configure +--- exmap-console-0.4.1.orig/configure 2007-02-26 12:44:00.000000000 +0100 ++++ exmap-console-0.4.1/configure 2011-01-11 12:44:16.000000000 +0100 +@@ -1,58 +1,85 @@ + #! /bin/sh + # Guess values for system-dependent variables and create Makefiles. +-# Generated by GNU Autoconf 2.60 for exmap-console 0.4.1. ++# Generated by GNU Autoconf 2.68 for exmap-console 0.4.1. + # + # Report bugs to . + # ++# + # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +-# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. ++# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software ++# Foundation, Inc. ++# ++# + # This configure script is free software; the Free Software Foundation + # gives unlimited permission to copy, distribute and modify it. +-## --------------------- ## +-## M4sh Initialization. ## +-## --------------------- ## ++## -------------------- ## ++## M4sh Initialization. ## ++## -------------------- ## + +-# Be Bourne compatible +-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then ++# Be more Bourne compatible ++DUALCASE=1; export DUALCASE # for MKS sh ++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: +- # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which ++ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST + else +- case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac ++ case `(set -o) 2>/dev/null` in #( ++ *posix*) : ++ set -o posix ;; #( ++ *) : ++ ;; ++esac + fi +-BIN_SH=xpg4; export BIN_SH # for Tru64 +-DUALCASE=1; export DUALCASE # for MKS sh +- + +-# PATH needs CR +-# Avoid depending upon Character Ranges. +-as_cr_letters='abcdefghijklmnopqrstuvwxyz' +-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +-as_cr_Letters=$as_cr_letters$as_cr_LETTERS +-as_cr_digits='0123456789' +-as_cr_alnum=$as_cr_Letters$as_cr_digits + +-# The user is always right. +-if test "${PATH_SEPARATOR+set}" != set; then +- echo "#! /bin/sh" >conf$$.sh +- echo "exit 0" >>conf$$.sh +- chmod +x conf$$.sh +- if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then +- PATH_SEPARATOR=';' ++as_nl=' ++' ++export as_nl ++# Printing a long string crashes Solaris 7 /usr/bin/printf. ++as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo ++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo ++# Prefer a ksh shell builtin over an external printf program on Solaris, ++# but without wasting forks for bash or zsh. ++if test -z "$BASH_VERSION$ZSH_VERSION" \ ++ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then ++ as_echo='print -r --' ++ as_echo_n='print -rn --' ++elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then ++ as_echo='printf %s\n' ++ as_echo_n='printf %s' ++else ++ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then ++ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' ++ as_echo_n='/usr/ucb/echo -n' + else +- PATH_SEPARATOR=: ++ as_echo_body='eval expr "X$1" : "X\\(.*\\)"' ++ as_echo_n_body='eval ++ arg=$1; ++ case $arg in #( ++ *"$as_nl"*) ++ expr "X$arg" : "X\\(.*\\)$as_nl"; ++ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; ++ esac; ++ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ++ ' ++ export as_echo_n_body ++ as_echo_n='sh -c $as_echo_n_body as_echo' + fi +- rm -f conf$$.sh ++ export as_echo_body ++ as_echo='sh -c $as_echo_body as_echo' + fi + +-# Support unset when possible. +-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then +- as_unset=unset +-else +- as_unset=false ++# The user is always right. ++if test "${PATH_SEPARATOR+set}" != set; then ++ PATH_SEPARATOR=: ++ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { ++ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || ++ PATH_SEPARATOR=';' ++ } + fi + + +@@ -61,20 +88,19 @@ + # there to prevent editors from complaining about space-tab. + # (If _AS_PATH_WALK were called with IFS unset, it would disable word + # splitting by setting IFS to empty value.) +-as_nl=' +-' + IFS=" "" $as_nl" + + # Find who we are. Look in the path if we contain no directory separator. +-case $0 in ++as_myself= ++case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + for as_dir in $PATH + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +-done ++ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break ++ done + IFS=$as_save_IFS + + ;; +@@ -85,352 +111,328 @@ + as_myself=$0 + fi + if test ! -f "$as_myself"; then +- echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 +- { (exit 1); exit 1; } ++ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 ++ exit 1 + fi + +-# Work around bugs in pre-3.0 UWIN ksh. +-for as_var in ENV MAIL MAILPATH +-do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var ++# Unset variables that we do not need and which cause bugs (e.g. in ++# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" ++# suppresses any "Segmentation fault" message there. '((' could ++# trigger a bug in pdksh 5.2.14. ++for as_var in BASH_ENV ENV MAIL MAILPATH ++do eval test x\${$as_var+set} = xset \ ++ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : + done + PS1='$ ' + PS2='> ' + PS4='+ ' + + # NLS nuisances. +-for as_var in \ +- LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ +- LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ +- LC_TELEPHONE LC_TIME +-do +- if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then +- eval $as_var=C; export $as_var +- else +- ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +- fi +-done +- +-# Required to use basename. +-if expr a : '\(a\)' >/dev/null 2>&1 && +- test "X`expr 00001 : '.*\(...\)'`" = X001; then +- as_expr=expr +-else +- as_expr=false +-fi +- +-if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then +- as_basename=basename +-else +- as_basename=false +-fi +- +- +-# Name of the executable. +-as_me=`$as_basename -- "$0" || +-$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ +- X"$0" : 'X\(//\)$' \| \ +- X"$0" : 'X\(/\)' \| . 2>/dev/null || +-echo X/"$0" | +- sed '/^.*\/\([^/][^/]*\)\/*$/{ +- s//\1/ +- q +- } +- /^X\/\(\/\/\)$/{ +- s//\1/ +- q +- } +- /^X\/\(\/\).*/{ +- s//\1/ +- q +- } +- s/.*/./; q'` ++LC_ALL=C ++export LC_ALL ++LANGUAGE=C ++export LANGUAGE + + # CDPATH. +-$as_unset CDPATH +- ++(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + if test "x$CONFIG_SHELL" = x; then +- if (eval ":") 2>/dev/null; then +- as_have_required=yes ++ as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : ++ emulate sh ++ NULLCMD=: ++ # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which ++ # is contrary to our usage. Disable this feature. ++ alias -g '\${1+\"\$@\"}'='\"\$@\"' ++ setopt NO_GLOB_SUBST + else +- as_have_required=no ++ case \`(set -o) 2>/dev/null\` in #( ++ *posix*) : ++ set -o posix ;; #( ++ *) : ++ ;; ++esac + fi +- +- if test $as_have_required = yes && (eval ": +-(as_func_return () { +- (exit \$1) +-} +-as_func_success () { +- as_func_return 0 +-} +-as_func_failure () { +- as_func_return 1 +-} +-as_func_ret_success () { +- return 0 +-} +-as_func_ret_failure () { +- return 1 +-} ++" ++ as_required="as_fn_return () { (exit \$1); } ++as_fn_success () { as_fn_return 0; } ++as_fn_failure () { as_fn_return 1; } ++as_fn_ret_success () { return 0; } ++as_fn_ret_failure () { return 1; } + + exitcode=0 +-if as_func_success; then +- : +-else +- exitcode=1 +- echo as_func_success failed. +-fi +- +-if as_func_failure; then +- exitcode=1 +- echo as_func_failure succeeded. +-fi ++as_fn_success || { exitcode=1; echo as_fn_success failed.; } ++as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } ++as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } ++as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } ++if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +-if as_func_ret_success; then +- : + else +- exitcode=1 +- echo as_func_ret_success failed. +-fi +- +-if as_func_ret_failure; then +- exitcode=1 +- echo as_func_ret_failure succeeded. +-fi +- +-if ( set x; as_func_ret_success y && test x = \"\$1\" ); then +- : ++ exitcode=1; echo positional parameters were not saved. ++fi ++test x\$exitcode = x0 || exit 1" ++ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO ++ as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO ++ eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && ++ test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" ++ if (eval "$as_required") 2>/dev/null; then : ++ as_have_required=yes + else +- exitcode=1 +- echo positional parameters were not saved. ++ as_have_required=no + fi ++ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +-test \$exitcode = 0) || { (exit 1); exit 1; } +- +-( +- as_lineno_1=\$LINENO +- as_lineno_2=\$LINENO +- test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && +- test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +-") 2> /dev/null; then +- : +-else +- as_candidate_shells= +- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +-for as_dir in /usr/bin/posix$PATH_SEPARATOR/bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH ++else ++ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR ++as_found=false ++for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- case $as_dir in ++ as_found=: ++ case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do +- as_candidate_shells="$as_candidate_shells $as_dir/$as_base" ++ # Try only shells that exist, to save several forks. ++ as_shell=$as_dir/$as_base ++ if { test -f "$as_shell" || test -f "$as_shell.exe"; } && ++ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : ++ CONFIG_SHELL=$as_shell as_have_required=yes ++ if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : ++ break 2 ++fi ++fi + done;; + esac ++ as_found=false + done ++$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && ++ { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : ++ CONFIG_SHELL=$SHELL as_have_required=yes ++fi; } + IFS=$as_save_IFS + + +- for as_shell in $as_candidate_shells $SHELL; do +- # Try only shells that exist, to save several forks. +- if { test -f "$as_shell" || test -f "$as_shell.exe"; } && +- { ("$as_shell") 2> /dev/null <<\_ASEOF +-# Be Bourne compatible +-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then +- emulate sh +- NULLCMD=: +- # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which +- # is contrary to our usage. Disable this feature. +- alias -g '${1+"$@"}'='"$@"' +- setopt NO_GLOB_SUBST +-else +- case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +-fi +-BIN_SH=xpg4; export BIN_SH # for Tru64 +-DUALCASE=1; export DUALCASE # for MKS sh +- +-: +-_ASEOF +-}; then +- CONFIG_SHELL=$as_shell +- as_have_required=yes +- if { "$as_shell" 2> /dev/null <<\_ASEOF +-# Be Bourne compatible +-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then +- emulate sh +- NULLCMD=: +- # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which +- # is contrary to our usage. Disable this feature. +- alias -g '${1+"$@"}'='"$@"' +- setopt NO_GLOB_SUBST +-else +- case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +-fi +-BIN_SH=xpg4; export BIN_SH # for Tru64 +-DUALCASE=1; export DUALCASE # for MKS sh +- +-: +-(as_func_return () { +- (exit $1) +-} +-as_func_success () { +- as_func_return 0 +-} +-as_func_failure () { +- as_func_return 1 +-} +-as_func_ret_success () { +- return 0 +-} +-as_func_ret_failure () { +- return 1 +-} +- +-exitcode=0 +-if as_func_success; then +- : +-else +- exitcode=1 +- echo as_func_success failed. +-fi +- +-if as_func_failure; then +- exitcode=1 +- echo as_func_failure succeeded. +-fi +- +-if as_func_ret_success; then +- : +-else +- exitcode=1 +- echo as_func_ret_success failed. +-fi +- +-if as_func_ret_failure; then +- exitcode=1 +- echo as_func_ret_failure succeeded. +-fi +- +-if ( set x; as_func_ret_success y && test x = "$1" ); then +- : +-else +- exitcode=1 +- echo positional parameters were not saved. ++ if test "x$CONFIG_SHELL" != x; then : ++ # We cannot yet assume a decent shell, so we have to provide a ++ # neutralization value for shells without unset; and this also ++ # works around shells that cannot unset nonexistent variables. ++ # Preserve -v and -x to the replacement shell. ++ BASH_ENV=/dev/null ++ ENV=/dev/null ++ (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV ++ export CONFIG_SHELL ++ case $- in # (((( ++ *v*x* | *x*v* ) as_opts=-vx ;; ++ *v* ) as_opts=-v ;; ++ *x* ) as_opts=-x ;; ++ * ) as_opts= ;; ++ esac ++ exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} + fi + +-test $exitcode = 0) || { (exit 1); exit 1; } +- +-( +- as_lineno_1=$LINENO +- as_lineno_2=$LINENO +- test "x$as_lineno_1" != "x$as_lineno_2" && +- test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } +- +-_ASEOF +-}; then +- break ++ if test x$as_have_required = xno; then : ++ $as_echo "$0: This script requires a shell more modern than all" ++ $as_echo "$0: the shells that I found on your system." ++ if test x${ZSH_VERSION+set} = xset ; then ++ $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" ++ $as_echo "$0: be upgraded to zsh 4.3.4 or later." ++ else ++ $as_echo "$0: Please tell bug-autoconf@gnu.org and tf@o-hand.com ++$0: about your system, including any error possibly output ++$0: before this message. Then install a modern shell, or ++$0: manually run the script under such a shell if you do ++$0: have one." ++ fi ++ exit 1 + fi +- + fi +- +- done +- +- if test "x$CONFIG_SHELL" != x; then +- for as_var in BASH_ENV ENV +- do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +- done +- export CONFIG_SHELL +- exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} + fi ++SHELL=${CONFIG_SHELL-/bin/sh} ++export SHELL ++# Unset more variables known to interfere with behavior of common tools. ++CLICOLOR_FORCE= GREP_OPTIONS= ++unset CLICOLOR_FORCE GREP_OPTIONS + ++## --------------------- ## ++## M4sh Shell Functions. ## ++## --------------------- ## ++# as_fn_unset VAR ++# --------------- ++# Portably unset VAR. ++as_fn_unset () ++{ ++ { eval $1=; unset $1;} ++} ++as_unset=as_fn_unset + +- if test $as_have_required = no; then +- echo This script requires a shell more modern than all the +- echo shells that I found on your system. Please install a +- echo modern shell, or manually run the script under such a +- echo shell if you do have one. +- { (exit 1); exit 1; } +-fi ++# as_fn_set_status STATUS ++# ----------------------- ++# Set $? to STATUS, without forking. ++as_fn_set_status () ++{ ++ return $1 ++} # as_fn_set_status + ++# as_fn_exit STATUS ++# ----------------- ++# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. ++as_fn_exit () ++{ ++ set +e ++ as_fn_set_status $1 ++ exit $1 ++} # as_fn_exit ++ ++# as_fn_mkdir_p ++# ------------- ++# Create "$as_dir" as a directory, including parents if necessary. ++as_fn_mkdir_p () ++{ + +-fi ++ case $as_dir in #( ++ -*) as_dir=./$as_dir;; ++ esac ++ test -d "$as_dir" || eval $as_mkdir_p || { ++ as_dirs= ++ while :; do ++ case $as_dir in #( ++ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( ++ *) as_qdir=$as_dir;; ++ esac ++ as_dirs="'$as_qdir' $as_dirs" ++ as_dir=`$as_dirname -- "$as_dir" || ++$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ ++ X"$as_dir" : 'X\(//\)[^/]' \| \ ++ X"$as_dir" : 'X\(//\)$' \| \ ++ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || ++$as_echo X"$as_dir" | ++ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)[^/].*/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\).*/{ ++ s//\1/ ++ q ++ } ++ s/.*/./; q'` ++ test -d "$as_dir" && break ++ done ++ test -z "$as_dirs" || eval "mkdir $as_dirs" ++ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + +-fi + ++} # as_fn_mkdir_p ++# as_fn_append VAR VALUE ++# ---------------------- ++# Append the text in VALUE to the end of the definition contained in VAR. Take ++# advantage of any shell optimizations that allow amortized linear growth over ++# repeated appends, instead of the typical quadratic growth present in naive ++# implementations. ++if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : ++ eval 'as_fn_append () ++ { ++ eval $1+=\$2 ++ }' ++else ++ as_fn_append () ++ { ++ eval $1=\$$1\$2 ++ } ++fi # as_fn_append ++ ++# as_fn_arith ARG... ++# ------------------ ++# Perform arithmetic evaluation on the ARGs, and store the result in the ++# global $as_val. Take advantage of shells that can avoid forks. The arguments ++# must be portable across $(()) and expr. ++if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : ++ eval 'as_fn_arith () ++ { ++ as_val=$(( $* )) ++ }' ++else ++ as_fn_arith () ++ { ++ as_val=`expr "$@" || test $? -eq 1` ++ } ++fi # as_fn_arith + + +-(eval "as_func_return () { +- (exit \$1) +-} +-as_func_success () { +- as_func_return 0 +-} +-as_func_failure () { +- as_func_return 1 +-} +-as_func_ret_success () { +- return 0 +-} +-as_func_ret_failure () { +- return 1 +-} ++# as_fn_error STATUS ERROR [LINENO LOG_FD] ++# ---------------------------------------- ++# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are ++# provided, also output the error to LOG_FD, referencing LINENO. Then exit the ++# script with STATUS, using 1 if that was 0. ++as_fn_error () ++{ ++ as_status=$1; test $as_status -eq 0 && as_status=1 ++ if test "$4"; then ++ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 ++ fi ++ $as_echo "$as_me: error: $2" >&2 ++ as_fn_exit $as_status ++} # as_fn_error + +-exitcode=0 +-if as_func_success; then +- : ++if expr a : '\(a\)' >/dev/null 2>&1 && ++ test "X`expr 00001 : '.*\(...\)'`" = X001; then ++ as_expr=expr + else +- exitcode=1 +- echo as_func_success failed. +-fi +- +-if as_func_failure; then +- exitcode=1 +- echo as_func_failure succeeded. ++ as_expr=false + fi + +-if as_func_ret_success; then +- : ++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then ++ as_basename=basename + else +- exitcode=1 +- echo as_func_ret_success failed. +-fi +- +-if as_func_ret_failure; then +- exitcode=1 +- echo as_func_ret_failure succeeded. ++ as_basename=false + fi + +-if ( set x; as_func_ret_success y && test x = \"\$1\" ); then +- : ++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then ++ as_dirname=dirname + else +- exitcode=1 +- echo positional parameters were not saved. ++ as_dirname=false + fi + +-test \$exitcode = 0") || { +- echo No shell found that supports shell functions. +- echo Please tell autoconf@gnu.org about your system, +- echo including any error possibly output before this +- echo message +-} ++as_me=`$as_basename -- "$0" || ++$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ ++ X"$0" : 'X\(//\)$' \| \ ++ X"$0" : 'X\(/\)' \| . 2>/dev/null || ++$as_echo X/"$0" | ++ sed '/^.*\/\([^/][^/]*\)\/*$/{ ++ s//\1/ ++ q ++ } ++ /^X\/\(\/\/\)$/{ ++ s//\1/ ++ q ++ } ++ /^X\/\(\/\).*/{ ++ s//\1/ ++ q ++ } ++ s/.*/./; q'` + ++# Avoid depending upon Character Ranges. ++as_cr_letters='abcdefghijklmnopqrstuvwxyz' ++as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' ++as_cr_Letters=$as_cr_letters$as_cr_LETTERS ++as_cr_digits='0123456789' ++as_cr_alnum=$as_cr_Letters$as_cr_digits + + +- as_lineno_1=$LINENO +- as_lineno_2=$LINENO +- test "x$as_lineno_1" != "x$as_lineno_2" && +- test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { +- +- # Create $as_me.lineno as a copy of $as_myself, but with $LINENO +- # uniformly replaced by the line number. The first 'sed' inserts a +- # line-number line after each line using $LINENO; the second 'sed' +- # does the real work. The second script uses 'N' to pair each +- # line-number line with the line containing $LINENO, and appends +- # trailing '-' during substitution so that $LINENO is not a special +- # case at line end. +- # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the +- # scripts with optimization help from Paolo Bonzini. Blame Lee +- # E. McMahon (1931-1989) for sed's syntax. :-) ++ as_lineno_1=$LINENO as_lineno_1a=$LINENO ++ as_lineno_2=$LINENO as_lineno_2a=$LINENO ++ eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && ++ test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { ++ # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= +@@ -447,8 +449,7 @@ + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || +- { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 +- { (exit 1); exit 1; }; } ++ { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the +@@ -458,49 +459,40 @@ + exit + } + +- +-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then +- as_dirname=dirname +-else +- as_dirname=false +-fi +- + ECHO_C= ECHO_N= ECHO_T= +-case `echo -n x` in ++case `echo -n x` in #((((( + -n*) +- case `echo 'x\c'` in ++ case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. +- *) ECHO_C='\c';; ++ xy) ECHO_C='\c';; ++ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ++ ECHO_T=' ';; + esac;; + *) + ECHO_N='-n';; + esac + +-if expr a : '\(a\)' >/dev/null 2>&1 && +- test "X`expr 00001 : '.*\(...\)'`" = X001; then +- as_expr=expr +-else +- as_expr=false +-fi +- + rm -f conf$$ conf$$.exe conf$$.file + if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file + else + rm -f conf$$.dir +- mkdir conf$$.dir ++ mkdir conf$$.dir 2>/dev/null + fi +-echo >conf$$.file +-if ln -s conf$$.file conf$$ 2>/dev/null; then +- as_ln_s='ln -s' +- # ... but there are two gotchas: +- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. +- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. +- # In both cases, we have to default to `cp -p'. +- ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || ++if (echo >conf$$.file) 2>/dev/null; then ++ if ln -s conf$$.file conf$$ 2>/dev/null; then ++ as_ln_s='ln -s' ++ # ... but there are two gotchas: ++ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. ++ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. ++ # In both cases, we have to default to `cp -p'. ++ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || ++ as_ln_s='cp -p' ++ elif ln conf$$.file conf$$ 2>/dev/null; then ++ as_ln_s=ln ++ else + as_ln_s='cp -p' +-elif ln conf$$.file conf$$ 2>/dev/null; then +- as_ln_s=ln ++ fi + else + as_ln_s='cp -p' + fi +@@ -508,25 +500,34 @@ + rmdir conf$$.dir 2>/dev/null + + if mkdir -p . 2>/dev/null; then +- as_mkdir_p=: ++ as_mkdir_p='mkdir -p "$as_dir"' + else + test -d ./-p && rmdir ./-p + as_mkdir_p=false + fi + +-# Find out whether ``test -x'' works. Don't use a zero-byte file, as +-# systems may use methods other than mode bits to determine executability. +-cat >conf$$.file <<_ASEOF +-#! /bin/sh +-exit 0 +-_ASEOF +-chmod +x conf$$.file +-if test -x conf$$.file >/dev/null 2>&1; then +- as_executable_p="test -x" ++if test -x / >/dev/null 2>&1; then ++ as_test_x='test -x' + else +- as_executable_p=: ++ if ls -dL / >/dev/null 2>&1; then ++ as_ls_L_option=L ++ else ++ as_ls_L_option= ++ fi ++ as_test_x=' ++ eval sh -c '\'' ++ if test -d "$1"; then ++ test -d "$1/."; ++ else ++ case $1 in #( ++ -*)set "./$1";; ++ esac; ++ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ++ ???[sx]*):;;*)false;;esac;fi ++ '\'' sh ++ ' + fi +-rm -f conf$$.file ++as_executable_p=$as_test_x + + # Sed expression to map a string onto a valid CPP name. + as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +@@ -535,11 +536,11 @@ + as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +- +-exec 7<&0 &1 ++test -n "$DJDIR" || exec 7<&0 &1 + + # Name of the host. +-# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, ++# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, + # so uname gets run too. + ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +@@ -554,7 +555,6 @@ + subdirs= + MFLAGS= + MAKEFLAGS= +-SHELL=${CONFIG_SHELL-/bin/sh} + + # Identity of this package. + PACKAGE_NAME='exmap-console' +@@ -562,106 +562,128 @@ + PACKAGE_VERSION='0.4.1' + PACKAGE_STRING='exmap-console 0.4.1' + PACKAGE_BUGREPORT='tf@o-hand.com' ++PACKAGE_URL='' + + ac_unique_file="src/exmap.c" +-ac_subst_vars='SHELL +-PATH_SEPARATOR +-PACKAGE_NAME +-PACKAGE_TARNAME +-PACKAGE_VERSION +-PACKAGE_STRING +-PACKAGE_BUGREPORT +-exec_prefix +-prefix +-program_transform_name +-bindir +-sbindir +-libexecdir +-datarootdir +-datadir +-sysconfdir +-sharedstatedir +-localstatedir +-includedir +-oldincludedir +-docdir +-infodir +-htmldir +-dvidir +-pdfdir +-psdir +-libdir +-localedir +-mandir +-DEFS +-ECHO_C +-ECHO_N +-ECHO_T +-LIBS ++ac_subst_vars='am__EXEEXT_FALSE ++am__EXEEXT_TRUE ++LTLIBOBJS ++LIBOBJS ++GCC_CFLAGS ++HAVE_HELPTOMAN_FALSE ++HAVE_HELPTOMAN_TRUE ++HELPTOMAN ++GLIB_LIBS ++GLIB_CFLAGS ++PKG_CONFIG_LIBDIR ++PKG_CONFIG_PATH ++PKG_CONFIG ++DODOCS_FALSE ++DODOCS_TRUE ++READLINE_CFLAGS ++READLINE_LIBS ++INTERACTIVE_FALSE ++INTERACTIVE_TRUE ++am__fastdepCC_FALSE ++am__fastdepCC_TRUE ++CCDEPMODE ++AMDEPBACKSLASH ++AMDEP_FALSE ++AMDEP_TRUE ++am__quote ++am__include ++DEPDIR ++OBJEXT ++EXEEXT ++ac_ct_CC ++CPPFLAGS ++LDFLAGS ++CFLAGS ++CC ++am__untar ++am__tar ++AMTAR ++am__leading_dot ++SET_MAKE ++AWK ++mkdir_p ++MKDIR_P ++INSTALL_STRIP_PROGRAM ++STRIP ++install_sh ++MAKEINFO ++AUTOHEADER ++AUTOMAKE ++AUTOCONF ++ACLOCAL ++VERSION ++PACKAGE ++CYGPATH_W ++am__isrc ++INSTALL_DATA ++INSTALL_SCRIPT ++INSTALL_PROGRAM ++target_alias ++host_alias + build_alias ++LIBS ++ECHO_T ++ECHO_N ++ECHO_C ++DEFS ++mandir ++localedir ++libdir ++psdir ++pdfdir ++dvidir ++htmldir ++infodir ++docdir ++oldincludedir ++includedir ++localstatedir ++sharedstatedir ++sysconfdir ++datadir ++datarootdir ++libexecdir ++sbindir ++bindir ++program_transform_name ++prefix ++exec_prefix ++PACKAGE_URL ++PACKAGE_BUGREPORT ++PACKAGE_STRING ++PACKAGE_VERSION ++PACKAGE_TARNAME ++PACKAGE_NAME ++PATH_SEPARATOR ++SHELL' ++ac_subst_files='' ++ac_user_opts=' ++enable_option_checking ++with_readline ++enable_dependency_tracking ++enable_doc ++with_help2man ++' ++ ac_precious_vars='build_alias + host_alias + target_alias +-INSTALL_PROGRAM +-INSTALL_SCRIPT +-INSTALL_DATA +-CYGPATH_W +-PACKAGE +-VERSION +-ACLOCAL +-AUTOCONF +-AUTOMAKE +-AUTOHEADER +-MAKEINFO +-install_sh +-STRIP +-INSTALL_STRIP_PROGRAM +-mkdir_p +-AWK +-SET_MAKE +-am__leading_dot +-AMTAR +-am__tar +-am__untar + CC + CFLAGS + LDFLAGS ++LIBS + CPPFLAGS +-ac_ct_CC +-EXEEXT +-OBJEXT +-DEPDIR +-am__include +-am__quote +-AMDEP_TRUE +-AMDEP_FALSE +-AMDEPBACKSLASH +-CCDEPMODE +-am__fastdepCC_TRUE +-am__fastdepCC_FALSE +-INTERACTIVE_TRUE +-INTERACTIVE_FALSE +-READLINE_LIBS +-READLINE_CFLAGS +-DODOCS_TRUE +-DODOCS_FALSE +-PKG_CONFIG +-GLIB_CFLAGS +-GLIB_LIBS +-HELPTOMAN +-HAVE_HELPTOMAN_TRUE +-HAVE_HELPTOMAN_FALSE +-GCC_CFLAGS +-LIBOBJS +-LTLIBOBJS' +-ac_subst_files='' +- ac_precious_vars='build_alias +-host_alias +-target_alias + CC +-CFLAGS + LDFLAGS ++LIBS + CPPFLAGS + PKG_CONFIG ++PKG_CONFIG_PATH ++PKG_CONFIG_LIBDIR + GLIB_CFLAGS + GLIB_LIBS' + +@@ -669,6 +691,8 @@ + # Initialize some variables set by options. + ac_init_help= + ac_init_version=false ++ac_unrecognized_opts= ++ac_unrecognized_sep= + # The variables have the same names as the options, with + # dashes changed to underlines. + cache_file=/dev/null +@@ -724,8 +748,9 @@ + fi + + case $ac_option in +- *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; +- *) ac_optarg=yes ;; ++ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; ++ *=) ac_optarg= ;; ++ *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. +@@ -767,13 +792,20 @@ + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) +- ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` ++ ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. +- expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && +- { echo "$as_me: error: invalid feature name: $ac_feature" >&2 +- { (exit 1); exit 1; }; } +- ac_feature=`echo $ac_feature | sed 's/-/_/g'` +- eval enable_$ac_feature=no ;; ++ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && ++ as_fn_error $? "invalid feature name: $ac_useropt" ++ ac_useropt_orig=$ac_useropt ++ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` ++ case $ac_user_opts in ++ *" ++"enable_$ac_useropt" ++"*) ;; ++ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ++ ac_unrecognized_sep=', ';; ++ esac ++ eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; +@@ -786,13 +818,20 @@ + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) +- ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` ++ ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. +- expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && +- { echo "$as_me: error: invalid feature name: $ac_feature" >&2 +- { (exit 1); exit 1; }; } +- ac_feature=`echo $ac_feature | sed 's/-/_/g'` +- eval enable_$ac_feature=\$ac_optarg ;; ++ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && ++ as_fn_error $? "invalid feature name: $ac_useropt" ++ ac_useropt_orig=$ac_useropt ++ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` ++ case $ac_user_opts in ++ *" ++"enable_$ac_useropt" ++"*) ;; ++ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ++ ac_unrecognized_sep=', ';; ++ esac ++ eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ +@@ -983,22 +1022,36 @@ + ac_init_version=: ;; + + -with-* | --with-*) +- ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` ++ ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. +- expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && +- { echo "$as_me: error: invalid package name: $ac_package" >&2 +- { (exit 1); exit 1; }; } +- ac_package=`echo $ac_package| sed 's/-/_/g'` +- eval with_$ac_package=\$ac_optarg ;; ++ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && ++ as_fn_error $? "invalid package name: $ac_useropt" ++ ac_useropt_orig=$ac_useropt ++ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` ++ case $ac_user_opts in ++ *" ++"with_$ac_useropt" ++"*) ;; ++ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ++ ac_unrecognized_sep=', ';; ++ esac ++ eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) +- ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` ++ ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. +- expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && +- { echo "$as_me: error: invalid package name: $ac_package" >&2 +- { (exit 1); exit 1; }; } +- ac_package=`echo $ac_package | sed 's/-/_/g'` +- eval with_$ac_package=no ;; ++ expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && ++ as_fn_error $? "invalid package name: $ac_useropt" ++ ac_useropt_orig=$ac_useropt ++ ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` ++ case $ac_user_opts in ++ *" ++"with_$ac_useropt" ++"*) ;; ++ *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ++ ac_unrecognized_sep=', ';; ++ esac ++ eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. +@@ -1018,26 +1071,26 @@ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + +- -*) { echo "$as_me: error: unrecognized option: $ac_option +-Try \`$0 --help' for more information." >&2 +- { (exit 1); exit 1; }; } ++ -*) as_fn_error $? "unrecognized option: \`$ac_option' ++Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. +- expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && +- { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 +- { (exit 1); exit 1; }; } ++ case $ac_envvar in #( ++ '' | [0-9]* | *[!_$as_cr_alnum]* ) ++ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; ++ esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. +- echo "$as_me: WARNING: you should use --build, --host, --target" >&2 ++ $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && +- echo "$as_me: WARNING: invalid host type: $ac_option" >&2 +- : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ++ $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 ++ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +@@ -1045,23 +1098,36 @@ + + if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` +- { echo "$as_me: error: missing argument to $ac_option" >&2 +- { (exit 1); exit 1; }; } ++ as_fn_error $? "missing argument to $ac_option" + fi + +-# Be sure to have absolute directory names. ++if test -n "$ac_unrecognized_opts"; then ++ case $enable_option_checking in ++ no) ;; ++ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; ++ *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; ++ esac ++fi ++ ++# Check all directory arguments for consistency. + for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir + do + eval ac_val=\$$ac_var ++ # Remove trailing slashes. ++ case $ac_val in ++ */ ) ++ ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` ++ eval $ac_var=\$ac_val;; ++ esac ++ # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac +- { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 +- { (exit 1); exit 1; }; } ++ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" + done + + # There might be people who depend on the old broken behavior: `$host' +@@ -1075,8 +1141,8 @@ + if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe +- echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. +- If a cross compiler is detected then cross compile mode will be used." >&2 ++ $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. ++ If a cross compiler is detected then cross compile mode will be used" >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +@@ -1091,23 +1157,21 @@ + ac_pwd=`pwd` && test -n "$ac_pwd" && + ac_ls_di=`ls -di .` && + ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || +- { echo "$as_me: error: Working directory cannot be determined" >&2 +- { (exit 1); exit 1; }; } ++ as_fn_error $? "working directory cannot be determined" + test "X$ac_ls_di" = "X$ac_pwd_ls_di" || +- { echo "$as_me: error: pwd does not report name of working directory" >&2 +- { (exit 1); exit 1; }; } ++ as_fn_error $? "pwd does not report name of working directory" + + + # Find the source files, if location was not specified. + if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. +- ac_confdir=`$as_dirname -- "$0" || +-$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ +- X"$0" : 'X\(//\)[^/]' \| \ +- X"$0" : 'X\(//\)$' \| \ +- X"$0" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$0" | ++ ac_confdir=`$as_dirname -- "$as_myself" || ++$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ ++ X"$as_myself" : 'X\(//\)[^/]' \| \ ++ X"$as_myself" : 'X\(//\)$' \| \ ++ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || ++$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q +@@ -1134,13 +1198,11 @@ + fi + if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." +- { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 +- { (exit 1); exit 1; }; } ++ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" + fi + ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" + ac_abs_confdir=`( +- cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 +- { (exit 1); exit 1; }; } ++ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` + # When building in place, set srcdir=. + if test "$ac_abs_confdir" = "$ac_pwd"; then +@@ -1180,7 +1242,7 @@ + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit +- -q, --quiet, --silent do not print \`checking...' messages ++ -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files +@@ -1188,9 +1250,9 @@ + + Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX +- [$ac_default_prefix] ++ [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX +- [PREFIX] ++ [PREFIX] + + By default, \`make install' will install all the files in + \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +@@ -1200,25 +1262,25 @@ + For better control, use the options below. + + Fine tuning of the installation directories: +- --bindir=DIR user executables [EPREFIX/bin] +- --sbindir=DIR system admin executables [EPREFIX/sbin] +- --libexecdir=DIR program executables [EPREFIX/libexec] +- --sysconfdir=DIR read-only single-machine data [PREFIX/etc] +- --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] +- --localstatedir=DIR modifiable single-machine data [PREFIX/var] +- --libdir=DIR object code libraries [EPREFIX/lib] +- --includedir=DIR C header files [PREFIX/include] +- --oldincludedir=DIR C header files for non-gcc [/usr/include] +- --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] +- --datadir=DIR read-only architecture-independent data [DATAROOTDIR] +- --infodir=DIR info documentation [DATAROOTDIR/info] +- --localedir=DIR locale-dependent data [DATAROOTDIR/locale] +- --mandir=DIR man documentation [DATAROOTDIR/man] +- --docdir=DIR documentation root [DATAROOTDIR/doc/exmap-console] +- --htmldir=DIR html documentation [DOCDIR] +- --dvidir=DIR dvi documentation [DOCDIR] +- --pdfdir=DIR pdf documentation [DOCDIR] +- --psdir=DIR ps documentation [DOCDIR] ++ --bindir=DIR user executables [EPREFIX/bin] ++ --sbindir=DIR system admin executables [EPREFIX/sbin] ++ --libexecdir=DIR program executables [EPREFIX/libexec] ++ --sysconfdir=DIR read-only single-machine data [PREFIX/etc] ++ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] ++ --localstatedir=DIR modifiable single-machine data [PREFIX/var] ++ --libdir=DIR object code libraries [EPREFIX/lib] ++ --includedir=DIR C header files [PREFIX/include] ++ --oldincludedir=DIR C header files for non-gcc [/usr/include] ++ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] ++ --datadir=DIR read-only architecture-independent data [DATAROOTDIR] ++ --infodir=DIR info documentation [DATAROOTDIR/info] ++ --localedir=DIR locale-dependent data [DATAROOTDIR/locale] ++ --mandir=DIR man documentation [DATAROOTDIR/man] ++ --docdir=DIR documentation root [DATAROOTDIR/doc/exmap-console] ++ --htmldir=DIR html documentation [DOCDIR] ++ --dvidir=DIR dvi documentation [DOCDIR] ++ --pdfdir=DIR pdf documentation [DOCDIR] ++ --psdir=DIR ps documentation [DOCDIR] + _ACEOF + + cat <<\_ACEOF +@@ -1237,12 +1299,12 @@ + cat <<\_ACEOF + + Optional Features: ++ --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --disable-dependency-tracking speeds up one-time build + --enable-dependency-tracking do not reject slow dependency extractors + --disable-doc Do not build documentation +- --enable-debug Enable debuggin information + + Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] +@@ -1255,9 +1317,14 @@ + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory +- CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if ++ LIBS libraries to pass to the linker, e.g. -l ++ CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + PKG_CONFIG path to pkg-config utility ++ PKG_CONFIG_PATH ++ directories to add to pkg-config's search path ++ PKG_CONFIG_LIBDIR ++ path overriding pkg-config's built-in search path + GLIB_CFLAGS C compiler flags for GLIB, overriding pkg-config + GLIB_LIBS linker flags for GLIB, overriding pkg-config + +@@ -1272,15 +1339,17 @@ + if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue +- test -d "$ac_dir" || continue ++ test -d "$ac_dir" || ++ { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || ++ continue + ac_builddir=. + + case "$ac_dir" in + .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) +- ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` ++ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. +- ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` ++ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; +@@ -1316,7 +1385,7 @@ + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else +- echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 ++ $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +@@ -1326,21 +1395,108 @@ + if $ac_init_version; then + cat <<\_ACEOF + exmap-console configure 0.4.1 +-generated by GNU Autoconf 2.60 ++generated by GNU Autoconf 2.68 + +-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +-2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. ++Copyright (C) 2010 Free Software Foundation, Inc. + This configure script is free software; the Free Software Foundation + gives unlimited permission to copy, distribute and modify it. + _ACEOF + exit + fi ++ ++## ------------------------ ## ++## Autoconf initialization. ## ++## ------------------------ ## ++ ++# ac_fn_c_try_compile LINENO ++# -------------------------- ++# Try to compile conftest.$ac_ext, and return whether this succeeded. ++ac_fn_c_try_compile () ++{ ++ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ rm -f conftest.$ac_objext ++ if { { ac_try="$ac_compile" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_compile") 2>conftest.err ++ ac_status=$? ++ if test -s conftest.err; then ++ grep -v '^ *+' conftest.err >conftest.er1 ++ cat conftest.er1 >&5 ++ mv -f conftest.er1 conftest.err ++ fi ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest.$ac_objext; then : ++ ac_retval=0 ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_retval=1 ++fi ++ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno ++ as_fn_set_status $ac_retval ++ ++} # ac_fn_c_try_compile ++ ++# ac_fn_c_try_link LINENO ++# ----------------------- ++# Try to link conftest.$ac_ext, and return whether this succeeded. ++ac_fn_c_try_link () ++{ ++ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ rm -f conftest.$ac_objext conftest$ac_exeext ++ if { { ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_link") 2>conftest.err ++ ac_status=$? ++ if test -s conftest.err; then ++ grep -v '^ *+' conftest.err >conftest.er1 ++ cat conftest.er1 >&5 ++ mv -f conftest.er1 conftest.err ++ fi ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } && { ++ test -z "$ac_c_werror_flag" || ++ test ! -s conftest.err ++ } && test -s conftest$ac_exeext && { ++ test "$cross_compiling" = yes || ++ $as_test_x conftest$ac_exeext ++ }; then : ++ ac_retval=0 ++else ++ $as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 ++ ++ ac_retval=1 ++fi ++ # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information ++ # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would ++ # interfere with the next link command; also delete a directory that is ++ # left behind by Apple's compiler. We do this before executing the actions. ++ rm -rf conftest.dSYM conftest_ipa8_conftest.oo ++ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno ++ as_fn_set_status $ac_retval ++ ++} # ac_fn_c_try_link + cat >config.log <<_ACEOF + This file contains any messages produced by compilers while + running configure, to aid debugging if configure makes a mistake. + + It was created by exmap-console $as_me 0.4.1, which was +-generated by GNU Autoconf 2.60. Invocation command line was ++generated by GNU Autoconf 2.68. Invocation command line was + + $ $0 $@ + +@@ -1376,8 +1532,8 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- echo "PATH: $as_dir" +-done ++ $as_echo "PATH: $as_dir" ++ done + IFS=$as_save_IFS + + } >&5 +@@ -1411,12 +1567,12 @@ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) +- ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; ++ ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in +- 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; ++ 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) +- ac_configure_args1="$ac_configure_args1 '$ac_arg'" ++ as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else +@@ -1432,13 +1588,13 @@ + -* ) ac_must_keep_next=true ;; + esac + fi +- ac_configure_args="$ac_configure_args '$ac_arg'" ++ as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done + done +-$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +-$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } ++{ ac_configure_args0=; unset ac_configure_args0;} ++{ ac_configure_args1=; unset ac_configure_args1;} + + # When interrupted or exit'd, cleanup temporary files, and complete + # config.log. We remove comments because anyway the quotes in there +@@ -1450,11 +1606,9 @@ + { + echo + +- cat <<\_ASBOX +-## ---------------- ## ++ $as_echo "## ---------------- ## + ## Cache variables. ## +-## ---------------- ## +-_ASBOX ++## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, + ( +@@ -1463,12 +1617,13 @@ + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( +- *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +-echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; ++ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 ++$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( +- *) $as_unset $ac_var ;; ++ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( ++ *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done +@@ -1487,128 +1642,136 @@ + ) + echo + +- cat <<\_ASBOX +-## ----------------- ## ++ $as_echo "## ----------------- ## + ## Output variables. ## +-## ----------------- ## +-_ASBOX ++## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in +- *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; ++ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac +- echo "$ac_var='\''$ac_val'\''" ++ $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then +- cat <<\_ASBOX +-## ------------------- ## ++ $as_echo "## ------------------- ## + ## File substitutions. ## +-## ------------------- ## +-_ASBOX ++## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in +- *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; ++ *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac +- echo "$ac_var='\''$ac_val'\''" ++ $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then +- cat <<\_ASBOX +-## ----------- ## ++ $as_echo "## ----------- ## + ## confdefs.h. ## +-## ----------- ## +-_ASBOX ++## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && +- echo "$as_me: caught signal $ac_signal" +- echo "$as_me: exit $exit_status" ++ $as_echo "$as_me: caught signal $ac_signal" ++ $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 + for ac_signal in 1 2 13 15; do +- trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal ++ trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal + done + ac_signal=0 + + # confdefs.h avoids OS command line length limits that DEFS can exceed. + rm -f -r conftest* confdefs.h + ++$as_echo "/* confdefs.h */" > confdefs.h ++ + # Predefined preprocessor variables. + + cat >>confdefs.h <<_ACEOF + #define PACKAGE_NAME "$PACKAGE_NAME" + _ACEOF + +- + cat >>confdefs.h <<_ACEOF + #define PACKAGE_TARNAME "$PACKAGE_TARNAME" + _ACEOF + +- + cat >>confdefs.h <<_ACEOF + #define PACKAGE_VERSION "$PACKAGE_VERSION" + _ACEOF + +- + cat >>confdefs.h <<_ACEOF + #define PACKAGE_STRING "$PACKAGE_STRING" + _ACEOF + +- + cat >>confdefs.h <<_ACEOF + #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" + _ACEOF + ++cat >>confdefs.h <<_ACEOF ++#define PACKAGE_URL "$PACKAGE_URL" ++_ACEOF ++ + + # Let the site file select an alternate cache file if it wants to. +-# Prefer explicitly selected file to automatically selected ones. ++# Prefer an explicitly selected file to automatically selected ones. ++ac_site_file1=NONE ++ac_site_file2=NONE + if test -n "$CONFIG_SITE"; then +- set x "$CONFIG_SITE" ++ # We do not want a PATH search for config.site. ++ case $CONFIG_SITE in #(( ++ -*) ac_site_file1=./$CONFIG_SITE;; ++ */*) ac_site_file1=$CONFIG_SITE;; ++ *) ac_site_file1=./$CONFIG_SITE;; ++ esac + elif test "x$prefix" != xNONE; then +- set x "$prefix/share/config.site" "$prefix/etc/config.site" ++ ac_site_file1=$prefix/share/config.site ++ ac_site_file2=$prefix/etc/config.site + else +- set x "$ac_default_prefix/share/config.site" \ +- "$ac_default_prefix/etc/config.site" ++ ac_site_file1=$ac_default_prefix/share/config.site ++ ac_site_file2=$ac_default_prefix/etc/config.site + fi +-shift +-for ac_site_file ++for ac_site_file in "$ac_site_file1" "$ac_site_file2" + do +- if test -r "$ac_site_file"; then +- { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +-echo "$as_me: loading site script $ac_site_file" >&6;} ++ test "x$ac_site_file" = xNONE && continue ++ if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 ++$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 +- . "$ac_site_file" ++ . "$ac_site_file" \ ++ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "failed to load site script $ac_site_file ++See \`config.log' for more details" "$LINENO" 5; } + fi + done + + if test -r "$cache_file"; then +- # Some versions of bash will fail to source /dev/null (special +- # files actually), so we avoid doing that. +- if test -f "$cache_file"; then +- { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +-echo "$as_me: loading cache $cache_file" >&6;} ++ # Some versions of bash will fail to source /dev/null (special files ++ # actually), so we avoid doing that. DJGPP emulates it as a regular file. ++ if test /dev/null != "$cache_file" && test -f "$cache_file"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 ++$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi + else +- { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +-echo "$as_me: creating cache $cache_file" >&6;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 ++$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file + fi + +@@ -1622,68 +1785,56 @@ + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) +- { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +-echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 ++$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) +- { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +-echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 ++$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then +- { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +-echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} +- { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +-echo "$as_me: former value: $ac_old_val" >&2;} +- { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +-echo "$as_me: current value: $ac_new_val" >&2;} +- ac_cache_corrupted=: ++ # differences in whitespace do not lead to failure. ++ ac_old_val_w=`echo x $ac_old_val` ++ ac_new_val_w=`echo x $ac_new_val` ++ if test "$ac_old_val_w" != "$ac_new_val_w"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 ++$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ++ ac_cache_corrupted=: ++ else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 ++$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} ++ eval $ac_var=\$ac_old_val ++ fi ++ { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 ++$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 ++$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in +- *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; ++ *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. +- *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; ++ *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi + done + if $ac_cache_corrupted; then +- { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +-echo "$as_me: error: changes in the environment can compromise the build" >&2;} +- { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +-echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} +- { (exit 1); exit 1; }; } +-fi +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- ++ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 ++$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} ++ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 ++fi ++## -------------------- ## ++## Main body of script. ## ++## -------------------- ## + + ac_ext=c + ac_cpp='$CPP $CPPFLAGS' +@@ -1694,7 +1845,8 @@ + + + +-am__api_version="1.9" ++am__api_version='1.11' ++ + ac_aux_dir= + for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then +@@ -1712,9 +1864,7 @@ + fi + done + if test -z "$ac_aux_dir"; then +- { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +-echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 + fi + + # These three variables are undocumented and unsupported, +@@ -1739,22 +1889,23 @@ + # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" + # OS/2's system install, which has a completely different semantic + # ./install, which can be erroneously created by make from ./install.sh. +-{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +-echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } ++# Reject install programs that cannot install multiple files. ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 ++$as_echo_n "checking for a BSD-compatible install... " >&6; } + if test -z "$INSTALL"; then +-if test "${ac_cv_path_install+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++if ${ac_cv_path_install+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + for as_dir in $PATH + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- # Account for people who put trailing slashes in PATH elements. +-case $as_dir/ in +- ./ | .// | /cC/* | \ ++ # Account for people who put trailing slashes in PATH elements. ++case $as_dir/ in #(( ++ ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ +- ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ ++ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. +@@ -1762,7 +1913,7 @@ + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; }; then ++ if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. +@@ -1772,17 +1923,29 @@ + # program-specific install script used by HP pwplus--don't use. + : + else +- ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" +- break 3 ++ rm -rf conftest.one conftest.two conftest.dir ++ echo one > conftest.one ++ echo two > conftest.two ++ mkdir conftest.dir ++ if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && ++ test -s conftest.one && test -s conftest.two && ++ test -s conftest.dir/conftest.one && ++ test -s conftest.dir/conftest.two ++ then ++ ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" ++ break 3 ++ fi + fi + fi + done + done + ;; + esac +-done ++ ++ done + IFS=$as_save_IFS + ++rm -rf conftest.one conftest.two conftest.dir + + fi + if test "${ac_cv_path_install+set}" = set; then +@@ -1795,8 +1958,8 @@ + INSTALL=$ac_install_sh + fi + fi +-{ echo "$as_me:$LINENO: result: $INSTALL" >&5 +-echo "${ECHO_T}$INSTALL" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 ++$as_echo "$INSTALL" >&6; } + + # Use test -z because SunOS4 sh mishandles braces in ${var-val}. + # It thinks the first close brace ends the variable substitution. +@@ -1806,21 +1969,34 @@ + + test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +-{ echo "$as_me:$LINENO: checking whether build environment is sane" >&5 +-echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 ++$as_echo_n "checking whether build environment is sane... " >&6; } + # Just in case + sleep 1 + echo timestamp > conftest.file ++# Reject unsafe characters in $srcdir or the absolute working directory ++# name. Accept space and tab only in the latter. ++am_lf=' ++' ++case `pwd` in ++ *[\\\"\#\$\&\'\`$am_lf]*) ++ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; ++esac ++case $srcdir in ++ *[\\\"\#\$\&\'\`$am_lf\ \ ]*) ++ as_fn_error $? "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; ++esac ++ + # Do `set' in a subshell so we don't clobber the current shell's + # arguments. Must try -L first in case configure is actually a + # symlink; some systems play weird games with the mod time of symlinks + # (eg FreeBSD returns the mod time of the symlink's containing + # directory). + if ( +- set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` ++ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. +- set X `ls -t $srcdir/configure conftest.file` ++ set X `ls -t "$srcdir/configure" conftest.file` + fi + rm -f conftest.file + if test "$*" != "X $srcdir/configure conftest.file" \ +@@ -1830,11 +2006,8 @@ + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". +- { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken +-alias in your environment" >&5 +-echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken +-alias in your environment" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken ++alias in your environment" "$LINENO" 5 + fi + + test "$2" = conftest.file +@@ -1843,204 +2016,50 @@ + # Ok. + : + else +- { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! +-Check your system clock" >&5 +-echo "$as_me: error: newly created file is older than distributed files! +-Check your system clock" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "newly created file is older than distributed files! ++Check your system clock" "$LINENO" 5 + fi +-{ echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } + test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" + # Use a double $ so make ignores it. + test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +-# Double any \ or $. echo might interpret backslashes. ++# Double any \ or $. + # By default was `s,x,x', remove it if useless. +-cat <<\_ACEOF >conftest.sed +-s/[\\$]/&&/g;s/;s,x,x,$// +-_ACEOF +-program_transform_name=`echo $program_transform_name | sed -f conftest.sed` +-rm -f conftest.sed ++ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' ++program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + + # expand $ac_aux_dir to an absolute path + am_aux_dir=`cd $ac_aux_dir && pwd` + +-test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" ++if test x"${MISSING+set}" != xset; then ++ case $am_aux_dir in ++ *\ * | *\ *) ++ MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; ++ *) ++ MISSING="\${SHELL} $am_aux_dir/missing" ;; ++ esac ++fi + # Use eval to expand $SHELL + if eval "$MISSING --run true"; then + am_missing_run="$MISSING --run " + else + am_missing_run= +- { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 +-echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} +-fi +- +-if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then +- # We used to keeping the `.' as first argument, in order to +- # allow $(mkdir_p) to be used without argument. As in +- # $(mkdir_p) $(somedir) +- # where $(somedir) is conditionally defined. However this is wrong +- # for two reasons: +- # 1. if the package is installed by a user who cannot write `.' +- # make install will fail, +- # 2. the above comment should most certainly read +- # $(mkdir_p) $(DESTDIR)$(somedir) +- # so it does not work when $(somedir) is undefined and +- # $(DESTDIR) is not. +- # To support the latter case, we have to write +- # test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir), +- # so the `.' trick is pointless. +- mkdir_p='mkdir -p --' +-else +- # On NextStep and OpenStep, the `mkdir' command does not +- # recognize any option. It will interpret all options as +- # directories to create, and then abort because `.' already +- # exists. +- for d in ./-p ./--version; +- do +- test -d $d && rmdir $d +- done +- # $(mkinstalldirs) is defined by Automake if mkinstalldirs exists. +- if test -f "$ac_aux_dir/mkinstalldirs"; then +- mkdir_p='$(mkinstalldirs)' +- else +- mkdir_p='$(install_sh) -d' +- fi +-fi +- +-for ac_prog in gawk mawk nawk awk +-do +- # Extract the first word of "$ac_prog", so it can be a program name with args. +-set dummy $ac_prog; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_AWK+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 +-else +- if test -n "$AWK"; then +- ac_cv_prog_AWK="$AWK" # Let the user override the test. +-else +-as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +-for as_dir in $PATH +-do +- IFS=$as_save_IFS +- test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then +- ac_cv_prog_AWK="$ac_prog" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 +- break 2 +- fi +-done +-done +-IFS=$as_save_IFS +- +-fi +-fi +-AWK=$ac_cv_prog_AWK +-if test -n "$AWK"; then +- { echo "$as_me:$LINENO: result: $AWK" >&5 +-echo "${ECHO_T}$AWK" >&6; } +-else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 ++$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} + fi + +- +- test -n "$AWK" && break +-done +- +-{ echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +-echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; } +-set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +-if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 +-else +- cat >conftest.make <<\_ACEOF +-SHELL = /bin/sh +-all: +- @echo '@@@%%%=$(MAKE)=@@@%%%' +-_ACEOF +-# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +-case `${MAKE-make} -f conftest.make 2>/dev/null` in +- *@@@%%%=?*=@@@%%%*) +- eval ac_cv_prog_make_${ac_make}_set=yes;; ++if test x"${install_sh}" != xset; then ++ case $am_aux_dir in ++ *\ * | *\ *) ++ install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) +- eval ac_cv_prog_make_${ac_make}_set=no;; +-esac +-rm -f conftest.make +-fi +-if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then +- { echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } +- SET_MAKE= +-else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } +- SET_MAKE="MAKE=${MAKE-make}" +-fi +- +-rm -rf .tst 2>/dev/null +-mkdir .tst 2>/dev/null +-if test -d .tst; then +- am__leading_dot=. +-else +- am__leading_dot=_ +-fi +-rmdir .tst 2>/dev/null +- +-# test to see if srcdir already configured +-if test "`cd $srcdir && pwd`" != "`pwd`" && +- test -f $srcdir/config.status; then +- { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 +-echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} +- { (exit 1); exit 1; }; } +-fi +- +-# test whether we have cygpath +-if test -z "$CYGPATH_W"; then +- if (cygpath --version) >/dev/null 2>/dev/null; then +- CYGPATH_W='cygpath -w' +- else +- CYGPATH_W=echo +- fi ++ install_sh="\${SHELL} $am_aux_dir/install-sh" ++ esac + fi + +- +-# Define the identity of the package. +- PACKAGE='exmap-console' +- VERSION='0.4.1' +- +- +-cat >>confdefs.h <<_ACEOF +-#define PACKAGE "$PACKAGE" +-_ACEOF +- +- +-cat >>confdefs.h <<_ACEOF +-#define VERSION "$VERSION" +-_ACEOF +- +-# Some tools Automake needs. +- +-ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} +- +- +-AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} +- +- +-AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} +- +- +-AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} +- +- +-MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} +- +-install_sh=${install_sh-"$am_aux_dir/install-sh"} +- + # Installed binaries are usually stripped using `strip' when the user + # run `make install-strip'. However `strip' might not be the right + # tool to use in cross-compilation environments, therefore Automake +@@ -2049,10 +2068,10 @@ + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. + set dummy ${ac_tool_prefix}strip; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_STRIP+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_STRIP+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +@@ -2062,25 +2081,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + STRIP=$ac_cv_prog_STRIP + if test -n "$STRIP"; then +- { echo "$as_me:$LINENO: result: $STRIP" >&5 +-echo "${ECHO_T}$STRIP" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 ++$as_echo "$STRIP" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2089,10 +2108,10 @@ + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. + set dummy strip; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_ac_ct_STRIP+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +@@ -2102,48 +2121,224 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP + if test -n "$ac_ct_STRIP"; then +- { echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 +-echo "${ECHO_T}$ac_ct_STRIP" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 ++$as_echo "$ac_ct_STRIP" >&6; } ++else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++fi ++ ++ if test "x$ac_ct_STRIP" = x; then ++ STRIP=":" ++ else ++ case $cross_compiling:$ac_tool_warned in ++yes:) ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ++ac_tool_warned=yes ;; ++esac ++ STRIP=$ac_ct_STRIP ++ fi ++else ++ STRIP="$ac_cv_prog_STRIP" ++fi ++ ++fi ++INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 ++$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } ++if test -z "$MKDIR_P"; then ++ if ${ac_cv_path_mkdir+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR ++for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin ++do ++ IFS=$as_save_IFS ++ test -z "$as_dir" && as_dir=. ++ for ac_prog in mkdir gmkdir; do ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue ++ case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( ++ 'mkdir (GNU coreutils) '* | \ ++ 'mkdir (coreutils) '* | \ ++ 'mkdir (fileutils) '4.1*) ++ ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext ++ break 3;; ++ esac ++ done ++ done ++ done ++IFS=$as_save_IFS ++ ++fi ++ ++ test -d ./--version && rmdir ./--version ++ if test "${ac_cv_path_mkdir+set}" = set; then ++ MKDIR_P="$ac_cv_path_mkdir -p" ++ else ++ # As a last resort, use the slow shell script. Don't cache a ++ # value for MKDIR_P within a source directory, because that will ++ # break other packages using the cache if that directory is ++ # removed, or if the value is a relative name. ++ MKDIR_P="$ac_install_sh -d" ++ fi ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 ++$as_echo "$MKDIR_P" >&6; } ++ ++mkdir_p="$MKDIR_P" ++case $mkdir_p in ++ [\\/$]* | ?:[\\/]*) ;; ++ */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; ++esac ++ ++for ac_prog in gawk mawk nawk awk ++do ++ # Extract the first word of "$ac_prog", so it can be a program name with args. ++set dummy $ac_prog; ac_word=$2 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_AWK+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ if test -n "$AWK"; then ++ ac_cv_prog_AWK="$AWK" # Let the user override the test. ++else ++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR ++for as_dir in $PATH ++do ++ IFS=$as_save_IFS ++ test -z "$as_dir" && as_dir=. ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ++ ac_cv_prog_AWK="$ac_prog" ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ break 2 ++ fi ++done ++ done ++IFS=$as_save_IFS ++ ++fi ++fi ++AWK=$ac_cv_prog_AWK ++if test -n "$AWK"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 ++$as_echo "$AWK" >&6; } ++else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++fi ++ ++ ++ test -n "$AWK" && break ++done ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 ++$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } ++set x ${MAKE-make} ++ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` ++if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ cat >conftest.make <<\_ACEOF ++SHELL = /bin/sh ++all: ++ @echo '@@@%%%=$(MAKE)=@@@%%%' ++_ACEOF ++# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. ++case `${MAKE-make} -f conftest.make 2>/dev/null` in ++ *@@@%%%=?*=@@@%%%*) ++ eval ac_cv_prog_make_${ac_make}_set=yes;; ++ *) ++ eval ac_cv_prog_make_${ac_make}_set=no;; ++esac ++rm -f conftest.make ++fi ++if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ SET_MAKE= ++else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ SET_MAKE="MAKE=${MAKE-make}" ++fi ++ ++rm -rf .tst 2>/dev/null ++mkdir .tst 2>/dev/null ++if test -d .tst; then ++ am__leading_dot=. + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ am__leading_dot=_ + fi ++rmdir .tst 2>/dev/null + +- if test "x$ac_ct_STRIP" = x; then +- STRIP=":" +- else +- case $cross_compiling:$ac_tool_warned in +-yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} +-ac_tool_warned=yes ;; +-esac +- STRIP=$ac_ct_STRIP ++if test "`cd $srcdir && pwd`" != "`pwd`"; then ++ # Use -I$(srcdir) only when $(srcdir) != ., so that make's output ++ # is not polluted with repeated "-I." ++ am__isrc=' -I$(srcdir)' ++ # test to see if srcdir already configured ++ if test -f $srcdir/config.status; then ++ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +-else +- STRIP="$ac_cv_prog_STRIP" + fi + ++# test whether we have cygpath ++if test -z "$CYGPATH_W"; then ++ if (cygpath --version) >/dev/null 2>/dev/null; then ++ CYGPATH_W='cygpath -w' ++ else ++ CYGPATH_W=echo ++ fi + fi +-INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s" ++ ++ ++# Define the identity of the package. ++ PACKAGE='exmap-console' ++ VERSION='0.4.1' ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define PACKAGE "$PACKAGE" ++_ACEOF ++ ++ ++cat >>confdefs.h <<_ACEOF ++#define VERSION "$VERSION" ++_ACEOF ++ ++# Some tools Automake needs. ++ ++ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} ++ ++ ++AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} ++ ++ ++AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} ++ ++ ++AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} ++ ++ ++MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + + # We need awk for the "check" target. The system "awk" is bad on + # some platforms. +@@ -2165,7 +2360,7 @@ + + + # Check whether --with-readline was given. +-if test "${with_readline+set}" = set; then ++if test "${with_readline+set}" = set; then : + withval=$with_readline; WITH_READLINE=$enableval + else + WITH_READLINE=yes +@@ -2182,44 +2377,44 @@ + am_make=${MAKE-make} + cat > confinc << 'END' + am__doit: +- @echo done ++ @echo this is the am__doit target + .PHONY: am__doit + END + # If we don't find an include directive, just comment out the code. +-{ echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 +-echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 ++$as_echo_n "checking for style of include used by $am_make... " >&6; } + am__include="#" + am__quote= + _am_result=none + # First try GNU make style include. + echo "include confinc" > confmf +-# We grep out `Entering directory' and `Leaving directory' +-# messages which can occur if `w' ends up in MAKEFLAGS. +-# In particular we don't look at `^make:' because GNU make might +-# be invoked under some other name (usually "gmake"), in which +-# case it prints its new name instead of `make'. +-if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then +- am__include=include +- am__quote= +- _am_result=GNU +-fi ++# Ignore all kinds of additional output from `make'. ++case `$am_make -s -f confmf 2> /dev/null` in #( ++*the\ am__doit\ target*) ++ am__include=include ++ am__quote= ++ _am_result=GNU ++ ;; ++esac + # Now try BSD make style include. + if test "$am__include" = "#"; then + echo '.include "confinc"' > confmf +- if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then +- am__include=.include +- am__quote="\"" +- _am_result=BSD +- fi ++ case `$am_make -s -f confmf 2> /dev/null` in #( ++ *the\ am__doit\ target*) ++ am__include=.include ++ am__quote="\"" ++ _am_result=BSD ++ ;; ++ esac + fi + + +-{ echo "$as_me:$LINENO: result: $_am_result" >&5 +-echo "${ECHO_T}$_am_result" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 ++$as_echo "$_am_result" >&6; } + rm -f confinc confmf + + # Check whether --enable-dependency-tracking was given. +-if test "${enable_dependency_tracking+set}" = set; then ++if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; + fi + +@@ -2227,9 +2422,7 @@ + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + fi +- +- +-if test "x$enable_dependency_tracking" != xno; then ++ if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' + else +@@ -2238,7 +2431,6 @@ + fi + + +- + ac_ext=c + ac_cpp='$CPP $CPPFLAGS' + ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +@@ -2247,10 +2439,10 @@ + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. + set dummy ${ac_tool_prefix}gcc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -2260,25 +2452,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2287,10 +2479,10 @@ + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. + set dummy gcc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_ac_ct_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +@@ -2300,25 +2492,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + ac_ct_CC=$ac_cv_prog_ac_ct_CC + if test -n "$ac_ct_CC"; then +- { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +-echo "${ECHO_T}$ac_ct_CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 ++$as_echo "$ac_ct_CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + if test "x$ac_ct_CC" = x; then +@@ -2326,12 +2518,8 @@ + else + case $cross_compiling:$ac_tool_warned in + yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} + ac_tool_warned=yes ;; + esac + CC=$ac_ct_CC +@@ -2344,10 +2532,10 @@ + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. + set dummy ${ac_tool_prefix}cc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -2357,25 +2545,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2384,10 +2572,10 @@ + if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. + set dummy cc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -2398,18 +2586,18 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + if test $ac_prog_rejected = yes; then +@@ -2428,11 +2616,11 @@ + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2443,10 +2631,10 @@ + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. + set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -2456,25 +2644,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2487,10 +2675,10 @@ + do + # Extract the first word of "$ac_prog", so it can be a program name with args. + set dummy $ac_prog; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_ac_ct_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +@@ -2500,25 +2688,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + ac_ct_CC=$ac_cv_prog_ac_ct_CC + if test -n "$ac_ct_CC"; then +- { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +-echo "${ECHO_T}$ac_ct_CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 ++$as_echo "$ac_ct_CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -2530,12 +2718,8 @@ + else + case $cross_compiling:$ac_tool_warned in + yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} + ac_tool_warned=yes ;; + esac + CC=$ac_ct_CC +@@ -2545,51 +2729,37 @@ + fi + + +-test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +-See \`config.log' for more details." >&5 +-echo "$as_me: error: no acceptable C compiler found in \$PATH +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } ++test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "no acceptable C compiler found in \$PATH ++See \`config.log' for more details" "$LINENO" 5; } + + # Provide some information about the compiler. +-echo "$as_me:$LINENO: checking for C compiler version" >&5 +-ac_compiler=`set X $ac_compile; echo $2` +-{ (ac_try="$ac_compiler --version >&5" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler --version >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } +-{ (ac_try="$ac_compiler -v >&5" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler -v >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } +-{ (ac_try="$ac_compiler -V >&5" ++$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 ++set X $ac_compile ++ac_compiler=$2 ++for ac_option in --version -v -V -qversion; do ++ { { ac_try="$ac_compiler $ac_option >&5" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler -V >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_compiler $ac_option >&5") 2>conftest.err ++ ac_status=$? ++ if test -s conftest.err; then ++ sed '10a\ ++... rest of stderr output deleted ... ++ 10q' conftest.err >conftest.er1 ++ cat conftest.er1 >&5 ++ fi ++ rm -f conftest.er1 conftest.err ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } ++done + +-cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -2601,59 +2771,55 @@ + } + _ACEOF + ac_clean_files_save=$ac_clean_files +-ac_clean_files="$ac_clean_files a.out a.exe b.out" ++ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" + # Try to create an executable without -o first, disregard a.out. + # It will help us diagnose broken compilers, and finding out an intuition + # of exeext. +-{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +-echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +-ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +-# +-# List of possible output files, starting from the most likely. +-# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +-# only as a last resort. b.out is created by i960 compilers. +-ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +-# +-# The IRIX 6 linker writes into existing files which may not be +-# executable, retaining their permissions. Remove them first so a +-# subsequent execution test works. ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 ++$as_echo_n "checking whether the C compiler works... " >&6; } ++ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` ++ ++# The possible output files: ++ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ++ + ac_rmfiles= + for ac_file in $ac_files + do + case $ac_file in +- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; ++ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac + done + rm -f $ac_rmfiles + +-if { (ac_try="$ac_link_default" ++if { { ac_try="$ac_link_default" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; then ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. + # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' + # in a Makefile. We should not override ac_cv_exeext if it was cached, + # so that the user can short-circuit this test for compilers unknown to + # Autoconf. +-for ac_file in $ac_files ++for ac_file in $ac_files '' + do + test -f "$ac_file" || continue + case $ac_file in +- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ++ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) +- if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; ++ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi +@@ -2670,76 +2836,43 @@ + test "$ac_cv_exeext" = no && ac_cv_exeext= + + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +-{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +-See \`config.log' for more details." >&5 +-echo "$as_me: error: C compiler cannot create executables +-See \`config.log' for more details." >&2;} +- { (exit 77); exit 77; }; } ++ ac_file='' + fi ++if test -z "$ac_file"; then : ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++$as_echo "$as_me: failed program was:" >&5 ++sed 's/^/| /' conftest.$ac_ext >&5 + ++{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error 77 "C compiler cannot create executables ++See \`config.log' for more details" "$LINENO" 5; } ++else ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 ++$as_echo_n "checking for C compiler default output file name... " >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 ++$as_echo "$ac_file" >&6; } + ac_exeext=$ac_cv_exeext +-{ echo "$as_me:$LINENO: result: $ac_file" >&5 +-echo "${ECHO_T}$ac_file" >&6; } +- +-# Check that the compiler produces executables we can run. If not, either +-# the compiler is broken, or we cross compile. +-{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +-echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +-# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +-# If not cross compiling, check that we can run a simple program. +-if test "$cross_compiling" != yes; then +- if { ac_try='./$ac_file' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then +- cross_compiling=no +- else +- if test "$cross_compiling" = maybe; then +- cross_compiling=yes +- else +- { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +-If you meant to cross compile, use \`--host'. +-See \`config.log' for more details." >&5 +-echo "$as_me: error: cannot run C compiled programs. +-If you meant to cross compile, use \`--host'. +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } +- fi +- fi +-fi +-{ echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } + +-rm -f a.out a.exe conftest$ac_cv_exeext b.out ++rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out + ac_clean_files=$ac_clean_files_save +-# Check that the compiler produces executables we can run. If not, either +-# the compiler is broken, or we cross compile. +-{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +-echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +-{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +-echo "${ECHO_T}$cross_compiling" >&6; } +- +-{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +-echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +-if { (ac_try="$ac_link" ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 ++$as_echo_n "checking for suffix of executables... " >&6; } ++if { { ac_try="$ac_link" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; then ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) + # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will + # work properly (i.e., refer to `conftest.exe'), while it won't with +@@ -2747,37 +2880,90 @@ + for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in +- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; ++ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac + done + else +- { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +-See \`config.log' for more details." >&5 +-echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } +-fi +- +-rm -f conftest$ac_cv_exeext +-{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +-echo "${ECHO_T}$ac_cv_exeext" >&6; } ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "cannot compute suffix of executables: cannot compile and link ++See \`config.log' for more details" "$LINENO" 5; } ++fi ++rm -f conftest conftest$ac_cv_exeext ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 ++$as_echo "$ac_cv_exeext" >&6; } + + rm -f conftest.$ac_ext + EXEEXT=$ac_cv_exeext + ac_exeext=$EXEEXT +-{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +-echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +-if test "${ac_cv_objext+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 +-else +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext ++/* end confdefs.h. */ ++#include ++int ++main () ++{ ++FILE *f = fopen ("conftest.out", "w"); ++ return ferror (f) || fclose (f) != 0; ++ ++ ; ++ return 0; ++} + _ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ac_clean_files="$ac_clean_files conftest.out" ++# Check that the compiler produces executables we can run. If not, either ++# the compiler is broken, or we cross compile. ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 ++$as_echo_n "checking whether we are cross compiling... " >&6; } ++if test "$cross_compiling" != yes; then ++ { { ac_try="$ac_link" ++case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_link") 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } ++ if { ac_try='./conftest$ac_cv_exeext' ++ { { case "(($ac_try" in ++ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; ++ *) ac_try_echo=$ac_try;; ++esac ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_try") 2>&5 ++ ac_status=$? ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; }; then ++ cross_compiling=no ++ else ++ if test "$cross_compiling" = maybe; then ++ cross_compiling=yes ++ else ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "cannot run C compiled programs. ++If you meant to cross compile, use \`--host'. ++See \`config.log' for more details" "$LINENO" 5; } ++ fi ++ fi ++fi ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 ++$as_echo "$cross_compiling" >&6; } ++ ++rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ++ac_clean_files=$ac_clean_files_save ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 ++$as_echo_n "checking for suffix of object files... " >&6; } ++if ${ac_cv_objext+:} false; then : ++ $as_echo_n "(cached) " >&6 ++else ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -2789,51 +2975,46 @@ + } + _ACEOF + rm -f conftest.o conftest.obj +-if { (ac_try="$ac_compile" ++if { { ac_try="$ac_compile" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; then ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in +- *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; ++ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac + done + else +- echo "$as_me: failed program was:" >&5 ++ $as_echo "$as_me: failed program was:" >&5 + sed 's/^/| /' conftest.$ac_ext >&5 + +-{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +-See \`config.log' for more details." >&5 +-echo "$as_me: error: cannot compute suffix of object files: cannot compile +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } ++{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "cannot compute suffix of object files: cannot compile ++See \`config.log' for more details" "$LINENO" 5; } + fi +- + rm -f conftest.$ac_cv_objext conftest.$ac_ext + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +-echo "${ECHO_T}$ac_cv_objext" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 ++$as_echo "$ac_cv_objext" >&6; } + OBJEXT=$ac_cv_objext + ac_objext=$OBJEXT +-{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +-echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +-if test "${ac_cv_c_compiler_gnu+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 ++$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } ++if ${ac_cv_c_compiler_gnu+:} false; then : ++ $as_echo_n "(cached) " >&6 + else +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -2847,71 +3028,34 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- ac_compiler_gnu=no ++ ac_compiler_gnu=no + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_c_compiler_gnu=$ac_compiler_gnu + + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +-echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +-GCC=`test $ac_compiler_gnu = yes && echo yes` ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 ++$as_echo "$ac_cv_c_compiler_gnu" >&6; } ++if test $ac_compiler_gnu = yes; then ++ GCC=yes ++else ++ GCC= ++fi + ac_test_CFLAGS=${CFLAGS+set} + ac_save_CFLAGS=$CFLAGS +-{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +-echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +-if test "${ac_cv_prog_cc_g+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 ++$as_echo_n "checking whether $CC accepts -g... " >&6; } ++if ${ac_cv_prog_cc_g+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -2922,51 +3066,11 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- CFLAGS="" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ CFLAGS="" ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -2977,52 +3081,12 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then +- : +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 ++if ac_fn_c_try_compile "$LINENO"; then : + +- ac_c_werror_flag=$ac_save_c_werror_flag ++else ++ ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -3033,59 +3097,18 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +-echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 ++$as_echo "$ac_cv_prog_cc_g" >&6; } + if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS + elif test $ac_cv_prog_cc_g = yes; then +@@ -3101,18 +3124,14 @@ + CFLAGS= + fi + fi +-{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +-echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +-if test "${ac_cv_prog_cc_c89+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 ++$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } ++if ${ac_cv_prog_cc_c89+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_cv_prog_cc_c89=no + ac_save_CC=$CC +-cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + #include + #include +@@ -3169,48 +3188,9 @@ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" + do + CC="$ac_save_CC $ac_arg" +- rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++ if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- + fi +- + rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break + done +@@ -3221,17 +3201,19 @@ + # AC_CACHE_VAL + case "x$ac_cv_prog_cc_c89" in + x) +- { echo "$as_me:$LINENO: result: none needed" >&5 +-echo "${ECHO_T}none needed" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 ++$as_echo "none needed" >&6; } ;; + xno) +- { echo "$as_me:$LINENO: result: unsupported" >&5 +-echo "${ECHO_T}unsupported" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 ++$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" +- { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +-echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 ++$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; + esac ++if test "x$ac_cv_prog_cc_c89" != xno; then : + ++fi + + ac_ext=c + ac_cpp='$CPP $CPPFLAGS' +@@ -3241,10 +3223,10 @@ + + depcc="$CC" am_compiler_list= + +-{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +-echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; } +-if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 ++$as_echo_n "checking dependency style of $depcc... " >&6; } ++if ${am_cv_CC_dependencies_compiler_type+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up +@@ -3269,6 +3251,11 @@ + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi ++ am__universal=false ++ case " $depcc " in #( ++ *\ -arch\ *\ -arch\ *) am__universal=true ;; ++ esac ++ + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and +@@ -3286,7 +3273,17 @@ + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + ++ # We check with `-c' and `-o' for the sake of the "dashmstdout" ++ # mode. It turns out that the SunPro C++ compiler does not properly ++ # handle `-M -o', and we need to detect this. Also, some Intel ++ # versions had trouble with output in subdirs ++ am__obj=sub/conftest.${OBJEXT-o} ++ am__minus_obj="-o $am__obj" + case $depmode in ++ gcc) ++ # This depmode causes a compiler race in universal mode. ++ test "$am__universal" = false || continue ++ ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested +@@ -3296,18 +3293,23 @@ + break + fi + ;; ++ msvisualcpp | msvcmsys) ++ # This compiler won't grok `-c -o', but also, the minuso test has ++ # not run yet. These depmodes are late enough in the game, and ++ # so weak that their functioning should not be impacted. ++ am__obj=conftest.${OBJEXT-o} ++ am__minus_obj= ++ ;; + none) break ;; + esac +- # We check with `-c' and `-o' for the sake of the "dashmstdout" +- # mode. It turns out that the SunPro C++ compiler does not properly +- # handle `-M -o', and we need to detect this. + if depmode=$depmode \ +- source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ ++ source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ +- $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ ++ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && ++ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && +- grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && ++ grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message +@@ -3331,13 +3333,11 @@ + fi + + fi +-{ echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +-echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 ++$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } + CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + +- +- +-if ++ if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= +@@ -3349,18 +3349,14 @@ + + + +-{ echo "$as_me:$LINENO: checking for readline in -lreadline" >&5 +-echo $ECHO_N "checking for readline in -lreadline... $ECHO_C" >&6; } +-if test "${ac_cv_lib_readline_readline+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for readline in -lreadline" >&5 ++$as_echo_n "checking for readline in -lreadline... " >&6; } ++if ${ac_cv_lib_readline_readline+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_check_lib_save_LIBS=$LIBS + LIBS="-lreadline $LIBS" +-cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + /* Override any GCC internal prototype to avoid an error. +@@ -3378,72 +3374,31 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext conftest$ac_exeext +-if { (ac_try="$ac_link" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_link") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest$ac_exeext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_readline_readline=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- ac_cv_lib_readline_readline=no ++ ac_cv_lib_readline_readline=no + fi +- + rm -f core conftest.err conftest.$ac_objext \ +- conftest$ac_exeext conftest.$ac_ext ++ conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_lib_save_LIBS + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_lib_readline_readline" >&5 +-echo "${ECHO_T}$ac_cv_lib_readline_readline" >&6; } +-if test $ac_cv_lib_readline_readline = yes; then ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_readline_readline" >&5 ++$as_echo "$ac_cv_lib_readline_readline" >&6; } ++if test "x$ac_cv_lib_readline_readline" = xyes; then : + WORKING_READLINE=yes + else + WORKING_READLINE=no + fi + +-{ echo "$as_me:$LINENO: checking for add_history in -lhistory" >&5 +-echo $ECHO_N "checking for add_history in -lhistory... $ECHO_C" >&6; } +-if test "${ac_cv_lib_history_add_history+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for add_history in -lhistory" >&5 ++$as_echo_n "checking for add_history in -lhistory... " >&6; } ++if ${ac_cv_lib_history_add_history+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_check_lib_save_LIBS=$LIBS + LIBS="-lhistory $LIBS" +-cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + /* Override any GCC internal prototype to avoid an error. +@@ -3461,55 +3416,18 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext conftest$ac_exeext +-if { (ac_try="$ac_link" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_link") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest$ac_exeext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_history_add_history=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- ac_cv_lib_history_add_history=no ++ ac_cv_lib_history_add_history=no + fi +- + rm -f core conftest.err conftest.$ac_objext \ +- conftest$ac_exeext conftest.$ac_ext ++ conftest$ac_exeext conftest.$ac_ext + LIBS=$ac_check_lib_save_LIBS + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_lib_history_add_history" >&5 +-echo "${ECHO_T}$ac_cv_lib_history_add_history" >&6; } +-if test $ac_cv_lib_history_add_history = yes; then ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_history_add_history" >&5 ++$as_echo "$ac_cv_lib_history_add_history" >&6; } ++if test "x$ac_cv_lib_history_add_history" = xyes; then : + WORKING_READLINE=yes + else + WORKING_READLINE=no +@@ -3524,9 +3442,7 @@ + fi + fi + +- +- +-if test "x$WITH_READLINE" = "xyes"; then ++ if test "x$WITH_READLINE" = "xyes"; then + INTERACTIVE_TRUE= + INTERACTIVE_FALSE='#' + else +@@ -3544,7 +3460,7 @@ + + + # Check whether --enable-doc was given. +-if test "${enable_doc+set}" = set; then ++if test "${enable_doc+set}" = set; then : + enableval=$enable_doc; DO_DOCS=$enableval + else + DO_DOCS=$enableval +@@ -3553,16 +3469,14 @@ + + + # Check whether --with-help2man was given. +-if test "${with_help2man+set}" = set; then ++if test "${with_help2man+set}" = set; then : + withval=$with_help2man; WITH_H2MAN=$withval + else + WITH_H2MAN=yes + fi + + +- +- +-if test "x$DO_DOCS" != "xno"; then ++ if test "x$DO_DOCS" != "xno"; then + DODOCS_TRUE= + DODOCS_FALSE='#' + else +@@ -3571,14 +3485,6 @@ + fi + + +-# Check whether --enable-debug was given. +-if test "${enable_debug+set}" = set; then +- enableval=$enable_debug; CFLAGS="-g -O0" +-else +- CFLAGS="-g -O2" +-fi +- +- + CFLAGS="$CFLAGS -DEXMAP_VERSION=\\\"$EXMAP_VERSION\\\" -DEXMAP_PROTOCOL_VERSION=\\\"$EXMAP_PROTOCOL_VERSION\\\"" + + +@@ -3591,10 +3497,10 @@ + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. + set dummy ${ac_tool_prefix}gcc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -3604,25 +3510,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -3631,10 +3537,10 @@ + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. + set dummy gcc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_ac_ct_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +@@ -3644,25 +3550,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + ac_ct_CC=$ac_cv_prog_ac_ct_CC + if test -n "$ac_ct_CC"; then +- { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +-echo "${ECHO_T}$ac_ct_CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 ++$as_echo "$ac_ct_CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + if test "x$ac_ct_CC" = x; then +@@ -3670,12 +3576,8 @@ + else + case $cross_compiling:$ac_tool_warned in + yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} + ac_tool_warned=yes ;; + esac + CC=$ac_ct_CC +@@ -3688,10 +3590,10 @@ + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. + set dummy ${ac_tool_prefix}cc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -3701,25 +3603,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -3728,10 +3630,10 @@ + if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. + set dummy cc; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -3742,18 +3644,18 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + if test $ac_prog_rejected = yes; then +@@ -3772,11 +3674,11 @@ + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -3787,10 +3689,10 @@ + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. + set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +@@ -3800,25 +3702,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + CC=$ac_cv_prog_CC + if test -n "$CC"; then +- { echo "$as_me:$LINENO: result: $CC" >&5 +-echo "${ECHO_T}$CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 ++$as_echo "$CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -3831,10 +3733,10 @@ + do + # Extract the first word of "$ac_prog", so it can be a program name with args. + set dummy $ac_prog; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_prog_ac_ct_CC+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +@@ -3844,25 +3746,25 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + fi + fi + ac_ct_CC=$ac_cv_prog_ac_ct_CC + if test -n "$ac_ct_CC"; then +- { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +-echo "${ECHO_T}$ac_ct_CC" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 ++$as_echo "$ac_ct_CC" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -3874,12 +3776,8 @@ + else + case $cross_compiling:$ac_tool_warned in + yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} + ac_tool_warned=yes ;; + esac + CC=$ac_ct_CC +@@ -3889,56 +3787,42 @@ + fi + + +-test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +-See \`config.log' for more details." >&5 +-echo "$as_me: error: no acceptable C compiler found in \$PATH +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } ++test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "no acceptable C compiler found in \$PATH ++See \`config.log' for more details" "$LINENO" 5; } + + # Provide some information about the compiler. +-echo "$as_me:$LINENO: checking for C compiler version" >&5 +-ac_compiler=`set X $ac_compile; echo $2` +-{ (ac_try="$ac_compiler --version >&5" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler --version >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } +-{ (ac_try="$ac_compiler -v >&5" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler -v >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } +-{ (ac_try="$ac_compiler -V >&5" ++$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 ++set X $ac_compile ++ac_compiler=$2 ++for ac_option in --version -v -V -qversion; do ++ { { ac_try="$ac_compiler $ac_option >&5" + case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; + esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compiler -V >&5") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } +- +-{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +-echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +-if test "${ac_cv_c_compiler_gnu+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" ++$as_echo "$ac_try_echo"; } >&5 ++ (eval "$ac_compiler $ac_option >&5") 2>conftest.err ++ ac_status=$? ++ if test -s conftest.err; then ++ sed '10a\ ++... rest of stderr output deleted ... ++ 10q' conftest.err >conftest.er1 ++ cat conftest.er1 >&5 ++ fi ++ rm -f conftest.er1 conftest.err ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; } ++done ++ ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 ++$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } ++if ${ac_cv_c_compiler_gnu+:} false; then : ++ $as_echo_n "(cached) " >&6 + else +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -3952,71 +3836,34 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- ac_compiler_gnu=no ++ ac_compiler_gnu=no + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_c_compiler_gnu=$ac_compiler_gnu + + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +-echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +-GCC=`test $ac_compiler_gnu = yes && echo yes` ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 ++$as_echo "$ac_cv_c_compiler_gnu" >&6; } ++if test $ac_compiler_gnu = yes; then ++ GCC=yes ++else ++ GCC= ++fi + ac_test_CFLAGS=${CFLAGS+set} + ac_save_CFLAGS=$CFLAGS +-{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +-echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +-if test "${ac_cv_prog_cc_g+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 ++$as_echo_n "checking whether $CC accepts -g... " >&6; } ++if ${ac_cv_prog_cc_g+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -4027,51 +3874,11 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes + else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- CFLAGS="" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ CFLAGS="" ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -4082,52 +3889,12 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then +- : +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 ++if ac_fn_c_try_compile "$LINENO"; then : + +- ac_c_werror_flag=$ac_save_c_werror_flag ++else ++ ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" +- cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++ cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + + int +@@ -4138,59 +3905,18 @@ + return 0; + } + _ACEOF +-rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + fi +- + rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag + fi +-{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +-echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 ++$as_echo "$ac_cv_prog_cc_g" >&6; } + if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS + elif test $ac_cv_prog_cc_g = yes; then +@@ -4206,18 +3932,14 @@ + CFLAGS= + fi + fi +-{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +-echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +-if test "${ac_cv_prog_cc_c89+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 ++$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } ++if ${ac_cv_prog_cc_c89+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + ac_cv_prog_cc_c89=no + ac_save_CC=$CC +-cat >conftest.$ac_ext <<_ACEOF +-/* confdefs.h. */ +-_ACEOF +-cat confdefs.h >>conftest.$ac_ext +-cat >>conftest.$ac_ext <<_ACEOF ++cat confdefs.h - <<_ACEOF >conftest.$ac_ext + /* end confdefs.h. */ + #include + #include +@@ -4274,48 +3996,9 @@ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" + do + CC="$ac_save_CC $ac_arg" +- rm -f conftest.$ac_objext +-if { (ac_try="$ac_compile" +-case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_compile") 2>conftest.er1 +- ac_status=$? +- grep -v '^ *+' conftest.er1 >conftest.err +- rm -f conftest.er1 +- cat conftest.err >&5 +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); } && +- { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; } && +- { ac_try='test -s conftest.$ac_objext' +- { (case "(($ac_try" in +- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; +- *) ac_try_echo=$ac_try;; +-esac +-eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +- (eval "$ac_try") 2>&5 +- ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; }; then ++ if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +-else +- echo "$as_me: failed program was:" >&5 +-sed 's/^/| /' conftest.$ac_ext >&5 +- +- + fi +- + rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break + done +@@ -4326,17 +4009,19 @@ + # AC_CACHE_VAL + case "x$ac_cv_prog_cc_c89" in + x) +- { echo "$as_me:$LINENO: result: none needed" >&5 +-echo "${ECHO_T}none needed" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 ++$as_echo "none needed" >&6; } ;; + xno) +- { echo "$as_me:$LINENO: result: unsupported" >&5 +-echo "${ECHO_T}unsupported" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 ++$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" +- { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +-echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 ++$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; + esac ++if test "x$ac_cv_prog_cc_c89" != xno; then : + ++fi + + ac_ext=c + ac_cpp='$CPP $CPPFLAGS' +@@ -4346,10 +4031,10 @@ + + depcc="$CC" am_compiler_list= + +-{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 +-echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; } +-if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 ++$as_echo_n "checking dependency style of $depcc... " >&6; } ++if ${am_cv_CC_dependencies_compiler_type+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up +@@ -4374,6 +4059,11 @@ + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi ++ am__universal=false ++ case " $depcc " in #( ++ *\ -arch\ *\ -arch\ *) am__universal=true ;; ++ esac ++ + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and +@@ -4391,7 +4081,17 @@ + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + ++ # We check with `-c' and `-o' for the sake of the "dashmstdout" ++ # mode. It turns out that the SunPro C++ compiler does not properly ++ # handle `-M -o', and we need to detect this. Also, some Intel ++ # versions had trouble with output in subdirs ++ am__obj=sub/conftest.${OBJEXT-o} ++ am__minus_obj="-o $am__obj" + case $depmode in ++ gcc) ++ # This depmode causes a compiler race in universal mode. ++ test "$am__universal" = false || continue ++ ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested +@@ -4401,18 +4101,23 @@ + break + fi + ;; ++ msvisualcpp | msvcmsys) ++ # This compiler won't grok `-c -o', but also, the minuso test has ++ # not run yet. These depmodes are late enough in the game, and ++ # so weak that their functioning should not be impacted. ++ am__obj=conftest.${OBJEXT-o} ++ am__minus_obj= ++ ;; + none) break ;; + esac +- # We check with `-c' and `-o' for the sake of the "dashmstdout" +- # mode. It turns out that the SunPro C++ compiler does not properly +- # handle `-M -o', and we need to detect this. + if depmode=$depmode \ +- source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ ++ source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ +- $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ ++ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && ++ grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && +- grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && ++ grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message +@@ -4436,13 +4141,11 @@ + fi + + fi +-{ echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 +-echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 ++$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } + CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + +- +- +-if ++ if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= +@@ -4456,14 +4159,18 @@ + + + ++ ++ ++ ++ + if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. + set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_path_PKG_CONFIG+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_path_PKG_CONFIG+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + case $PKG_CONFIG in + [\\/]* | ?:[\\/]*) +@@ -4475,14 +4182,14 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + ;; +@@ -4490,11 +4197,11 @@ + fi + PKG_CONFIG=$ac_cv_path_PKG_CONFIG + if test -n "$PKG_CONFIG"; then +- { echo "$as_me:$LINENO: result: $PKG_CONFIG" >&5 +-echo "${ECHO_T}$PKG_CONFIG" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 ++$as_echo "$PKG_CONFIG" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + +@@ -4503,10 +4210,10 @@ + ac_pt_PKG_CONFIG=$PKG_CONFIG + # Extract the first word of "pkg-config", so it can be a program name with args. + set dummy pkg-config; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_path_ac_pt_PKG_CONFIG+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + case $ac_pt_PKG_CONFIG in + [\\/]* | ?:[\\/]*) +@@ -4518,14 +4225,14 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + ;; +@@ -4533,11 +4240,11 @@ + fi + ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG + if test -n "$ac_pt_PKG_CONFIG"; then +- { echo "$as_me:$LINENO: result: $ac_pt_PKG_CONFIG" >&5 +-echo "${ECHO_T}$ac_pt_PKG_CONFIG" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 ++$as_echo "$ac_pt_PKG_CONFIG" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + if test "x$ac_pt_PKG_CONFIG" = x; then +@@ -4545,12 +4252,8 @@ + else + case $cross_compiling:$ac_tool_warned in + yes:) +-{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&5 +-echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +-whose name does not start with the host triplet. If you think this +-configuration is useful to you, please write to autoconf@gnu.org." >&2;} ++{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 ++$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} + ac_tool_warned=yes ;; + esac + PKG_CONFIG=$ac_pt_PKG_CONFIG +@@ -4562,63 +4265,60 @@ + fi + if test -n "$PKG_CONFIG"; then + _pkg_min_version=0.9.0 +- { echo "$as_me:$LINENO: checking pkg-config is at least version $_pkg_min_version" >&5 +-echo $ECHO_N "checking pkg-config is at least version $_pkg_min_version... $ECHO_C" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 ++$as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then +- { echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + PKG_CONFIG="" + fi +- + fi + + pkg_failed=no +-{ echo "$as_me:$LINENO: checking for GLIB" >&5 +-echo $ECHO_N "checking for GLIB... $ECHO_C" >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GLIB" >&5 ++$as_echo_n "checking for GLIB... " >&6; } + +-if test -n "$PKG_CONFIG"; then +- if test -n "$GLIB_CFLAGS"; then +- pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" +- else +- if test -n "$PKG_CONFIG" && \ +- { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0\"") >&5 ++if test -n "$GLIB_CFLAGS"; then ++ pkg_cv_GLIB_CFLAGS="$GLIB_CFLAGS" ++ elif test -n "$PKG_CONFIG"; then ++ if test -n "$PKG_CONFIG" && \ ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "glib-2.0") 2>&5 + ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; then ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then + pkg_cv_GLIB_CFLAGS=`$PKG_CONFIG --cflags "glib-2.0" 2>/dev/null` + else + pkg_failed=yes + fi +- fi +-else +- pkg_failed=untried ++ else ++ pkg_failed=untried + fi +-if test -n "$PKG_CONFIG"; then +- if test -n "$GLIB_LIBS"; then +- pkg_cv_GLIB_LIBS="$GLIB_LIBS" +- else +- if test -n "$PKG_CONFIG" && \ +- { (echo "$as_me:$LINENO: \$PKG_CONFIG --exists --print-errors \"glib-2.0\"") >&5 ++if test -n "$GLIB_LIBS"; then ++ pkg_cv_GLIB_LIBS="$GLIB_LIBS" ++ elif test -n "$PKG_CONFIG"; then ++ if test -n "$PKG_CONFIG" && \ ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"glib-2.0\""; } >&5 + ($PKG_CONFIG --exists --print-errors "glib-2.0") 2>&5 + ac_status=$? +- echo "$as_me:$LINENO: \$? = $ac_status" >&5 +- (exit $ac_status); }; then ++ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 ++ test $ac_status = 0; }; then + pkg_cv_GLIB_LIBS=`$PKG_CONFIG --libs "glib-2.0" 2>/dev/null` + else + pkg_failed=yes + fi +- fi +-else +- pkg_failed=untried ++ else ++ pkg_failed=untried + fi + + + + if test $pkg_failed = yes; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + + if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +@@ -4626,25 +4326,14 @@ + _pkg_short_errors_supported=no + fi + if test $_pkg_short_errors_supported = yes; then +- GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "glib-2.0"` ++ GLIB_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors "glib-2.0" 2>&1` + else +- GLIB_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "glib-2.0"` ++ GLIB_PKG_ERRORS=`$PKG_CONFIG --print-errors "glib-2.0" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$GLIB_PKG_ERRORS" >&5 + +- { { echo "$as_me:$LINENO: error: Package requirements (glib-2.0) were not met: +- +-$GLIB_PKG_ERRORS +- +-Consider adjusting the PKG_CONFIG_PATH environment variable if you +-installed software in a non-standard prefix. +- +-Alternatively, you may set the environment variables GLIB_CFLAGS +-and GLIB_LIBS to avoid the need to call pkg-config. +-See the pkg-config man page for more details. +-" >&5 +-echo "$as_me: error: Package requirements (glib-2.0) were not met: ++ as_fn_error $? "Package requirements (glib-2.0) were not met: + + $GLIB_PKG_ERRORS + +@@ -4653,21 +4342,13 @@ + + Alternatively, you may set the environment variables GLIB_CFLAGS + and GLIB_LIBS to avoid the need to call pkg-config. +-See the pkg-config man page for more details. +-" >&2;} +- { (exit 1); exit 1; }; } ++See the pkg-config man page for more details." "$LINENO" 5 + elif test $pkg_failed = untried; then +- { { echo "$as_me:$LINENO: error: The pkg-config script could not be found or is too old. Make sure it +-is in your PATH or set the PKG_CONFIG environment variable to the full +-path to pkg-config. +- +-Alternatively, you may set the environment variables GLIB_CFLAGS +-and GLIB_LIBS to avoid the need to call pkg-config. +-See the pkg-config man page for more details. +- +-To get pkg-config, see . +-See \`config.log' for more details." >&5 +-echo "$as_me: error: The pkg-config script could not be found or is too old. Make sure it ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } ++ { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 ++$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} ++as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it + is in your PATH or set the PKG_CONFIG environment variable to the full + path to pkg-config. + +@@ -4675,15 +4356,14 @@ + and GLIB_LIBS to avoid the need to call pkg-config. + See the pkg-config man page for more details. + +-To get pkg-config, see . +-See \`config.log' for more details." >&2;} +- { (exit 1); exit 1; }; } ++To get pkg-config, see . ++See \`config.log' for more details" "$LINENO" 5; } + else + GLIB_CFLAGS=$pkg_cv_GLIB_CFLAGS + GLIB_LIBS=$pkg_cv_GLIB_LIBS +- { echo "$as_me:$LINENO: result: yes" >&5 +-echo "${ECHO_T}yes" >&6; } +- : ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 ++$as_echo "yes" >&6; } ++ + fi + + +@@ -4695,10 +4375,10 @@ + #autodetect, error if not found + # Extract the first word of "help2man", so it can be a program name with args. + set dummy help2man; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_path_HELPTOMAN+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_path_HELPTOMAN+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + case $HELPTOMAN in + [\\/]* | ?:[\\/]*) +@@ -4710,14 +4390,14 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_HELPTOMAN="$as_dir/$ac_word$ac_exec_ext" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + test -z "$ac_cv_path_HELPTOMAN" && ac_cv_path_HELPTOMAN="no" +@@ -4726,18 +4406,16 @@ + fi + HELPTOMAN=$ac_cv_path_HELPTOMAN + if test -n "$HELPTOMAN"; then +- { echo "$as_me:$LINENO: result: $HELPTOMAN" >&5 +-echo "${ECHO_T}$HELPTOMAN" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HELPTOMAN" >&5 ++$as_echo "$HELPTOMAN" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + + if test "x$HELPTOMAN" = "xno" ; then +- { { echo "$as_me:$LINENO: error: help2man not found -- either install it or use --without-help2man or --disable-doc" >&5 +-echo "$as_me: error: help2man not found -- either install it or use --without-help2man or --disable-doc" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "help2man not found -- either install it or use --without-help2man or --disable-doc" "$LINENO" 5 + fi + else + if test x$WITH_H2MAN = xno ; then +@@ -4749,10 +4427,10 @@ + if test x$H2M_PATH = x$WITH_H2MAN ; then + # Extract the first word of "$H2M_PROG", so it can be a program name with args. + set dummy $H2M_PROG; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_path_HELPTOMAN+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_path_HELPTOMAN+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + case $HELPTOMAN in + [\\/]* | ?:[\\/]*) +@@ -4764,14 +4442,14 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_HELPTOMAN="$as_dir/$ac_word$ac_exec_ext" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + test -z "$ac_cv_path_HELPTOMAN" && ac_cv_path_HELPTOMAN="no" +@@ -4780,21 +4458,21 @@ + fi + HELPTOMAN=$ac_cv_path_HELPTOMAN + if test -n "$HELPTOMAN"; then +- { echo "$as_me:$LINENO: result: $HELPTOMAN" >&5 +-echo "${ECHO_T}$HELPTOMAN" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HELPTOMAN" >&5 ++$as_echo "$HELPTOMAN" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + + else + # Extract the first word of "$H2M_PROG", so it can be a program name with args. + set dummy $H2M_PROG; ac_word=$2 +-{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +-echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +-if test "${ac_cv_path_HELPTOMAN+set}" = set; then +- echo $ECHO_N "(cached) $ECHO_C" >&6 ++{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 ++$as_echo_n "checking for $ac_word... " >&6; } ++if ${ac_cv_path_HELPTOMAN+:} false; then : ++ $as_echo_n "(cached) " >&6 + else + case $HELPTOMAN in + [\\/]* | ?:[\\/]*) +@@ -4806,14 +4484,14 @@ + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- for ac_exec_ext in '' $ac_executable_extensions; do +- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; }; then ++ for ac_exec_ext in '' $ac_executable_extensions; do ++ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_path_HELPTOMAN="$as_dir/$ac_word$ac_exec_ext" +- echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 ++ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi + done +-done ++ done + IFS=$as_save_IFS + + test -z "$ac_cv_path_HELPTOMAN" && ac_cv_path_HELPTOMAN="no" +@@ -4822,28 +4500,24 @@ + fi + HELPTOMAN=$ac_cv_path_HELPTOMAN + if test -n "$HELPTOMAN"; then +- { echo "$as_me:$LINENO: result: $HELPTOMAN" >&5 +-echo "${ECHO_T}$HELPTOMAN" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $HELPTOMAN" >&5 ++$as_echo "$HELPTOMAN" >&6; } + else +- { echo "$as_me:$LINENO: result: no" >&5 +-echo "${ECHO_T}no" >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 ++$as_echo "no" >&6; } + fi + + + fi + + if test "x$HELPTOMAN" = "xno" ; then +- { { echo "$as_me:$LINENO: error: $WITH_H2MAN not found" >&5 +-echo "$as_me: error: $WITH_H2MAN not found" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "$WITH_H2MAN not found" "$LINENO" 5 + fi + fi + fi + fi + +- +- +-if test "x$HELPTOMAN" != "xno"; then ++ if test "x$HELPTOMAN" != "xno"; then + HAVE_HELPTOMAN_TRUE= + HAVE_HELPTOMAN_FALSE='#' + else +@@ -4889,12 +4563,13 @@ + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( +- *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +-echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; ++ *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 ++$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( +- *) $as_unset $ac_var ;; ++ BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( ++ *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done +@@ -4902,8 +4577,8 @@ + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) +- # `set' does not quote correctly, so add quotes (double-quote +- # substitution turns \\\\ into \\, and sed turns \\ into \). ++ # `set' does not quote correctly, so add quotes: double-quote ++ # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" +@@ -4925,13 +4600,24 @@ + :end' >>confcache + if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then +- test "x$cache_file" != "x/dev/null" && +- { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +-echo "$as_me: updating cache $cache_file" >&6;} +- cat confcache >$cache_file ++ if test "x$cache_file" != "x/dev/null"; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 ++$as_echo "$as_me: updating cache $cache_file" >&6;} ++ if test ! -f "$cache_file" || test -h "$cache_file"; then ++ cat confcache >"$cache_file" ++ else ++ case $cache_file in #( ++ */* | ?:*) ++ mv -f confcache "$cache_file"$$ && ++ mv -f "$cache_file"$$ "$cache_file" ;; #( ++ *) ++ mv -f confcache "$cache_file" ;; ++ esac ++ fi ++ fi + else +- { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +-echo "$as_me: not updating unwritable cache $cache_file" >&6;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 ++$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi + fi + rm -f confcache +@@ -4948,6 +4634,12 @@ + # take arguments), then branch to the quote section. Otherwise, + # look for a macro that doesn't take arguments. + ac_script=' ++:mline ++/\\$/{ ++ N ++ s,\\\n,, ++ b mline ++} + t clear + :clear + s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +@@ -4974,69 +4666,62 @@ + + ac_libobjs= + ac_ltlibobjs= ++U= + for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' +- ac_i=`echo "$ac_i" | sed "$ac_script"` ++ ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. +- ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" +- ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' ++ as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" ++ as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' + done + LIBOBJS=$ac_libobjs + + LTLIBOBJS=$ac_ltlibobjs + + ++ if test -n "$EXEEXT"; then ++ am__EXEEXT_TRUE= ++ am__EXEEXT_FALSE='#' ++else ++ am__EXEEXT_TRUE='#' ++ am__EXEEXT_FALSE= ++fi ++ + if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"AMDEP\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"AMDEP\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"am__fastdepCC\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + if test -z "${INTERACTIVE_TRUE}" && test -z "${INTERACTIVE_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"INTERACTIVE\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"INTERACTIVE\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"INTERACTIVE\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + if test -z "${DODOCS_TRUE}" && test -z "${DODOCS_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"DODOCS\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"DODOCS\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"DODOCS\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"am__fastdepCC\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + if test -z "${HAVE_HELPTOMAN_TRUE}" && test -z "${HAVE_HELPTOMAN_FALSE}"; then +- { { echo "$as_me:$LINENO: error: conditional \"HAVE_HELPTOMAN\" was never defined. +-Usually this means the macro was only invoked conditionally." >&5 +-echo "$as_me: error: conditional \"HAVE_HELPTOMAN\" was never defined. +-Usually this means the macro was only invoked conditionally." >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "conditional \"HAVE_HELPTOMAN\" was never defined. ++Usually this means the macro was only invoked conditionally." "$LINENO" 5 + fi + +-: ${CONFIG_STATUS=./config.status} ++: "${CONFIG_STATUS=./config.status}" ++ac_write_fail=0 + ac_clean_files_save=$ac_clean_files + ac_clean_files="$ac_clean_files $CONFIG_STATUS" +-{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +-echo "$as_me: creating $CONFIG_STATUS" >&6;} +-cat >$CONFIG_STATUS <<_ACEOF ++{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 ++$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} ++as_write_fail=0 ++cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 + #! $SHELL + # Generated by $as_me. + # Run this file to recreate the current configuration. +@@ -5046,55 +4731,79 @@ + debug=false + ac_cs_recheck=false + ac_cs_silent=false +-SHELL=\${CONFIG_SHELL-$SHELL} +-_ACEOF + +-cat >>$CONFIG_STATUS <<\_ACEOF +-## --------------------- ## +-## M4sh Initialization. ## +-## --------------------- ## ++SHELL=\${CONFIG_SHELL-$SHELL} ++export SHELL ++_ASEOF ++cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ++## -------------------- ## ++## M4sh Initialization. ## ++## -------------------- ## + +-# Be Bourne compatible +-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then ++# Be more Bourne compatible ++DUALCASE=1; export DUALCASE # for MKS sh ++if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: +- # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which ++ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST + else +- case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac ++ case `(set -o) 2>/dev/null` in #( ++ *posix*) : ++ set -o posix ;; #( ++ *) : ++ ;; ++esac + fi +-BIN_SH=xpg4; export BIN_SH # for Tru64 +-DUALCASE=1; export DUALCASE # for MKS sh +- + +-# PATH needs CR +-# Avoid depending upon Character Ranges. +-as_cr_letters='abcdefghijklmnopqrstuvwxyz' +-as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +-as_cr_Letters=$as_cr_letters$as_cr_LETTERS +-as_cr_digits='0123456789' +-as_cr_alnum=$as_cr_Letters$as_cr_digits + +-# The user is always right. +-if test "${PATH_SEPARATOR+set}" != set; then +- echo "#! /bin/sh" >conf$$.sh +- echo "exit 0" >>conf$$.sh +- chmod +x conf$$.sh +- if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then +- PATH_SEPARATOR=';' ++as_nl=' ++' ++export as_nl ++# Printing a long string crashes Solaris 7 /usr/bin/printf. ++as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo ++as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo ++# Prefer a ksh shell builtin over an external printf program on Solaris, ++# but without wasting forks for bash or zsh. ++if test -z "$BASH_VERSION$ZSH_VERSION" \ ++ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then ++ as_echo='print -r --' ++ as_echo_n='print -rn --' ++elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then ++ as_echo='printf %s\n' ++ as_echo_n='printf %s' ++else ++ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then ++ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' ++ as_echo_n='/usr/ucb/echo -n' + else +- PATH_SEPARATOR=: ++ as_echo_body='eval expr "X$1" : "X\\(.*\\)"' ++ as_echo_n_body='eval ++ arg=$1; ++ case $arg in #( ++ *"$as_nl"*) ++ expr "X$arg" : "X\\(.*\\)$as_nl"; ++ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; ++ esac; ++ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ++ ' ++ export as_echo_n_body ++ as_echo_n='sh -c $as_echo_n_body as_echo' + fi +- rm -f conf$$.sh ++ export as_echo_body ++ as_echo='sh -c $as_echo_body as_echo' + fi + +-# Support unset when possible. +-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then +- as_unset=unset +-else +- as_unset=false ++# The user is always right. ++if test "${PATH_SEPARATOR+set}" != set; then ++ PATH_SEPARATOR=: ++ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { ++ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || ++ PATH_SEPARATOR=';' ++ } + fi + + +@@ -5103,20 +4812,19 @@ + # there to prevent editors from complaining about space-tab. + # (If _AS_PATH_WALK were called with IFS unset, it would disable word + # splitting by setting IFS to empty value.) +-as_nl=' +-' + IFS=" "" $as_nl" + + # Find who we are. Look in the path if we contain no directory separator. +-case $0 in ++as_myself= ++case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + for as_dir in $PATH + do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. +- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +-done ++ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break ++ done + IFS=$as_save_IFS + + ;; +@@ -5127,32 +4835,111 @@ + as_myself=$0 + fi + if test ! -f "$as_myself"; then +- echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 +- { (exit 1); exit 1; } ++ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 ++ exit 1 + fi + +-# Work around bugs in pre-3.0 UWIN ksh. +-for as_var in ENV MAIL MAILPATH +-do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var ++# Unset variables that we do not need and which cause bugs (e.g. in ++# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" ++# suppresses any "Segmentation fault" message there. '((' could ++# trigger a bug in pdksh 5.2.14. ++for as_var in BASH_ENV ENV MAIL MAILPATH ++do eval test x\${$as_var+set} = xset \ ++ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : + done + PS1='$ ' + PS2='> ' + PS4='+ ' + + # NLS nuisances. +-for as_var in \ +- LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ +- LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ +- LC_TELEPHONE LC_TIME +-do +- if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then +- eval $as_var=C; export $as_var +- else +- ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +- fi +-done ++LC_ALL=C ++export LC_ALL ++LANGUAGE=C ++export LANGUAGE ++ ++# CDPATH. ++(unset CDPATH) >/dev/null 2>&1 && unset CDPATH ++ ++ ++# as_fn_error STATUS ERROR [LINENO LOG_FD] ++# ---------------------------------------- ++# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are ++# provided, also output the error to LOG_FD, referencing LINENO. Then exit the ++# script with STATUS, using 1 if that was 0. ++as_fn_error () ++{ ++ as_status=$1; test $as_status -eq 0 && as_status=1 ++ if test "$4"; then ++ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack ++ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 ++ fi ++ $as_echo "$as_me: error: $2" >&2 ++ as_fn_exit $as_status ++} # as_fn_error ++ ++ ++# as_fn_set_status STATUS ++# ----------------------- ++# Set $? to STATUS, without forking. ++as_fn_set_status () ++{ ++ return $1 ++} # as_fn_set_status ++ ++# as_fn_exit STATUS ++# ----------------- ++# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. ++as_fn_exit () ++{ ++ set +e ++ as_fn_set_status $1 ++ exit $1 ++} # as_fn_exit ++ ++# as_fn_unset VAR ++# --------------- ++# Portably unset VAR. ++as_fn_unset () ++{ ++ { eval $1=; unset $1;} ++} ++as_unset=as_fn_unset ++# as_fn_append VAR VALUE ++# ---------------------- ++# Append the text in VALUE to the end of the definition contained in VAR. Take ++# advantage of any shell optimizations that allow amortized linear growth over ++# repeated appends, instead of the typical quadratic growth present in naive ++# implementations. ++if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : ++ eval 'as_fn_append () ++ { ++ eval $1+=\$2 ++ }' ++else ++ as_fn_append () ++ { ++ eval $1=\$$1\$2 ++ } ++fi # as_fn_append ++ ++# as_fn_arith ARG... ++# ------------------ ++# Perform arithmetic evaluation on the ARGs, and store the result in the ++# global $as_val. Take advantage of shells that can avoid forks. The arguments ++# must be portable across $(()) and expr. ++if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : ++ eval 'as_fn_arith () ++ { ++ as_val=$(( $* )) ++ }' ++else ++ as_fn_arith () ++ { ++ as_val=`expr "$@" || test $? -eq 1` ++ } ++fi # as_fn_arith ++ + +-# Required to use basename. + if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +@@ -5166,13 +4953,17 @@ + as_basename=false + fi + ++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then ++ as_dirname=dirname ++else ++ as_dirname=false ++fi + +-# Name of the executable. + as_me=`$as_basename -- "$0" || + $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +-echo X/"$0" | ++$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q +@@ -5187,122 +4978,130 @@ + } + s/.*/./; q'` + +-# CDPATH. +-$as_unset CDPATH +- +- +- +- as_lineno_1=$LINENO +- as_lineno_2=$LINENO +- test "x$as_lineno_1" != "x$as_lineno_2" && +- test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { +- +- # Create $as_me.lineno as a copy of $as_myself, but with $LINENO +- # uniformly replaced by the line number. The first 'sed' inserts a +- # line-number line after each line using $LINENO; the second 'sed' +- # does the real work. The second script uses 'N' to pair each +- # line-number line with the line containing $LINENO, and appends +- # trailing '-' during substitution so that $LINENO is not a special +- # case at line end. +- # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the +- # scripts with optimization help from Paolo Bonzini. Blame Lee +- # E. McMahon (1931-1989) for sed's syntax. :-) +- sed -n ' +- p +- /[$]LINENO/= +- ' <$as_myself | +- sed ' +- s/[$]LINENO.*/&-/ +- t lineno +- b +- :lineno +- N +- :loop +- s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ +- t loop +- s/-\n.*// +- ' >$as_me.lineno && +- chmod +x "$as_me.lineno" || +- { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 +- { (exit 1); exit 1; }; } +- +- # Don't try to exec as it changes $[0], causing all sort of problems +- # (the dirname of $[0] is not the place where we might find the +- # original and so on. Autoconf is especially sensitive to this). +- . "./$as_me.lineno" +- # Exit status is that of the last command. +- exit +-} +- +- +-if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then +- as_dirname=dirname +-else +- as_dirname=false +-fi ++# Avoid depending upon Character Ranges. ++as_cr_letters='abcdefghijklmnopqrstuvwxyz' ++as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' ++as_cr_Letters=$as_cr_letters$as_cr_LETTERS ++as_cr_digits='0123456789' ++as_cr_alnum=$as_cr_Letters$as_cr_digits + + ECHO_C= ECHO_N= ECHO_T= +-case `echo -n x` in ++case `echo -n x` in #((((( + -n*) +- case `echo 'x\c'` in ++ case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. +- *) ECHO_C='\c';; ++ xy) ECHO_C='\c';; ++ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ++ ECHO_T=' ';; + esac;; + *) + ECHO_N='-n';; + esac + +-if expr a : '\(a\)' >/dev/null 2>&1 && +- test "X`expr 00001 : '.*\(...\)'`" = X001; then +- as_expr=expr +-else +- as_expr=false +-fi +- + rm -f conf$$ conf$$.exe conf$$.file + if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file + else + rm -f conf$$.dir +- mkdir conf$$.dir ++ mkdir conf$$.dir 2>/dev/null + fi +-echo >conf$$.file +-if ln -s conf$$.file conf$$ 2>/dev/null; then +- as_ln_s='ln -s' +- # ... but there are two gotchas: +- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. +- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. +- # In both cases, we have to default to `cp -p'. +- ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || ++if (echo >conf$$.file) 2>/dev/null; then ++ if ln -s conf$$.file conf$$ 2>/dev/null; then ++ as_ln_s='ln -s' ++ # ... but there are two gotchas: ++ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. ++ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. ++ # In both cases, we have to default to `cp -p'. ++ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || ++ as_ln_s='cp -p' ++ elif ln conf$$.file conf$$ 2>/dev/null; then ++ as_ln_s=ln ++ else + as_ln_s='cp -p' +-elif ln conf$$.file conf$$ 2>/dev/null; then +- as_ln_s=ln ++ fi + else + as_ln_s='cp -p' + fi + rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file + rmdir conf$$.dir 2>/dev/null + ++ ++# as_fn_mkdir_p ++# ------------- ++# Create "$as_dir" as a directory, including parents if necessary. ++as_fn_mkdir_p () ++{ ++ ++ case $as_dir in #( ++ -*) as_dir=./$as_dir;; ++ esac ++ test -d "$as_dir" || eval $as_mkdir_p || { ++ as_dirs= ++ while :; do ++ case $as_dir in #( ++ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( ++ *) as_qdir=$as_dir;; ++ esac ++ as_dirs="'$as_qdir' $as_dirs" ++ as_dir=`$as_dirname -- "$as_dir" || ++$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ ++ X"$as_dir" : 'X\(//\)[^/]' \| \ ++ X"$as_dir" : 'X\(//\)$' \| \ ++ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || ++$as_echo X"$as_dir" | ++ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)[^/].*/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\).*/{ ++ s//\1/ ++ q ++ } ++ s/.*/./; q'` ++ test -d "$as_dir" && break ++ done ++ test -z "$as_dirs" || eval "mkdir $as_dirs" ++ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" ++ ++ ++} # as_fn_mkdir_p + if mkdir -p . 2>/dev/null; then +- as_mkdir_p=: ++ as_mkdir_p='mkdir -p "$as_dir"' + else + test -d ./-p && rmdir ./-p + as_mkdir_p=false + fi + +-# Find out whether ``test -x'' works. Don't use a zero-byte file, as +-# systems may use methods other than mode bits to determine executability. +-cat >conf$$.file <<_ASEOF +-#! /bin/sh +-exit 0 +-_ASEOF +-chmod +x conf$$.file +-if test -x conf$$.file >/dev/null 2>&1; then +- as_executable_p="test -x" ++if test -x / >/dev/null 2>&1; then ++ as_test_x='test -x' + else +- as_executable_p=: ++ if ls -dL / >/dev/null 2>&1; then ++ as_ls_L_option=L ++ else ++ as_ls_L_option= ++ fi ++ as_test_x=' ++ eval sh -c '\'' ++ if test -d "$1"; then ++ test -d "$1/."; ++ else ++ case $1 in #( ++ -*)set "./$1";; ++ esac; ++ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ++ ???[sx]*):;;*)false;;esac;fi ++ '\'' sh ++ ' + fi +-rm -f conf$$.file ++as_executable_p=$as_test_x + + # Sed expression to map a string onto a valid CPP name. + as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +@@ -5312,13 +5111,19 @@ + + + exec 6>&1 ++## ----------------------------------- ## ++## Main body of $CONFIG_STATUS script. ## ++## ----------------------------------- ## ++_ASEOF ++test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +-# Save the log message, to keep $[0] and so on meaningful, and to ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ++# Save the log message, to keep $0 and so on meaningful, and to + # report actual input values of CONFIG_FILES etc. instead of their + # values after options handling. + ac_log=" + This file was extended by exmap-console $as_me 0.4.1, which was +-generated by GNU Autoconf 2.60. Invocation command line was ++generated by GNU Autoconf 2.68. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS +@@ -5331,27 +5136,36 @@ + + _ACEOF + +-cat >>$CONFIG_STATUS <<_ACEOF ++case $ac_config_files in *" ++"*) set x $ac_config_files; shift; ac_config_files=$*;; ++esac ++ ++ ++ ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + # Files that config.status was made for. + config_files="$ac_config_files" + config_commands="$ac_config_commands" + + _ACEOF + +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + ac_cs_usage="\ +-\`$as_me' instantiates files from templates according to the +-current configuration. ++\`$as_me' instantiates files and other configuration actions ++from templates according to the current configuration. Unless the files ++and actions are specified as TAGs, all are instantiated by default. + +-Usage: $0 [OPTIONS] [FILE]... ++Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit +- -V, --version print version number, then exit +- -q, --quiet do not print progress messages ++ -V, --version print version number and configuration settings, then exit ++ --config print configuration, then exit ++ -q, --quiet, --silent ++ do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions +- --file=FILE[:TEMPLATE] +- instantiate the configuration file FILE ++ --file=FILE[:TEMPLATE] ++ instantiate the configuration file FILE + + Configuration files: + $config_files +@@ -5359,36 +5173,44 @@ + Configuration commands: + $config_commands + +-Report bugs to ." ++Report bugs to ." + + _ACEOF +-cat >>$CONFIG_STATUS <<_ACEOF ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ++ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" + ac_cs_version="\\ + exmap-console config.status 0.4.1 +-configured by $0, generated by GNU Autoconf 2.60, +- with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" ++configured by $0, generated by GNU Autoconf 2.68, ++ with options \\"\$ac_cs_config\\" + +-Copyright (C) 2006 Free Software Foundation, Inc. ++Copyright (C) 2010 Free Software Foundation, Inc. + This config.status script is free software; the Free Software Foundation + gives unlimited permission to copy, distribute and modify it." + + ac_pwd='$ac_pwd' + srcdir='$srcdir' + INSTALL='$INSTALL' ++MKDIR_P='$MKDIR_P' ++AWK='$AWK' ++test -n "\$AWK" || AWK=awk + _ACEOF + +-cat >>$CONFIG_STATUS <<\_ACEOF +-# If no file are specified by the user, then we need to provide default +-# value. By we need to know if files were specified by the user. ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ++# The default lists apply if the user does not specify any file. + ac_need_defaults=: + while test $# != 0 + do + case $1 in +- --*=*) ++ --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; ++ --*=) ++ ac_option=`expr "X$1" : 'X\([^=]*\)='` ++ ac_optarg= ++ ac_shift=: ++ ;; + *) + ac_option=$1 + ac_optarg=$2 +@@ -5401,25 +5223,30 @@ + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) +- echo "$ac_cs_version"; exit ;; ++ $as_echo "$ac_cs_version"; exit ;; ++ --config | --confi | --conf | --con | --co | --c ) ++ $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift +- CONFIG_FILES="$CONFIG_FILES $ac_optarg" ++ case $ac_optarg in ++ *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; ++ '') as_fn_error $? "missing file argument" ;; ++ esac ++ as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) +- echo "$ac_cs_usage"; exit ;; ++ $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. +- -*) { echo "$as_me: error: unrecognized option: $1 +-Try \`$0 --help' for more information." >&2 +- { (exit 1); exit 1; }; } ;; ++ -*) as_fn_error $? "unrecognized option: \`$1' ++Try \`$0 --help' for more information." ;; + +- *) ac_config_targets="$ac_config_targets $1" ++ *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac +@@ -5434,27 +5261,29 @@ + fi + + _ACEOF +-cat >>$CONFIG_STATUS <<_ACEOF ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + if \$ac_cs_recheck; then +- echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 +- CONFIG_SHELL=$SHELL ++ set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion ++ shift ++ \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 ++ CONFIG_SHELL='$SHELL' + export CONFIG_SHELL +- exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion ++ exec "\$@" + fi + + _ACEOF +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + exec 5>>config.log + { + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX + ## Running $as_me. ## + _ASBOX +- echo "$ac_log" ++ $as_echo "$ac_log" + } >&5 + + _ACEOF +-cat >>$CONFIG_STATUS <<_ACEOF ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + # + # INIT-COMMANDS + # +@@ -5462,7 +5291,7 @@ + + _ACEOF + +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + + # Handling of arguments. + for ac_config_target in $ac_config_targets +@@ -5473,9 +5302,7 @@ + "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; + "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; + +- *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +-echo "$as_me: error: invalid argument: $ac_config_target" >&2;} +- { (exit 1); exit 1; }; };; ++ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac + done + +@@ -5497,200 +5324,194 @@ + # after its creation but before its name has been assigned to `$tmp'. + $debug || + { +- tmp= ++ tmp= ac_tmp= + trap 'exit_status=$? +- { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status ++ : "${ac_tmp:=$tmp}" ++ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status + ' 0 +- trap '{ (exit 1); exit 1; }' 1 2 13 15 ++ trap 'as_fn_exit 1' 1 2 13 15 + } + # Create a (secure) tmp directory for tmp files. + + { + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && +- test -n "$tmp" && test -d "$tmp" ++ test -d "$tmp" + } || + { + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +-} || +-{ +- echo "$me: cannot create a temporary directory in ." >&2 +- { (exit 1); exit 1; } +-} +- +-# +-# Set up the sed scripts for CONFIG_FILES section. +-# ++} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ++ac_tmp=$tmp + +-# No need to generate the scripts if there are no CONFIG_FILES. +-# This happens for instance when ./config.status config.h ++# Set up the scripts for CONFIG_FILES section. ++# No need to generate them if there are no CONFIG_FILES. ++# This happens for instance with `./config.status config.h'. + if test -n "$CONFIG_FILES"; then + +-_ACEOF + ++ac_cr=`echo X | tr X '\015'` ++# On cygwin, bash can eat \r inside `` if the user requested igncr. ++# But we know of no other shell where ac_cr would be empty at this ++# point, so we can use a bashism as a fallback. ++if test "x$ac_cr" = x; then ++ eval ac_cr=\$\'\\r\' ++fi ++ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` ++if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ++ ac_cs_awk_cr='\\r' ++else ++ ac_cs_awk_cr=$ac_cr ++fi ++ ++echo 'BEGIN {' >"$ac_tmp/subs1.awk" && ++_ACEOF + + ++{ ++ echo "cat >conf$$subs.awk <<_ACEOF" && ++ echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && ++ echo "_ACEOF" ++} >conf$$subs.sh || ++ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ++ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` + ac_delim='%!_!# ' + for ac_last_try in false false false false false :; do +- cat >conf$$subs.sed <<_ACEOF +-SHELL!$SHELL$ac_delim +-PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +-PACKAGE_NAME!$PACKAGE_NAME$ac_delim +-PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +-PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +-PACKAGE_STRING!$PACKAGE_STRING$ac_delim +-PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +-exec_prefix!$exec_prefix$ac_delim +-prefix!$prefix$ac_delim +-program_transform_name!$program_transform_name$ac_delim +-bindir!$bindir$ac_delim +-sbindir!$sbindir$ac_delim +-libexecdir!$libexecdir$ac_delim +-datarootdir!$datarootdir$ac_delim +-datadir!$datadir$ac_delim +-sysconfdir!$sysconfdir$ac_delim +-sharedstatedir!$sharedstatedir$ac_delim +-localstatedir!$localstatedir$ac_delim +-includedir!$includedir$ac_delim +-oldincludedir!$oldincludedir$ac_delim +-docdir!$docdir$ac_delim +-infodir!$infodir$ac_delim +-htmldir!$htmldir$ac_delim +-dvidir!$dvidir$ac_delim +-pdfdir!$pdfdir$ac_delim +-psdir!$psdir$ac_delim +-libdir!$libdir$ac_delim +-localedir!$localedir$ac_delim +-mandir!$mandir$ac_delim +-DEFS!$DEFS$ac_delim +-ECHO_C!$ECHO_C$ac_delim +-ECHO_N!$ECHO_N$ac_delim +-ECHO_T!$ECHO_T$ac_delim +-LIBS!$LIBS$ac_delim +-build_alias!$build_alias$ac_delim +-host_alias!$host_alias$ac_delim +-target_alias!$target_alias$ac_delim +-INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim +-INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim +-INSTALL_DATA!$INSTALL_DATA$ac_delim +-CYGPATH_W!$CYGPATH_W$ac_delim +-PACKAGE!$PACKAGE$ac_delim +-VERSION!$VERSION$ac_delim +-ACLOCAL!$ACLOCAL$ac_delim +-AUTOCONF!$AUTOCONF$ac_delim +-AUTOMAKE!$AUTOMAKE$ac_delim +-AUTOHEADER!$AUTOHEADER$ac_delim +-MAKEINFO!$MAKEINFO$ac_delim +-install_sh!$install_sh$ac_delim +-STRIP!$STRIP$ac_delim +-INSTALL_STRIP_PROGRAM!$INSTALL_STRIP_PROGRAM$ac_delim +-mkdir_p!$mkdir_p$ac_delim +-AWK!$AWK$ac_delim +-SET_MAKE!$SET_MAKE$ac_delim +-am__leading_dot!$am__leading_dot$ac_delim +-AMTAR!$AMTAR$ac_delim +-am__tar!$am__tar$ac_delim +-am__untar!$am__untar$ac_delim +-CC!$CC$ac_delim +-CFLAGS!$CFLAGS$ac_delim +-LDFLAGS!$LDFLAGS$ac_delim +-CPPFLAGS!$CPPFLAGS$ac_delim +-ac_ct_CC!$ac_ct_CC$ac_delim +-EXEEXT!$EXEEXT$ac_delim +-OBJEXT!$OBJEXT$ac_delim +-DEPDIR!$DEPDIR$ac_delim +-am__include!$am__include$ac_delim +-am__quote!$am__quote$ac_delim +-AMDEP_TRUE!$AMDEP_TRUE$ac_delim +-AMDEP_FALSE!$AMDEP_FALSE$ac_delim +-AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim +-CCDEPMODE!$CCDEPMODE$ac_delim +-am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim +-am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim +-INTERACTIVE_TRUE!$INTERACTIVE_TRUE$ac_delim +-INTERACTIVE_FALSE!$INTERACTIVE_FALSE$ac_delim +-READLINE_LIBS!$READLINE_LIBS$ac_delim +-READLINE_CFLAGS!$READLINE_CFLAGS$ac_delim +-DODOCS_TRUE!$DODOCS_TRUE$ac_delim +-DODOCS_FALSE!$DODOCS_FALSE$ac_delim +-PKG_CONFIG!$PKG_CONFIG$ac_delim +-GLIB_CFLAGS!$GLIB_CFLAGS$ac_delim +-GLIB_LIBS!$GLIB_LIBS$ac_delim +-HELPTOMAN!$HELPTOMAN$ac_delim +-HAVE_HELPTOMAN_TRUE!$HAVE_HELPTOMAN_TRUE$ac_delim +-HAVE_HELPTOMAN_FALSE!$HAVE_HELPTOMAN_FALSE$ac_delim +-GCC_CFLAGS!$GCC_CFLAGS$ac_delim +-LIBOBJS!$LIBOBJS$ac_delim +-LTLIBOBJS!$LTLIBOBJS$ac_delim +-_ACEOF ++ . ./conf$$subs.sh || ++ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + +- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 89; then ++ ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` ++ if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then +- { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +-echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} +- { (exit 1); exit 1; }; } ++ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi + done ++rm -f conf$$subs.sh + +-ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +-if test -n "$ac_eof"; then +- ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` +- ac_eof=`expr $ac_eof + 1` +-fi +- +-cat >>$CONFIG_STATUS <<_ACEOF +-cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +-/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +-_ACEOF +-sed ' +-s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +-s/^/s,@/; s/!/@,|#_!!_#|/ +-:n +-t n +-s/'"$ac_delim"'$/,g/; t +-s/$/\\/; p +-N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +-' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF +-:end +-s/|#_!!_#|//g +-CEOF$ac_eof ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ++cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && + _ACEOF ++sed -n ' ++h ++s/^/S["/; s/!.*/"]=/ ++p ++g ++s/^[^!]*!// ++:repl ++t repl ++s/'"$ac_delim"'$// ++t delim ++:nl ++h ++s/\(.\{148\}\)..*/\1/ ++t more1 ++s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ ++p ++n ++b repl ++:more1 ++s/["\\]/\\&/g; s/^/"/; s/$/"\\/ ++p ++g ++s/.\{148\}// ++t nl ++:delim ++h ++s/\(.\{148\}\)..*/\1/ ++t more2 ++s/["\\]/\\&/g; s/^/"/; s/$/"/ ++p ++b ++:more2 ++s/["\\]/\\&/g; s/^/"/; s/$/"\\/ ++p ++g ++s/.\{148\}// ++t delim ++' >$CONFIG_STATUS || ac_write_fail=1 ++rm -f conf$$subs.awk ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ++_ACAWK ++cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && ++ for (key in S) S_is_set[key] = 1 ++ FS = "" ++ ++} ++{ ++ line = $ 0 ++ nfields = split(line, field, "@") ++ substed = 0 ++ len = length(field[1]) ++ for (i = 2; i < nfields; i++) { ++ key = field[i] ++ keylen = length(key) ++ if (S_is_set[key]) { ++ value = S[key] ++ line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) ++ len += length(value) + length(field[++i]) ++ substed = 1 ++ } else ++ len += 1 + keylen ++ } ++ ++ print line ++} + ++_ACAWK ++_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ++if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then ++ sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" ++else ++ cat ++fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ ++ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 ++_ACEOF + +-# VPATH may cause trouble with some makes, so we remove $(srcdir), +-# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and ++# VPATH may cause trouble with some makes, so we remove sole $(srcdir), ++# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and + # trailing colons and then remove the whole line if VPATH becomes empty + # (actually we leave an empty line to preserve line numbers). + if test "x$srcdir" = x.; then +- ac_vpsub='/^[ ]*VPATH[ ]*=/{ +-s/:*\$(srcdir):*/:/ +-s/:*\${srcdir}:*/:/ +-s/:*@srcdir@:*/:/ +-s/^\([^=]*=[ ]*\):*/\1/ ++ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ ++h ++s/// ++s/^/:/ ++s/[ ]*$/:/ ++s/:\$(srcdir):/:/g ++s/:\${srcdir}:/:/g ++s/:@srcdir@:/:/g ++s/^:*// + s/:*$// ++x ++s/\(=[ ]*\).*/\1/ ++G ++s/\n// + s/^[^=]*=[ ]*$// + }' + fi + +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + fi # test -n "$CONFIG_FILES" + + +-for ac_tag in :F $CONFIG_FILES :C $CONFIG_COMMANDS ++eval set X " :F $CONFIG_FILES :C $CONFIG_COMMANDS" ++shift ++for ac_tag + do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; +- :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +-echo "$as_me: error: Invalid tag $ac_tag." >&2;} +- { (exit 1); exit 1; }; };; ++ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac +@@ -5709,7 +5530,7 @@ + for ac_f + do + case $ac_f in +- -) ac_f="$tmp/stdin";; ++ -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. +@@ -5718,26 +5539,34 @@ + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || +- { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +-echo "$as_me: error: cannot find input file: $ac_f" >&2;} +- { (exit 1); exit 1; }; };; ++ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac +- ac_file_inputs="$ac_file_inputs $ac_f" ++ case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac ++ as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ +- configure_input="Generated from "`IFS=: +- echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." ++ configure_input='Generated from '` ++ $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' ++ `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" +- { echo "$as_me:$LINENO: creating $ac_file" >&5 +-echo "$as_me: creating $ac_file" >&6;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 ++$as_echo "$as_me: creating $ac_file" >&6;} + fi ++ # Neutralize special characters interpreted by sed in replacement strings. ++ case $configure_input in #( ++ *\&* | *\|* | *\\* ) ++ ac_sed_conf_input=`$as_echo "$configure_input" | ++ sed 's/[\\\\&|]/\\\\&/g'`;; #( ++ *) ac_sed_conf_input=$configure_input;; ++ esac + + case $ac_tag in +- *:-:* | *:-) cat >"$tmp/stdin";; ++ *:-:* | *:-) cat >"$ac_tmp/stdin" \ ++ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac +@@ -5747,42 +5576,7 @@ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$ac_file" | +- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ +- s//\1/ +- q +- } +- /^X\(\/\/\)[^/].*/{ +- s//\1/ +- q +- } +- /^X\(\/\/\)$/{ +- s//\1/ +- q +- } +- /^X\(\/\).*/{ +- s//\1/ +- q +- } +- s/.*/./; q'` +- { as_dir="$ac_dir" +- case $as_dir in #( +- -*) as_dir=./$as_dir;; +- esac +- test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { +- as_dirs= +- while :; do +- case $as_dir in #( +- *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( +- *) as_qdir=$as_dir;; +- esac +- as_dirs="'$as_qdir' $as_dirs" +- as_dir=`$as_dirname -- "$as_dir" || +-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ +- X"$as_dir" : 'X\(//\)[^/]' \| \ +- X"$as_dir" : 'X\(//\)$' \| \ +- X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$as_dir" | ++$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q +@@ -5800,20 +5594,15 @@ + q + } + s/.*/./; q'` +- test -d "$as_dir" && break +- done +- test -z "$as_dirs" || eval "mkdir $as_dirs" +- } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +-echo "$as_me: error: cannot create directory $as_dir" >&2;} +- { (exit 1); exit 1; }; }; } ++ as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + + case "$ac_dir" in + .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) +- ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` ++ ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. +- ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` ++ ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; +@@ -5851,14 +5640,19 @@ + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac ++ ac_MKDIR_P=$MKDIR_P ++ case $MKDIR_P in ++ [\\/$]* | ?:[\\/]* ) ;; ++ */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; ++ esac + _ACEOF + +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + # If the template does not know about datarootdir, expand it. + # FIXME: This hack should be removed a few years after 2.60. + ac_datarootdir_hack=; ac_datarootdir_seen= +- +-case `sed -n '/datarootdir/ { ++ac_sed_dataroot=' ++/datarootdir/ { + p + q + } +@@ -5866,36 +5660,37 @@ + /@docdir@/p + /@infodir@/p + /@localedir@/p +-/@mandir@/p +-' $ac_file_inputs` in ++/@mandir@/p' ++case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in + *datarootdir*) ac_datarootdir_seen=yes;; + *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) +- { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +-echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 ++$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} + _ACEOF +-cat >>$CONFIG_STATUS <<_ACEOF ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g +- s&\\\${datarootdir}&$datarootdir&g' ;; ++ s&\\\${datarootdir}&$datarootdir&g' ;; + esac + _ACEOF + + # Neutralize VPATH when `$srcdir' = `.'. + # Shell code in configure.ac might set extrasub. + # FIXME: do we really want to maintain this feature? +-cat >>$CONFIG_STATUS <<_ACEOF +- sed "$ac_vpsub ++cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ++ac_sed_extra="$ac_vpsub + $extrasub + _ACEOF +-cat >>$CONFIG_STATUS <<\_ACEOF ++cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + :t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b +-s&@configure_input@&$configure_input&;t t ++s|@configure_input@|$ac_sed_conf_input|;t t + s&@top_builddir@&$ac_top_builddir_sub&;t t ++s&@top_build_prefix@&$ac_top_build_prefix&;t t + s&@srcdir@&$ac_srcdir&;t t + s&@abs_srcdir@&$ac_abs_srcdir&;t t + s&@top_srcdir@&$ac_top_srcdir&;t t +@@ -5904,48 +5699,64 @@ + s&@abs_builddir@&$ac_abs_builddir&;t t + s&@abs_top_builddir@&$ac_abs_top_builddir&;t t + s&@INSTALL@&$ac_INSTALL&;t t ++s&@MKDIR_P@&$ac_MKDIR_P&;t t + $ac_datarootdir_hack +-" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out ++" ++eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ ++ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + + test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && +- { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && +- { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && +- { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +-which seems to be undefined. Please make sure it is defined." >&5 +-echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +-which seems to be undefined. Please make sure it is defined." >&2;} ++ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && ++ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ ++ "$ac_tmp/out"`; test -z "$ac_out"; } && ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' ++which seems to be undefined. Please make sure it is defined" >&5 ++$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' ++which seems to be undefined. Please make sure it is defined" >&2;} + +- rm -f "$tmp/stdin" ++ rm -f "$ac_tmp/stdin" + case $ac_file in +- -) cat "$tmp/out"; rm -f "$tmp/out";; +- *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; +- esac ++ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; ++ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; ++ esac \ ++ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + +- :C) { echo "$as_me:$LINENO: executing $ac_file commands" >&5 +-echo "$as_me: executing $ac_file commands" >&6;} ++ :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 ++$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in +- "depfiles":C) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do +- # Strip MF so we end up with the name of the file. +- mf=`echo "$mf" | sed -e 's/:.*$//'` +- # Check whether this is an Automake generated Makefile or not. +- # We used to match only the files named `Makefile.in', but +- # some people rename them; so instead we look at the file content. +- # Grep'ing the first line is not enough: some people post-process +- # each Makefile.in and add a new line on top of each file to say so. +- # So let's grep whole file. +- if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then +- dirpart=`$as_dirname -- "$mf" || ++ "depfiles":C) test x"$AMDEP_TRUE" != x"" || { ++ # Autoconf 2.62 quotes --file arguments for eval, but not when files ++ # are listed without --file. Let's play safe and only enable the eval ++ # if we detect the quoting. ++ case $CONFIG_FILES in ++ *\'*) eval set x "$CONFIG_FILES" ;; ++ *) set x $CONFIG_FILES ;; ++ esac ++ shift ++ for mf ++ do ++ # Strip MF so we end up with the name of the file. ++ mf=`echo "$mf" | sed -e 's/:.*$//'` ++ # Check whether this is an Automake generated Makefile or not. ++ # We used to match only the files named `Makefile.in', but ++ # some people rename them; so instead we look at the file content. ++ # Grep'ing the first line is not enough: some people post-process ++ # each Makefile.in and add a new line on top of each file to say so. ++ # Grep'ing the whole file is not good either: AIX grep has a line ++ # limit of 2048, but all sed's we know have understand at least 4000. ++ if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then ++ dirpart=`$as_dirname -- "$mf" || + $as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$mf" | ++$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q +@@ -5963,68 +5774,33 @@ + q + } + s/.*/./; q'` +- else +- continue +- fi +- # Extract the definition of DEPDIR, am__include, and am__quote +- # from the Makefile without running `make'. +- DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` +- test -z "$DEPDIR" && continue +- am__include=`sed -n 's/^am__include = //p' < "$mf"` +- test -z "am__include" && continue +- am__quote=`sed -n 's/^am__quote = //p' < "$mf"` +- # When using ansi2knr, U may be empty or an underscore; expand it +- U=`sed -n 's/^U = //p' < "$mf"` +- # Find all dependency output files, they are included files with +- # $(DEPDIR) in their names. We invoke sed twice because it is the +- # simplest approach to changing $(DEPDIR) to its actual value in the +- # expansion. +- for file in `sed -n " +- s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ +- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do +- # Make sure the directory exists. +- test -f "$dirpart/$file" && continue +- fdir=`$as_dirname -- "$file" || ++ else ++ continue ++ fi ++ # Extract the definition of DEPDIR, am__include, and am__quote ++ # from the Makefile without running `make'. ++ DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` ++ test -z "$DEPDIR" && continue ++ am__include=`sed -n 's/^am__include = //p' < "$mf"` ++ test -z "am__include" && continue ++ am__quote=`sed -n 's/^am__quote = //p' < "$mf"` ++ # When using ansi2knr, U may be empty or an underscore; expand it ++ U=`sed -n 's/^U = //p' < "$mf"` ++ # Find all dependency output files, they are included files with ++ # $(DEPDIR) in their names. We invoke sed twice because it is the ++ # simplest approach to changing $(DEPDIR) to its actual value in the ++ # expansion. ++ for file in `sed -n " ++ s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ ++ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do ++ # Make sure the directory exists. ++ test -f "$dirpart/$file" && continue ++ fdir=`$as_dirname -- "$file" || + $as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$file" | +- sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ +- s//\1/ +- q +- } +- /^X\(\/\/\)[^/].*/{ +- s//\1/ +- q +- } +- /^X\(\/\/\)$/{ +- s//\1/ +- q +- } +- /^X\(\/\).*/{ +- s//\1/ +- q +- } +- s/.*/./; q'` +- { as_dir=$dirpart/$fdir +- case $as_dir in #( +- -*) as_dir=./$as_dir;; +- esac +- test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { +- as_dirs= +- while :; do +- case $as_dir in #( +- *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( +- *) as_qdir=$as_dir;; +- esac +- as_dirs="'$as_qdir' $as_dirs" +- as_dir=`$as_dirname -- "$as_dir" || +-$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ +- X"$as_dir" : 'X\(//\)[^/]' \| \ +- X"$as_dir" : 'X\(//\)$' \| \ +- X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +-echo X"$as_dir" | ++$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q +@@ -6042,27 +5818,25 @@ + q + } + s/.*/./; q'` +- test -d "$as_dir" && break ++ as_dir=$dirpart/$fdir; as_fn_mkdir_p ++ # echo "creating $dirpart/$file" ++ echo '# dummy' > "$dirpart/$file" + done +- test -z "$as_dirs" || eval "mkdir $as_dirs" +- } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +-echo "$as_me: error: cannot create directory $as_dir" >&2;} +- { (exit 1); exit 1; }; }; } +- # echo "creating $dirpart/$file" +- echo '# dummy' > "$dirpart/$file" + done +-done ++} + ;; + + esac + done # for ac_tag + + +-{ (exit 0); exit 0; } ++as_fn_exit 0 + _ACEOF +-chmod +x $CONFIG_STATUS + ac_clean_files=$ac_clean_files_save + ++test $ac_write_fail = 0 || ++ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 ++ + + # configure is writing to config.log, and then calls config.status. + # config.status does its own redirection, appending to config.log. +@@ -6082,14 +5856,18 @@ + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. +- $ac_cs_success || { (exit 1); exit 1; } ++ $ac_cs_success || as_fn_exit 1 ++fi ++if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then ++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 ++$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} + fi + + + if test x$WITH_READLINE = xerror ; then +- { echo "$as_me:$LINENO: result: WARNING: Could not link against readline libraries; interactive mode will not be available." >&5 +-echo "${ECHO_T}WARNING: Could not link against readline libraries; interactive mode will not be available." >&6; } ++ { $as_echo "$as_me:${as_lineno-$LINENO}: result: WARNING: Could not link against readline libraries; interactive mode will not be available." >&5 ++$as_echo "WARNING: Could not link against readline libraries; interactive mode will not be available." >&6; } + fi + +-{ echo "$as_me:$LINENO: result: Type make to build exmap-console." >&5 +-echo "${ECHO_T}Type make to build exmap-console." >&6; } ++{ $as_echo "$as_me:${as_lineno-$LINENO}: result: Type make to build exmap-console." >&5 ++$as_echo "Type make to build exmap-console." >&6; } +diff -Nur exmap-console-0.4.1.orig/configure.ac exmap-console-0.4.1/configure.ac +--- exmap-console-0.4.1.orig/configure.ac 2007-02-26 12:00:48.000000000 +0100 ++++ exmap-console-0.4.1/configure.ac 2011-01-11 12:44:09.000000000 +0100 +@@ -46,10 +46,6 @@ + + AM_CONDITIONAL(DODOCS, test "x$DO_DOCS" != "xno") + +-AC_ARG_ENABLE(debug, AS_HELP_STRING([--enable-debug], +- [Enable debuggin information]), +- [CFLAGS="-g -O0"], [CFLAGS="-g -O2"]) +- + CFLAGS="$CFLAGS -DEXMAP_VERSION=\\\"$EXMAP_VERSION\\\" -DEXMAP_PROTOCOL_VERSION=\\\"$EXMAP_PROTOCOL_VERSION\\\"" + + AC_SUBST(CFLAGS) +diff -Nur exmap-console-0.4.1.orig/depcomp exmap-console-0.4.1/depcomp +--- exmap-console-0.4.1.orig/depcomp 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/depcomp 2011-01-11 12:44:16.000000000 +0100 +@@ -1,9 +1,10 @@ + #! /bin/sh + # depcomp - compile a program generating dependencies as side-effects + +-scriptversion=2005-07-09.11 ++scriptversion=2009-04-28.21; # UTC + +-# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. ++# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free ++# Software Foundation, Inc. + + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU General Public License as published by +@@ -16,9 +17,7 @@ + # 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 Street, Fifth Floor, Boston, MA +-# 02110-1301, USA. ++# along with this program. If not, see . + + # As a special exception to the GNU General Public License, if you + # distribute this file as part of a program that contains a +@@ -86,12 +85,34 @@ + depmode=dashmstdout + fi + ++cygpath_u="cygpath -u -f -" ++if test "$depmode" = msvcmsys; then ++ # This is just like msvisualcpp but w/o cygpath translation. ++ # Just convert the backslash-escaped backslashes to single forward ++ # slashes to satisfy depend.m4 ++ cygpath_u="sed s,\\\\\\\\,/,g" ++ depmode=msvisualcpp ++fi ++ + case "$depmode" in + gcc3) + ## gcc 3 implements dependency tracking that does exactly what + ## we want. Yay! Note: for some reason libtool 1.4 doesn't like + ## it if -MD -MP comes after the -MF stuff. Hmm. +- "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" ++## Unfortunately, FreeBSD c89 acceptance of flags depends upon ++## the command line argument order; so add the flags where they ++## appear in depend2.am. Note that the slowdown incurred here ++## affects only configure: in makefiles, %FASTDEP% shortcuts this. ++ for arg ++ do ++ case $arg in ++ -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; ++ *) set fnord "$@" "$arg" ;; ++ esac ++ shift # fnord ++ shift # $arg ++ done ++ "$@" + stat=$? + if test $stat -eq 0; then : + else +@@ -178,14 +199,14 @@ + ' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +-' ' ' >> $depfile +- echo >> $depfile ++' ' ' >> "$depfile" ++ echo >> "$depfile" + + # The second pass generates a dummy entry for each header file. + tr ' ' ' + ' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ +- >> $depfile ++ >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile +@@ -201,34 +222,39 @@ + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. +- stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` +- tmpdepfile="$stripped.u" ++ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` ++ test "x$dir" = "x$object" && dir= ++ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then ++ tmpdepfile1=$dir$base.u ++ tmpdepfile2=$base.u ++ tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else ++ tmpdepfile1=$dir$base.u ++ tmpdepfile2=$dir$base.u ++ tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + +- if test -f "$tmpdepfile"; then : +- else +- stripped=`echo "$stripped" | sed 's,^.*/,,'` +- tmpdepfile="$stripped.u" +- fi +- + if test $stat -eq 0; then : + else +- rm -f "$tmpdepfile" ++ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + ++ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" ++ do ++ test -f "$tmpdepfile" && break ++ done + if test -f "$tmpdepfile"; then +- outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. +- sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" +- sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" ++ sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" ++ # That's a tab and a space in the []. ++ sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile +@@ -276,6 +302,51 @@ + rm -f "$tmpdepfile" + ;; + ++hp2) ++ # The "hp" stanza above does not work with aCC (C++) and HP's ia64 ++ # compilers, which have integrated preprocessors. The correct option ++ # to use with these is +Maked; it writes dependencies to a file named ++ # 'foo.d', which lands next to the object file, wherever that ++ # happens to be. ++ # Much of this is similar to the tru64 case; see comments there. ++ dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` ++ test "x$dir" = "x$object" && dir= ++ base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` ++ if test "$libtool" = yes; then ++ tmpdepfile1=$dir$base.d ++ tmpdepfile2=$dir.libs/$base.d ++ "$@" -Wc,+Maked ++ else ++ tmpdepfile1=$dir$base.d ++ tmpdepfile2=$dir$base.d ++ "$@" +Maked ++ fi ++ stat=$? ++ if test $stat -eq 0; then : ++ else ++ rm -f "$tmpdepfile1" "$tmpdepfile2" ++ exit $stat ++ fi ++ ++ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" ++ do ++ test -f "$tmpdepfile" && break ++ done ++ if test -f "$tmpdepfile"; then ++ sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" ++ # Add `dependent.h:' lines. ++ sed -ne '2,${ ++ s/^ *// ++ s/ \\*$// ++ s/$/:/ ++ p ++ }' "$tmpdepfile" >> "$depfile" ++ else ++ echo "#dummy" > "$depfile" ++ fi ++ rm -f "$tmpdepfile" "$tmpdepfile2" ++ ;; ++ + tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. +@@ -288,13 +359,13 @@ + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a +- # static library. This mecanism is used in libtool 1.4 series to ++ # static library. This mechanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two +- # compilations output dependencies in in $dir.libs/$base.o.d and ++ # compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is +@@ -345,7 +416,7 @@ + + # Remove the call to Libtool. + if test "$libtool" = yes; then +- while test $1 != '--mode=compile'; do ++ while test "X$1" != 'X--mode=compile'; do + shift + done + shift +@@ -396,32 +467,39 @@ + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then +- while test $1 != '--mode=compile'; do ++ while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift +- cleared=no +- for arg in "$@"; do ++ cleared=no eat=no ++ for arg ++ do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac ++ if test $eat = yes; then ++ eat=no ++ continue ++ fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. ++ -arch) ++ eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done +- obj_suffix="`echo $object | sed 's/^.*\././'`" ++ obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" +@@ -441,7 +519,7 @@ + + # Remove the call to Libtool. + if test "$libtool" = yes; then +- while test $1 != '--mode=compile'; do ++ while test "X$1" != 'X--mode=compile'; do + shift + done + shift +@@ -479,13 +557,27 @@ + + msvisualcpp) + # Important note: in order to support this mode, a compiler *must* +- # always write the preprocessed file to stdout, regardless of -o, +- # because we must use -o when running libtool. ++ # always write the preprocessed file to stdout. + "$@" || exit $? ++ ++ # Remove the call to Libtool. ++ if test "$libtool" = yes; then ++ while test "X$1" != 'X--mode=compile'; do ++ shift ++ done ++ shift ++ fi ++ + IFS=" " + for arg + do + case "$arg" in ++ -o) ++ shift ++ ;; ++ $object) ++ shift ++ ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift +@@ -498,16 +590,23 @@ + ;; + esac + done +- "$@" -E | +- sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" ++ "$@" -E 2>/dev/null | ++ sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" +- . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" ++ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" +- . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" ++ sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + ++msvcmsys) ++ # This case exists only to let depend.m4 do its work. It works by ++ # looking at the text of this script. This case will never be run, ++ # since it is checked for above. ++ exit 1 ++ ;; ++ + none) + exec "$@" + ;; +@@ -526,5 +625,6 @@ + # eval: (add-hook 'write-file-hooks 'time-stamp) + # time-stamp-start: "scriptversion=" + # time-stamp-format: "%:y-%02m-%02d.%02H" +-# time-stamp-end: "$" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" + # End: +diff -Nur exmap-console-0.4.1.orig/doc/Makefile.in exmap-console-0.4.1/doc/Makefile.in +--- exmap-console-0.4.1.orig/doc/Makefile.in 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/doc/Makefile.in 2011-01-11 12:44:16.000000000 +0100 +@@ -1,8 +1,9 @@ +-# Makefile.in generated by automake 1.9.6 from Makefile.am. ++# Makefile.in generated by automake 1.11.1 from Makefile.am. + # @configure_input@ + + # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005 Free Software Foundation, Inc. ++# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, ++# Inc. + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. +@@ -13,15 +14,12 @@ + # PARTICULAR PURPOSE. + + @SET_MAKE@ +-srcdir = @srcdir@ +-top_srcdir = @top_srcdir@ + VPATH = @srcdir@ + pkgdatadir = $(datadir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ + pkgincludedir = $(includedir)/@PACKAGE@ +-top_builddir = .. ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ + am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-INSTALL = @INSTALL@ + install_sh_DATA = $(install_sh) -c -m 644 + install_sh_PROGRAM = $(install_sh) -c + install_sh_SCRIPT = $(install_sh) -c +@@ -41,16 +39,36 @@ + $(ACLOCAL_M4) + mkinstalldirs = $(install_sh) -d + CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = + SOURCES = + DIST_SOURCES = ++am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; ++am__vpath_adj = case $$p in \ ++ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ ++ *) f=$$p;; \ ++ esac; ++am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; ++am__install_max = 40 ++am__nobase_strip_setup = \ ++ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` ++am__nobase_strip = \ ++ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" ++am__nobase_list = $(am__nobase_strip_setup); \ ++ for p in $$list; do echo "$$p $$p"; done | \ ++ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ ++ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ ++ if (++n[$$2] == $(am__install_max)) \ ++ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ ++ END { for (dir in files) print dir, files[dir] }' ++am__base_list = \ ++ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ ++ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' + man1dir = $(mandir)/man1 + am__installdirs = "$(DESTDIR)$(man1dir)" + NROFF = nroff + MANS = $(man_MANS) + DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + ACLOCAL = @ACLOCAL@ +-AMDEP_FALSE = @AMDEP_FALSE@ +-AMDEP_TRUE = @AMDEP_TRUE@ + AMTAR = @AMTAR@ + AUTOCONF = @AUTOCONF@ + AUTOHEADER = @AUTOHEADER@ +@@ -63,8 +81,6 @@ + CYGPATH_W = @CYGPATH_W@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ +-DODOCS_FALSE = @DODOCS_FALSE@ +-DODOCS_TRUE = @DODOCS_TRUE@ + ECHO_C = @ECHO_C@ + ECHO_N = @ECHO_N@ + ECHO_T = @ECHO_T@ +@@ -72,38 +88,41 @@ + GCC_CFLAGS = @GCC_CFLAGS@ + GLIB_CFLAGS = @GLIB_CFLAGS@ + GLIB_LIBS = @GLIB_LIBS@ +-HAVE_HELPTOMAN_FALSE = @HAVE_HELPTOMAN_FALSE@ +-HAVE_HELPTOMAN_TRUE = @HAVE_HELPTOMAN_TRUE@ + HELPTOMAN = @HELPTOMAN@ ++INSTALL = @INSTALL@ + INSTALL_DATA = @INSTALL_DATA@ + INSTALL_PROGRAM = @INSTALL_PROGRAM@ + INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-INTERACTIVE_FALSE = @INTERACTIVE_FALSE@ +-INTERACTIVE_TRUE = @INTERACTIVE_TRUE@ + LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LTLIBOBJS = @LTLIBOBJS@ + MAKEINFO = @MAKEINFO@ ++MKDIR_P = @MKDIR_P@ + OBJEXT = @OBJEXT@ + PACKAGE = @PACKAGE@ + PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + PACKAGE_NAME = @PACKAGE_NAME@ + PACKAGE_STRING = @PACKAGE_STRING@ + PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ + PACKAGE_VERSION = @PACKAGE_VERSION@ + PATH_SEPARATOR = @PATH_SEPARATOR@ + PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ + READLINE_CFLAGS = @READLINE_CFLAGS@ + READLINE_LIBS = @READLINE_LIBS@ + SET_MAKE = @SET_MAKE@ + SHELL = @SHELL@ + STRIP = @STRIP@ + VERSION = @VERSION@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ + ac_ct_CC = @ac_ct_CC@ +-am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +-am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ + am__include = @am__include@ + am__leading_dot = @am__leading_dot@ + am__quote = @am__quote@ +@@ -111,6 +130,7 @@ + am__untar = @am__untar@ + bindir = @bindir@ + build_alias = @build_alias@ ++builddir = @builddir@ + datadir = @datadir@ + datarootdir = @datarootdir@ + docdir = @docdir@ +@@ -134,8 +154,12 @@ + psdir = @psdir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ + DISTCLEANFILES = *~ Makefile.in + @DODOCS_TRUE@@INTERACTIVE_TRUE@interactive = exmapserver.1 + @DODOCS_TRUE@man_MANS = exmap.1 exmapd.1 $(interactive) +@@ -146,14 +170,14 @@ + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ +- && exit 0; \ ++ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ ++ && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ +- cd $(top_srcdir) && \ +- $(AUTOMAKE) --gnu doc/Makefile ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu doc/Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --gnu doc/Makefile + .PRECIOUS: Makefile + Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ +@@ -171,52 +195,45 @@ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + $(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-uninstall-info-am: +-install-man1: $(man1_MANS) $(man_MANS) ++$(am__aclocal_m4_deps): ++install-man1: $(man_MANS) + @$(NORMAL_INSTALL) +- test -z "$(man1dir)" || $(mkdir_p) "$(DESTDIR)$(man1dir)" +- @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ +- l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ +- for i in $$l2; do \ +- case "$$i" in \ +- *.1*) list="$$list $$i" ;; \ +- esac; \ ++ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)" ++ @list=''; test -n "$(man1dir)" || exit 0; \ ++ { for i in $$list; do echo "$$i"; done; \ ++ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ ++ sed -n '/\.1[a-z]*$$/p'; \ ++ } | while read p; do \ ++ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ ++ echo "$$d$$p"; echo "$$p"; \ ++ done | \ ++ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ ++ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ ++ sed 'N;N;s,\n, ,g' | { \ ++ list=; while read file base inst; do \ ++ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ ++ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ ++ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ ++ fi; \ + done; \ +- for i in $$list; do \ +- if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \ +- else file=$$i; fi; \ +- ext=`echo $$i | sed -e 's/^.*\\.//'`; \ +- case "$$ext" in \ +- 1*) ;; \ +- *) ext='1' ;; \ +- esac; \ +- inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ +- inst=`echo $$inst | sed -e 's/^.*\///'`; \ +- inst=`echo $$inst | sed '$(transform)'`.$$ext; \ +- echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ +- $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst"; \ +- done ++ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ ++ while read files; do \ ++ test -z "$$files" || { \ ++ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ ++ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ ++ done; } ++ + uninstall-man1: + @$(NORMAL_UNINSTALL) +- @list='$(man1_MANS) $(dist_man1_MANS) $(nodist_man1_MANS)'; \ +- l2='$(man_MANS) $(dist_man_MANS) $(nodist_man_MANS)'; \ +- for i in $$l2; do \ +- case "$$i" in \ +- *.1*) list="$$list $$i" ;; \ +- esac; \ +- done; \ +- for i in $$list; do \ +- ext=`echo $$i | sed -e 's/^.*\\.//'`; \ +- case "$$ext" in \ +- 1*) ;; \ +- *) ext='1' ;; \ +- esac; \ +- inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \ +- inst=`echo $$inst | sed -e 's/^.*\///'`; \ +- inst=`echo $$inst | sed '$(transform)'`.$$ext; \ +- echo " rm -f '$(DESTDIR)$(man1dir)/$$inst'"; \ +- rm -f "$(DESTDIR)$(man1dir)/$$inst"; \ +- done ++ @list=''; test -n "$(man1dir)" || exit 0; \ ++ files=`{ for i in $$list; do echo "$$i"; done; \ ++ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ ++ sed -n '/\.1[a-z]*$$/p'; \ ++ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ ++ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ ++ test -z "$$files" || { \ ++ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \ ++ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; } + tags: TAGS + TAGS: + +@@ -225,29 +242,45 @@ + + + distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ +- list='$(DISTFILES)'; for file in $$list; do \ +- case $$file in \ +- $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ +- $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ +- esac; \ ++ @list='$(MANS)'; if test -n "$$list"; then \ ++ list=`for p in $$list; do \ ++ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ ++ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \ ++ if test -n "$$list" && \ ++ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \ ++ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \ ++ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \ ++ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \ ++ echo " typically \`make maintainer-clean' will remove them" >&2; \ ++ exit 1; \ ++ else :; fi; \ ++ else :; fi ++ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ list='$(DISTFILES)'; \ ++ dist_files=`for file in $$list; do echo $$file; done | \ ++ sed -e "s|^$$srcdirstrip/||;t" \ ++ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ ++ case $$dist_files in \ ++ */*) $(MKDIR_P) `echo "$$dist_files" | \ ++ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ ++ sort -u` ;; \ ++ esac; \ ++ for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test "$$dir" != "$$file" && test "$$dir" != "."; then \ +- dir="/$$dir"; \ +- $(mkdir_p) "$(distdir)$$dir"; \ +- else \ +- dir=''; \ +- fi; \ + if test -d $$d/$$file; then \ ++ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ ++ if test -d "$(distdir)/$$file"; then \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ ++ fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ +- cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ +- test -f $(distdir)/$$file \ +- || cp -p $$d/$$file $(distdir)/$$file \ ++ test -f "$(distdir)/$$file" \ ++ || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +@@ -256,7 +289,7 @@ + all-am: Makefile $(MANS) + installdirs: + for dir in "$(DESTDIR)$(man1dir)"; do \ +- test -z "$$dir" || $(mkdir_p) "$$dir"; \ ++ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done + install: install-am + install-exec: install-exec-am +@@ -278,6 +311,7 @@ + + distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + + maintainer-clean-generic: +@@ -297,18 +331,38 @@ + + html: html-am + ++html-am: ++ + info: info-am + + info-am: + + install-data-am: install-man + ++install-dvi: install-dvi-am ++ ++install-dvi-am: ++ + install-exec-am: + ++install-html: install-html-am ++ ++install-html-am: ++ + install-info: install-info-am + ++install-info-am: ++ + install-man: install-man1 + ++install-pdf: install-pdf-am ++ ++install-pdf-am: ++ ++install-ps: install-ps-am ++ ++install-ps-am: ++ + installcheck-am: + + maintainer-clean: maintainer-clean-am +@@ -327,18 +381,22 @@ + + ps-am: + +-uninstall-am: uninstall-info-am uninstall-man ++uninstall-am: uninstall-man + + uninstall-man: uninstall-man1 + ++.MAKE: install-am install-strip ++ + .PHONY: all all-am check check-am clean clean-generic distclean \ + distclean-generic distdir dvi dvi-am html html-am info info-am \ +- install install-am install-data install-data-am install-exec \ +- install-exec-am install-info install-info-am install-man \ +- install-man1 install-strip installcheck installcheck-am \ ++ install install-am install-data install-data-am install-dvi \ ++ install-dvi-am install-exec install-exec-am install-html \ ++ install-html-am install-info install-info-am install-man \ ++ install-man1 install-pdf install-pdf-am install-ps \ ++ install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic pdf pdf-am ps ps-am uninstall \ +- uninstall-am uninstall-info-am uninstall-man uninstall-man1 ++ uninstall-am uninstall-man uninstall-man1 + + + @DODOCS_TRUE@@HAVE_HELPTOMAN_TRUE@exmap.1: ../src/exmap ./man.include +@@ -349,6 +407,7 @@ + + @DODOCS_TRUE@@HAVE_HELPTOMAN_TRUE@@INTERACTIVE_TRUE@exmapserver.1: ../src/exmapserver ./man-s.include + @DODOCS_TRUE@@HAVE_HELPTOMAN_TRUE@@INTERACTIVE_TRUE@ $(HELPTOMAN) -N -I ./man-s.include -m "Exmap server" ../src/exmapserver > ./exmapserver.1 ++ + # Tell versions [3.59,3.63) of GNU make to not export all variables. + # Otherwise a system limit (for SysV at least) may be exceeded. + .NOEXPORT: +diff -Nur exmap-console-0.4.1.orig/install-sh exmap-console-0.4.1/install-sh +--- exmap-console-0.4.1.orig/install-sh 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/install-sh 2011-01-11 12:44:16.000000000 +0100 +@@ -1,7 +1,7 @@ + #!/bin/sh + # install - install a program, script, or datafile + +-scriptversion=2005-05-14.22 ++scriptversion=2009-04-28.21; # UTC + + # This originates from X11R5 (mit/util/scripts/install.sh), which was + # later released in X11R6 (xc/config/util/install.sh) with the +@@ -39,38 +39,68 @@ + # when there is no Makefile. + # + # This script is compatible with the BSD install script, but was written +-# from scratch. It can only install one file at a time, a restriction +-# shared with many OS's install programs. ++# from scratch. ++ ++nl=' ++' ++IFS=" "" $nl" + + # set DOITPROG to echo to test this script + + # Don't use :- since 4.3BSD and earlier shells don't like it. +-doit="${DOITPROG-}" ++doit=${DOITPROG-} ++if test -z "$doit"; then ++ doit_exec=exec ++else ++ doit_exec=$doit ++fi + +-# put in absolute paths if you don't have them in your path; or use env. vars. ++# Put in absolute file names if you don't have them in your path; ++# or use environment vars. + +-mvprog="${MVPROG-mv}" +-cpprog="${CPPROG-cp}" +-chmodprog="${CHMODPROG-chmod}" +-chownprog="${CHOWNPROG-chown}" +-chgrpprog="${CHGRPPROG-chgrp}" +-stripprog="${STRIPPROG-strip}" +-rmprog="${RMPROG-rm}" +-mkdirprog="${MKDIRPROG-mkdir}" ++chgrpprog=${CHGRPPROG-chgrp} ++chmodprog=${CHMODPROG-chmod} ++chownprog=${CHOWNPROG-chown} ++cmpprog=${CMPPROG-cmp} ++cpprog=${CPPROG-cp} ++mkdirprog=${MKDIRPROG-mkdir} ++mvprog=${MVPROG-mv} ++rmprog=${RMPROG-rm} ++stripprog=${STRIPPROG-strip} ++ ++posix_glob='?' ++initialize_posix_glob=' ++ test "$posix_glob" != "?" || { ++ if (set -f) 2>/dev/null; then ++ posix_glob= ++ else ++ posix_glob=: ++ fi ++ } ++' ++ ++posix_mkdir= ++ ++# Desired mode of installed file. ++mode=0755 + +-chmodcmd="$chmodprog 0755" +-chowncmd= + chgrpcmd= +-stripcmd= ++chmodcmd=$chmodprog ++chowncmd= ++mvcmd=$mvprog + rmcmd="$rmprog -f" +-mvcmd="$mvprog" ++stripcmd= ++ + src= + dst= + dir_arg= +-dstarg= ++dst_arg= ++ ++copy_on_change=false + no_target_directory= + +-usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE ++usage="\ ++Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... +@@ -80,81 +110,86 @@ + In the 4th, create DIRECTORIES. + + Options: +--c (ignored) +--d create directories instead of installing files. +--g GROUP $chgrpprog installed files to GROUP. +--m MODE $chmodprog installed files to MODE. +--o USER $chownprog installed files to USER. +--s $stripprog installed files. +--t DIRECTORY install into DIRECTORY. +--T report an error if DSTFILE is a directory. +---help display this help and exit. +---version display version info and exit. ++ --help display this help and exit. ++ --version display version info and exit. ++ ++ -c (ignored) ++ -C install only if different (preserve the last data modification time) ++ -d create directories instead of installing files. ++ -g GROUP $chgrpprog installed files to GROUP. ++ -m MODE $chmodprog installed files to MODE. ++ -o USER $chownprog installed files to USER. ++ -s $stripprog installed files. ++ -t DIRECTORY install into DIRECTORY. ++ -T report an error if DSTFILE is a directory. + + Environment variables override the default commands: +- CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG ++ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG ++ RMPROG STRIPPROG + " + +-while test -n "$1"; do ++while test $# -ne 0; do + case $1 in +- -c) shift +- continue;; ++ -c) ;; + +- -d) dir_arg=true +- shift +- continue;; ++ -C) copy_on_change=true;; ++ ++ -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" +- shift +- shift +- continue;; ++ shift;; + + --help) echo "$usage"; exit $?;; + +- -m) chmodcmd="$chmodprog $2" +- shift +- shift +- continue;; ++ -m) mode=$2 ++ case $mode in ++ *' '* | *' '* | *' ++'* | *'*'* | *'?'* | *'['*) ++ echo "$0: invalid mode: $mode" >&2 ++ exit 1;; ++ esac ++ shift;; + + -o) chowncmd="$chownprog $2" +- shift +- shift +- continue;; +- +- -s) stripcmd=$stripprog +- shift +- continue;; +- +- -t) dstarg=$2 +- shift +- shift +- continue;; +- +- -T) no_target_directory=true +- shift +- continue;; ++ shift;; ++ ++ -s) stripcmd=$stripprog;; ++ ++ -t) dst_arg=$2 ++ shift;; ++ ++ -T) no_target_directory=true;; + + --version) echo "$0 $scriptversion"; exit $?;; + +- *) # When -d is used, all remaining arguments are directories to create. +- # When -t is used, the destination is already specified. +- test -n "$dir_arg$dstarg" && break +- # Otherwise, the last argument is the destination. Remove it from $@. +- for arg +- do +- if test -n "$dstarg"; then +- # $@ is not empty: it contains at least $arg. +- set fnord "$@" "$dstarg" +- shift # fnord +- fi +- shift # arg +- dstarg=$arg +- done ++ --) shift + break;; ++ ++ -*) echo "$0: invalid option: $1" >&2 ++ exit 1;; ++ ++ *) break;; + esac ++ shift + done + +-if test -z "$1"; then ++if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then ++ # When -d is used, all remaining arguments are directories to create. ++ # When -t is used, the destination is already specified. ++ # Otherwise, the last argument is the destination. Remove it from $@. ++ for arg ++ do ++ if test -n "$dst_arg"; then ++ # $@ is not empty: it contains at least $arg. ++ set fnord "$@" "$dst_arg" ++ shift # fnord ++ fi ++ shift # arg ++ dst_arg=$arg ++ done ++fi ++ ++if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 +@@ -164,24 +199,47 @@ + exit 0 + fi + ++if test -z "$dir_arg"; then ++ trap '(exit $?); exit' 1 2 13 15 ++ ++ # Set umask so as not to create temps with too-generous modes. ++ # However, 'strip' requires both read and write access to temps. ++ case $mode in ++ # Optimize common cases. ++ *644) cp_umask=133;; ++ *755) cp_umask=22;; ++ ++ *[0-7]) ++ if test -z "$stripcmd"; then ++ u_plus_rw= ++ else ++ u_plus_rw='% 200' ++ fi ++ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; ++ *) ++ if test -z "$stripcmd"; then ++ u_plus_rw= ++ else ++ u_plus_rw=,u+rw ++ fi ++ cp_umask=$mode$u_plus_rw;; ++ esac ++fi ++ + for src + do + # Protect names starting with `-'. + case $src in +- -*) src=./$src ;; ++ -*) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src +- src= +- +- if test -d "$dst"; then +- mkdircmd=: +- chmodcmd= +- else +- mkdircmd=$mkdirprog +- fi ++ dstdir=$dst ++ test -d "$dstdir" ++ dstdir_status=$? + else ++ + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. +@@ -190,71 +248,199 @@ + exit 1 + fi + +- if test -z "$dstarg"; then ++ if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + +- dst=$dstarg ++ dst=$dst_arg + # Protect names starting with `-'. + case $dst in +- -*) dst=./$dst ;; ++ -*) dst=./$dst;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then +- echo "$0: $dstarg: Is a directory" >&2 ++ echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi +- dst=$dst/`basename "$src"` ++ dstdir=$dst ++ dst=$dstdir/`basename "$src"` ++ dstdir_status=0 ++ else ++ # Prefer dirname, but fall back on a substitute if dirname fails. ++ dstdir=` ++ (dirname "$dst") 2>/dev/null || ++ expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ ++ X"$dst" : 'X\(//\)[^/]' \| \ ++ X"$dst" : 'X\(//\)$' \| \ ++ X"$dst" : 'X\(/\)' \| . 2>/dev/null || ++ echo X"$dst" | ++ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)[^/].*/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\/\)$/{ ++ s//\1/ ++ q ++ } ++ /^X\(\/\).*/{ ++ s//\1/ ++ q ++ } ++ s/.*/./; q' ++ ` ++ ++ test -d "$dstdir" ++ dstdir_status=$? + fi + fi + +- # This sed command emulates the dirname command. +- dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` +- +- # Make sure that the destination directory exists. ++ obsolete_mkdir_used=false + +- # Skip lots of stat calls in the usual case. +- if test ! -d "$dstdir"; then +- defaultIFS=' +- ' +- IFS="${IFS-$defaultIFS}" +- +- oIFS=$IFS +- # Some sh's can't handle IFS=/ for some reason. +- IFS='%' +- set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` +- shift +- IFS=$oIFS ++ if test $dstdir_status != 0; then ++ case $posix_mkdir in ++ '') ++ # Create intermediate dirs using mode 755 as modified by the umask. ++ # This is like FreeBSD 'install' as of 1997-10-28. ++ umask=`umask` ++ case $stripcmd.$umask in ++ # Optimize common cases. ++ *[2367][2367]) mkdir_umask=$umask;; ++ .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; ++ ++ *[0-7]) ++ mkdir_umask=`expr $umask + 22 \ ++ - $umask % 100 % 40 + $umask % 20 \ ++ - $umask % 10 % 4 + $umask % 2 ++ `;; ++ *) mkdir_umask=$umask,go-w;; ++ esac ++ ++ # With -d, create the new directory with the user-specified mode. ++ # Otherwise, rely on $mkdir_umask. ++ if test -n "$dir_arg"; then ++ mkdir_mode=-m$mode ++ else ++ mkdir_mode= ++ fi ++ ++ posix_mkdir=false ++ case $umask in ++ *[123567][0-7][0-7]) ++ # POSIX mkdir -p sets u+wx bits regardless of umask, which ++ # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ++ ;; ++ *) ++ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ ++ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 ++ ++ if (umask $mkdir_umask && ++ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 ++ then ++ if test -z "$dir_arg" || { ++ # Check for POSIX incompatibilities with -m. ++ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or ++ # other-writeable bit of parent directory when it shouldn't. ++ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ++ ls_ld_tmpdir=`ls -ld "$tmpdir"` ++ case $ls_ld_tmpdir in ++ d????-?r-*) different_mode=700;; ++ d????-?--*) different_mode=755;; ++ *) false;; ++ esac && ++ $mkdirprog -m$different_mode -p -- "$tmpdir" && { ++ ls_ld_tmpdir_1=`ls -ld "$tmpdir"` ++ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" ++ } ++ } ++ then posix_mkdir=: ++ fi ++ rmdir "$tmpdir/d" "$tmpdir" ++ else ++ # Remove any dirs left behind by ancient mkdir implementations. ++ rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null ++ fi ++ trap '' 0;; ++ esac;; ++ esac + +- pathcomp= ++ if ++ $posix_mkdir && ( ++ umask $mkdir_umask && ++ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ++ ) ++ then : ++ else + +- while test $# -ne 0 ; do +- pathcomp=$pathcomp$1 ++ # The umask is ridiculous, or mkdir does not conform to POSIX, ++ # or it failed possibly due to a race condition. Create the ++ # directory the slow way, step by step, checking for races as we go. ++ ++ case $dstdir in ++ /*) prefix='/';; ++ -*) prefix='./';; ++ *) prefix='';; ++ esac ++ ++ eval "$initialize_posix_glob" ++ ++ oIFS=$IFS ++ IFS=/ ++ $posix_glob set -f ++ set fnord $dstdir + shift +- if test ! -d "$pathcomp"; then +- $mkdirprog "$pathcomp" +- # mkdir can fail with a `File exist' error in case several +- # install-sh are creating the directory concurrently. This +- # is OK. +- test -d "$pathcomp" || exit ++ $posix_glob set +f ++ IFS=$oIFS ++ ++ prefixes= ++ ++ for d ++ do ++ test -z "$d" && continue ++ ++ prefix=$prefix$d ++ if test -d "$prefix"; then ++ prefixes= ++ else ++ if $posix_mkdir; then ++ (umask=$mkdir_umask && ++ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break ++ # Don't fail if two instances are running concurrently. ++ test -d "$prefix" || exit 1 ++ else ++ case $prefix in ++ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; ++ *) qprefix=$prefix;; ++ esac ++ prefixes="$prefixes '$qprefix'" ++ fi ++ fi ++ prefix=$prefix/ ++ done ++ ++ if test -n "$prefixes"; then ++ # Don't fail if two instances are running concurrently. ++ (umask $mkdir_umask && ++ eval "\$doit_exec \$mkdirprog $prefixes") || ++ test -d "$dstdir" || exit 1 ++ obsolete_mkdir_used=true + fi +- pathcomp=$pathcomp/ +- done ++ fi + fi + + if test -n "$dir_arg"; then +- $doit $mkdircmd "$dst" \ +- && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ +- && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ +- && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ +- && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } +- ++ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && ++ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && ++ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || ++ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else +- dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ +@@ -262,10 +448,9 @@ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 +- trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. +- $doit $cpprog "$src" "$dsttmp" && ++ (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # +@@ -273,51 +458,63 @@ + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # +- { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ +- && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ +- && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ +- && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && +- +- # Now rename the file to the real destination. +- { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ +- || { +- # The rename failed, perhaps because mv can't rename something else +- # to itself, or perhaps because mv is so ancient that it does not +- # support -f. +- +- # Now remove or move aside any old file at destination location. +- # We try this two ways since rm can't unlink itself on some +- # systems and the destination file might be busy for other +- # reasons. In this case, the final cleanup might fail but the new +- # file should still install successfully. +- { +- if test -f "$dstdir/$dstfile"; then +- $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ +- || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ +- || { +- echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 +- (exit 1); exit 1 +- } +- else +- : +- fi +- } && +- +- # Now rename the file to the real destination. +- $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" +- } +- } +- fi || { (exit 1); exit 1; } +-done ++ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && ++ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && ++ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && ++ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && ++ ++ # If -C, don't bother to copy if it wouldn't change the file. ++ if $copy_on_change && ++ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && ++ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && ++ ++ eval "$initialize_posix_glob" && ++ $posix_glob set -f && ++ set X $old && old=:$2:$4:$5:$6 && ++ set X $new && new=:$2:$4:$5:$6 && ++ $posix_glob set +f && ++ ++ test "$old" = "$new" && ++ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 ++ then ++ rm -f "$dsttmp" ++ else ++ # Rename the file to the real destination. ++ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + +-# The final little trick to "correctly" pass the exit status to the exit trap. +-{ +- (exit 0); exit 0 +-} ++ # The rename failed, perhaps because mv can't rename something else ++ # to itself, or perhaps because mv is so ancient that it does not ++ # support -f. ++ { ++ # Now remove or move aside any old file at destination location. ++ # We try this two ways since rm can't unlink itself on some ++ # systems and the destination file might be busy for other ++ # reasons. In this case, the final cleanup might fail but the new ++ # file should still install successfully. ++ { ++ test ! -f "$dst" || ++ $doit $rmcmd -f "$dst" 2>/dev/null || ++ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && ++ { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } ++ } || ++ { echo "$0: cannot unlink or rename $dst" >&2 ++ (exit 1); exit 1 ++ } ++ } && ++ ++ # Now rename the file to the real destination. ++ $doit $mvcmd "$dsttmp" "$dst" ++ } ++ fi || exit 1 ++ ++ trap '' 0 ++ fi ++done + + # Local variables: + # eval: (add-hook 'write-file-hooks 'time-stamp) + # time-stamp-start: "scriptversion=" + # time-stamp-format: "%:y-%02m-%02d.%02H" +-# time-stamp-end: "$" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" + # End: +diff -Nur exmap-console-0.4.1.orig/missing exmap-console-0.4.1/missing +--- exmap-console-0.4.1.orig/missing 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/missing 2011-01-11 12:44:16.000000000 +0100 +@@ -1,10 +1,10 @@ + #! /bin/sh + # Common stub for a few missing GNU programs while installing. + +-scriptversion=2005-06-08.21 ++scriptversion=2009-04-28.21; # UTC + +-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005 +-# Free Software Foundation, Inc. ++# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, ++# 2008, 2009 Free Software Foundation, Inc. + # Originally by Fran,cois Pinard , 1996. + + # This program is free software; you can redistribute it and/or modify +@@ -18,9 +18,7 @@ + # 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 Street, Fifth Floor, Boston, MA +-# 02110-1301, USA. ++# along with this program. If not, see . + + # As a special exception to the GNU General Public License, if you + # distribute this file as part of a program that contains a +@@ -33,6 +31,8 @@ + fi + + run=: ++sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' ++sed_minuso='s/.* -o \([^ ]*\).*/\1/p' + + # In the cases where this matters, `missing' is being run in the + # srcdir already. +@@ -44,7 +44,7 @@ + + msg="missing on your system" + +-case "$1" in ++case $1 in + --run) + # Try to run requested program, and just exit if it succeeds. + run= +@@ -77,6 +77,7 @@ + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' ++ autom4te touch the output file, or create a stub one + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c +@@ -86,6 +87,9 @@ + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + ++Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and ++\`g' are ignored when checking the name. ++ + Send bug reports to ." + exit $? + ;; +@@ -103,15 +107,22 @@ + + esac + ++# normalize program name to check for. ++program=`echo "$1" | sed ' ++ s/^gnu-//; t ++ s/^gnu//; t ++ s/^g//; t'` ++ + # Now exit if we have it, but it failed. Also exit now if we + # don't have it and --version was passed (most likely to detect +-# the program). +-case "$1" in +- lex|yacc) ++# the program). This is about non-GNU programs, so use $1 not ++# $program. ++case $1 in ++ lex*|yacc*) + # Not GNU programs, they don't have --version. + ;; + +- tar) ++ tar*) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 +@@ -135,7 +146,7 @@ + + # If it does not exist, or fails to run (possibly an outdated version), + # try to emulate it. +-case "$1" in ++case $program in + aclocal*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if +@@ -145,7 +156,7 @@ + touch aclocal.m4 + ;; + +- autoconf) ++ autoconf*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the +@@ -154,7 +165,7 @@ + touch configure + ;; + +- autoheader) ++ autoheader*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want +@@ -164,7 +175,7 @@ + test -z "$files" && files="config.h" + touch_files= + for f in $files; do +- case "$f" in ++ case $f in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; +@@ -184,7 +195,7 @@ + while read f; do touch "$f"; done + ;; + +- autom4te) ++ autom4te*) + echo 1>&2 "\ + WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the +@@ -192,8 +203,8 @@ + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + +- file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` +- test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` ++ file=`echo "$*" | sed -n "$sed_output"` ++ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else +@@ -207,80 +218,78 @@ + fi + ;; + +- bison|yacc) ++ bison*|yacc*) + echo 1>&2 "\ + WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h +- if [ $# -ne 1 ]; then ++ if test $# -ne 1; then + eval LASTARG="\${$#}" +- case "$LASTARG" in ++ case $LASTARG in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` +- if [ -f "$SRCFILE" ]; then ++ if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` +- if [ -f "$SRCFILE" ]; then ++ if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi +- if [ ! -f y.tab.h ]; then ++ if test ! -f y.tab.h; then + echo >y.tab.h + fi +- if [ ! -f y.tab.c ]; then ++ if test ! -f y.tab.c; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + +- lex|flex) ++ lex*|flex*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c +- if [ $# -ne 1 ]; then ++ if test $# -ne 1; then + eval LASTARG="\${$#}" +- case "$LASTARG" in ++ case $LASTARG in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` +- if [ -f "$SRCFILE" ]; then ++ if test -f "$SRCFILE"; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi +- if [ ! -f lex.yy.c ]; then ++ if test ! -f lex.yy.c; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + +- help2man) ++ help2man*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + +- file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` +- if test -z "$file"; then +- file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` +- fi +- if [ -f "$file" ]; then ++ file=`echo "$*" | sed -n "$sed_output"` ++ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` ++ if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" +- exit 1 ++ exit $? + fi + ;; + +- makeinfo) ++ makeinfo*) + echo 1>&2 "\ + WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file +@@ -289,11 +298,17 @@ + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... +- file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` ++ file=`echo "$*" | sed -n "$sed_output"` ++ test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` +- file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $infile` ++ file=`sed -n ' ++ /^@setfilename/{ ++ s/.* \([^ ]*\) *$/\1/ ++ p ++ q ++ }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi +@@ -303,7 +318,7 @@ + touch $file + ;; + +- tar) ++ tar*) + shift + + # We have already tried tar in the generic part. +@@ -317,13 +332,13 @@ + fi + firstarg="$1" + if shift; then +- case "$firstarg" in ++ case $firstarg in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac +- case "$firstarg" in ++ case $firstarg in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 +@@ -356,5 +371,6 @@ + # eval: (add-hook 'write-file-hooks 'time-stamp) + # time-stamp-start: "scriptversion=" + # time-stamp-format: "%:y-%02m-%02d.%02H" +-# time-stamp-end: "$" ++# time-stamp-time-zone: "UTC" ++# time-stamp-end: "; # UTC" + # End: +diff -Nur exmap-console-0.4.1.orig/src/Makefile.in exmap-console-0.4.1/src/Makefile.in +--- exmap-console-0.4.1.orig/src/Makefile.in 2007-02-26 12:44:02.000000000 +0100 ++++ exmap-console-0.4.1/src/Makefile.in 2011-01-11 12:44:16.000000000 +0100 +@@ -1,8 +1,9 @@ +-# Makefile.in generated by automake 1.9.6 from Makefile.am. ++# Makefile.in generated by automake 1.11.1 from Makefile.am. + # @configure_input@ + + # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005 Free Software Foundation, Inc. ++# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, ++# Inc. + # This Makefile.in is free software; the Free Software Foundation + # gives unlimited permission to copy and/or distribute it, + # with or without modifications, as long as this notice is preserved. +@@ -14,15 +15,12 @@ + + @SET_MAKE@ + +-srcdir = @srcdir@ +-top_srcdir = @top_srcdir@ + VPATH = @srcdir@ + pkgdatadir = $(datadir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ + pkgincludedir = $(includedir)/@PACKAGE@ +-top_builddir = .. ++pkglibdir = $(libdir)/@PACKAGE@ ++pkglibexecdir = $(libexecdir)/@PACKAGE@ + am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-INSTALL = @INSTALL@ + install_sh_DATA = $(install_sh) -c -m 644 + install_sh_PROGRAM = $(install_sh) -c + install_sh_SCRIPT = $(install_sh) -c +@@ -43,8 +41,8 @@ + $(ACLOCAL_M4) + mkinstalldirs = $(install_sh) -d + CONFIG_CLEAN_FILES = ++CONFIG_CLEAN_VPATH_FILES = + am__installdirs = "$(DESTDIR)$(bindir)" +-binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) + PROGRAMS = $(bin_PROGRAMS) + am__exmap_SOURCES_DIST = interactive.c exmap.c exelf.c + @INTERACTIVE_TRUE@am__objects_1 = interactive.$(OBJEXT) +@@ -58,9 +56,10 @@ + am_exmapserver_OBJECTS = exmapserver.$(OBJEXT) + exmapserver_OBJECTS = $(am_exmapserver_OBJECTS) + exmapserver_DEPENDENCIES = $(am__DEPENDENCIES_1) +-DEFAULT_INCLUDES = -I. -I$(srcdir) ++DEFAULT_INCLUDES = -I.@am__isrc@ + depcomp = $(SHELL) $(top_srcdir)/depcomp + am__depfiles_maybe = depfiles ++am__mv = mv -f + COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) + CCLD = $(CC) +@@ -72,8 +71,6 @@ + CTAGS = ctags + DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + ACLOCAL = @ACLOCAL@ +-AMDEP_FALSE = @AMDEP_FALSE@ +-AMDEP_TRUE = @AMDEP_TRUE@ + AMTAR = @AMTAR@ + AUTOCONF = @AUTOCONF@ + AUTOHEADER = @AUTOHEADER@ +@@ -86,8 +83,6 @@ + CYGPATH_W = @CYGPATH_W@ + DEFS = @DEFS@ + DEPDIR = @DEPDIR@ +-DODOCS_FALSE = @DODOCS_FALSE@ +-DODOCS_TRUE = @DODOCS_TRUE@ + ECHO_C = @ECHO_C@ + ECHO_N = @ECHO_N@ + ECHO_T = @ECHO_T@ +@@ -95,38 +90,41 @@ + GCC_CFLAGS = @GCC_CFLAGS@ + GLIB_CFLAGS = @GLIB_CFLAGS@ + GLIB_LIBS = @GLIB_LIBS@ +-HAVE_HELPTOMAN_FALSE = @HAVE_HELPTOMAN_FALSE@ +-HAVE_HELPTOMAN_TRUE = @HAVE_HELPTOMAN_TRUE@ + HELPTOMAN = @HELPTOMAN@ ++INSTALL = @INSTALL@ + INSTALL_DATA = @INSTALL_DATA@ + INSTALL_PROGRAM = @INSTALL_PROGRAM@ + INSTALL_SCRIPT = @INSTALL_SCRIPT@ + INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-INTERACTIVE_FALSE = @INTERACTIVE_FALSE@ +-INTERACTIVE_TRUE = @INTERACTIVE_TRUE@ + LDFLAGS = @LDFLAGS@ + LIBOBJS = @LIBOBJS@ + LIBS = @LIBS@ + LTLIBOBJS = @LTLIBOBJS@ + MAKEINFO = @MAKEINFO@ ++MKDIR_P = @MKDIR_P@ + OBJEXT = @OBJEXT@ + PACKAGE = @PACKAGE@ + PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ + PACKAGE_NAME = @PACKAGE_NAME@ + PACKAGE_STRING = @PACKAGE_STRING@ + PACKAGE_TARNAME = @PACKAGE_TARNAME@ ++PACKAGE_URL = @PACKAGE_URL@ + PACKAGE_VERSION = @PACKAGE_VERSION@ + PATH_SEPARATOR = @PATH_SEPARATOR@ + PKG_CONFIG = @PKG_CONFIG@ ++PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ ++PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ + READLINE_CFLAGS = @READLINE_CFLAGS@ + READLINE_LIBS = @READLINE_LIBS@ + SET_MAKE = @SET_MAKE@ + SHELL = @SHELL@ + STRIP = @STRIP@ + VERSION = @VERSION@ ++abs_builddir = @abs_builddir@ ++abs_srcdir = @abs_srcdir@ ++abs_top_builddir = @abs_top_builddir@ ++abs_top_srcdir = @abs_top_srcdir@ + ac_ct_CC = @ac_ct_CC@ +-am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +-am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ + am__include = @am__include@ + am__leading_dot = @am__leading_dot@ + am__quote = @am__quote@ +@@ -134,6 +132,7 @@ + am__untar = @am__untar@ + bindir = @bindir@ + build_alias = @build_alias@ ++builddir = @builddir@ + datadir = @datadir@ + datarootdir = @datarootdir@ + docdir = @docdir@ +@@ -157,8 +156,12 @@ + psdir = @psdir@ + sbindir = @sbindir@ + sharedstatedir = @sharedstatedir@ ++srcdir = @srcdir@ + sysconfdir = @sysconfdir@ + target_alias = @target_alias@ ++top_build_prefix = @top_build_prefix@ ++top_builddir = @top_builddir@ ++top_srcdir = @top_srcdir@ + INCLUDES = -DSBINDIR=\"$(sbindir)\" + AM_CFLAGS = $(GCC_CFLAGS) $(GLIB_CFLAGS) $(READLINE_CFLAGS) + @INTERACTIVE_TRUE@interactive = interactive.c +@@ -177,14 +180,14 @@ + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ +- && exit 0; \ ++ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ ++ && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ +- cd $(top_srcdir) && \ +- $(AUTOMAKE) --gnu src/Makefile ++ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ ++ $(am__cd) $(top_srcdir) && \ ++ $(AUTOMAKE) --gnu src/Makefile + .PRECIOUS: Makefile + Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ +@@ -202,38 +205,53 @@ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + $(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ++$(am__aclocal_m4_deps): + install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) +- test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" +- @list='$(bin_PROGRAMS)'; for p in $$list; do \ +- p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ +- if test -f $$p \ +- ; then \ +- f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ +- echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ +- $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ +- else :; fi; \ +- done ++ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" ++ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ ++ for p in $$list; do echo "$$p $$p"; done | \ ++ sed 's/$(EXEEXT)$$//' | \ ++ while read p p1; do if test -f $$p; \ ++ then echo "$$p"; echo "$$p"; else :; fi; \ ++ done | \ ++ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ ++ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ ++ sed 'N;N;N;s,\n, ,g' | \ ++ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ ++ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ ++ if ($$2 == $$4) files[d] = files[d] " " $$1; \ ++ else { print "f", $$3 "/" $$4, $$1; } } \ ++ END { for (d in files) print "f", d, files[d] }' | \ ++ while read type dir files; do \ ++ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ ++ test -z "$$files" || { \ ++ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ ++ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ ++ } \ ++ ; done + + uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) +- @list='$(bin_PROGRAMS)'; for p in $$list; do \ +- f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ +- echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ +- rm -f "$(DESTDIR)$(bindir)/$$f"; \ +- done ++ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ ++ files=`for p in $$list; do echo "$$p"; done | \ ++ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ ++ -e 's/$$/$(EXEEXT)/' `; \ ++ test -n "$$list" || exit 0; \ ++ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ ++ cd "$(DESTDIR)$(bindir)" && rm -f $$files + + clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + exmap$(EXEEXT): $(exmap_OBJECTS) $(exmap_DEPENDENCIES) + @rm -f exmap$(EXEEXT) +- $(LINK) $(exmap_LDFLAGS) $(exmap_OBJECTS) $(exmap_LDADD) $(LIBS) ++ $(LINK) $(exmap_OBJECTS) $(exmap_LDADD) $(LIBS) + exmapd$(EXEEXT): $(exmapd_OBJECTS) $(exmapd_DEPENDENCIES) + @rm -f exmapd$(EXEEXT) +- $(LINK) $(exmapd_LDFLAGS) $(exmapd_OBJECTS) $(exmapd_LDADD) $(LIBS) ++ $(LINK) $(exmapd_OBJECTS) $(exmapd_LDADD) $(LIBS) + exmapserver$(EXEEXT): $(exmapserver_OBJECTS) $(exmapserver_DEPENDENCIES) + @rm -f exmapserver$(EXEEXT) +- $(LINK) $(exmapserver_LDFLAGS) $(exmapserver_OBJECTS) $(exmapserver_LDADD) $(LIBS) ++ $(LINK) $(exmapserver_OBJECTS) $(exmapserver_LDADD) $(LIBS) + + mostlyclean-compile: + -rm -f *.$(OBJEXT) +@@ -248,92 +266,98 @@ + @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/interactive.Po@am__quote@ + + .c.o: +-@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +-@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi ++@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< ++@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(COMPILE) -c $< + + .c.obj: +-@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +-@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi ++@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` ++@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po + @AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ + @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + @am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +-uninstall-info-am: + + ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique + tags: TAGS + + TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) +- tags=; \ ++ set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ +- if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ ++ shift; \ ++ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ +- $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ +- $$tags $$unique; \ ++ if test $$# -gt 0; then \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ "$$@" $$unique; \ ++ else \ ++ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ ++ $$unique; \ ++ fi; \ + fi + ctags: CTAGS + CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) +- tags=; \ +- here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ +- $(AWK) ' { files[$$0] = 1; } \ +- END { for (i in files) print i; }'`; \ +- test -z "$(CTAGS_ARGS)$$tags$$unique" \ ++ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ ++ END { if (nonempty) { for (i in files) print i; }; }'`; \ ++ test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ +- $$tags $$unique ++ $$unique + + GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ +- && cd $(top_srcdir) \ +- && gtags -i $(GTAGS_ARGS) $$here ++ && $(am__cd) $(top_srcdir) \ ++ && gtags -i $(GTAGS_ARGS) "$$here" + + distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + + distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ +- list='$(DISTFILES)'; for file in $$list; do \ +- case $$file in \ +- $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ +- $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ +- esac; \ ++ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ ++ list='$(DISTFILES)'; \ ++ dist_files=`for file in $$list; do echo $$file; done | \ ++ sed -e "s|^$$srcdirstrip/||;t" \ ++ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ ++ case $$dist_files in \ ++ */*) $(MKDIR_P) `echo "$$dist_files" | \ ++ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ ++ sort -u` ;; \ ++ esac; \ ++ for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test "$$dir" != "$$file" && test "$$dir" != "."; then \ +- dir="/$$dir"; \ +- $(mkdir_p) "$(distdir)$$dir"; \ +- else \ +- dir=''; \ +- fi; \ + if test -d $$d/$$file; then \ ++ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ ++ if test -d "$(distdir)/$$file"; then \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ ++ fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ ++ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ +- cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ ++ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ +- test -f $(distdir)/$$file \ +- || cp -p $$d/$$file $(distdir)/$$file \ ++ test -f "$(distdir)/$$file" \ ++ || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +@@ -342,7 +366,7 @@ + all-am: Makefile $(PROGRAMS) + installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ +- test -z "$$dir" || $(mkdir_p) "$$dir"; \ ++ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done + install: install-am + install-exec: install-exec-am +@@ -364,6 +388,7 @@ + + distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) ++ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + + maintainer-clean-generic: +@@ -385,18 +410,38 @@ + + html: html-am + ++html-am: ++ + info: info-am + + info-am: + + install-data-am: + ++install-dvi: install-dvi-am ++ ++install-dvi-am: ++ + install-exec-am: install-binPROGRAMS + ++install-html: install-html-am ++ ++install-html-am: ++ + install-info: install-info-am + ++install-info-am: ++ + install-man: + ++install-pdf: install-pdf-am ++ ++install-pdf-am: ++ ++install-ps: install-ps-am ++ ++install-ps-am: ++ + installcheck-am: + + maintainer-clean: maintainer-clean-am +@@ -416,18 +461,23 @@ + + ps-am: + +-uninstall-am: uninstall-binPROGRAMS uninstall-info-am ++uninstall-am: uninstall-binPROGRAMS ++ ++.MAKE: install-am install-strip + + .PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ +- install-data install-data-am install-exec install-exec-am \ +- install-info install-info-am install-man install-strip \ ++ install-data install-data-am install-dvi install-dvi-am \ ++ install-exec install-exec-am install-html install-html-am \ ++ install-info install-info-am install-man install-pdf \ ++ install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \ +- uninstall-am uninstall-binPROGRAMS uninstall-info-am ++ uninstall-am uninstall-binPROGRAMS ++ + + # Tell versions [3.59,3.63) of GNU make to not export all variables. + # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/package/exmap/patches/patch-Makefile_in b/package/exmap/patches/patch-Makefile_in deleted file mode 100644 index cb3281aff..000000000 --- a/package/exmap/patches/patch-Makefile_in +++ /dev/null @@ -1,12 +0,0 @@ -$Id: update-patches 24 2008-08-31 14:56:13Z wbx $ ---- exmap-console-0.4.1.orig/Makefile.in 2007-02-26 12:44:02.000000000 +0100 -+++ exmap-console-0.4.1/Makefile.in 2009-04-18 16:16:03.926793719 +0200 -@@ -155,7 +155,7 @@ sbindir = @sbindir@ - sharedstatedir = @sharedstatedir@ - sysconfdir = @sysconfdir@ - target_alias = @target_alias@ --SUBDIRS = kernel src doc -+SUBDIRS = src - DISTCLEANFILES = *~ Makefile.in install-sh missing depcomp *.m4 config.log config.status Makefile - all: all-recursive - diff --git a/package/gpm/patches/patch-src_Makefile_in b/package/gpm/patches/patch-src_Makefile_in new file mode 100644 index 000000000..3957260ae --- /dev/null +++ b/package/gpm/patches/patch-src_Makefile_in @@ -0,0 +1,51 @@ +--- gpm-1.20.6.orig/src/Makefile.in 2009-02-09 10:58:53.000000000 +0100 ++++ gpm-1.20.6/src/Makefile.in 2011-01-11 16:48:43.000000000 +0100 +@@ -59,19 +59,19 @@ STRIP = -s + + # the prog rules are not very clean... + prog/%.o: prog/%.c +- $(CC) -Iheaders @CPPFLAGS@ $(CPPFLAGS) @CFLAGS@ $(CFLAGS) -c -o $@ $< ++ $(CC) -Iheaders @CPPFLAGS@ @CFLAGS@ -c -o $@ $< + + prog/%: prog/%.o +- $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $^ @LIBS@ $(LIBS) ++ $(CC) @LDFLAGS@ -o $@ $^ @LIBS@ + + %.o: %.c +- $(CC) @CPPFLAGS@ $(CPPFLAGS) @CFLAGS@ $(CFLAGS) -c -o $@ $< ++ $(CC) @CPPFLAGS@ @CFLAGS@ -c -o $@ $< + + %.lo: %.c +- $(CC) @CPPFLAGS@ $(CPPFLAGS) @PICFLAGS@ @CFLAGS@ $(CFLAGS) -c -o $@ $< ++ $(CC) @CPPFLAGS@ @PICFLAGS@ @CFLAGS@ -c -o $@ $< + + %: %.o +- $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $^ @LIBS@ $(LIBS) ++ $(CC) @LDFLAGS@ -o $@ $^ @LIBS@ + + # old, unused, but good rule [dependings] + #%.d: $(srcdir)/%.c +@@ -82,7 +82,7 @@ prog/%: prog/%.o + all: gpm lib/libgpm.so.@abi_lev@ lib/libgpm.a $(PROG) + + gpm: $(GOBJ) +- $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $(GOBJ) @LIBS@ $(LIBS) -lm ++ $(CC) @LDFLAGS@ -o $@ $(GOBJ) @LIBS@ -lm + + # construct dependings of sourcefiles and link sourcefiles + $(DEPFILE) dep: prog/gpm-root.c +@@ -148,11 +148,11 @@ prog/gpm-root.c: $(srcdir)/prog/gpm-root + + # gpm-root needs an own rule, because gpm-root.c is not in $(srcdir) + prog/gpm-root: prog/gpm-root.c lib/libgpm.so.@abi_lev@ +- $(CC) -I. @CPPFLAGS@ $(CPPFLAGS) @CFLAGS@ $(CFLAGS) -c -o $@.o $< +- $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $@.o @LIBS@ $(LIBS) lib/libgpm.so.@abi_lev@ ++ $(CC) -I. @CPPFLAGS@ @CFLAGS@ -c -o $@.o $< ++ $(CC) @LDFLAGS@ -o $@ $@.o @LIBS@ lib/libgpm.so.@abi_lev@ + + prog/mouse-test: prog/mouse-test.o mice.o twiddler.o synaptics.o prog/open_console.o +- $(CC) @LDFLAGS@ $(LDFLAGS) -o $@ $^ @LIBS@ $(LIBS) -lm ++ $(CC) @LDFLAGS@ -o $@ $^ @LIBS@ -lm + + $(PROG): lib/libgpm.so.@abi_lev@ + diff --git a/package/ipcad/patches/patch-Makefile_in b/package/ipcad/patches/patch-Makefile_in new file mode 100644 index 000000000..40bcdf546 --- /dev/null +++ b/package/ipcad/patches/patch-Makefile_in @@ -0,0 +1,17 @@ +--- ipcad-3.7.3.orig/Makefile.in 2007-04-22 10:08:45.000000000 +0200 ++++ ipcad-3.7.3/Makefile.in 2011-01-11 17:16:15.000000000 +0100 +@@ -6,10 +6,10 @@ sysconfdir= @sysconfdir@ + datadir= @datadir@ + + CC= @CC@ +-LDFLAGS+= @LDFLAGS@ +-LIBS+= @LIBS@ +-CFLAGS+= @CFLAGS@ @DEFS@ -W -Wall +-CPPFLAGS+=-DIPCAD_VERSION=\"@IPCAD_VERSION@\" ++LDFLAGS= @LDFLAGS@ ++LIBS= @LIBS@ ++CFLAGS= @CFLAGS@ @DEFS@ -W -Wall ++CPPFLAGS=-DIPCAD_VERSION=\"@IPCAD_VERSION@\" + CPPFLAGS+=-DCONFIG_FILE=\"${sysconfdir}/ipcad.conf\" + CPPFLAGS+=@DEFS@ -D_REENTRANT -D_THREAD_SAFE + CPPFLAGS+=-DPSRC_@PSRC@ -DIFST_@IFST@ diff --git a/package/libxslt/patches/patch-libexslt_Makefile_in b/package/libxslt/patches/patch-libexslt_Makefile_in new file mode 100644 index 000000000..53165a669 --- /dev/null +++ b/package/libxslt/patches/patch-libexslt_Makefile_in @@ -0,0 +1,11 @@ +--- libxslt-1.1.24.orig/libexslt/Makefile.in 2008-05-13 17:52:13.000000000 +0200 ++++ libxslt-1.1.24/libexslt/Makefile.in 2011-01-11 17:04:29.000000000 +0100 +@@ -247,7 +247,7 @@ top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libxslt -I$(top_srcdir)/libexslt \ + -I$(top_builddir) -I$(top_builddir)/libxslt \ +- -I$(top_builddir)/libexslt $(LIBXML_CFLAGS) $(CFLAGS) ++ -I$(top_builddir)/libexslt $(LIBXML_CFLAGS) + + AM_CFLAGS = $(LIBGCRYPT_CFLAGS) + lib_LTLIBRARIES = libexslt.la diff --git a/package/libxslt/patches/patch-xsltproc_Makefile_in b/package/libxslt/patches/patch-xsltproc_Makefile_in new file mode 100644 index 000000000..3802ce0fa --- /dev/null +++ b/package/libxslt/patches/patch-xsltproc_Makefile_in @@ -0,0 +1,11 @@ +--- libxslt-1.1.24.orig/xsltproc/Makefile.in 2008-05-13 17:52:15.000000000 +0200 ++++ libxslt-1.1.24/xsltproc/Makefile.in 2011-01-11 17:06:56.000000000 +0100 +@@ -234,7 +234,7 @@ top_builddir = @top_builddir@ + top_srcdir = @top_srcdir@ + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/libxslt -I$(top_srcdir)/libexslt \ + -I$(top_builddir) -I$(top_builddir)/libxslt \ +- -I$(top_builddir)/libexslt $(LIBXML_CFLAGS) $(CFLAGS) ++ -I$(top_builddir)/libexslt $(LIBXML_CFLAGS) + + AM_CFLAGS = $(LIBGCRYPT_CFLAGS) + xsltproc_SOURCES = xsltproc.c diff --git a/package/lvm/Makefile b/package/lvm/Makefile index b45a262e1..67ae4d6cc 100644 --- a/package/lvm/Makefile +++ b/package/lvm/Makefile @@ -4,9 +4,9 @@ include ${TOPDIR}/rules.mk PKG_NAME:= lvm -PKG_VERSION:= 2.02.79 +PKG_VERSION:= 2.02.80 PKG_RELEASE:= 1 -PKG_MD5SUM:= e6929251a90b0b626c024942a242f337 +PKG_MD5SUM:= 816f4cee75674e3a21fd40f1961279f2 PKG_DESCR:= logical volume management PKG_SECTION:= fs PKG_DEPENDS:= libdevmapper libncurses @@ -31,15 +31,16 @@ CONFIGURE_ARGS+= --with-user="" --with-group="" \ --with-optimisation="" \ --with-lvm1=none -post-install: - # lvm +lvm-install: ${INSTALL_DIR} ${IDIR_LVM}/usr/sbin ${INSTALL_BIN} ${WRKINST}/usr/sbin/lvm ${IDIR_LVM}/usr/sbin/lvm - # libdevmapper + +libdevmapper-install: ${INSTALL_DIR} ${IDIR_LIBDEVMAPPER}/usr/lib ${CP} ${WRKINST}/usr/lib/libdevmapper.so* \ ${IDIR_LIBDEVMAPPER}/usr/lib - # device-mapper + +device-mapper-install: ${INSTALL_DIR} ${IDIR_DEVICE_MAPPER}/usr/sbin ${INSTALL_BIN} ${WRKINST}/usr/sbin/dmsetup \ ${IDIR_DEVICE_MAPPER}/usr/sbin diff --git a/package/lvm/patches/patch-make_tmpl_in b/package/lvm/patches/patch-make_tmpl_in index 43bd07008..f5286dfa4 100644 --- a/package/lvm/patches/patch-make_tmpl_in +++ b/package/lvm/patches/patch-make_tmpl_in @@ -1,5 +1,14 @@ ---- LVM2.2.02.79.orig/make.tmpl.in 2010-11-05 17:18:38.000000000 +0100 -+++ LVM2.2.02.79/make.tmpl.in 2010-12-26 22:15:11.000000000 +0100 +--- LVM2.2.02.80.orig/make.tmpl.in 2011-01-10 14:00:53.000000000 +0100 ++++ LVM2.2.02.80/make.tmpl.in 2011-01-11 11:39:53.000000000 +0100 +@@ -33,7 +33,7 @@ LIBS = @LIBS@ + # Extra libraries always linked with static binaries + STATIC_LIBS = $(SELINUX_LIBS) $(UDEV_LIBS) + DEFS += @DEFS@ +-CFLAGS += @CFLAGS@ ++CFLAGS = @CFLAGS@ + CLDFLAGS += @CLDFLAGS@ + LDDEPS += @LDDEPS@ + LDFLAGS += @LDFLAGS@ @@ -322,7 +322,8 @@ $(LIB_SHARED): $(LIB_SHARED).$(LIB_VERSI $(LN_S) -f $( Date: Mon, 10 Jan 2011 22:14:50 +0100 Subject: ifupdown: allow setting the bridge forward delay Signed-off-by: Phil Sutter --- package/base-files/src/etc/network/if-pre-up.d/03-bridge | 1 + 1 file changed, 1 insertion(+) diff --git a/package/base-files/src/etc/network/if-pre-up.d/03-bridge b/package/base-files/src/etc/network/if-pre-up.d/03-bridge index 41ddb2b58..9bdbf8302 100755 --- a/package/base-files/src/etc/network/if-pre-up.d/03-bridge +++ b/package/base-files/src/etc/network/if-pre-up.d/03-bridge @@ -15,6 +15,7 @@ case "$IF_BRIDGE_PORTS" in esac brctl addbr $IFACE || exit 1 +[[ -n $IF_BRIDGE_FD ]] && brctl setfd $IFACE $IF_BRIDGE_FD for IF in $INTERFACES; do if ! grep -q $IF /proc/net/dev; then -- cgit v1.2.3 From 3b5884bfdfc56e66d9ac6028272b805451a62113 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 10 Jan 2011 22:14:51 +0100 Subject: .template: add a hint about master sites Signed-off-by: Phil Sutter --- package/.template/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/.template/Makefile b/package/.template/Makefile index e5593b3fb..7175f4a48 100644 --- a/package/.template/Makefile +++ b/package/.template/Makefile @@ -14,6 +14,8 @@ PKG_DEPENDS:= add dependendant package names PKG_BUILDDEP:= add packages which need to be compiled PKG_URL:= add project url PKG_SITES:= add download url without package name +# alternatively use a pre-defined set of mirrors, see mk/mirrors.mk: +#PKG_SITES:= ${MASTER_SITE_SOURCEFORGE:=sub/path/} # if more than one binary package is created, add symbol names of all here # leave variable empty, when no binary package is created. You can set it -- cgit v1.2.3 From 846440bfa59b9a04a68247720edc6d81dcabfec2 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 10 Jan 2011 22:14:52 +0100 Subject: pkgmaker: improve error message on missing package category This patch bores up fatal_error(), so that it now takes arguments in printf-style, but without changing the basic semantics (i.e., messages prefixed by "Fatal error. " and trailing newline automatically appended). Signed-off-by: Phil Sutter --- tools/adk/pkgmaker.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tools/adk/pkgmaker.c b/tools/adk/pkgmaker.c index e2b6e6e99..dd4b90f46 100644 --- a/tools/adk/pkgmaker.c +++ b/tools/adk/pkgmaker.c @@ -37,10 +37,11 @@ static int nobinpkgs; -static void fatal_error(const char *message) { - - fprintf(stderr, "Fatal error. %s\n", message); - exit(1); +#define fatal_error(...) { \ + fprintf(stderr, "Fatal error. "); \ + fprintf(stderr, __VA_ARGS__); \ + fprintf(stderr, "\n"); \ + exit(1); \ } static int parse_var_hash(char *buf, const char *varname, StrMap *strmap) { @@ -304,7 +305,8 @@ int main() { fclose(section); } } else - fatal_error("Can not find section description for package."); + fatal_error("Can not find section description for package %s.", + pkgdirp->d_name); fclose(pkg); continue; @@ -501,7 +503,7 @@ int main() { fclose(section); } } else - fatal_error("Can not find section description for package"); + fatal_error("Can not find section description for package %s.", pkgdirp->d_name); unlink(path); cfg = fopen(path, "w"); -- cgit v1.2.3 From edc22d3a25c5c963a5c03277d141599cd19a046f Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Mon, 10 Jan 2011 22:14:53 +0100 Subject: grep: need pcre, no parallel building possible Signed-off-by: Phil Sutter --- package/grep/Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package/grep/Makefile b/package/grep/Makefile index d608bd8be..d9fc4d56c 100644 --- a/package/grep/Makefile +++ b/package/grep/Makefile @@ -9,8 +9,10 @@ PKG_RELEASE:= 2 PKG_MD5SUM:= 92258031d98d4f12dfc6a6d24057e672 PKG_DESCR:= global search for a regular expression and print out matched lines PKG_SECTION:= utils +PKG_BUILDDEP:= pcre PKG_URL:= http://www.gnu.org/software/grep/ PKG_SITES:= ftp://ftp.gnu.org/gnu/grep/ +PKG_NOPARALLEL:= 1 include $(TOPDIR)/mk/package.mk -- cgit v1.2.3