From 2dac355c4670f66a976cbf765cc2af057e3c3a65 Mon Sep 17 00:00:00 2001 From: Manuel Date: Mon, 20 May 2013 17:35:28 +0000 Subject: [PATCH] New kernel --- acpi-call/PKGBUILD | 56 - acpi-call/acpi_call.install | 23 - aufs3-util/PKGBUILD | 8 +- linux-lts/PKGBUILD | 8 +- linux-lts/linux-lts.install | 2 +- linux/PKGBUILD | 27 +- linux/aufs3-base.patch | 18 +- linux/aufs3-kbuild.patch | 14 +- linux/aufs3-loopback.patch | 48 +- linux/aufs3-proc_map.patch | 4 +- linux/aufs3-standalone.patch | 40 +- linux/aufs3.patch | 11511 +++++++++-------- linux/config.x86_64 | 2 +- linux/linux.install | 4 +- ndiswrapper/PKGBUILD | 14 +- ndiswrapper/ndiswrapper-1.58-add_taint.patch | 24 + 16 files changed, 6151 insertions(+), 5652 deletions(-) delete mode 100644 acpi-call/PKGBUILD delete mode 100755 acpi-call/acpi_call.install create mode 100644 ndiswrapper/ndiswrapper-1.58-add_taint.patch diff --git a/acpi-call/PKGBUILD b/acpi-call/PKGBUILD deleted file mode 100644 index 797a2c36f..000000000 --- a/acpi-call/PKGBUILD +++ /dev/null @@ -1,56 +0,0 @@ -# -# Core Packages for Chakra, part of chakra-project.org - -_kver="3.9.0-1-CHAKRA" - -pkgname=acpi_call -_pkgname=acpi_call -pkgver=1.0.0 -pkgrel=6 -pkgdesc="A kernel module that enables you to call parameterless ACPI methods by writing the method name to /proc/acpi/call, e.g. to turn off discrete graphics card in a dual graphics environment (like NVIDIA Optimus)." -arch=('x86_64') -url=("http://github.com/mkottman/acpi_call") -license=('GPL') -depends=('linux>=3.9' 'linux<3.10' 'linux-headers') -makedepends=('git') -provides=('acpi-call') -replaces=('acpi-call') -conflicts=('acpi-call') -install=acpi_call.install -source=("https://github.com/mkottman/acpi_call/archive/v${pkgver}.tar.gz") -md5sums=('3c71239792ddd725d13f7c8d168b4ecf') - -build() { - cd ${srcdir}/$_pkgname-${pkgver} - - make -} -package() { - cd ${srcdir}/$_pkgname-${pkgver} - install -d ${pkgdir}/usr/share/$_pkgname - install -d ${pkgdir}/usr/bin - install -Dm755 ${srcdir}/$_pkgname-${pkgver}/examples/asus1215n.sh \ - ${pkgdir}/usr/share/$_pkgname - install -Dm755 ${srcdir}/$_pkgname-${pkgver}/examples/m11xr2.sh \ - ${pkgdir}/usr/share/$_pkgname - install -Dm755 ${srcdir}/$_pkgname-${pkgver}/examples/dellL702X.sh \ - ${pkgdir}/usr/share/$_pkgname - install -Dm755 ${srcdir}/$_pkgname-${pkgver}/examples/turn_off_gpu.sh \ - ${pkgdir}/usr/share/$_pkgname - ln -s /usr/share/$_pkgname/turn_off_gpu.sh \ - ${pkgdir}/usr/bin/turn_off_gpu.sh - install -Dm755 ${srcdir}/$_pkgname-${pkgver}/support/query_dsdt.pl \ - ${pkgdir}/usr/share/$_pkgname-${pkgver} - cp -R support/windump_hack \ - ${pkgdir}/usr/share/$_pkgname/ - install -Dm644 README.md \ - ${pkgdir}/usr/share/$_pkgname/README.md - - msg2 "Building module for $_kver..." - - # KDIR is necessary even when cleaning - #make KDIR=/usr/src/linux-${_kver} clean - make KDIR=/usr/src/linux-${_kver} - - install -D -m644 acpi_call.ko $pkgdir/lib/modules/${_kver}/kernel/drivers/acpi/acpi_call.ko -} diff --git a/acpi-call/acpi_call.install b/acpi-call/acpi_call.install deleted file mode 100755 index 596ff3d18..000000000 --- a/acpi-call/acpi_call.install +++ /dev/null @@ -1,23 +0,0 @@ -post_install() { - depmod -a - echo "" - echo "[+] modprobe acpi_call" - echo ">>> run /usr/bin/turn_off_gpu.sh to test/view different acpi_call functions to see if it disables your secondary/hybrid video card" - echo ">>> There are a few scripts to use in /usr/share/acpi_call/ - some for specific laptops => asus1215n.sh, dellL702X.sh, m11xr2.sh" -} - -post_upgrade() { - depmod -a - echo "" - echo "[+] modprobe acpi_call" - echo ">>> run /usr/bin/turn_off_gpu.sh to test/view different acpi_call functions to see if it disables your secondary/hybrid video card" - echo ">>> There are a few scripts to use in /usr/share/acpi_call/ - some for specific laptops => asus1215n.sh, dellL702X.sh, m11xr2.sh" -} - -post_remove() { - depmod -a -} - -op=$1 -shift -$op $* diff --git a/aufs3-util/PKGBUILD b/aufs3-util/PKGBUILD index 14fc6b7a9..7277e443b 100644 --- a/aufs3-util/PKGBUILD +++ b/aufs3-util/PKGBUILD @@ -1,13 +1,11 @@ # # Chakra Packages for Chakra, part of chakra-project.org -# -# maintainer abveritas@chakra-project.org pkgname=aufs3-util pkgver=20120607 pkgrel=2 pkgdesc="Another Unionfs Implementation that supports NFS branches" -arch=('i686' 'x86_64') +arch=('x86_64') url="http://aufs.sourceforge.net/" license=('GPL2') depends=('glibc') @@ -19,8 +17,8 @@ md5sums=('a1012045b45ec13ccc1be231d5e76464') build() { cd $srcdir/aufs3-util-$pkgver - export CPPFLAGS="-I /usr/src/linux-3.4.2-1-CHAKRA/include/" - make + export CPPFLAGS="-I /usr/src/linux-3.9.0-1-CHAKRA/include/" + make } package() { diff --git a/linux-lts/PKGBUILD b/linux-lts/PKGBUILD index a03a55420..096c420b5 100644 --- a/linux-lts/PKGBUILD +++ b/linux-lts/PKGBUILD @@ -1,14 +1,12 @@ # # Core Packages for Chakra, part of chakra-project.org -# -# maintainer abveritas@chakra-project.org pkgbase="linux-lts" pkgname=('linux-lts' 'linux-lts-headers' 'linux-lts-docs') # Build stock -CHAKRA-LTS kernel # pkgname=linux-custom # Build kernel with a different name _kernelname=${pkgname#linux} _basekernel=3.0 -pkgver=3.0.62 +pkgver=3.0.78 pkgrel=1 makedepends=('xmlto' 'docbook-xsl') arch=('x86_64') @@ -35,7 +33,7 @@ source=("ftp://ftp.kernel.org/pub/linux/kernel/v3.0/linux-$_basekernel.tar.bz2" 'module-symbol-waiting-3.0.patch' 'module-init-wait-3.0.patch') md5sums=('398e95866794def22b12dfbc15ce89c0' - 'b0c86830c65280b02223fd8fff8aec08' + 'e82abb9211fdf67d11dff1e9498bb26c' '2d787099d3a44531bcb3ab39aee5539e' '4925d736212481eb1f7b318ef9e8b95f' '821e1112493f187dc70df9412e61f324' @@ -311,4 +309,4 @@ package_linux-lts-docs() { # remove a file already in linux package rm -f "${pkgdir}/usr/src/linux-${_kernver}/Documentation/DocBook/Makefile" -} \ No newline at end of file +} diff --git a/linux-lts/linux-lts.install b/linux-lts/linux-lts.install index 1f37bb605..bcdbfbeea 100644 --- a/linux-lts/linux-lts.install +++ b/linux-lts/linux-lts.install @@ -2,7 +2,7 @@ # arg 2: the old package version KERNEL_NAME=-lts -KERNEL_VERSION=3.0.62-1-lts +KERNEL_VERSION=3.0.78-1-lts post_install () { # updating module dependencies diff --git a/linux/PKGBUILD b/linux/PKGBUILD index 97e9bb540..8c5f40d79 100644 --- a/linux/PKGBUILD +++ b/linux/PKGBUILD @@ -7,7 +7,7 @@ pkgbase=linux pkgname=('linux' 'linux-headers' 'linux-docs') _kernelname=${pkgname#linux} _basekernel=3.9 -pkgver=3.9.0 +pkgver=3.9.2 pkgrel=1 arch=('x86_64') url="http://www.kernel.org/" @@ -15,7 +15,7 @@ license=('GPL2') makedepends=('xmlto' 'docbook-xsl') options=('!strip') source=("http://www.kernel.org/pub/linux/kernel/v3.x/linux-${_basekernel}.tar.xz" - #"http://www.kernel.org/pub/linux/kernel/v3.x/patch-${pkgver}.xz" + "http://www.kernel.org/pub/linux/kernel/v3.x/patch-${pkgver}.xz" # the main kernel config files 'config.x86_64' #aufs patches @@ -26,21 +26,19 @@ source=("http://www.kernel.org/pub/linux/kernel/v3.x/linux-${_basekernel}.tar.xz 'aufs3-kbuild.patch' 'aufs3.patch' 'config.aufs' - # Fixes - alsa-firmware-loading-3.8.8.patch # standard config files for mkinitcpio ramdisk 'linux.preset' 'change-default-console-loglevel.patch') md5sums=('4348c9b6b2eb3144d601e87c19d5d909' - 'b052c16a6f0b1861233166b9d52033d2' - '52690fe8baa6d6abd858f04c4531d437' - '94a873436f6c62580ac54c1b9f0e021a' - 'a2678afaf5205f24c58affd4b1570503' - '7693e0258036ab1c6e6a42d7ee573666' - 'f9d33aeebb5c637eeefc222643632035' - '5e73a0f39d73095bdff6a9168ecc05cd' + 'adeb2556568f79e827e7a0ce4c483605' + '698318024a36e40b4d44eac67c3a7ecd' + '89fbb605ef398297b8dc43409c849c92' + 'd5f57f666f02ca25ba298210e2332ce8' + 'cf2ad9065886e56698524eab323ac656' + '460247f1b4da5dccdaacfa6d161131f0' + '498abc1e34df8a7d19e43980c74f2bc2' + '3ce07d164880e16ae622d1dadb6e5b7a' '52f4a2c7f6277774117c834d949d6b81' - 'e2ac681ffa439e969b4c3b4616852454' 'eb14dcfd80c00852ef81ded6e826826a' '65cbe8e4c8efaf96dd162102e46ce81d') @@ -48,7 +46,7 @@ build() { cd "${srcdir}/linux-${_basekernel}" # add upstream patch - # patch -p1 -i "${srcdir}/patch-${pkgver}" + patch -p1 -i "${srcdir}/patch-${pkgver}" # add latest fixes from stable queue, if needed # http://git.kernel.org/?p=linux/kernel/git/stable/stable-queue.git @@ -58,9 +56,6 @@ build() { # (relevant patch sent upstream: https://lkml.org/lkml/2011/7/26/227) patch -p1 -i "${srcdir}/change-default-console-loglevel.patch" - # Fix for alsa - patch -Np1 -i "${srcdir}/alsa-firmware-loading-3.8.8.patch" - #aufs patches for Live: patch -p1 -i "${srcdir}/aufs3.patch" patch -p1 -i "${srcdir}/aufs3-base.patch" diff --git a/linux/aufs3-base.patch b/linux/aufs3-base.patch index 854c2feec..91d549c09 100644 --- a/linux/aufs3-base.patch +++ b/linux/aufs3-base.patch @@ -1,7 +1,7 @@ -aufs3.x-rcN base patch +aufs3.9 base patch diff --git a/fs/file_table.c b/fs/file_table.c -index de9e965..e73287a 100644 +index cd4d87a..ca5948f 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -36,7 +36,7 @@ struct files_stat_struct files_stat = { @@ -14,10 +14,10 @@ index de9e965..e73287a 100644 /* SLAB cache for file structures */ static struct kmem_cache *filp_cachep __read_mostly; diff --git a/fs/inode.c b/fs/inode.c -index 14084b7..ece87ed 100644 +index a898b3d..e83cd1e 100644 --- a/fs/inode.c +++ b/fs/inode.c -@@ -1503,7 +1503,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, +@@ -1498,7 +1498,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode, * This does the actual work of updating an inodes time or version. Must have * had called mnt_want_write() before calling this. */ @@ -27,10 +27,10 @@ index 14084b7..ece87ed 100644 if (inode->i_op->update_time) return inode->i_op->update_time(inode, time, flags); diff --git a/fs/splice.c b/fs/splice.c -index 6909d89..020c7bc 100644 +index 29e394e..7117387 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1092,8 +1092,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); +@@ -1095,8 +1095,8 @@ EXPORT_SYMBOL(generic_splice_sendpage); /* * Attempt to initiate a splice from pipe to file. */ @@ -41,7 +41,7 @@ index 6909d89..020c7bc 100644 { ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); -@@ -1120,9 +1120,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -1123,9 +1123,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out, /* * Attempt to initiate a splice from a file to a pipe. */ @@ -55,10 +55,10 @@ index 6909d89..020c7bc 100644 ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); diff --git a/include/linux/fs.h b/include/linux/fs.h -index 7617ee0..1a39c33 100644 +index 2c28271..5c49108 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h -@@ -2551,6 +2551,7 @@ extern int inode_change_ok(const struct inode *, struct iattr *); +@@ -2558,6 +2558,7 @@ extern int inode_change_ok(const struct inode *, struct iattr *); extern int inode_newsize_ok(const struct inode *, loff_t offset); extern void setattr_copy(struct inode *inode, const struct iattr *attr); diff --git a/linux/aufs3-kbuild.patch b/linux/aufs3-kbuild.patch index cb4bb2cb2..095163594 100644 --- a/linux/aufs3-kbuild.patch +++ b/linux/aufs3-kbuild.patch @@ -1,4 +1,4 @@ -aufs3.x-rcN kbuild patch +aufs3.9 kbuild patch diff --git a/fs/Kconfig b/fs/Kconfig index 780725a..d460c05 100644 @@ -21,3 +21,15 @@ index 9d53192..e70f08f 100644 obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ +obj-$(CONFIG_AUFS_FS) += aufs/ +diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild +index 5c8a1d2..fea7572 100644 +--- a/include/uapi/linux/Kbuild ++++ b/include/uapi/linux/Kbuild +@@ -56,6 +56,7 @@ header-y += atmppp.h + 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 --git a/linux/aufs3-loopback.patch b/linux/aufs3-loopback.patch index 3c1372f9c..1aed72303 100644 --- a/linux/aufs3-loopback.patch +++ b/linux/aufs3-loopback.patch @@ -1,10 +1,10 @@ -aufs3.x-rcN loopback patch +aufs3.9 loopback patch diff --git a/drivers/block/loop.c b/drivers/block/loop.c -index ae12512..ed8295b 100644 +index dfe7583..c175e22 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c -@@ -506,7 +506,7 @@ out: +@@ -511,7 +511,7 @@ out: } struct switch_request { @@ -13,7 +13,7 @@ index ae12512..ed8295b 100644 struct completion wait; }; -@@ -568,7 +568,8 @@ static int loop_thread(void *data) +@@ -573,7 +573,8 @@ static int loop_thread(void *data) * First it needs to flush existing IO, it does this by sending a magic * BIO down the pipe. The completion of this BIO does the actual switch. */ @@ -23,7 +23,7 @@ index ae12512..ed8295b 100644 { struct switch_request w; struct bio *bio = bio_alloc(GFP_KERNEL, 0); -@@ -576,6 +577,7 @@ static int loop_switch(struct loop_device *lo, struct file *file) +@@ -581,6 +582,7 @@ static int loop_switch(struct loop_device *lo, struct file *file) return -ENOMEM; init_completion(&w.wait); w.file = file; @@ -31,7 +31,7 @@ index ae12512..ed8295b 100644 bio->bi_private = &w; bio->bi_bdev = NULL; loop_make_request(lo->lo_queue, bio); -@@ -592,7 +594,7 @@ static int loop_flush(struct loop_device *lo) +@@ -597,7 +599,7 @@ static int loop_flush(struct loop_device *lo) if (!lo->lo_thread) return 0; @@ -40,7 +40,7 @@ index ae12512..ed8295b 100644 } /* -@@ -611,6 +613,7 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) +@@ -616,6 +618,7 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p) mapping = file->f_mapping; mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask); lo->lo_backing_file = file; @@ -48,7 +48,7 @@ index ae12512..ed8295b 100644 lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ? mapping->host->i_bdev->bd_block_size : PAGE_SIZE; lo->old_gfp_mask = mapping_gfp_mask(mapping); -@@ -619,6 +622,13 @@ out: +@@ -624,6 +627,13 @@ out: complete(&p->wait); } @@ -62,7 +62,7 @@ index ae12512..ed8295b 100644 /* * loop_change_fd switched the backing store of a loopback device to -@@ -632,6 +642,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, +@@ -637,6 +647,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, unsigned int arg) { struct file *file, *old_file; @@ -70,7 +70,7 @@ index ae12512..ed8295b 100644 struct inode *inode; int error; -@@ -648,9 +659,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, +@@ -653,9 +664,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, file = fget(arg); if (!file) goto out; @@ -87,7 +87,7 @@ index ae12512..ed8295b 100644 error = -EINVAL; -@@ -662,17 +680,21 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, +@@ -667,17 +685,21 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev, goto out_putf; /* and ... switch */ @@ -110,7 +110,7 @@ index ae12512..ed8295b 100644 out: return error; } -@@ -815,7 +837,7 @@ static void loop_config_discard(struct loop_device *lo) +@@ -820,7 +842,7 @@ static void loop_config_discard(struct loop_device *lo) static int loop_set_fd(struct loop_device *lo, fmode_t mode, struct block_device *bdev, unsigned int arg) { @@ -119,7 +119,7 @@ index ae12512..ed8295b 100644 struct inode *inode; struct address_space *mapping; unsigned lo_blocksize; -@@ -830,6 +852,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, +@@ -835,6 +857,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, file = fget(arg); if (!file) goto out; @@ -132,7 +132,7 @@ index ae12512..ed8295b 100644 error = -EBUSY; if (lo->lo_state != Lo_unbound) -@@ -878,6 +906,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, +@@ -883,6 +911,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode, lo->lo_device = bdev; lo->lo_flags = lo_flags; lo->lo_backing_file = file; @@ -140,7 +140,7 @@ index ae12512..ed8295b 100644 lo->transfer = transfer_none; lo->ioctl = NULL; lo->lo_sizelimit = 0; -@@ -924,6 +953,7 @@ out_clr: +@@ -934,6 +963,7 @@ out_clr: lo->lo_thread = NULL; lo->lo_device = NULL; lo->lo_backing_file = NULL; @@ -148,7 +148,7 @@ index ae12512..ed8295b 100644 lo->lo_flags = 0; set_capacity(lo->lo_disk, 0); invalidate_bdev(bdev); -@@ -933,6 +963,8 @@ out_clr: +@@ -943,6 +973,8 @@ out_clr: lo->lo_state = Lo_unbound; out_putf: fput(file); @@ -157,7 +157,7 @@ index ae12512..ed8295b 100644 out: /* This is safe: open() is still holding a reference. */ module_put(THIS_MODULE); -@@ -979,6 +1011,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, +@@ -989,6 +1021,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer, static int loop_clr_fd(struct loop_device *lo) { struct file *filp = lo->lo_backing_file; @@ -165,7 +165,7 @@ index ae12512..ed8295b 100644 gfp_t gfp = lo->old_gfp_mask; struct block_device *bdev = lo->lo_device; -@@ -1012,6 +1045,7 @@ static int loop_clr_fd(struct loop_device *lo) +@@ -1022,6 +1055,7 @@ static int loop_clr_fd(struct loop_device *lo) spin_lock_irq(&lo->lo_lock); lo->lo_backing_file = NULL; @@ -173,7 +173,7 @@ index ae12512..ed8295b 100644 spin_unlock_irq(&lo->lo_lock); loop_release_xfer(lo); -@@ -1052,6 +1086,8 @@ static int loop_clr_fd(struct loop_device *lo) +@@ -1064,6 +1098,8 @@ static int loop_clr_fd(struct loop_device *lo) * bd_mutex which is usually taken before lo_ctl_mutex. */ fput(filp); @@ -183,7 +183,7 @@ index ae12512..ed8295b 100644 } diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c -index 5c35454..88f507b 100644 +index 56924c5..b832bf8 100644 --- a/fs/aufs/f_op.c +++ b/fs/aufs/f_op.c @@ -352,7 +352,7 @@ static ssize_t aufs_splice_read(struct file *file, loff_t *ppos, @@ -196,10 +196,10 @@ index 5c35454..88f507b 100644 if (file->f_mapping != h_file->f_mapping) { file->f_mapping = h_file->f_mapping; diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c -index 4fa75a4..a923899 100644 +index ccae19c..15dc5c2 100644 --- a/fs/aufs/loop.c +++ b/fs/aufs/loop.c -@@ -131,3 +131,19 @@ void au_loopback_fin(void) +@@ -133,3 +133,19 @@ void au_loopback_fin(void) { kfree(au_warn_loopback_array); } @@ -242,7 +242,7 @@ index 88d019c..8707c3a 100644 #endif /* __KERNEL__ */ diff --git a/fs/aufs/super.c b/fs/aufs/super.c -index ab4c909..8e654b8 100644 +index 4f57ba0..786ffea 100644 --- a/fs/aufs/super.c +++ b/fs/aufs/super.c @@ -811,7 +811,10 @@ static const struct super_operations aufs_sop = { @@ -258,7 +258,7 @@ index ab4c909..8e654b8 100644 /* ---------------------------------------------------------------------- */ diff --git a/include/linux/fs.h b/include/linux/fs.h -index 1a39c33..146e26f 100644 +index 5c49108..f8e9fd4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1614,6 +1614,10 @@ struct super_operations { diff --git a/linux/aufs3-proc_map.patch b/linux/aufs3-proc_map.patch index 77f34c48f..5b6e2e8eb 100644 --- a/linux/aufs3-proc_map.patch +++ b/linux/aufs3-proc_map.patch @@ -1,4 +1,4 @@ -aufs3.x-rcN proc_map patch +aufs3.9 proc_map patch diff --git a/fs/buffer.c b/fs/buffer.c index b4dcb34..7b6b475 100644 @@ -163,7 +163,7 @@ index 0db0de1..147446c 100644 new_vma->vm_ops->open(new_vma); vma_link(mm, new_vma, prev, rb_link, rb_parent); diff --git a/mm/nommu.c b/mm/nommu.c -index 2f3ea74..06529ce 100644 +index e001768..e9f09eb 100644 --- a/mm/nommu.c +++ b/mm/nommu.c @@ -650,6 +650,8 @@ static void __put_nommu_region(struct vm_region *region) diff --git a/linux/aufs3-standalone.patch b/linux/aufs3-standalone.patch index 6007f3f6c..3dfc2f070 100644 --- a/linux/aufs3-standalone.patch +++ b/linux/aufs3-standalone.patch @@ -1,7 +1,7 @@ -aufs3.x-rcN standalone patch +aufs3.9 standalone patch diff --git a/fs/file_table.c b/fs/file_table.c -index e73287a..b33aebe 100644 +index ca5948f..b553610 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -37,6 +37,7 @@ struct files_stat_struct files_stat = { @@ -12,7 +12,7 @@ index e73287a..b33aebe 100644 /* SLAB cache for file structures */ static struct kmem_cache *filp_cachep __read_mostly; -@@ -403,6 +404,8 @@ void file_sb_list_del(struct file *file) +@@ -404,6 +405,8 @@ void file_sb_list_del(struct file *file) } } @@ -22,7 +22,7 @@ index e73287a..b33aebe 100644 /* diff --git a/fs/inode.c b/fs/inode.c -index ece87ed..38f7bc8 100644 +index e83cd1e..fa2245a 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -56,6 +56,7 @@ static struct hlist_head *inode_hashtable __read_mostly; @@ -33,7 +33,7 @@ index ece87ed..38f7bc8 100644 /* * Empty aops. Can be used for the cases where the user does not -@@ -1519,6 +1520,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags) +@@ -1514,6 +1515,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags) mark_inode_dirty_sync(inode); return 0; } @@ -42,7 +42,7 @@ index ece87ed..38f7bc8 100644 /** * touch_atime - update the access time diff --git a/fs/namespace.c b/fs/namespace.c -index 55605c5..aed7607 100644 +index 341d3f5..18f5bb0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -52,6 +52,7 @@ EXPORT_SYMBOL_GPL(fs_kobj); @@ -53,7 +53,15 @@ index 55605c5..aed7607 100644 static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) { -@@ -1424,6 +1425,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, +@@ -425,6 +426,7 @@ void __mnt_drop_write(struct vfsmount *mnt) + mnt_dec_writers(real_mount(mnt)); + preempt_enable(); + } ++EXPORT_SYMBOL_GPL(__mnt_drop_write); + + /** + * mnt_drop_write - give up write access to a mount +@@ -1417,6 +1419,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg, } return 0; } @@ -134,10 +142,10 @@ index fc6b49b..a6bb87d 100644 static int fsnotify_mark_destroy(void *ignored) { diff --git a/fs/open.c b/fs/open.c -index 9b33c0c..e3365035 100644 +index 6835446..df2262a 100644 --- a/fs/open.c +++ b/fs/open.c -@@ -60,6 +60,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, +@@ -61,6 +61,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, mutex_unlock(&dentry->d_inode->i_mutex); return ret; } @@ -146,10 +154,10 @@ index 9b33c0c..e3365035 100644 long vfs_truncate(struct path *path, loff_t length) { diff --git a/fs/splice.c b/fs/splice.c -index 020c7bc..a622ade 100644 +index 7117387..af8fd2d 100644 --- a/fs/splice.c +++ b/fs/splice.c -@@ -1116,6 +1116,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, +@@ -1119,6 +1119,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out, return splice_write(pipe, out, ppos, len, flags); } @@ -157,7 +165,7 @@ index 020c7bc..a622ade 100644 /* * Attempt to initiate a splice from a file to a pipe. -@@ -1142,6 +1143,7 @@ long do_splice_to(struct file *in, loff_t *ppos, +@@ -1145,6 +1146,7 @@ long do_splice_to(struct file *in, loff_t *ppos, return splice_read(in, ppos, pipe, len, flags); } @@ -166,7 +174,7 @@ index 020c7bc..a622ade 100644 /** * splice_direct_to_actor - splices data directly between two non-pipes diff --git a/security/commoncap.c b/security/commoncap.c -index 7ee08c7..176edf1 100644 +index c44b6fe..d78b003 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -988,9 +988,11 @@ int cap_mmap_addr(unsigned long addr) @@ -182,7 +190,7 @@ index 7ee08c7..176edf1 100644 } +EXPORT_SYMBOL_GPL(cap_mmap_file); diff --git a/security/device_cgroup.c b/security/device_cgroup.c -index d794abc..a20f167 100644 +index 1c69e38..7392d19 100644 --- a/security/device_cgroup.c +++ b/security/device_cgroup.c @@ -7,6 +7,7 @@ @@ -193,7 +201,7 @@ index d794abc..a20f167 100644 #include #include #include -@@ -631,6 +632,7 @@ int __devcgroup_inode_permission(struct inode *inode, int mask) +@@ -634,6 +635,7 @@ int __devcgroup_inode_permission(struct inode *inode, int mask) return __devcgroup_check_permission(type, imajor(inode), iminor(inode), access); } @@ -202,7 +210,7 @@ index d794abc..a20f167 100644 int devcgroup_inode_mknod(int mode, dev_t dev) { diff --git a/security/security.c b/security/security.c -index 7b88c6a..5d00a30 100644 +index 03f248b..892e803 100644 --- a/security/security.c +++ b/security/security.c @@ -396,6 +396,7 @@ int security_path_rmdir(struct path *dir, struct dentry *dentry) diff --git a/linux/aufs3.patch b/linux/aufs3.patch index 9ffc1dc8f..dc095e882 100644 --- a/linux/aufs3.patch +++ b/linux/aufs3.patch @@ -1,6 +1,6 @@ --- /dev/null -+++ linux-3.x-rcN/Documentation/ABI/testing/debugfs-aufs 2013-02-19 11:26:12.672456917 -0500 -@@ -0,0 +1,37 @@ ++++ linux-3.9/Documentation/ABI/testing/debugfs-aufs 2013-05-13 17:13:17.203794085 +0200 +@@ -0,0 +1,50 @@ +What: /debug/aufs/si_/ +Date: March 2009 +Contact: J. R. Okajima @@ -9,6 +9,19 @@ + per aufs mount, where is a unique id generated + internally. + ++What: /debug/aufs/si_/plink ++Date: Apr 2013 ++Contact: J. R. Okajima ++Description: ++ It has three lines and shows the information about the ++ pseudo-link. The first line is a single number ++ representing a number of buckets. The second line is a ++ number of pseudo-links per buckets (separated by a ++ blank). The last line is a single number representing a ++ total number of psedo-links. ++ When the aufs mount option 'noplink' is specified, it ++ will show "1\n0\n0\n". ++ +What: /debug/aufs/si_/xib +Date: March 2009 +Contact: J. R. Okajima @@ -39,7 +52,7 @@ + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. --- /dev/null -+++ linux-3.x-rcN/Documentation/ABI/testing/sysfs-aufs 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/ABI/testing/sysfs-aufs 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,24 @@ +What: /sys/fs/aufs/si_/ +Date: March 2009 @@ -66,7 +79,355 @@ + When the aufs mount option 'noxino' is specified, it + will be empty. About XINO files, see the aufs manual. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/01intro.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/README 2013-05-13 17:13:17.203794085 +0200 +@@ -0,0 +1,345 @@ ++ ++Aufs3 -- advanced multi layered unification filesystem version 3.x ++http://aufs.sf.net ++Junjiro R. Okajima ++ ++ ++0. Introduction ++---------------------------------------- ++In the early days, aufs was entirely re-designed and re-implemented ++Unionfs Version 1.x series. After many original ideas, approaches, ++improvements and implementations, it becomes totally different from ++Unionfs while keeping the basic features. ++Recently, Unionfs Version 2.x series begin taking some of the same ++approaches to aufs1's. ++Unionfs is being developed by Professor Erez Zadok at Stony Brook ++University and his team. ++ ++Aufs3 supports linux-3.0 and later. ++If you want older kernel version support, try aufs2-2.6.git or ++aufs2-standalone.git repository, aufs1 from CVS on SourceForge. ++ ++Note: it becomes clear that "Aufs was rejected. Let's give it up." ++According to Christoph Hellwig, linux rejects all union-type filesystems ++but UnionMount. ++ ++ ++ ++1. Features ++---------------------------------------- ++- unite several directories into a single virtual filesystem. The member ++ directory is called as a branch. ++- you can specify the permission flags to the branch, which are 'readonly', ++ 'readwrite' and 'whiteout-able.' ++- by upper writable branch, internal copyup and whiteout, files/dirs on ++ readonly branch are modifiable logically. ++- dynamic branch manipulation, add, del. ++- etc... ++ ++Also there are many enhancements in aufs1, such as: ++- readdir(3) in userspace. ++- keep inode number by external inode number table ++- keep the timestamps of file/dir in internal copyup operation ++- seekable directory, supporting NFS readdir. ++- whiteout is hardlinked in order to reduce the consumption of inodes ++ on branch ++- do not copyup, nor create a whiteout when it is unnecessary ++- revert a single systemcall when an error occurs in aufs ++- remount interface instead of ioctl ++- maintain /etc/mtab by an external command, /sbin/mount.aufs. ++- loopback mounted filesystem as a branch ++- kernel thread for removing the dir who has a plenty of whiteouts ++- support copyup sparse file (a file which has a 'hole' in it) ++- default permission flags for branches ++- selectable permission flags for ro branch, whether whiteout can ++ exist or not ++- export via NFS. ++- support /fs/aufs and /aufs. ++- support multiple writable branches, some policies to select one ++ among multiple writable branches. ++- a new semantics for link(2) and rename(2) to support multiple ++ writable branches. ++- no glibc changes are required. ++- pseudo hardlink (hardlink over branches) ++- allow a direct access manually to a file on branch, e.g. bypassing aufs. ++ including NFS or remote filesystem branch. ++- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. ++- and more... ++ ++Currently these features are dropped temporary from aufs3. ++See design/08plan.txt in detail. ++- test only the highest one for the directory permission (dirperm1) ++- copyup on open (coo=) ++- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs ++ (robr) ++- statistics of aufs thread (/sys/fs/aufs/stat) ++- delegation mode (dlgt) ++ a delegation of the internal branch access to support task I/O ++ accounting, which also supports Linux Security Modules (LSM) mainly ++ for Suse AppArmor. ++- intent.open/create (file open in a single lookup) ++ ++Features or just an idea in the future (see also design/*.txt), ++- reorder the branch index without del/re-add. ++- permanent xino files for NFSD ++- an option for refreshing the opened files after add/del branches ++- 'move' policy for copy-up between two writable branches, after ++ checking free space. ++- light version, without branch manipulation. (unnecessary?) ++- copyup in userspace ++- inotify in userspace ++- readv/writev ++- xattr, acl ++ ++ ++2. Download ++---------------------------------------- ++There were three GIT trees for aufs3, aufs3-linux.git, ++aufs3-standalone.git, and aufs-util.git. Note that there is no "3" in ++"aufs-util.git." ++While the aufs-util is always necessary, you need either of aufs3-linux ++or aufs3-standalone. ++ ++The aufs3-linux tree includes the whole linux mainline GIT tree, ++git://git.kernel.org/.../torvalds/linux.git. ++And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot ++build aufs3 as an external kernel module. ++ ++On the other hand, the aufs3-standalone tree has only aufs source files ++and necessary patches, and you can select CONFIG_AUFS_FS=m. ++ ++You will find GIT branches whose name is in form of "aufs3.x" where "x" ++represents the linux kernel version, "linux-3.x". For instance, ++"aufs3.0" is for linux-3.0. For latest "linux-3.x-rcN", use ++"aufs3.x-rcN" branch. ++ ++o aufs3-linux tree ++$ git clone --reference /your/linux/git/tree \ ++ git://git.code.sf.net/p/aufs/aufs3-linux aufs-aufs3-linux \ ++ aufs3-linux.git ++- if you don't have linux GIT tree, then remove "--reference ..." ++$ cd aufs3-linux.git ++$ git checkout origin/aufs3.0 ++ ++o aufs3-standalone tree ++$ git clone git://git.code.sf.net/p/aufs/aufs3-standalone \ ++ aufs3-standalone.git ++$ cd aufs3-standalone.git ++$ git checkout origin/aufs3.0 ++ ++o aufs-util tree ++$ git clone git://git.code.sf.net/p/aufs/aufs-util \ ++ aufs-util.git ++$ cd aufs-util.git ++$ git checkout origin/aufs3.0 ++ ++Note: The 3.x-rcN branch is to be used with `rc' kernel versions ONLY. ++The minor version number, 'x' in '3.x', of aufs may not always ++follow the minor version number of the kernel. ++Because changes in the kernel that cause the use of a new ++minor version number do not always require changes to aufs-util. ++ ++Since aufs-util has its own minor version number, you may not be ++able to find a GIT branch in aufs-util for your kernel's ++exact minor version number. ++In this case, you should git-checkout the branch for the ++nearest lower number. ++ ++For (an unreleased) example: ++If you are using "linux-3.10" and the "aufs3.10" branch ++does not exist in aufs-util repository, then "aufs3.9", "aufs3.8" ++or something numerically smaller is the branch for your kernel. ++ ++Also you can view all branches by ++ $ git branch -a ++ ++ ++3. Configuration and Compilation ++---------------------------------------- ++Make sure you have git-checkout'ed the correct branch. ++ ++For aufs3-linux tree, ++- enable CONFIG_AUFS_FS. ++- set other aufs configurations if necessary. ++ ++For aufs3-standalone tree, ++There are several ways to build. ++ ++1. ++- apply ./aufs3-kbuild.patch to your kernel source files. ++- apply ./aufs3-base.patch too. ++- apply ./aufs3-proc_map.patch too, if you want to make /proc/PID/maps (and ++ others including lsof(1)) show the file path on aufs instead of the ++ path on the branch fs. ++- apply ./aufs3-standalone.patch too, if you have a plan to set ++ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs3-standalone.patch. ++- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your ++ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. ++- enable CONFIG_AUFS_FS, you can select either ++ =m or =y. ++- and build your kernel as usual. ++- install the built kernel. ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file if you set CONFIG_AUFS_FS=m. ++- install the header files too by "make headers_install" to the ++ directory where you specify. By default, it is $PWD/usr. ++ "make help" shows a brief note for headers_install. ++- and reboot your system. ++ ++2. ++- module only (CONFIG_AUFS_FS=m). ++- apply ./aufs3-base.patch to your kernel source files. ++- apply ./aufs3-proc_map.patch too to your kernel source files, ++ if you want to make /proc/PID/maps (and others including lsof(1)) show ++ the file path on aufs instead of the path on the branch fs. ++- apply ./aufs3-standalone.patch too. ++- build your kernel, don't forget "make headers_install", and reboot. ++- edit ./config.mk and set other aufs configurations if necessary. ++ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes ++ every aufs configurations. ++- build the module by simple "make". ++ Note: Since linux-3.9, every filesystem module requires an alias ++ "fs-". You should make sure that "fs-aufs" is listed in your ++ modules.aliases file. ++- you can specify ${KDIR} make variable which points to your kernel ++ source tree. ++- install the files ++ + run "make install" to install the aufs module, or copy the built ++ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). ++ + run "make install_headers" (instead of headers_install) to install ++ the modified aufs header file (you can specify DESTDIR which is ++ available in aufs standalone version's Makefile only), or copy ++ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever ++ you like manually. By default, the target directory is $PWD/usr. ++- no need to apply aufs3-kbuild.patch, nor copying source files to your ++ kernel source tree. ++ ++Note: The header file aufs_type.h is necessary to build aufs-util ++ as well as "make headers_install" in the kernel source tree. ++ headers_install is subject to be forgotten, but it is essentially ++ necessary, not only for building aufs-util. ++ You may not meet problems without headers_install in some older ++ version though. ++ ++And then, ++- read README in aufs-util, build and install it ++- note that your distribution may contain an obsoleted version of ++ aufs_type.h in /usr/include/linux or something. When you build aufs ++ utilities, make sure that your compiler refers the correct aufs header ++ file which is built by "make headers_install." ++- if you want to use readdir(3) in userspace or pathconf(3) wrapper, ++ then run "make install_ulib" too. And refer to the aufs manual in ++ detail. ++ ++ ++4. Usage ++---------------------------------------- ++At first, make sure aufs-util are installed, and please read the aufs ++manual, aufs.5 in aufs-util.git tree. ++$ man -l aufs.5 ++ ++And then, ++$ mkdir /tmp/rw /tmp/aufs ++# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs ++ ++Here is another example. The result is equivalent. ++# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs ++ Or ++# mount -t aufs -o br:/tmp/rw none /tmp/aufs ++# mount -o remount,append:${HOME} /tmp/aufs ++ ++Then, you can see whole tree of your home dir through /tmp/aufs. If ++you modify a file under /tmp/aufs, the one on your home directory is ++not affected, instead the same named file will be newly created under ++/tmp/rw. And all of your modification to a file will be applied to ++the one under /tmp/rw. This is called the file based Copy on Write ++(COW) method. ++Aufs mount options are described in aufs.5. ++If you run chroot or something and make your aufs as a root directory, ++then you need to customize the shutdown script. See the aufs manual in ++detail. ++ ++Additionally, there are some sample usages of aufs which are a ++diskless system with network booting, and LiveCD over NFS. ++See sample dir in CVS tree on SourceForge. ++ ++ ++5. Contact ++---------------------------------------- ++When you have any problems or strange behaviour in aufs, please let me ++know with: ++- /proc/mounts (instead of the output of mount(8)) ++- /sys/module/aufs/* ++- /sys/fs/aufs/* (if you have them) ++- /debug/aufs/* (if you have them) ++- linux kernel version ++ if your kernel is not plain, for example modified by distributor, ++ the url where i can download its source is necessary too. ++- aufs version which was printed at loading the module or booting the ++ system, instead of the date you downloaded. ++- configuration (define/undefine CONFIG_AUFS_xxx) ++- kernel configuration or /proc/config.gz (if you have it) ++- behaviour which you think to be incorrect ++- actual operation, reproducible one is better ++- mailto: aufs-users at lists.sourceforge.net ++ ++Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, ++and Feature Requests) on SourceForge. Please join and write to ++aufs-users ML. ++ ++ ++6. Acknowledgements ++---------------------------------------- ++Thanks to everyone who have tried and are using aufs, whoever ++have reported a bug or any feedback. ++ ++Especially donators: ++Tomas Matejicek(slax.org) made a donation (much more than once). ++ Since Apr 2010, Tomas M (the author of Slax and Linux Live ++ scripts) is making "doubling" donations. ++ Unfortunately I cannot list all of the donators, but I really ++ appreciate. ++ It ends Aug 2010, but the ordinary donation URL is still available. ++ ++Dai Itasaka made a donation (2007/8). ++Chuck Smith made a donation (2008/4, 10 and 12). ++Henk Schoneveld made a donation (2008/9). ++Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). ++Francois Dupoux made a donation (2008/11). ++Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public ++ aufs2 GIT tree (2009/2). ++William Grant made a donation (2009/3). ++Patrick Lane made a donation (2009/4). ++The Mail Archive (mail-archive.com) made donations (2009/5). ++Nippy Networks (Ed Wildgoose) made a donation (2009/7). ++New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). ++Pavel Pronskiy made a donation (2011/2). ++Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy ++ Networks (Ed Wildgoose) made a donation for hardware (2011/3). ++Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and ++11). ++Sam Liddicott made a donation (2011/9). ++Era Scarecrow made a donation (2013/4). ++Bor Ratajc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++POIRETTE Marc made a donation (2013/4). ++Alessandro Gorreta made a donation (2013/4). ++lauri kasvandik made a donation (2013/5). ++ ++Thank you very much. ++Donations are always, including future donations, very important and ++helpful for me to keep on developing aufs. ++ ++ ++7. ++---------------------------------------- ++If you are an experienced user, no explanation is needed. Aufs is ++just a linux filesystem. ++ ++ ++Enjoy! ++ ++# Local variables: ; ++# mode: text; ++# End: ; +--- /dev/null ++++ linux-3.9/Documentation/filesystems/aufs/design/01intro.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,162 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -231,7 +592,7 @@ +helper, instead of doing in kernel space. Actually I am still thinking +about it. But currently I have implemented it in kernel space. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/02struct.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/02struct.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,226 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -460,7 +821,7 @@ +When the trigger systemcall does not update the timestamps of the parent +dir, aufs reverts it after copy-up. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/03lookup.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/03lookup.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,106 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -569,7 +930,7 @@ + aufs performance when system surely hide the aufs branches from user, + by over-mounting something (or another method). --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/04branch.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/04branch.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,76 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -648,7 +1009,7 @@ + - a regular file on the branch is opened for write and there is no + same named entry on the upper branch. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/05wbr_policy.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/05wbr_policy.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,65 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -716,7 +1077,7 @@ + one. If the selected branch is readonly, then aufs follows the + copyup policy. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/06mmap.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/06mmap.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,47 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -766,7 +1127,7 @@ +approach, aufs met a hard problem and I could not solve it without +switching the approach. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/07export.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/07export.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,59 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -828,7 +1189,7 @@ +- readdir(): call lockdep_on/off() because filldir in NFSD calls + lookup_one_len(), vfs_getattr(), encode_fh() and others. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/08shwh.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/08shwh.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,53 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -884,7 +1245,7 @@ +This new squashfs archive can be stored on the boot device and the +initramfs will use it to replace the old one at the next boot. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/10dynop.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/10dynop.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,47 @@ + +# Copyright (C) 2010-2013 Junjiro R. Okajima @@ -934,7 +1295,7 @@ +Currently this approach is applied to file_operations and +vm_operations_struct for regular files only. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/design/99plan.txt 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/Documentation/filesystems/aufs/design/99plan.txt 2013-05-13 17:13:17.203794085 +0200 @@ -0,0 +1,96 @@ + +# Copyright (C) 2005-2013 Junjiro R. Okajima @@ -1033,343 +1394,257 @@ +/new. +Otherwise from /new. --- /dev/null -+++ linux-3.x-rcN/Documentation/filesystems/aufs/README 2013-02-19 11:26:12.672456917 -0500 -@@ -0,0 +1,333 @@ ++++ linux-3.9/fs/aufs/Kconfig 2013-05-13 17:13:17.207129054 +0200 +@@ -0,0 +1,202 @@ ++config AUFS_FS ++ tristate "Aufs (Advanced multi layered unification filesystem) support" ++ 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. + -+Aufs3 -- advanced multi layered unification filesystem version 3.x -+http://aufs.sf.net -+Junjiro R. Okajima ++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. + -+0. Introduction -+---------------------------------------- -+In the early days, aufs was entirely re-designed and re-implemented -+Unionfs Version 1.x series. After many original ideas, approaches, -+improvements and implementations, it becomes totally different from -+Unionfs while keeping the basic features. -+Recently, Unionfs Version 2.x series begin taking some of the same -+approaches to aufs1's. -+Unionfs is being developed by Professor Erez Zadok at Stony Brook -+University and his team. ++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. + -+Aufs3 supports linux-3.0 and later. -+If you want older kernel version support, try aufs2-2.6.git or -+aufs2-standalone.git repository, aufs1 from CVS on SourceForge. ++choice ++ prompt "method" if AUFS_HNOTIFY ++ default AUFS_HFSNOTIFY ++config AUFS_HFSNOTIFY ++ bool "fsnotify" ++ select FSNOTIFY ++endchoice + -+Note: it becomes clear that "Aufs was rejected. Let's give it up." -+According to Christoph Hellwig, linux rejects all union-type filesystems -+but UnionMount. -+ ++config AUFS_EXPORT ++ bool "NFS-exportable aufs" ++ depends on 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 */ + -+1. Features -+---------------------------------------- -+- unite several directories into a single virtual filesystem. The member -+ directory is called as a branch. -+- you can specify the permission flags to the branch, which are 'readonly', -+ 'readwrite' and 'whiteout-able.' -+- by upper writable branch, internal copyup and whiteout, files/dirs on -+ readonly branch are modifiable logically. -+- dynamic branch manipulation, add, del. -+- etc... ++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. + -+Also there are many enhancements in aufs1, such as: -+- readdir(3) in userspace. -+- keep inode number by external inode number table -+- keep the timestamps of file/dir in internal copyup operation -+- seekable directory, supporting NFS readdir. -+- whiteout is hardlinked in order to reduce the consumption of inodes -+ on branch -+- do not copyup, nor create a whiteout when it is unnecessary -+- revert a single systemcall when an error occurs in aufs -+- remount interface instead of ioctl -+- maintain /etc/mtab by an external command, /sbin/mount.aufs. -+- loopback mounted filesystem as a branch -+- kernel thread for removing the dir who has a plenty of whiteouts -+- support copyup sparse file (a file which has a 'hole' in it) -+- default permission flags for branches -+- selectable permission flags for ro branch, whether whiteout can -+ exist or not -+- export via NFS. -+- support /fs/aufs and /aufs. -+- support multiple writable branches, some policies to select one -+ among multiple writable branches. -+- a new semantics for link(2) and rename(2) to support multiple -+ writable branches. -+- no glibc changes are required. -+- pseudo hardlink (hardlink over branches) -+- allow a direct access manually to a file on branch, e.g. bypassing aufs. -+ including NFS or remote filesystem branch. -+- userspace wrapper for pathconf(3)/fpathconf(3) with _PC_LINK_MAX. -+- and more... ++config AUFS_PROC_MAP ++ bool "support for /proc/maps and lsof(1)" ++ depends on PROC_FS ++ help ++ When you issue mmap(2) in aufs, it is actually a direct mmap(2) ++ call to the file on the branch fs since the file in aufs is ++ purely virtual. And the file path printed in /proc/maps (and ++ others) will be the path on the branch fs. In most cases, it ++ does no harm. But some utilities like lsof(1) may confuse since ++ the utility or user may expect the file path in aufs to be ++ printed. ++ To address this issue, aufs provides a patch which introduces a ++ new member called vm_prfile into struct vm_are_struct. The patch ++ is meaningless without enabling this configuration since nobody ++ sets the new vm_prfile member. ++ If you don't apply the patch, then enabling this configuration ++ will cause a compile error. ++ This approach is fragile since if someone else make some changes ++ around vm_file, then vm_prfile may not work anymore. As a ++ workaround such case, aufs provides this configuration. If you ++ disable it, then lsof(1) may produce incorrect result but the ++ problem will be gone even if the aufs patch is applied (I hope). + -+Currently these features are dropped temporary from aufs3. -+See design/08plan.txt in detail. -+- test only the highest one for the directory permission (dirperm1) -+- copyup on open (coo=) -+- nested mount, i.e. aufs as readonly no-whiteout branch of another aufs -+ (robr) -+- statistics of aufs thread (/sys/fs/aufs/stat) -+- delegation mode (dlgt) -+ a delegation of the internal branch access to support task I/O -+ accounting, which also supports Linux Security Modules (LSM) mainly -+ for Suse AppArmor. -+- intent.open/create (file open in a single lookup) ++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. + -+Features or just an idea in the future (see also design/*.txt), -+- reorder the branch index without del/re-add. -+- permanent xino files for NFSD -+- an option for refreshing the opened files after add/del branches -+- 'move' policy for copy-up between two writable branches, after -+ checking free space. -+- light version, without branch manipulation. (unnecessary?) -+- copyup in userspace -+- inotify in userspace -+- readv/writev -+- xattr, acl ++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. + -+2. Download -+---------------------------------------- -+There were three GIT trees for aufs3, aufs3-linux.git, -+aufs3-standalone.git, and aufs-util.git. Note that there is no "3" in -+"aufs-util.git." -+While the aufs-util is always necessary, you need either of aufs3-linux -+or aufs3-standalone. ++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). + -+The aufs3-linux tree includes the whole linux mainline GIT tree, -+git://git.kernel.org/.../torvalds/linux.git. -+And you cannot select CONFIG_AUFS_FS=m for this version, eg. you cannot -+build aufs3 as an external kernel module. ++config AUFS_POLL ++ bool ++ help ++ Automatic configuration for internal use. + -+On the other hand, the aufs3-standalone tree has only aufs source files -+and necessary patches, and you can select CONFIG_AUFS_FS=m. ++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. + -+You will find GIT branches whose name is in form of "aufs3.x" where "x" -+represents the linux kernel version, "linux-3.x". For instance, -+"aufs3.0" is for linux-3.0. For latest "linux-3.x-rcN", use -+"aufs3.x-rcN" branch. ++config AUFS_BDEV_LOOP ++ bool ++ depends on BLK_DEV_LOOP ++ default y ++ help ++ Automatic configuration for internal use. ++ Convert =[ym] into =y. + -+o aufs3-linux tree -+$ git clone --reference /your/linux/git/tree \ -+ git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-linux.git \ -+ aufs3-linux.git -+- if you don't have linux GIT tree, then remove "--reference ..." -+$ cd aufs3-linux.git -+$ git checkout origin/aufs3.0 ++config AUFS_DEBUG ++ bool "Debug aufs" ++ help ++ Enable this to compile aufs internal debug code. ++ It will have a negative impact to the performance. + -+o aufs3-standalone tree -+$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs3-standalone.git \ -+ aufs3-standalone.git -+$ cd aufs3-standalone.git -+$ git checkout origin/aufs3.0 -+ -+o aufs-util tree -+$ git clone git://aufs.git.sourceforge.net/gitroot/aufs/aufs-util.git \ -+ aufs-util.git -+$ cd aufs-util.git -+$ git checkout origin/aufs3.0 -+ -+Note: The 3.x-rcN branch is to be used with `rc' kernel versions ONLY. -+The minor version number, 'x' in '3.x', of aufs may not always -+follow the minor version number of the kernel. -+Because changes in the kernel that cause the use of a new -+minor version number do not always require changes to aufs-util. -+ -+Since aufs-util has its own minor version number, you may not be -+able to find a GIT branch in aufs-util for your kernel's -+exact minor version number. -+In this case, you should git-checkout the branch for the -+nearest lower number. -+ -+For (an unreleased) example: -+If you are using "linux-3.10" and the "aufs3.10" branch -+does not exist in aufs-util repository, then "aufs3.9", "aufs3.8" -+or something numerically smaller is the branch for your kernel. -+ -+Also you can view all branches by -+ $ git branch -a -+ -+ -+3. Configuration and Compilation -+---------------------------------------- -+Make sure you have git-checkout'ed the correct branch. -+ -+For aufs3-linux tree, -+- enable CONFIG_EXPERIMENTAL and CONFIG_AUFS_FS. -+- set other aufs configurations if necessary. -+ -+For aufs3-standalone tree, -+There are several ways to build. -+ -+1. -+- apply ./aufs3-kbuild.patch to your kernel source files. -+- apply ./aufs3-base.patch too. -+- apply ./aufs3-proc_map.patch too, if you want to make /proc/PID/maps (and -+ others including lsof(1)) show the file path on aufs instead of the -+ path on the branch fs. -+- apply ./aufs3-standalone.patch too, if you have a plan to set -+ CONFIG_AUFS_FS=m. otherwise you don't need ./aufs3-standalone.patch. -+- copy ./{Documentation,fs,include/uapi/linux/aufs_type.h} files to your -+ kernel source tree. Never copy $PWD/include/uapi/linux/Kbuild. -+- enable CONFIG_EXPERIMENTAL and CONFIG_AUFS_FS, you can select either -+ =m or =y. -+- and build your kernel as usual. -+- install the built kernel. -+- install the header files too by "make headers_install" to the -+ directory where you specify. By default, it is $PWD/usr. -+ "make help" shows a brief note for headers_install. -+- and reboot your system. -+ -+2. -+- module only (CONFIG_AUFS_FS=m). -+- apply ./aufs3-base.patch to your kernel source files. -+- apply ./aufs3-proc_map.patch too to your kernel source files, -+ if you want to make /proc/PID/maps (and others including lsof(1)) show -+ the file path on aufs instead of the path on the branch fs. -+- apply ./aufs3-standalone.patch too. -+- build your kernel, don't forget "make headers_install", and reboot. -+- edit ./config.mk and set other aufs configurations if necessary. -+ Note: You should read $PWD/fs/aufs/Kconfig carefully which describes -+ every aufs configurations. -+- build the module by simple "make". -+- you can specify ${KDIR} make variable which points to your kernel -+ source tree. -+- install the files -+ + run "make install" to install the aufs module, or copy the built -+ $PWD/aufs.ko to /lib/modules/... and run depmod -a (or reboot simply). -+ + run "make install_headers" (instead of headers_install) to install -+ the modified aufs header file (you can specify DESTDIR which is -+ available in aufs standalone version's Makefile only), or copy -+ $PWD/usr/include/linux/aufs_type.h to /usr/include/linux or wherever -+ you like manually. By default, the target directory is $PWD/usr. -+- no need to apply aufs3-kbuild.patch, nor copying source files to your -+ kernel source tree. -+ -+Note: The header file aufs_type.h is necessary to build aufs-util -+ as well as "make headers_install" in the kernel source tree. -+ headers_install is subject to be forgotten, but it is essentially -+ necessary, not only for building aufs-util. -+ You may not meet problems without headers_install in some older -+ version though. -+ -+And then, -+- read README in aufs-util, build and install it -+- note that your distribution may contain an obsoleted version of -+ aufs_type.h in /usr/include/linux or something. When you build aufs -+ utilities, make sure that your compiler refers the correct aufs header -+ file which is built by "make headers_install." -+- if you want to use readdir(3) in userspace or pathconf(3) wrapper, -+ then run "make install_ulib" too. And refer to the aufs manual in -+ detail. -+ -+ -+4. Usage -+---------------------------------------- -+At first, make sure aufs-util are installed, and please read the aufs -+manual, aufs.5 in aufs-util.git tree. -+$ man -l aufs.5 -+ -+And then, -+$ mkdir /tmp/rw /tmp/aufs -+# mount -t aufs -o br=/tmp/rw:${HOME} none /tmp/aufs -+ -+Here is another example. The result is equivalent. -+# mount -t aufs -o br=/tmp/rw=rw:${HOME}=ro none /tmp/aufs -+ Or -+# mount -t aufs -o br:/tmp/rw none /tmp/aufs -+# mount -o remount,append:${HOME} /tmp/aufs -+ -+Then, you can see whole tree of your home dir through /tmp/aufs. If -+you modify a file under /tmp/aufs, the one on your home directory is -+not affected, instead the same named file will be newly created under -+/tmp/rw. And all of your modification to a file will be applied to -+the one under /tmp/rw. This is called the file based Copy on Write -+(COW) method. -+Aufs mount options are described in aufs.5. -+If you run chroot or something and make your aufs as a root directory, -+then you need to customize the shutdown script. See the aufs manual in -+detail. -+ -+Additionally, there are some sample usages of aufs which are a -+diskless system with network booting, and LiveCD over NFS. -+See sample dir in CVS tree on SourceForge. -+ -+ -+5. Contact -+---------------------------------------- -+When you have any problems or strange behaviour in aufs, please let me -+know with: -+- /proc/mounts (instead of the output of mount(8)) -+- /sys/module/aufs/* -+- /sys/fs/aufs/* (if you have them) -+- /debug/aufs/* (if you have them) -+- linux kernel version -+ if your kernel is not plain, for example modified by distributor, -+ the url where i can download its source is necessary too. -+- aufs version which was printed at loading the module or booting the -+ system, instead of the date you downloaded. -+- configuration (define/undefine CONFIG_AUFS_xxx) -+- kernel configuration or /proc/config.gz (if you have it) -+- behaviour which you think to be incorrect -+- actual operation, reproducible one is better -+- mailto: aufs-users at lists.sourceforge.net -+ -+Usually, I don't watch the Public Areas(Bugs, Support Requests, Patches, -+and Feature Requests) on SourceForge. Please join and write to -+aufs-users ML. -+ -+ -+6. Acknowledgements -+---------------------------------------- -+Thanks to everyone who have tried and are using aufs, whoever -+have reported a bug or any feedback. -+ -+Especially donators: -+Tomas Matejicek(slax.org) made a donation (much more than once). -+ Since Apr 2010, Tomas M (the author of Slax and Linux Live -+ scripts) is making "doubling" donations. -+ Unfortunately I cannot list all of the donators, but I really -+ appreciate. -+ It ends Aug 2010, but the ordinary donation URL is still available. -+ -+Dai Itasaka made a donation (2007/8). -+Chuck Smith made a donation (2008/4, 10 and 12). -+Henk Schoneveld made a donation (2008/9). -+Chih-Wei Huang, ASUS, CTC donated Eee PC 4G (2008/10). -+Francois Dupoux made a donation (2008/11). -+Bruno Cesar Ribas and Luis Carlos Erpen de Bona, C3SL serves public -+ aufs2 GIT tree (2009/2). -+William Grant made a donation (2009/3). -+Patrick Lane made a donation (2009/4). -+The Mail Archive (mail-archive.com) made donations (2009/5). -+Nippy Networks (Ed Wildgoose) made a donation (2009/7). -+New Dream Network, LLC (www.dreamhost.com) made a donation (2009/11). -+Pavel Pronskiy made a donation (2011/2). -+Iridium and Inmarsat satellite phone retailer (www.mailasail.com), Nippy -+ Networks (Ed Wildgoose) made a donation for hardware (2011/3). -+Max Lekomcev (DOM-TV project) made a donation (2011/7, 12, 2012/3, 6 and -+11). -+Sam Liddicott made a donation (2011/9). -+ -+Thank you very much. -+Donations are always, including future donations, very important and -+helpful for me to keep on developing aufs. -+ -+ -+7. -+---------------------------------------- -+If you are an experienced user, no explanation is needed. Aufs is -+just a linux filesystem. -+ -+ -+Enjoy! -+ -+# Local variables: ; -+# mode: text; -+# End: ; ++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 --- /dev/null -+++ linux-3.x-rcN/fs/aufs/aufs.h 2013-02-19 11:26:12.672456917 -0500 ++++ linux-3.9/fs/aufs/Makefile 2013-05-13 17:13:17.207129054 +0200 +@@ -0,0 +1,42 @@ ++ ++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 requires the full pathname ++ifdef M ++ccflags-y += -include ${M}/../../include/linux/aufs_type.h ++else ++ccflags-y += -include ${srctree}/include/linux/aufs_type.h ++endif ++ ++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 +--- /dev/null ++++ linux-3.9/fs/aufs/aufs.h 2013-05-13 17:13:17.207129054 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -1432,8 +1707,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/branch.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,1172 @@ ++++ linux-3.9/fs/aufs/branch.c 2013-05-13 17:13:17.207129054 +0200 +@@ -0,0 +1,1213 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -1463,6 +1738,34 @@ +/* + * free a single branch + */ ++ ++/* prohibit rmdir to the root of the branch */ ++/* todo: another new flag? */ ++static void au_br_dflags_force(struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ ++ h_dentry = au_br_dentry(br); ++ spin_lock(&h_dentry->d_lock); ++ br->br_dflags = h_dentry->d_flags & DCACHE_MOUNTED; ++ h_dentry->d_flags |= DCACHE_MOUNTED; ++ spin_unlock(&h_dentry->d_lock); ++} ++ ++/* restore its d_flags */ ++static void au_br_dflags_restore(struct au_branch *br) ++{ ++ struct dentry *h_dentry; ++ ++ if (br->br_dflags) ++ return; ++ ++ h_dentry = au_br_dentry(br); ++ spin_lock(&h_dentry->d_lock); ++ h_dentry->d_flags &= ~DCACHE_MOUNTED; ++ spin_unlock(&h_dentry->d_lock); ++} ++ +static void au_br_do_free(struct au_branch *br) +{ + int i; @@ -1492,9 +1795,11 @@ + else + break; + ++ au_br_dflags_restore(br); ++ + /* recursive lock, s_umount of branch's */ + lockdep_off(); -+ mntput(br->br_mnt); ++ path_put(&br->br_path); + lockdep_on(); + kfree(wbr); + kfree(br); @@ -1708,7 +2013,7 @@ + * 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 new_perm) +{ + int err, old_perm; + aufs_bindex_t bindex; @@ -1716,6 +2021,10 @@ + struct au_wbr *wbr; + struct au_hinode *hdir; + ++ err = vfsub_mnt_want_write(au_br_mnt(br)); ++ if (unlikely(err)) ++ goto out; ++ + wbr = br->br_wbr; + old_perm = br->br_perm; + br->br_perm = new_perm; @@ -1726,20 +2035,21 @@ + 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; ++ h_mtx = &au_br_dentry(br)->d_inode->i_mutex; + mutex_lock_nested(h_mtx, AuLsc_I_PARENT); + } + if (!wbr) -+ err = au_wh_init(h_root, br, sb); ++ err = au_wh_init(br, sb); + else { + wbr_wh_write_lock(wbr); -+ err = au_wh_init(h_root, br, sb); ++ err = au_wh_init(br, sb); + wbr_wh_write_unlock(wbr); + } + if (hdir) + au_hn_imtx_unlock(hdir); + else + mutex_unlock(h_mtx); ++ vfsub_mnt_drop_write(au_br_mnt(br)); + br->br_perm = old_perm; + + if (!err && wbr && !au_br_writable(new_perm)) { @@ -1747,16 +2057,16 @@ + br->br_wbr = NULL; + } + ++out: + return err; +} + +static int au_wbr_init(struct au_branch *br, struct super_block *sb, -+ int perm, struct path *path) ++ int perm) +{ + int err; + struct kstatfs kst; + struct au_wbr *wbr; -+ struct dentry *h_dentry; + + wbr = br->br_wbr; + au_rw_init(&wbr->wbr_wh_rwsem); @@ -1768,17 +2078,16 @@ + * a limit for rmdir/rename a dir + * cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h + */ -+ err = vfs_statfs(path, &kst); ++ err = vfs_statfs(&br->br_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); ++ err = au_br_init_wh(sb, br, perm); + else + pr_err("%.*s(%s), unsupported namelen %ld\n", -+ AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb), -+ kst.f_namelen); ++ AuDLNPair(au_br_dentry(br)), ++ au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); + +out: + return err; @@ -1794,7 +2103,10 @@ + 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 */ ++ BUILD_BUG_ON(sizeof(br->br_dflags) ++ != sizeof(br->br_path.dentry->d_flags)); ++ br->br_dflags = DCACHE_MOUNTED; ++ br->br_path = add->path; /* set first, path_get() later */ + spin_lock_init(&br->br_dykey_lock); + memset(br->br_dykey, 0, sizeof(br->br_dykey)); + atomic_set(&br->br_count, 0); @@ -1804,7 +2116,7 @@ + AuDebugOn(br->br_id < 0); + + if (au_br_writable(add->perm)) { -+ err = au_wbr_init(br, sb, add->perm, &add->path); ++ err = au_wbr_init(br, sb, add->perm); + if (unlikely(err)) + goto out_err; + } @@ -1819,11 +2131,11 @@ + } + + sysaufs_br_init(br); -+ mntget(add->path.mnt); ++ path_get(&br->br_path); + goto out; /* success */ + +out_err: -+ br->br_mnt = NULL; ++ memset(&br->br_path, 0, sizeof(br->br_path)); +out: + return err; +} @@ -1875,17 +2187,20 @@ + 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) ++static void au_br_do_add(struct super_block *sb, struct au_branch *br, ++ aufs_bindex_t bindex) +{ -+ struct dentry *root; ++ struct dentry *root, *h_dentry; + struct inode *root_inode; + aufs_bindex_t bend, amount; + ++ au_br_dflags_force(br); ++ + root = sb->s_root; + root_inode = root->d_inode; + bend = au_sbend(sb); + amount = bend + 1 - bindex; ++ h_dentry = au_br_dentry(br); + au_sbilist_lock(); + au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); + au_br_do_add_hdp(au_di(root), bindex, bend, amount); @@ -1928,15 +2243,15 @@ + } + + add_bindex = add->bindex; -+ h_dentry = add->path.dentry; + if (!remount) -+ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); + else { + sysaufs_brs_del(sb, add_bindex); -+ au_br_do_add(sb, h_dentry, add_branch, add_bindex); ++ au_br_do_add(sb, add_branch, add_bindex); + sysaufs_brs_add(sb, add_bindex); + } + ++ h_dentry = add->path.dentry; + if (!add_bindex) { + au_cpup_attr_all(root_inode, /*force*/1); + sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; @@ -2242,6 +2557,7 @@ + goto out; + } + br = au_sbr(sb, bindex); ++ AuDebugOn(!path_equal(&br->br_path, &del->h_path)); + i = atomic_read(&br->br_count); + if (unlikely(i)) { + AuVerbose(verbose, "%d file(s) opened\n", i); @@ -2290,7 +2606,7 @@ + +out_wh: + /* revert */ -+ rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry); ++ rerr = au_br_init_wh(sb, br, br->br_perm); + if (rerr) + pr_warn("failed re-creating base whiteout, %s. (%d)\n", + del->pathname, rerr); @@ -2408,7 +2724,7 @@ + do_file_list_for_each_entry(sb, f) { + if (au_fi(f) + && file_count(f) -+ && !special_file(f->f_dentry->d_inode->i_mode)) { ++ && !special_file(file_inode(f)->i_mode)) { + get_file(f); + *p++ = f; + n++; @@ -2473,7 +2789,7 @@ + goto out_array; + } + -+ inode = file->f_dentry->d_inode; ++ inode = file_inode(file); + hfile = &au_fi(file)->fi_htop; + hf = hfile->hf_file; + if (!S_ISREG(inode->i_mode) @@ -2510,8 +2826,8 @@ + hf->f_mode &= ~FMODE_WRITE; + spin_unlock(&hf->f_lock); + if (!file_check_writeable(hf)) { ++ __mnt_drop_write(hf->f_path.mnt); + file_release_write(hf); -+ vfsub_mnt_drop_write(hf->f_vfsmnt); + } + } + @@ -2527,7 +2843,6 @@ +{ + int err, rerr; + aufs_bindex_t bindex; -+ struct path path; + struct dentry *root; + struct au_branch *br; + @@ -2547,12 +2862,13 @@ + goto out; + + br = au_sbr(sb, bindex); ++ AuDebugOn(mod->h_root != au_br_dentry(br)); + 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); ++ err = au_br_init_wh(sb, br, mod->perm); + if (unlikely(err)) + goto out; + @@ -2569,12 +2885,8 @@ + 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 (br->br_wbr) ++ rerr = au_wbr_init(br, sb, br->br_perm); + if (unlikely(rerr)) { + AuIOErr("nested error %d (%d)\n", + rerr, err); @@ -2587,9 +2899,7 @@ + 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); ++ err = au_wbr_init(br, sb, mod->perm); + if (unlikely(err)) { + kfree(br->br_wbr); + br->br_wbr = NULL; @@ -2598,6 +2908,12 @@ + } + + if (!err) { ++ if ((br->br_perm & AuBrAttr_UNPIN) ++ && !(mod->perm & AuBrAttr_UNPIN)) ++ au_br_dflags_force(br); ++ else if (!(br->br_perm & AuBrAttr_UNPIN) ++ && (mod->perm & AuBrAttr_UNPIN)) ++ au_br_dflags_restore(br); + *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); + br->br_perm = mod->perm; + } @@ -2607,8 +2923,8 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/branch.h 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,236 @@ ++++ linux-3.9/fs/aufs/branch.h 2013-05-13 17:13:17.207129054 +0200 +@@ -0,0 +1,255 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -2686,7 +3002,8 @@ + aufs_bindex_t br_id; + + int br_perm; -+ struct vfsmount *br_mnt; ++ unsigned int br_dflags; ++ struct path br_path; + spinlock_t br_dykey_lock; + struct au_dykey *br_dykey[AuBrDynOp]; + atomic_t br_count; @@ -2710,6 +3027,21 @@ + +/* ---------------------------------------------------------------------- */ + ++static inline struct vfsmount *au_br_mnt(struct au_branch *br) ++{ ++ return br->br_path.mnt; ++} ++ ++static inline struct dentry *au_br_dentry(struct au_branch *br) ++{ ++ return br->br_path.dentry; ++} ++ ++static inline struct super_block *au_br_sb(struct au_branch *br) ++{ ++ return au_br_mnt(br)->mnt_sb; ++} ++ +/* branch permissions and attributes */ +#define AuBrPerm_RW 1 /* writable, hardlinkable wh */ +#define AuBrPerm_RO (1 << 1) /* readonly */ @@ -2720,6 +3052,9 @@ + +#define AuBrWAttr_NoLinkWH (1 << 4) /* un-hardlinkable whiteouts */ + ++#define AuBrAttr_UNPIN (1 << 5) /* rename-able top dir of ++ branch */ ++ +static inline int au_br_writable(int brperm) +{ + return brperm & AuBrPerm_RW; @@ -2737,7 +3072,7 @@ + +static inline int au_br_rdonly(struct au_branch *br) +{ -+ return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) ++ return ((au_br_sb(br)->s_flags & MS_RDONLY) + || !au_br_writable(br->br_perm)) + ? -EROFS : 0; +} @@ -2807,13 +3142,13 @@ +static inline +struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) +{ -+ return au_sbr(sb, bindex)->br_mnt; ++ return au_br_mnt(au_sbr(sb, bindex)); +} + +static inline +struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) +{ -+ return au_sbr_mnt(sb, bindex)->mnt_sb; ++ return au_br_sb(au_sbr(sb, bindex)); +} + +static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) @@ -2846,7 +3181,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_BRANCH_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/conf.mk 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/conf.mk 2013-05-13 17:13:17.207129054 +0200 @@ -0,0 +1,38 @@ + +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS} @@ -2887,8 +3222,8 @@ + +-include ${srctree}/${src}/conf_priv.mk --- /dev/null -+++ linux-3.x-rcN/fs/aufs/cpup.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,1085 @@ ++++ linux-3.9/fs/aufs/cpup.c 2013-05-13 17:13:17.210464023 +0200 +@@ -0,0 +1,1220 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -2915,12 +3250,14 @@ +#include +#include "aufs.h" + -+void au_cpup_attr_flags(struct inode *dst, struct inode *src) ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) +{ + const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE + | S_NOATIME | S_NOCMTIME; + -+ dst->i_flags |= src->i_flags & ~mask; ++ BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); ++ ++ dst->i_flags |= iflags & ~mask; + if (au_test_fs_notime(dst->i_sb)) + dst->i_flags |= S_NOATIME | S_NOCMTIME; +} @@ -2979,7 +3316,7 @@ + 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); ++ au_cpup_attr_flags(inode, h_inode->i_flags); +} + +void au_cpup_igen(struct inode *inode, struct inode *h_inode) @@ -3040,13 +3377,22 @@ + +/* ---------------------------------------------------------------------- */ + ++/* internal use only */ ++struct au_cpup_reg_attr { ++ int valid; ++ struct kstat st; ++ unsigned int iflags; /* inode->i_flags */ ++}; ++ +static noinline_for_stack -+int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) ++int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err, sbits; + struct iattr ia; + struct path h_path; + struct inode *h_isrc, *h_idst; ++ struct kstat *h_st; + + h_path.dentry = au_h_dptr(dst, bindex); + h_idst = h_path.dentry->d_inode; @@ -3055,17 +3401,32 @@ + 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; ++ if (h_src_attr && h_src_attr->valid) { ++ h_st = &h_src_attr->st; ++ ia.ia_uid = h_st->uid; ++ ia.ia_gid = h_st->gid; ++ ia.ia_atime = h_st->atime; ++ ia.ia_mtime = h_st->mtime; ++ if (h_idst->i_mode != h_st->mode ++ && !S_ISLNK(h_idst->i_mode)) { ++ ia.ia_valid |= ATTR_MODE; ++ ia.ia_mode = h_st->mode; ++ } ++ sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); ++ au_cpup_attr_flags(h_idst, h_src_attr->iflags); ++ } else { ++ 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->i_flags); + } -+ 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? */ @@ -3165,7 +3526,7 @@ + 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; ++ h_mtx = &file_inode(dst)->i_mutex; + mutex_lock_nested(h_mtx, AuLsc_I_CHILD2); + err = vfsub_notify_change(&dst->f_path, ia); + mutex_unlock(h_mtx); @@ -3210,12 +3571,18 @@ + return err; +} + ++/* internal use only */ ++struct au_cpup_basic { ++ struct dentry *dentry; ++ aufs_bindex_t bdst, bsrc; ++ loff_t len; ++}; ++ +/* + * 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) ++static int au_cp_regular(struct au_cpup_basic *basic) +{ + int err, i; + enum { SRC, DST }; @@ -3227,14 +3594,14 @@ + void *label, *label_file; + } *f, file[] = { + { -+ .bindex = bsrc, ++ .bindex = basic->bsrc, + .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out, + .label_file = &&out_src + }, + { -+ .bindex = bdst, ++ .bindex = basic->bdst, + .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, + .file = NULL, + .label = &&out_src, @@ -3244,11 +3611,12 @@ + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ -+ sb = dentry->d_sb; ++ sb = basic->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); ++ f->dentry = au_h_dptr(basic->dentry, f->bindex); ++ f->file = au_h_open(basic->dentry, f->bindex, f->flags, ++ /*file*/NULL); + err = PTR_ERR(f->file); + if (IS_ERR(f->file)) + goto *f->label; @@ -3259,7 +3627,7 @@ + + /* try stopping to update while we copyup */ + IMustLock(file[SRC].dentry->d_inode); -+ err = au_copy_file(file[DST].file, file[SRC].file, len); ++ err = au_copy_file(file[DST].file, file[SRC].file, basic->len); + +out_dst: + fput(file[DST].file); @@ -3271,27 +3639,38 @@ + 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) ++static int au_do_cpup_regular(struct au_cpup_basic *basic, struct au_pin *pin, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err, rerr; + loff_t l; ++ struct path h_path; ++ struct inode *h_src_inode; + + 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 */ ++ h_src_inode = au_h_iptr(basic->dentry->d_inode, basic->bsrc); ++ l = i_size_read(h_src_inode); ++ if (basic->len == -1 || l < basic->len) ++ basic->len = l; ++ if (basic->len) { ++ /* try stopping to update while we are referencing */ ++ mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); ++ au_pin_hdir_unlock(pin); + -+ 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; ++ h_path.dentry = au_h_dptr(basic->dentry, basic->bsrc); ++ h_path.mnt = au_sbr_mnt(basic->dentry->d_sb, basic->bsrc); ++ h_src_attr->iflags = h_src_inode->i_flags; ++ err = vfs_getattr(&h_path, &h_src_attr->st); ++ if (unlikely(err)) { ++ mutex_unlock(&h_src_inode->i_mutex); ++ goto out; ++ } ++ h_src_attr->valid = 1; ++ err = au_cp_regular(basic); ++ mutex_unlock(&h_src_inode->i_mutex); ++ rerr = au_pin_hdir_relock(pin); ++ if (!err && rerr) ++ err = rerr; + } + +out: @@ -3334,11 +3713,10 @@ + 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 cpup_entry(struct au_cpup_basic *basic, unsigned int flags, ++ struct dentry *dst_parent, struct au_pin *pin, ++ struct au_cpup_reg_attr *h_src_attr) +{ + int err; + umode_t mode; @@ -3352,19 +3730,22 @@ + struct super_block *sb; + + /* bsrc branch can be ro/rw. */ -+ h_src = au_h_dptr(dentry, bsrc); ++ h_src = au_h_dptr(basic->dentry, basic->bsrc); + h_inode = h_src->d_inode; -+ AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc)); ++ AuDebugOn(h_inode != au_h_iptr(basic->dentry->d_inode, basic->bsrc)); + + /* try stopping to be referenced while we are creating */ -+ h_dst = au_h_dptr(dentry, bdst); ++ h_dst = au_h_dptr(basic->dentry, basic->bdst); ++ if (au_ftest_cpup(flags, RENAME)) ++ AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, ++ AUFS_WH_PFX_LEN)); + 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); ++ sb = basic->dentry->d_sb; ++ h_path.mnt = au_sbr_mnt(sb, basic->bdst); + if (do_dt) { + h_path.dentry = h_parent; + au_dtime_store(&dt, dst_parent, &h_path); @@ -3375,14 +3756,10 @@ + 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, + /*want_excl*/true); + if (!err) -+ err = au_do_cpup_regular -+ (dentry, bdst, bsrc, len, -+ au_h_iptr(dst_parent->d_inode, bdst), &h_path); ++ err = au_do_cpup_regular(basic, pin, h_src_attr); + break; + case S_IFDIR: + isdir = 1; @@ -3392,10 +3769,10 @@ + * strange behaviour from the users view, + * particularry setattr case + */ -+ if (au_ibstart(dst_parent->d_inode) == bdst) ++ if (au_ibstart(dst_parent->d_inode) == basic->bdst) + au_cpup_attr_nlink(dst_parent->d_inode, + /*force*/1); -+ au_cpup_attr_nlink(dentry->d_inode, /*force*/1); ++ au_cpup_attr_nlink(basic->dentry->d_inode, /*force*/1); + } + break; + case S_IFLNK: @@ -3420,10 +3797,10 @@ + && au_opt_test(mnt_flags, XINO) + && h_inode->i_nlink == 1 + /* todo: unnecessary? */ -+ /* && dentry->d_inode->i_nlink == 1 */ -+ && bdst < bsrc ++ /* && basic->dentry->d_inode->i_nlink == 1 */ ++ && basic->bdst < basic->bsrc + && !au_ftest_cpup(flags, KEEPLINO)) -+ au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0); ++ au_xino_write(sb, basic->bsrc, h_inode->i_ino, /*ino*/0); + /* ignore this error */ + + if (do_dt) @@ -3431,15 +3808,42 @@ + return err; +} + ++static int au_do_ren_after_cpup(struct dentry *dentry, aufs_bindex_t bdst, ++ struct path *h_path) ++{ ++ int err; ++ struct dentry *h_dentry, *h_parent; ++ struct inode *h_dir; ++ ++ h_dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, NULL); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); ++ if (unlikely(err)) { ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ goto out; ++ } ++ ++ h_path->dentry = dget(au_h_dptr(dentry, bdst)); ++ au_set_h_dptr(dentry, bdst, h_dentry); ++ h_parent = h_dentry->d_parent; /* dir inode is locked */ ++ h_dir = h_parent->d_inode; ++ IMustLock(h_dir); ++ AuDbg("%.*s %.*s\n", AuDLNPair(h_dentry), AuDLNPair(h_path->dentry)); ++ err = vfsub_rename(h_dir, h_dentry, h_dir, h_path); ++ dput(h_path->dentry); ++ ++out: ++ 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) ++static int au_cpup_single(struct au_cpup_basic *basic, unsigned int flags, ++ struct dentry *dst_parent, struct au_pin *pin) +{ + int err, rerr; + aufs_bindex_t old_ibstart; @@ -3449,39 +3853,44 @@ + struct dentry *h_src, *h_dst, *h_parent; + struct inode *dst_inode, *h_dir, *inode; + struct super_block *sb; ++ struct au_branch *br; ++ struct au_cpup_reg_attr h_src_attr = { ++ .valid = 0 ++ }; + -+ AuDebugOn(bsrc <= bdst); ++ AuDebugOn(basic->bsrc <= basic->bdst); + -+ sb = dentry->d_sb; -+ h_path.mnt = au_sbr_mnt(sb, bdst); -+ h_dst = au_h_dptr(dentry, bdst); ++ sb = basic->dentry->d_sb; ++ br = au_sbr(sb, basic->bdst); ++ h_path.mnt = au_br_mnt(br); ++ h_dst = au_h_dptr(basic->dentry, basic->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; ++ h_src = au_h_dptr(basic->dentry, basic->bsrc); ++ inode = basic->dentry->d_inode; + + if (!dst_parent) -+ dst_parent = dget_parent(dentry); ++ dst_parent = dget_parent(basic->dentry); + else + dget(dst_parent); + + plink = !!au_opt_test(au_mntflags(sb), PLINK); -+ dst_inode = au_h_iptr(inode, bdst); ++ dst_inode = au_h_iptr(inode, basic->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); ++ dst_inode->i_ino, inode->i_ino, basic->bdst); + goto out; + } + + if (dst_inode->i_nlink) { + const int do_dt = au_ftest_cpup(flags, DTIME); + -+ h_src = au_plink_lkup(inode, bdst); ++ h_src = au_plink_lkup(inode, basic->bdst); + err = PTR_ERR(h_src); + if (IS_ERR(h_src)) + goto out; @@ -3498,8 +3907,12 @@ + 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 (!err && au_ftest_cpup(flags, RENAME)) ++ err = au_do_ren_after_cpup ++ (basic->dentry, basic->bdst, &h_path); + if (do_dt) + au_dtime_revert(&dt); + dput(h_src); @@ -3510,50 +3923,73 @@ + au_update_ibrange(inode, /*do_put_zero*/1); + } + ++ isdir = S_ISDIR(inode->i_mode); + old_ibstart = au_ibstart(inode); -+ err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent); ++ err = cpup_entry(basic, flags, dst_parent, pin, &h_src_attr); + if (unlikely(err)) -+ goto out; ++ goto out_rev; + dst_inode = h_dst->d_inode; + mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); ++ /* todo: necessary? */ ++ /* au_pin_hdir_unlock(pin); */ + -+ 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)); ++ err = cpup_iattr(basic->dentry, basic->bdst, h_src, &h_src_attr); ++ if (unlikely(err)) { ++ /* todo: necessary? */ ++ /* au_pin_hdir_relock(pin); */ /* ignore an error */ + 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 */ ++ goto out_rev; + } + ++ if (basic->bdst < old_ibstart) { ++ if (S_ISREG(inode->i_mode)) { ++ err = au_dy_iaop(inode, basic->bdst, dst_inode); ++ if (unlikely(err)) { ++ /* au_pin_hdir_relock(pin); ignore an error */ ++ mutex_unlock(&dst_inode->i_mutex); ++ goto out_rev; ++ } ++ } ++ au_set_ibstart(inode, basic->bdst); ++ } ++ au_set_h_iptr(inode, basic->bdst, au_igrab(dst_inode), ++ au_hi_flags(inode, isdir)); ++ ++ /* todo: necessary? */ ++ /* err = au_pin_hdir_relock(pin); */ ++ mutex_unlock(&dst_inode->i_mutex); ++ if (unlikely(err)) ++ goto out_rev; ++ ++ if (!isdir ++ && h_src->d_inode->i_nlink > 1 ++ && plink) ++ au_plink_append(inode, basic->bdst, h_dst); ++ ++ if (au_ftest_cpup(flags, RENAME)) { ++ h_path.dentry = h_dst; ++ err = au_do_ren_after_cpup(basic->dentry, basic->bdst, &h_path); ++ } ++ if (!err) ++ 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); ++ rerr = 0; ++ if (h_dst->d_inode) { ++ 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; @@ -3561,18 +3997,19 @@ + +struct au_cpup_single_args { + int *errp; -+ struct dentry *dentry; -+ aufs_bindex_t bdst, bsrc; -+ loff_t len; ++ struct au_cpup_basic *basic; + unsigned int flags; + struct dentry *dst_parent; ++ struct au_pin *pin; +}; + +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); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_single(a->basic, a->flags, a->dst_parent, a->pin); ++ au_pin_hdir_release(a->pin); +} + +/* @@ -3608,24 +4045,27 @@ + +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) ++ struct dentry *dst_parent, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *h_dentry; ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len ++ }; + + h_dentry = au_h_dptr(dentry, bsrc); + if (!au_cpup_sio_test(dentry->d_sb, h_dentry->d_inode->i_mode)) -+ err = au_cpup_single(dentry, bdst, bsrc, len, flags, -+ dst_parent); ++ err = au_cpup_single(&basic, flags, dst_parent, pin); + else { + struct au_cpup_single_args args = { + .errp = &err, -+ .dentry = dentry, -+ .bdst = bdst, -+ .bsrc = bsrc, -+ .len = len, ++ .basic = &basic, + .flags = flags, -+ .dst_parent = dst_parent ++ .dst_parent = dst_parent, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_single, &args); + if (unlikely(wkq_err)) @@ -3640,19 +4080,32 @@ + * using au_cpup_single(). + */ +static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags) ++ unsigned int flags, struct au_pin *pin) +{ + int err; + aufs_bindex_t bsrc, bend; ++ struct dentry *h_dentry; + ++ DiMustWriteLock(dentry); + bend = au_dbend(dentry); -+ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) -+ if (au_h_dptr(dentry, bsrc)) ++ for (bsrc = bdst + 1; bsrc <= bend; bsrc++) { ++ h_dentry = au_h_dptr(dentry, bsrc); ++ if (h_dentry) { ++ AuDebugOn(!h_dentry->d_inode); + break; ++ } ++ } ++ AuDebugOn(bsrc > bend); + -+ err = au_lkup_neg(dentry, bdst); ++ err = au_lkup_neg(dentry, bdst, /*wh*/1); + if (!err) { -+ err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL); ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = bsrc, ++ .len = len ++ }; ++ err = au_cpup_single(&basic, flags | AuCpup_RENAME, NULL, pin); + if (!err) + return 0; /* success */ + @@ -3670,16 +4123,20 @@ + aufs_bindex_t bdst; + loff_t len; + unsigned int flags; ++ struct au_pin *pin; +}; + +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); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags, a->pin); ++ au_pin_hdir_release(a->pin); +} + +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags) ++ unsigned int flags, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *parent; @@ -3689,14 +4146,15 @@ + h_dir = au_h_iptr(parent->d_inode, bdst); + if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) -+ err = au_cpup_simple(dentry, bdst, len, flags); ++ err = au_cpup_simple(dentry, bdst, len, flags, pin); + else { + struct au_cpup_simple_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, -+ .flags = flags ++ .flags = flags, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_simple, &args); + if (unlikely(wkq_err)) @@ -3714,10 +4172,15 @@ + */ +static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, + struct dentry *wh_dentry, struct file *file, -+ loff_t len) ++ loff_t len, struct au_pin *pin) +{ + int err; -+ aufs_bindex_t bstart; ++ struct au_cpup_basic basic = { ++ .dentry = dentry, ++ .bdst = bdst, ++ .bsrc = -1, ++ .len = len ++ }; + struct au_dinfo *dinfo; + struct dentry *h_d_dst, *h_d_start; + struct au_hdentry *hdp; @@ -3725,30 +4188,30 @@ + dinfo = au_di(dentry); + AuRwMustWriteLock(&dinfo->di_rwsem); + -+ bstart = dinfo->di_bstart; ++ basic.bsrc = 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; ++ h_d_start = NULL; + if (file) { -+ h_d_start = hdp[0 + bstart].hd_dentry; -+ hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry; ++ h_d_start = hdp[0 + basic.bsrc].hd_dentry; ++ hdp[0 + basic.bsrc].hd_dentry = au_hf_top(file)->f_dentry; + } -+ err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, -+ /*h_parent*/NULL); ++ err = au_cpup_single(&basic, !AuCpup_DTIME, /*h_parent*/NULL, pin); + if (file) { + if (!err) + err = au_reopen_nondir(file); -+ hdp[0 + bstart].hd_dentry = h_d_start; ++ hdp[0 + basic.bsrc].hd_dentry = h_d_start; + } + hdp[0 + bdst].hd_dentry = h_d_dst; -+ dinfo->di_bstart = bstart; ++ dinfo->di_bstart = basic.bsrc; + + return err; +} + +static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file) ++ struct file *file, struct au_pin *pin) +{ + int err; + struct au_dtime dt; @@ -3765,9 +4228,9 @@ + goto out; + + h_path.dentry = h_parent; -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + au_dtime_store(&dt, parent, &h_path); -+ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); ++ err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len, pin); + if (unlikely(err)) + goto out_wh; + @@ -3798,21 +4261,26 @@ + aufs_bindex_t bdst; + loff_t len; + struct file *file; ++ struct au_pin *pin; +}; + +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); ++ ++ au_pin_hdir_acquire_nest(a->pin); ++ *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file, a->pin); ++ au_pin_hdir_release(a->pin); +} + +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file) ++ struct file *file, struct au_pin *pin) +{ + int err, wkq_err; + struct dentry *parent, *h_orph, *h_parent, *h_dentry; -+ struct inode *dir, *h_dir, *h_tmpdir, *h_inode; ++ struct inode *dir, *h_dir, *h_tmpdir; + struct au_wbr *wbr; ++ struct au_pin wh_pin; + + parent = dget_parent(dentry); + dir = parent->d_inode; @@ -3829,29 +4297,29 @@ + 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()? */ ++ ++ au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, ++ AuLsc_I_PARENT3, pin->udba, AuPin_DI_LOCKED); ++ pin = &wh_pin; + } + + if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) + && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) -+ err = au_cpup_wh(dentry, bdst, len, file); ++ err = au_cpup_wh(dentry, bdst, len, file, pin); + else { + struct au_cpup_wh_args args = { + .errp = &err, + .dentry = dentry, + .bdst = bdst, + .len = len, -+ .file = file ++ .file = file, ++ .pin = pin + }; + wkq_err = au_wkq_wait(au_call_cpup_wh, &args); + if (unlikely(wkq_err)) @@ -3878,6 +4346,7 @@ +/* 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 au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg) +{ @@ -3923,7 +4392,7 @@ + au_pin_set_dentry(&pin, d); + err = au_do_pin(&pin); + if (!err) { -+ err = cp(d, bdst, h_parent, arg); ++ err = cp(d, bdst, &pin, h_parent, arg); + au_unpin(&pin); + } + } @@ -3940,10 +4409,11 @@ +} + +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent __maybe_unused , + void *arg __maybe_unused) +{ -+ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); ++ return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME, pin); +} + +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) @@ -3975,8 +4445,8 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/cpup.h 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,81 @@ ++++ linux-3.9/fs/aufs/cpup.h 2013-05-13 17:13:17.210464023 +0200 +@@ -0,0 +1,84 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -4008,8 +4478,9 @@ + +struct inode; +struct file; ++struct au_pin; + -+void au_cpup_attr_flags(struct inode *dst, struct inode *src); ++void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); +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); @@ -4022,6 +4493,7 @@ +#define AuCpup_DTIME 1 /* do dtime_store/revert */ +#define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, + for link(2) */ ++#define AuCpup_RENAME (1 << 2) /* rename after cpup */ +#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) +#define au_fset_cpup(flags, name) \ + do { (flags) |= AuCpup_##name; } while (0) @@ -4031,14 +4503,15 @@ +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); ++ struct dentry *dst_parent, struct au_pin *pin); +int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ unsigned int flags); ++ unsigned int flags, struct au_pin *pin); +int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, -+ struct file *file); ++ struct file *file, struct au_pin *pin); + +int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, + int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent, void *arg), + void *arg); +int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); @@ -4059,8 +4532,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_CPUP_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dbgaufs.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,334 @@ ++++ linux-3.9/fs/aufs/dbgaufs.c 2013-05-13 17:13:17.210464023 +0200 +@@ -0,0 +1,433 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -4126,7 +4599,7 @@ + if (!xf) + goto out; + -+ err = vfs_getattr(xf->f_vfsmnt, xf->f_dentry, &st); ++ err = vfs_getattr(&xf->f_path, &st); + if (!err) { + if (do_fcnt) + p->n = snprintf @@ -4159,6 +4632,101 @@ + +/* ---------------------------------------------------------------------- */ + ++struct dbgaufs_plink_arg { ++ int n; ++ char a[]; ++}; ++ ++static int dbgaufs_plink_release(struct inode *inode __maybe_unused, ++ struct file *file) ++{ ++ free_page((unsigned long)file->private_data); ++ return 0; ++} ++ ++static int dbgaufs_plink_open(struct inode *inode, struct file *file) ++{ ++ int err, i, limit; ++ unsigned long n, sum; ++ struct dbgaufs_plink_arg *p; ++ struct au_sbinfo *sbinfo; ++ struct super_block *sb; ++ struct au_sphlhead *sphl; ++ ++ err = -ENOMEM; ++ p = (void *)get_zeroed_page(GFP_NOFS); ++ if (unlikely(!p)) ++ goto out; ++ ++ err = -EFBIG; ++ sbinfo = inode->i_private; ++ sb = sbinfo->si_sb; ++ si_noflush_read_lock(sb); ++ if (au_opt_test(au_mntflags(sb), PLINK)) { ++ limit = PAGE_SIZE - sizeof(p->n); ++ ++ /* the number of buckets */ ++ n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); ++ p->n += n; ++ limit -= n; ++ ++ sum = 0; ++ for (i = 0, sphl = sbinfo->si_plink; ++ i < AuPlink_NHASH; ++ i++, sphl++) { ++ n = au_sphl_count(sphl); ++ sum += n; ++ ++ n = snprintf(p->a + p->n, limit, "%lu ", n); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } ++ p->a[p->n - 1] = '\n'; ++ ++ /* the sum of plinks */ ++ n = snprintf(p->a + p->n, limit, "%lu\n", sum); ++ p->n += n; ++ limit -= n; ++ if (unlikely(limit <= 0)) ++ goto out_free; ++ } else { ++#define str "1\n0\n0\n" ++ p->n = sizeof(str) - 1; ++ strcpy(p->a, str); ++#undef str ++ } ++ si_read_unlock(sb); ++ ++ err = 0; ++ file->private_data = p; ++ goto out; /* success */ ++ ++out_free: ++ free_page((unsigned long)p); ++out: ++ return err; ++} ++ ++static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct dbgaufs_plink_arg *p; ++ ++ p = file->private_data; ++ return simple_read_from_buffer(buf, count, ppos, p->a, p->n); ++} ++ ++static const struct file_operations dbgaufs_plink_fop = { ++ .owner = THIS_MODULE, ++ .open = dbgaufs_plink_open, ++ .release = dbgaufs_plink_release, ++ .read = dbgaufs_plink_read ++}; ++ ++/* ---------------------------------------------------------------------- */ ++ +static int dbgaufs_xib_open(struct inode *inode, struct file *file) +{ + int err; @@ -4238,10 +4806,8 @@ + 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; -+ } ++ debugfs_remove(xi->xi_dbgaufs); ++ xi->xi_dbgaufs = NULL; + } +} + @@ -4368,6 +4934,12 @@ + if (unlikely(!sbinfo->si_dbgaufs_xib)) + goto out_dir; + ++ sbinfo->si_dbgaufs_plink = debugfs_create_file ++ ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, ++ &dbgaufs_plink_fop); ++ if (unlikely(!sbinfo->si_dbgaufs_plink)) ++ goto out_dir; ++ + err = dbgaufs_xigen_init(sbinfo); + if (!err) + goto out; /* success */ @@ -4396,7 +4968,7 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dbgaufs.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dbgaufs.h 2013-05-13 17:13:17.210464023 +0200 @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -4448,7 +5020,7 @@ +#endif /* __KERNEL__ */ +#endif /* __DBGAUFS_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dcsub.c 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dcsub.c 2013-05-13 17:13:17.210464023 +0200 @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -4694,7 +5266,7 @@ + return path_is_under(path + 0, path + 1); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dcsub.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dcsub.h 2013-05-13 17:13:17.210464023 +0200 @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -4791,8 +5363,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DCSUB_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/debug.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,490 @@ ++++ linux-3.9/fs/aufs/debug.c 2013-05-13 17:13:17.210464023 +0200 +@@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -4836,17 +5408,16 @@ +{ + unsigned long ul, n; + struct hlist_head *head; -+ struct au_vdir_wh *tpos; -+ struct hlist_node *pos; ++ struct au_vdir_wh *pos; + + n = whlist->nh_num; + head = whlist->nh_head; + for (ul = 0; ul < n; ul++) { -+ hlist_for_each_entry(tpos, pos, head, wh_hash) ++ hlist_for_each_entry(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); ++ pos->wh_bindex, ++ pos->wh_str.len, pos->wh_str.name, ++ pos->wh_str.len); + head++; + } +} @@ -4933,10 +5504,9 @@ +void au_dpri_dalias(struct inode *inode) +{ + struct dentry *d; -+ struct hlist_node *p; + + spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, p, &inode->i_dentry, d_alias) ++ hlist_for_each_entry(d, &inode->i_dentry, d_alias) + au_dpri_dentry(d); + spin_unlock(&inode->i_lock); +} @@ -5052,7 +5622,7 @@ + + if (!br || IS_ERR(br)) + goto out; -+ mnt = br->br_mnt; ++ mnt = au_br_mnt(br); + if (!mnt || IS_ERR(mnt)) + goto out; + sb = mnt->mnt_sb; @@ -5093,7 +5663,7 @@ + + a->mnt.mnt_sb = sb; + a->fake.br_perm = 0; -+ a->fake.br_mnt = &a->mnt; ++ a->fake.br_path.mnt = &a->mnt; + a->fake.br_xino.xi_file = NULL; + atomic_set(&a->fake.br_count, 0); + smp_mb(); /* atomic_set */ @@ -5123,8 +5693,11 @@ + +void au_dbg_iattr(struct iattr *ia) +{ -+#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \ -+ dpri(#name "\n") ++#define AuBit(name) \ ++ do { \ ++ if (ia->ia_valid & ATTR_ ## name) \ ++ dpri(#name "\n"); \ ++ } while (0) + AuBit(MODE); + AuBit(UID); + AuBit(GID); @@ -5284,7 +5857,7 @@ + return 0; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/debug.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/debug.h 2013-05-13 17:13:17.210464023 +0200 @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -5529,8 +6102,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DEBUG_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dentry.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,1060 @@ ++++ linux-3.9/fs/aufs/dentry.c 2013-05-13 17:13:17.210464023 +0200 +@@ -0,0 +1,1065 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -5779,15 +6352,19 @@ +/* + * lookup @dentry on @bindex which should be negative. + */ -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) +{ + int err; + struct dentry *parent, *h_parent, *h_dentry; ++ struct au_branch *br; + + 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)); ++ br = au_sbr(dentry->d_sb, bindex); ++ if (wh) ++ h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); ++ else ++ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, br); + err = PTR_ERR(h_dentry); + if (IS_ERR(h_dentry)) + goto out; @@ -6588,11 +7165,12 @@ +} + +const struct dentry_operations aufs_dop = { -+ .d_revalidate = aufs_d_revalidate, -+ .d_release = aufs_d_release ++ .d_revalidate = aufs_d_revalidate, ++ .d_weak_revalidate = aufs_d_revalidate, ++ .d_release = aufs_d_release +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dentry.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dentry.h 2013-05-13 17:13:17.213798992 +0200 @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -6648,7 +7226,7 @@ + struct dentry *h_parent, struct au_branch *br); + +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type); -+int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); ++int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh); +int au_refresh_dentry(struct dentry *dentry, struct dentry *parent); +int au_reval_dpath(struct dentry *dentry, unsigned int sigen); + @@ -6829,7 +7407,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DENTRY_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dinfo.c 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dinfo.c 2013-05-13 17:13:17.213798992 +0200 @@ -0,0 +1,543 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -7375,8 +7953,8 @@ + return -1; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dir.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,633 @@ ++++ linux-3.9/fs/aufs/dir.c 2013-05-13 17:13:17.213798992 +0200 +@@ -0,0 +1,630 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -7439,19 +8017,16 @@ + + sz = 0; + if (file) { -+ AuDebugOn(!file->f_dentry); -+ AuDebugOn(!file->f_dentry->d_inode); -+ AuDebugOn(!S_ISDIR(file->f_dentry->d_inode->i_mode)); ++ AuDebugOn(!file_inode(file)); ++ AuDebugOn(!S_ISDIR(file_inode(file)->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); ++ if (h_file && file_inode(h_file)) ++ sz += vfsub_f_size_read(h_file); + } + } else { + AuDebugOn(!dentry); @@ -7689,7 +8264,7 @@ + goto out; + + sb = file->f_dentry->d_sb; -+ inode = file->f_dentry->d_inode; ++ inode = file_inode(file); + bend = au_fbend_dir(file); + for (bindex = au_fbstart(file); !err && bindex <= bend; bindex++) { + h_file = au_hf_dir(file, bindex); @@ -7862,7 +8437,7 @@ + + err = 0; + if (!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE) -+ && !h_file->f_dentry->d_inode->i_nlink) ++ && !file_inode(h_file)->i_nlink) + goto out_put; + + do { @@ -8011,7 +8586,7 @@ + .fsync = aufs_fsync_dir +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dir.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dir.h 2013-05-13 17:13:17.213798992 +0200 @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -8151,7 +8726,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DIR_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dynop.c 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dynop.c 2013-05-13 17:13:17.213798992 +0200 @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima @@ -8394,7 +8969,7 @@ + + key->dk_op.dy_hop = op->dy_hop; + kref_init(&key->dk_kref); -+ p->set(key, op->dy_hop, br->br_mnt->mnt_sb); ++ p->set(key, op->dy_hop, au_br_sb(br)); + old = dy_gadd(spl, key); + if (old) { + kfree(key); @@ -8533,7 +9108,7 @@ + WARN_ON(!list_empty(&dynop[i].head)); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/dynop.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/dynop.h 2013-05-13 17:13:17.213798992 +0200 @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima @@ -8612,8 +9187,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_DYNOP_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/export.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,812 @@ ++++ linux-3.9/fs/aufs/export.c 2013-05-13 17:13:17.213798992 +0200 +@@ -0,0 +1,826 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -8705,6 +9280,21 @@ + return !!(dentry->d_flags & DCACHE_DISCONNECTED); +} + ++int au_test_nfsd(void) ++{ ++ int ret; ++ struct task_struct *tsk = current; ++ char comm[sizeof(tsk->comm)]; ++ ++ ret = 0; ++ if (tsk->flags & PF_KTHREAD) { ++ get_task_comm(comm, tsk); ++ ret = !strcmp(comm, "nfsd"); ++ } ++ ++ return ret; ++} ++ +/* ---------------------------------------------------------------------- */ +/* inode generation external table */ + @@ -8763,7 +9353,7 @@ + file = sbinfo->si_xigen; + BUG_ON(!file); + -+ if (i_size_read(file->f_dentry->d_inode) ++ if (vfsub_f_size_read(file) + < 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, @@ -8827,7 +9417,6 @@ + struct dentry *dentry, *d; + struct inode *inode; + unsigned int sigen; -+ struct hlist_node *p; + + dentry = NULL; + inode = ilookup(sb, ino); @@ -8846,7 +9435,7 @@ + dentry = d_find_alias(inode); + else { + spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, p, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(d, &inode->i_dentry, d_alias) { + spin_lock(&d->d_lock); + if (!au_test_anon(d) + && d->d_parent->d_inode->i_ino == dir_ino) { @@ -9109,7 +9698,7 @@ + struct path path; + + br = au_sbr(sb, nsi_lock->bindex); -+ h_mnt = br->br_mnt; ++ h_mnt = au_br_mnt(br); + 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), @@ -9333,7 +9922,7 @@ + + err = -EPERM; + br = au_sbr(sb, bindex); -+ h_sb = br->br_mnt->mnt_sb; ++ h_sb = au_br_sb(br); + if (unlikely(!h_sb->s_export_op)) { + AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); + goto out_hparent; @@ -9427,8 +10016,1029 @@ + atomic_set(&sbinfo->si_xigen_next, u); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/file.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,683 @@ ++++ linux-3.9/fs/aufs/f_op.c 2013-05-13 17:13:17.213798992 +0200 +@@ -0,0 +1,720 @@ ++/* ++ * Copyright (C) 2005-2013 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 "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)); ++ atomic_set(&finfo->fi_mmapped, 0); ++ 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) ++ 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); ++} ++ ++/* ---------------------------------------------------------------------- */ ++/* ++ * read and write functions acquire [fdi]_rwsem once, but release before ++ * mmap_sem. This is because to stop a race condition between mmap(2). ++ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping ++ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in ++ * read functions after [fdi]_rwsem are released, but it should be harmless. ++ */ ++ ++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); ++ get_file(h_file); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ /* filedata may be obsoleted by concurrent copyup, but no problem */ ++ err = vfsub_read_u(h_file, buf, count, ppos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_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 super_block *sb; ++ struct inode *inode; ++ struct file *h_file; ++ char __user *buf = (char __user *)ubuf; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ 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)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = vfsub_write_u(h_file, buf, count, ppos); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(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; ++ lockdep_off(); ++ err = func(kio, iov, nv, pos); ++ lockdep_on(); ++ 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); ++ get_file(h_file); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ err = au_do_aio(h_file, MAY_READ, kio, iov, nv, pos); ++ /* todo: necessary? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_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; ++ struct super_block *sb; ++ ++ file = kio->ki_filp; ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ 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)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = au_do_aio(h_file, MAY_WRITE, kio, iov, nv, pos); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(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); ++ get_file(h_file); ++ if (au_test_loopback_kthread()) { ++ au_warn_loopback(h_file->f_dentry->d_sb); ++ if (file->f_mapping != h_file->f_mapping) { ++ file->f_mapping = h_file->f_mapping; ++ smp_mb(); /* unnecessary? */ ++ } ++ } ++ di_read_unlock(dentry, AuLock_IR); ++ fi_read_unlock(file); ++ ++ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); ++ /* todo: necessasry? */ ++ /* file->f_ra = h_file->f_ra; */ ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(dentry->d_inode, file_inode(h_file)); ++ fput(h_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; ++ struct super_block *sb; ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ 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)) { ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ goto out; ++ } ++ ++ h_file = au_hf_top(file); ++ get_file(h_file); ++ au_unpin(&pin); ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++ ++ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); ++ ii_write_lock_child(inode); ++ au_cpup_attr_timesizes(inode); ++ inode->i_mode = file_inode(h_file)->i_mode; ++ ii_write_unlock(inode); ++ fput(h_file); ++ ++out: ++ si_read_unlock(sb); ++ mutex_unlock(&inode->i_mutex); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++/* ++ * The locking order around current->mmap_sem. ++ * - in most and regular cases ++ * file I/O syscall -- aufs_read() or something ++ * -- si_rwsem for read -- mmap_sem ++ * (Note that [fdi]i_rwsem are released before mmap_sem). ++ * - in mmap case ++ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem ++ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for ++ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in ++ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. ++ * It means that when aufs acquires si_rwsem for write, the process should never ++ * acquire mmap_sem. ++ * ++ * Actually aufs_readdir() holds [fdi]i_rwsem before mmap_sem, but this is not a ++ * problem either since any directory is not able to be mmap-ed. ++ * The similar scenario is applied to aufs_readlink() too. ++ */ ++ ++/* 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, LOCKED); ++} ++ ++static int aufs_mmap(struct file *file, struct vm_area_struct *vma) ++{ ++ 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; ++ struct file *h_file; ++ struct au_branch *br; ++ struct au_pin pin; ++ ++ AuDbgVmRegion(file, vma); ++ ++ dentry = file->f_dentry; ++ sb = dentry->d_sb; ++ lockdep_off(); ++ si_read_lock(sb, AuLock_NOPLMW); ++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); ++ if (unlikely(err)) ++ goto out; ++ ++ if (wlock) { ++ err = au_ready_to_write(file, -1, &pin); ++ di_write_unlock(dentry); ++ if (unlikely(err)) { ++ fi_write_unlock(file); ++ goto out; ++ } ++ 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_set_mmapped(file); ++ fi_write_unlock(file); ++ lockdep_on(); ++ ++ au_vm_file_reset(vma, h_file); ++ err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), ++ au_flag_conv(vma->vm_flags)); ++ if (!err) ++ err = h_file->f_op->mmap(h_file, vma); ++ if (unlikely(err)) ++ goto out_reset; ++ ++ au_vm_prfile_set(vma, file); ++ /* update without lock, I don't think it a problem */ ++ fsstack_copy_attr_atime(file_inode(file), file_inode(h_file)); ++ goto out_fput; /* success */ ++ ++out_reset: ++ au_unset_mmapped(file); ++ au_vm_file_reset(vma, file); ++out_fput: ++ fput(h_file); ++ lockdep_off(); ++out: ++ si_read_unlock(sb); ++ lockdep_on(); ++ AuTraceErr(err); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, ++ 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; ++ sb = dentry->d_sb; ++ mutex_lock(&inode->i_mutex); ++ 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); ++ err = vfsub_fsync(h_file, &h_file->f_path, datasync); ++ au_cpup_attr_timesizes(inode); ++ ++out_unlock: ++ di_read_unlock(dentry, AuLock_IR); ++ fi_write_unlock(file); ++out_si: ++ si_read_unlock(sb); ++out: ++ mutex_unlock(&inode->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 mutex *h_mtx; ++ ++ h_mtx = &file_inode(h_file)->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, ++ ++ .llseek = default_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 ++}; +--- /dev/null ++++ linux-3.9/fs/aufs/f_op_sp.c 2013-05-13 17:13:17.213798992 +0200 +@@ -0,0 +1,295 @@ ++/* ++ * Copyright (C) 2005-2013 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 "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); ++ 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, &pin); ++ 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 = file_inode(h_file); ++ 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_inode(file), 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; ++} +--- /dev/null ++++ linux-3.9/fs/aufs/file.c 2013-05-13 17:13:17.213798992 +0200 +@@ -0,0 +1,688 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -9498,7 +11108,7 @@ + br = au_sbr(sb, bindex); + h_file = ERR_PTR(-EACCES); + exec_flag = flags & __FMODE_EXEC; -+ if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) ++ if (exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC)) + goto out; + + /* drop flags for writing */ @@ -9507,7 +11117,7 @@ + flags &= ~O_CREAT; + atomic_inc(&br->br_count); + h_path.dentry = h_dentry; -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + if (!au_special_file(h_inode->i_mode)) + h_file = vfsub_dentry_open(&h_path, flags); + else { @@ -9585,13 +11195,29 @@ + au_set_h_fptr(file, bstart, NULL); + } + AuDebugOn(au_fi(file)->fi_hdir); -+ AuDebugOn(au_fbstart(file) < bstart); ++ /* ++ * it can happen ++ * file exists on both of rw and ro ++ * open --> dbstart and fbstart are both 0 ++ * prepend a branch as rw, "rw" become ro ++ * remove rw/file ++ * delete the top branch, "rw" becomes rw again ++ * --> dbstart is 1, fbstart is still 0 ++ * write --> fbstart is 0 but dbstart is 1 ++ */ ++ /* 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)) ++ if (IS_ERR(h_file)) { ++ if (h_file_tmp) { ++ atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); ++ au_set_h_fptr(file, bstart, h_file_tmp); ++ h_file_tmp = NULL; ++ } + goto out; /* todo: close all? */ ++ } + + err = 0; + au_set_fbstart(file, bstart); @@ -9633,7 +11259,7 @@ +} + +static int au_ready_to_write_wh(struct file *file, loff_t len, -+ aufs_bindex_t bcpup) ++ aufs_bindex_t bcpup, struct au_pin *pin) +{ + int err; + struct inode *inode, *h_inode; @@ -9650,7 +11276,7 @@ + } + hi_wh = au_hi_wh(inode, bcpup); + if (!hi_wh && !h_inode) -+ err = au_sio_cpup_wh(dentry, bcpup, len, file); ++ err = au_sio_cpup_wh(dentry, bcpup, len, file, pin); + else + /* already copied-up after unlink */ + err = au_reopen_wh(file, bcpup, hi_wh); @@ -9671,7 +11297,7 @@ + int err; + aufs_bindex_t bstart, bcpup, dbstart; + struct dentry *dentry, *parent, *h_dentry; -+ struct inode *h_inode, *inode; ++ struct inode *inode; + struct super_block *sb; + struct file *h_file; + @@ -9707,43 +11333,35 @@ + 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)) { ++ if (IS_ERR(h_file)) + err = PTR_ERR(h_file); -+ h_file = NULL; -+ } else { ++ else { + di_downgrade_lock(parent, AuLock_IR); + if (dbstart > bcpup) + err = au_sio_cpup_simple(dentry, bcpup, len, -+ AuCpup_DTIME); ++ AuCpup_DTIME, pin); + if (!err) + err = au_reopen_nondir(file); ++ au_h_open_post(dentry, bstart, h_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); ++ err = au_ready_to_write_wh(file, len, bcpup, pin); + di_downgrade_lock(parent, AuLock_IR); -+ mutex_unlock(&h_inode->i_mutex); + } + + if (!err) { @@ -9769,13 +11387,11 @@ + 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; ++ inode = file_inode(file); ++ sb = inode->i_sb; + si_noflush_read_lock(sb); + fi_read_lock(file); + ii_read_lock_child(inode); @@ -9828,7 +11444,8 @@ + if (!S_ISDIR(inode->i_mode) + && au_opt_test(au_mntflags(sb), PLINK) + && au_plink_test(inode) -+ && !d_unhashed(dentry)) { ++ && !d_unhashed(dentry) ++ && bstart < au_dbstart(dentry)) { + err = au_test_and_cpup_dirs(dentry, bstart); + if (unlikely(err)) + goto out_unlock; @@ -9838,7 +11455,7 @@ + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) + err = au_sio_cpup_simple(dentry, bstart, -1, -+ AuCpup_DTIME); ++ AuCpup_DTIME, &pin); + au_unpin(&pin); + } else if (hi_wh) { + /* already copied-up after unlink */ @@ -9900,8 +11517,7 @@ + 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) ++ if (file_inode(p->hf_file)) + break; + else + au_hfput(p, file); @@ -9919,8 +11535,7 @@ + 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) ++ if (file_inode(p->hf_file)) + break; + else + au_hfput(p, file); @@ -10113,7 +11728,7 @@ +#endif /* CONFIG_AUFS_DEBUG */ +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/file.h 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/file.h 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -10414,7 +12029,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_FILE_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/finfo.c 2013-02-19 11:26:12.675790341 -0500 ++++ linux-3.9/fs/aufs/finfo.c 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -10574,1032 +12189,8 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/f_op.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,723 @@ -+/* -+ * Copyright (C) 2005-2013 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 "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)); -+ atomic_set(&finfo->fi_mmapped, 0); -+ 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) -+ 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); -+} -+ -+/* ---------------------------------------------------------------------- */ -+/* -+ * read and write functions acquire [fdi]_rwsem once, but release before -+ * mmap_sem. This is because to stop a race condition between mmap(2). -+ * Releasing these aufs-rwsem should be safe, no branch-mamagement (by keeping -+ * si_rwsem), no harmful copy-up should happen. Actually copy-up may happen in -+ * read functions after [fdi]_rwsem are released, but it should be harmless. -+ */ -+ -+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); -+ get_file(h_file); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ /* filedata may be obsoleted by concurrent copyup, but no problem */ -+ err = vfsub_read_u(h_file, buf, count, ppos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); -+ fput(h_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 super_block *sb; -+ struct inode *inode; -+ struct file *h_file; -+ char __user *buf = (char __user *)ubuf; -+ -+ dentry = file->f_dentry; -+ sb = dentry->d_sb; -+ 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)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = vfsub_write_u(h_file, buf, count, ppos); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = h_file->f_dentry->d_inode->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(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; -+ lockdep_off(); -+ err = func(kio, iov, nv, pos); -+ lockdep_on(); -+ 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); -+ get_file(h_file); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ err = au_do_aio(h_file, MAY_READ, kio, iov, nv, pos); -+ /* todo: necessary? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); -+ fput(h_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; -+ struct super_block *sb; -+ -+ file = kio->ki_filp; -+ dentry = file->f_dentry; -+ sb = dentry->d_sb; -+ 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)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = au_do_aio(h_file, MAY_WRITE, kio, iov, nv, pos); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = h_file->f_dentry->d_inode->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(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); -+ get_file(h_file); -+ if (au_test_loopback_kthread()) { -+ au_warn_loopback(h_file->f_dentry->d_sb); -+ if (file->f_mapping != h_file->f_mapping) { -+ file->f_mapping = h_file->f_mapping; -+ smp_mb(); /* unnecessary? */ -+ } -+ } -+ di_read_unlock(dentry, AuLock_IR); -+ fi_read_unlock(file); -+ -+ err = vfsub_splice_to(h_file, ppos, pipe, len, flags); -+ /* todo: necessasry? */ -+ /* file->f_ra = h_file->f_ra; */ -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(dentry->d_inode, h_file->f_dentry->d_inode); -+ fput(h_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; -+ struct super_block *sb; -+ -+ dentry = file->f_dentry; -+ sb = dentry->d_sb; -+ 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)) { -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ goto out; -+ } -+ -+ h_file = au_hf_top(file); -+ get_file(h_file); -+ au_unpin(&pin); -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+ -+ err = vfsub_splice_from(pipe, h_file, ppos, len, flags); -+ ii_write_lock_child(inode); -+ au_cpup_attr_timesizes(inode); -+ inode->i_mode = h_file->f_dentry->d_inode->i_mode; -+ ii_write_unlock(inode); -+ fput(h_file); -+ -+out: -+ si_read_unlock(sb); -+ mutex_unlock(&inode->i_mutex); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+/* -+ * The locking order around current->mmap_sem. -+ * - in most and regular cases -+ * file I/O syscall -- aufs_read() or something -+ * -- si_rwsem for read -- mmap_sem -+ * (Note that [fdi]i_rwsem are released before mmap_sem). -+ * - in mmap case -+ * mmap(2) -- mmap_sem -- aufs_mmap() -- si_rwsem for read -- [fdi]i_rwsem -+ * This AB-BA order is definitly bad, but is not a problem since "si_rwsem for -+ * read" allows muliple processes to acquire it and [fdi]i_rwsem are not held in -+ * file I/O. Aufs needs to stop lockdep in aufs_mmap() though. -+ * It means that when aufs acquires si_rwsem for write, the process should never -+ * acquire mmap_sem. -+ * -+ * Actually aufs_readdir() holds [fdi]i_rwsem before mmap_sem, but this is not a -+ * problem either since any directory is not able to be mmap-ed. -+ * The similar scenario is applied to aufs_readlink() too. -+ */ -+ -+/* 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, LOCKED); -+} -+ -+static int aufs_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ 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; -+ struct file *h_file; -+ struct au_branch *br; -+ struct au_pin pin; -+ -+ AuDbgVmRegion(file, vma); -+ -+ dentry = file->f_dentry; -+ sb = dentry->d_sb; -+ lockdep_off(); -+ si_read_lock(sb, AuLock_NOPLMW); -+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1); -+ if (unlikely(err)) -+ goto out; -+ -+ if (wlock) { -+ err = au_ready_to_write(file, -1, &pin); -+ di_write_unlock(dentry); -+ if (unlikely(err)) { -+ fi_write_unlock(file); -+ goto out; -+ } -+ 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_set_mmapped(file); -+ fi_write_unlock(file); -+ lockdep_on(); -+ -+ au_vm_file_reset(vma, h_file); -+ err = security_mmap_file(h_file, au_prot_conv(vma->vm_flags), -+ au_flag_conv(vma->vm_flags)); -+ if (!err) -+ err = h_file->f_op->mmap(h_file, vma); -+ if (unlikely(err)) -+ goto out_reset; -+ -+ au_vm_prfile_set(vma, file); -+ /* update without lock, I don't think it a problem */ -+ fsstack_copy_attr_atime(file->f_dentry->d_inode, -+ h_file->f_dentry->d_inode); -+ goto out_fput; /* success */ -+ -+out_reset: -+ au_unset_mmapped(file); -+ au_vm_file_reset(vma, file); -+out_fput: -+ fput(h_file); -+ lockdep_off(); -+out: -+ si_read_unlock(sb); -+ lockdep_on(); -+ AuTraceErr(err); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_fsync_nondir(struct file *file, loff_t start, loff_t end, -+ 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; -+ sb = dentry->d_sb; -+ mutex_lock(&inode->i_mutex); -+ 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); -+ err = vfsub_fsync(h_file, &h_file->f_path, datasync); -+ au_cpup_attr_timesizes(inode); -+ -+out_unlock: -+ di_read_unlock(dentry, AuLock_IR); -+ fi_write_unlock(file); -+out_si: -+ si_read_unlock(sb); -+out: -+ mutex_unlock(&inode->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, -+ -+ .llseek = default_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 -+}; ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/f_op_sp.c 2013-02-19 11:26:12.675790341 -0500 -@@ -0,0 +1,295 @@ -+/* -+ * Copyright (C) 2005-2013 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 "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); -+ 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; -+} ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/fstype.h 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,481 @@ ++++ linux-3.9/fs/aufs/fstype.h 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,480 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -12013,7 +12604,6 @@ + || au_test_ramfs(sb) +#endif + || au_test_ubifs(sb) -+ || au_test_btrfs(sb) + || au_test_hfsplus(sb); +} + @@ -12082,8 +12672,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_FSTYPE_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/hfsnotify.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,293 @@ ++++ linux-3.9/fs/aufs/hfsnotify.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,296 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -12202,8 +12792,11 @@ +static char *au_hfsn_name(u32 mask) +{ +#ifdef CONFIG_AUFS_DEBUG -+#define test_ret(flag) if (mask & flag) \ -+ return #flag; ++#define test_ret(flag) \ ++ do { \ ++ if (mask & flag) \ ++ return #flag; \ ++ } while (0) + test_ret(FS_ACCESS); + test_ret(FS_MODIFY); + test_ret(FS_ATTRIB); @@ -12378,8 +12971,8 @@ + .init_br = au_hfsn_init_br +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/hfsplus.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,57 @@ ++++ linux-3.9/fs/aufs/hfsplus.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima + * @@ -12418,7 +13011,6 @@ + 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) @@ -12438,8 +13030,8 @@ + } +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/hnotify.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,713 @@ ++++ linux-3.9/fs/aufs/hnotify.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,712 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -12642,7 +13234,6 @@ + int err; + struct dentry *d; + struct qstr *dname; -+ struct hlist_node *p; + + err = 1; + if (unlikely(inode->i_ino == AUFS_ROOT_INO)) { @@ -12655,7 +13246,7 @@ + AuDebugOn(!name); + au_iigen_dec(inode); + spin_lock(&inode->i_lock); -+ hlist_for_each_entry(d, p, &inode->i_dentry, d_alias) { ++ hlist_for_each_entry(d, &inode->i_dentry, d_alias) { + spin_lock(&d->d_lock); + dname = &d->d_name; + if (dname->len != nlen @@ -13154,7 +13745,3378 @@ + au_hn_destroy_cache(); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/iinfo.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/i_op.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,1107 @@ ++/* ++ * Copyright (C) 2005-2013 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 "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); ++ } 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; ++ ++ /* todo: support rcu-walk? */ ++ if (mask & MAY_NOT_BLOCK) ++ return -ECHILD; ++ ++ 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, au_br_mnt(br), 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, au_br_mnt(br), ++ 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, ++ unsigned int flags) ++{ ++ struct dentry *ret, *parent; ++ struct inode *inode; ++ struct super_block *sb; ++ int err, npositive; ++ ++ IMustLock(dir); ++ ++ /* todo: support rcu-walk? */ ++ ret = ERR_PTR(-ECHILD); ++ if (flags & LOOKUP_RCU) ++ goto out; ++ ++ ret = ERR_PTR(-ENAMETOOLONG); ++ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) ++ goto out; ++ ++ sb = dir->i_sb; ++ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out; ++ ++ err = au_di_init(dentry); ++ ret = ERR_PTR(err); ++ if (unlikely(err)) ++ goto out_si; ++ ++ inode = NULL; ++ 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); ++ err = npositive; ++ } ++ di_read_unlock(parent, AuLock_IR); ++ ret = ERR_PTR(err); ++ if (unlikely(err < 0)) ++ goto out_unlock; ++ ++ if (npositive) { ++ inode = au_new_inode(dentry, /*must_new*/0); ++ ret = (void *)inode; ++ } ++ if (IS_ERR(inode)) { ++ inode = NULL; ++ goto out_unlock; ++ } ++ ++ ret = d_splice_alias(inode, dentry); ++#if 0 ++ if (unlikely(d_need_lookup(dentry))) { ++ spin_lock(&dentry->d_lock); ++ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; ++ spin_unlock(&dentry->d_lock); ++ } else ++#endif ++ if (unlikely(IS_ERR(ret) && inode)) { ++ ii_write_unlock(inode); ++ iput(inode); ++ inode = NULL; ++ } ++ ++out_unlock: ++ di_write_unlock(dentry); ++ if (inode) { ++ /* verbose coding for lock class name */ ++ if (unlikely(S_ISLNK(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcSymlink_DIINFO); ++ else if (unlikely(S_ISDIR(inode->i_mode))) ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcDir_DIINFO); ++ else /* likely */ ++ au_rw_class(&au_di(dentry)->di_rwsem, ++ au_lc_key + AuLcNonDir_DIINFO); ++ } ++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, ++ au_ftest_wrdir(add_entry, TMP_WHENTRY)); ++ /* 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) ++ | au_ftest_wrdir(args->flags, TMP_WHENTRY); ++ 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; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++void au_pin_hdir_unlock(struct au_pin *p) ++{ ++ if (p->hdir) ++ au_hn_imtx_unlock(p->hdir); ++} ++ ++static int au_pin_hdir_lock(struct au_pin *p) ++{ ++ int err; ++ ++ err = 0; ++ if (!p->hdir) ++ goto out; ++ ++ /* even if an error happens later, keep this lock */ ++ au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); ++ ++ err = -EBUSY; ++ if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) ++ goto out; ++ ++ err = 0; ++ if (p->h_dentry) ++ err = au_h_verify(p->h_dentry, p->udba, p->hdir->hi_inode, ++ p->h_parent, p->br); ++ ++out: ++ return err; ++} ++ ++int au_pin_hdir_relock(struct au_pin *p) ++{ ++ int err, i; ++ struct inode *h_i; ++ struct dentry *h_d[] = { ++ p->h_dentry, ++ p->h_parent ++ }; ++ ++ err = au_pin_hdir_lock(p); ++ if (unlikely(err)) ++ goto out; ++ ++ for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) { ++ if (!h_d[i]) ++ continue; ++ h_i = h_d[i]->d_inode; ++ if (h_i) ++ err = !h_i->i_nlink; ++ } ++ ++out: ++ return err; ++} ++ ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) ++{ ++#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) ++ p->hdir->hi_inode->i_mutex.owner = task; ++#endif ++} ++ ++void au_pin_hdir_acquire_nest(struct au_pin *p) ++{ ++ if (p->hdir) { ++ mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.dep_map, ++ p->lsc_hi, 0, NULL, _RET_IP_); ++ au_pin_hdir_set_owner(p, current); ++ } ++} ++ ++void au_pin_hdir_release(struct au_pin *p) ++{ ++ if (p->hdir) { ++ au_pin_hdir_set_owner(p, p->task); ++ mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); ++ } ++} ++ ++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->hdir) ++ au_pin_hdir_unlock(p); ++ if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(p->h_mnt); ++ if (!p->hdir) ++ return; ++ ++ 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; ++ /* do not clear p->task */ ++} ++ ++int au_do_pin(struct au_pin *p) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_dir; ++ ++ err = 0; ++ sb = p->dentry->d_sb; ++ p->br = au_sbr(sb, p->bindex); ++ if (IS_ROOT(p->dentry)) { ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ goto out_err; ++ } ++ } ++ goto out; ++ } ++ ++ p->h_dentry = NULL; ++ if (p->bindex <= au_dbend(p->dentry)) ++ p->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; ++ p->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 || !p->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; ++ } ++ ++ if (au_ftest_pin(p->flags, MNT_WRITE)) { ++ p->h_mnt = au_br_mnt(p->br); ++ err = vfsub_mnt_want_write(p->h_mnt); ++ if (unlikely(err)) { ++ au_fclr_pin(p->flags, MNT_WRITE); ++ 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); ++ err = au_pin_hdir_lock(p); ++ if (!err) ++ goto out; /* success */ ++ ++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; ++ ++ p->h_dentry = NULL; ++ p->h_parent = NULL; ++ p->br = NULL; ++ p->task = current; ++} ++ ++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; ++ mutex_unlock(&a->h_inode->i_mutex); ++ ++ h_file = NULL; ++ hi_wh = NULL; ++ if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { ++ hi_wh = au_hi_wh(inode, a->btgt); ++ if (!hi_wh) { ++ err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL, ++ &a->pin); ++ 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); ++ else { ++ err = au_sio_cpup_simple(dentry, a->btgt, sz, ++ AuCpup_DTIME, &a->pin); ++ au_h_open_post(dentry, bstart, h_file); ++ } ++ 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: ++ a->h_inode = a->h_path.dentry->d_inode; ++ if (!err) ++ goto out; /* success */ ++ au_unpin(&a->pin); ++out_parent: ++ if (parent) { ++ di_write_unlock(parent); ++ dput(parent); ++ } ++out: ++ if (!err) ++ mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); ++ 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); ++ di_write_lock_child(dentry); ++ /* no d_unlinked(), to set UDBA_NONE for root */ ++ if (d_unhashed(dentry)) ++ a->udba = AuOpt_UDBA_NONE; ++ 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, 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) ++{ ++ unsigned int n; ++ ++ inode->i_mode = st->mode; ++ /* don't i_[ug]id_write() here */ ++ 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)) { ++ n = inode->i_nlink; ++ n -= nlink; ++ n += st->nlink; ++ /* 0 can happen */ ++ set_nlink(inode, n); ++ } ++ ++ 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 path h_path; ++ ++ 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 (!d_unlinked(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_path.mnt = au_sbr_mnt(sb, bindex); ++ h_sb = h_path.mnt->mnt_sb; ++ if (!au_test_fs_bad_iattr(h_sb) && udba_none) ++ goto out_fill; /* success */ ++ ++ h_path.dentry = NULL; ++ if (au_dbstart(dentry) == bindex) ++ h_path.dentry = dget(au_h_dptr(dentry, bindex)); ++ else if (au_opt_test(mnt_flags, PLINK) && au_plink_test(inode)) { ++ h_path.dentry = au_plink_lkup(inode, bindex); ++ if (IS_ERR(h_path.dentry)) ++ goto out_fill; /* pretending success */ ++ } ++ /* illegally overlapped or something */ ++ if (unlikely(!h_path.dentry)) ++ goto out_fill; /* pretending success */ ++ ++ positive = !!h_path.dentry->d_inode; ++ if (positive) ++ err = vfs_getattr(&h_path, st); ++ dput(h_path.dentry); ++ if (!err) { ++ if (positive) ++ au_refresh_iattr(inode, st, ++ h_path.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 = (void *)__get_free_page(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: ++ free_page((unsigned long)buf.k); ++out: ++ AuTraceErr(err); ++ return ERR_PTR(err); ++} ++ ++static void aufs_put_link(struct dentry *dentry __maybe_unused, ++ struct nameidata *nd, void *cookie __maybe_unused) ++{ ++ char *p; ++ ++ p = nd_get_link(nd); ++ if (!IS_ERR_OR_NULL(p)) ++ free_page((unsigned long)p); ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) ++{ ++ int err; ++ struct super_block *sb; ++ struct inode *h_inode; ++ ++ sb = inode->i_sb; ++ /* mmap_sem might be acquired already, cf. aufs_mmap() */ ++ lockdep_off(); ++ si_read_lock(sb, AuLock_FLUSH); ++ ii_write_lock_child(inode); ++ lockdep_on(); ++ h_inode = au_h_iptr(inode, au_ibstart(inode)); ++ err = vfsub_update_time(h_inode, ts, flags); ++ lockdep_off(); ++ ii_write_unlock(inode); ++ si_read_unlock(sb); ++ lockdep_on(); ++ return err; ++} ++ ++/* ---------------------------------------------------------------------- */ ++ ++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, ++ ++ /* .update_time = aufs_update_time */ ++}; ++ ++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, ++ ++ .update_time = aufs_update_time ++ /* no support for atomic_open() */ ++}; ++ ++struct inode_operations aufs_iop = { ++ .permission = aufs_permission, ++ .setattr = aufs_setattr, ++ .getattr = aufs_getattr, ++ ++ .update_time = aufs_update_time ++}; +--- /dev/null ++++ linux-3.9/fs/aufs/i_op_add.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,722 @@ ++/* ++ * Copyright (C) 2005-2013 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 = au_br_mnt(br) ++ }; ++ 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 { ++ umode_t mode; ++ bool want_excl; ++ } c; ++ struct { ++ const char *symname; ++ } s; ++ struct { ++ umode_t 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, ++ arg->u.c.want_excl); ++ 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, umode_t 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, umode_t mode, ++ bool want_excl) ++{ ++ struct simple_arg arg = { ++ .type = Creat, ++ .u.c = { ++ .mode = mode, ++ .want_excl = want_excl ++ } ++ }; ++ 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 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); ++ 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; ++ h_file = au_h_open_pre(src_dentry, a->bsrc); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ err = au_sio_cpup_simple(src_dentry, a->bdst, -1, ++ AuCpup_DTIME /* | AuCpup_KEEPLINO */, ++ &a->pin); ++ 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 dentry *dentry, ++ struct au_link_args *a) ++{ ++ int err; ++ unsigned char plink; ++ aufs_bindex_t bend; ++ struct dentry *h_src_dentry; ++ struct inode *h_inode, *inode; ++ 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. */ ++ bend = au_dbend(dentry); ++ if (bend < a->bsrc) ++ au_set_dbend(dentry, a->bsrc); ++ au_set_h_dptr(dentry, a->bsrc, ++ dget(au_h_dptr(src_dentry, a->bsrc))); ++ dget(a->h_path.dentry); ++ au_set_h_dptr(dentry, a->bdst, NULL); ++ dentry->d_inode = src_dentry->d_inode; /* tmp */ ++ h_file = au_h_open_pre(dentry, a->bsrc); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ err = au_sio_cpup_simple(dentry, a->bdst, -1, ++ AuCpup_KEEPLINO, &a->pin); ++ au_h_open_post(dentry, a->bsrc, h_file); ++ if (!err) { ++ dput(a->h_path.dentry); ++ a->h_path.dentry = au_h_dptr(dentry, a->bdst); ++ } else ++ au_set_h_dptr(dentry, a->bdst, ++ a->h_path.dentry); ++ } ++ dentry->d_inode = NULL; /* restore */ ++ au_set_h_dptr(dentry, a->bsrc, NULL); ++ au_set_dbend(dentry, bend); ++ } 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: ++ AuTraceErr(err); ++ 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_ibstart(inode); ++ ++ 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_ibstart(inode); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ if (!h_src_dentry) { ++ a->bsrc = au_dbstart(src_dentry); ++ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); ++ AuDebugOn(!h_src_dentry); ++ } else if (IS_ERR(h_src_dentry)) ++ goto out_parent; ++ ++ 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, dentry, a); ++ else ++ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), ++ &a->h_path); ++ dput(h_src_dentry); ++ } else { ++ /* ++ * copyup src_dentry to the branch we process, ++ * and then link(2) to it. ++ */ ++ dput(h_src_dentry); ++ 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: ++ AuTraceErr(err); ++ return err; ++} ++ ++int aufs_mkdir(struct inode *dir, struct dentry *dentry, umode_t 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; ++} +--- /dev/null ++++ linux-3.9/fs/aufs/i_op_del.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,477 @@ ++/* ++ * Copyright (C) 2005-2013 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); ++ 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; ++ err = au_alive_dir(dentry); ++ if (unlikely(err)) ++ goto out_unlock; ++ inode = dentry->d_inode; ++ 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; ++} +--- /dev/null ++++ linux-3.9/fs/aufs/i_op_ren.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,1053 @@ ++/* ++ * Copyright (C) 2005-2013 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 = vfsub_lkup_one(&a->src_dentry->d_name, ++ a->src_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("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 = vfsub_lkup_one(&a->dst_dentry->d_name, ++ a->dst_h_parent); ++ rerr = PTR_ERR(a->h_path.dentry); ++ if (IS_ERR(a->h_path.dentry)) { ++ RevertFailure("lkup one %.*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 { ++#if 1 ++ BUG(); ++#else ++ struct file *h_file; ++ ++ au_fset_ren(a->flags, CPUP); ++ 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); ++ else { ++ err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, ++ !AuCpup_DTIME, a->dst_parent); ++ 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); ++ } ++#endif ++ } ++ 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_warn("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, /*wh*/0); ++ 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) { ++#if 1 ++ BUG(); ++#else ++ struct file *h_file; ++ ++ h_file = au_h_open_pre(a->src_dentry, a->src_bstart); ++ if (IS_ERR(h_file)) ++ err = PTR_ERR(h_file); ++ else { ++ err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, ++ !AuCpup_DTIME); ++ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ } ++ if (unlikely(err)) ++ goto out_whtmp; ++#endif ++ } ++ ++ /* 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 = -ENOTEMPTY; ++ if (unlikely(a->dst_h_dentry == a->h_trap)) ++ 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; ++ } ++ ++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) ++{ ++ vfsub_unlock_rename(a->src_h_parent, a->src_hdir, ++ a->dst_h_parent, a->dst_hdir); ++ if (au_ftest_ren(a->flags, MNT_WRITE)) ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++} ++ ++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); ++ ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out; ++ au_fset_ren(a->flags, MNT_WRITE); ++ 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) ++ goto out; /* success */ ++ ++ err = au_busy_or_stale(); ++ 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; ++ ++ /* ++ * is it possible? ++ * yes, it happend (in linux-3.3-rcN) but I don't know why. ++ * there may exist a problem somewhere else. ++ */ ++ err = -EINVAL; ++ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) ++ 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 = au_br_mnt(a->br); ++ ++ /* 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); ++ ++ /* cpup src */ ++ if (a->src_bstart != a->btgt) { ++ struct file *h_file; ++ struct au_pin pin; ++ ++ err = au_pin(&pin, a->src_dentry, a->btgt, ++ au_opt_udba(a->src_dentry->d_sb), ++ AuPin_DI_LOCKED | AuPin_MNT_WRITE); ++ if (unlikely(err)) ++ goto out_children; ++ ++ 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, &pin); ++ au_h_open_post(a->src_dentry, a->src_bstart, h_file); ++ } ++ au_unpin(&pin); ++ if (unlikely(err)) ++ goto out_children; ++ a->src_bstart = a->btgt; ++ a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); ++ au_fset_ren(a->flags, WHSRC); ++ } ++ ++ /* lock them all */ ++ err = au_ren_lock(a); ++ if (unlikely(err)) ++ /* leave the copied-up one */ ++ 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; ++} +--- /dev/null ++++ linux-3.9/fs/aufs/iinfo.c 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -13433,7 +17395,7 @@ + AuRwDestroy(&iinfo->ii_rwsem); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/inode.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/inode.c 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,492 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -13928,8 +17890,8 @@ + return au_test_h_perm(h_inode, mask); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/inode.h 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,588 @@ ++++ linux-3.9/fs/aufs/inode.h 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,600 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -14033,8 +17995,19 @@ + struct dentry *parent; + struct au_hinode *hdir; + struct vfsmount *h_mnt; ++ ++ /* temporary unlock/relock for copyup */ ++ struct dentry *h_dentry, *h_parent; ++ struct au_branch *br; ++ struct task_struct *task; +}; + ++void au_pin_hdir_unlock(struct au_pin *p); ++int au_pin_hdir_relock(struct au_pin *p); ++void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); ++void au_pin_hdir_acquire_nest(struct au_pin *p); ++void au_pin_hdir_release(struct au_pin *p); ++ +/* ---------------------------------------------------------------------- */ + +static inline struct au_iinfo *au_ii(struct inode *inode) @@ -14076,7 +18049,8 @@ + +/* au_wr_dir flags */ +#define AuWrDir_ADD_ENTRY 1 -+#define AuWrDir_ISDIR (1 << 1) ++#define AuWrDir_TMP_WHENTRY (1 << 1) ++#define AuWrDir_ISDIR (1 << 2) +#define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) +#define au_fset_wrdir(flags, name) \ + do { (flags) |= AuWrDir_##name; } while (0) @@ -14519,7 +18493,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_INODE_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/ioctl.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/ioctl.c 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -14718,3472 +18692,8 @@ +#endif +#endif --- /dev/null -+++ linux-3.x-rcN/fs/aufs/i_op_add.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,713 @@ -+/* -+ * Copyright (C) 2005-2013 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 { -+ umode_t mode; -+ bool want_excl; -+ } c; -+ struct { -+ const char *symname; -+ } s; -+ struct { -+ umode_t 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, -+ arg->u.c.want_excl); -+ 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, umode_t 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, umode_t mode, -+ bool want_excl) -+{ -+ struct simple_arg arg = { -+ .type = Creat, -+ .u.c = { -+ .mode = mode, -+ .want_excl = want_excl -+ } -+ }; -+ 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, -1, -+ 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: -+ AuTraceErr(err); -+ 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_ibstart(inode); -+ -+ 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_ibstart(inode); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ if (!h_src_dentry) { -+ a->bsrc = au_dbstart(src_dentry); -+ h_src_dentry = au_h_d_alias(src_dentry, a->bsrc); -+ AuDebugOn(!h_src_dentry); -+ } else if (IS_ERR(h_src_dentry)) -+ goto out_parent; -+ -+ 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 -+ err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), -+ &a->h_path); -+ dput(h_src_dentry); -+ } else { -+ /* -+ * copyup src_dentry to the branch we process, -+ * and then link(2) to it. -+ */ -+ dput(h_src_dentry); -+ 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, umode_t 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; -+} ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/i_op.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,1030 @@ -+/* -+ * Copyright (C) 2005-2013 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 "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); -+ } 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; -+ -+ /* todo: support rcu-walk? */ -+ if (mask & MAY_NOT_BLOCK) -+ return -ECHILD; -+ -+ 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, -+ unsigned int flags) -+{ -+ struct dentry *ret, *parent; -+ struct inode *inode; -+ struct super_block *sb; -+ int err, npositive; -+ -+ IMustLock(dir); -+ -+ /* todo: support rcu-walk? */ -+ ret = ERR_PTR(-ECHILD); -+ if (flags & LOOKUP_RCU) -+ goto out; -+ -+ ret = ERR_PTR(-ENAMETOOLONG); -+ if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) -+ goto out; -+ -+ sb = dir->i_sb; -+ err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out; -+ -+ err = au_di_init(dentry); -+ ret = ERR_PTR(err); -+ if (unlikely(err)) -+ goto out_si; -+ -+ inode = NULL; -+ 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); -+ err = npositive; -+ } -+ di_read_unlock(parent, AuLock_IR); -+ ret = ERR_PTR(err); -+ if (unlikely(err < 0)) -+ goto out_unlock; -+ -+ if (npositive) { -+ inode = au_new_inode(dentry, /*must_new*/0); -+ ret = (void *)inode; -+ } -+ if (IS_ERR(inode)) { -+ inode = NULL; -+ goto out_unlock; -+ } -+ -+ ret = d_splice_alias(inode, dentry); -+#if 0 -+ if (unlikely(d_need_lookup(dentry))) { -+ spin_lock(&dentry->d_lock); -+ dentry->d_flags &= ~DCACHE_NEED_LOOKUP; -+ spin_unlock(&dentry->d_lock); -+ } else -+#endif -+ if (unlikely(IS_ERR(ret) && inode)) { -+ ii_write_unlock(inode); -+ iput(inode); -+ inode = NULL; -+ } -+ -+out_unlock: -+ di_write_unlock(dentry); -+ if (inode) { -+ /* verbose coding for lock class name */ -+ if (unlikely(S_ISLNK(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcSymlink_DIINFO); -+ else if (unlikely(S_ISDIR(inode->i_mode))) -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcDir_DIINFO); -+ else /* likely */ -+ au_rw_class(&au_di(dentry)->di_rwsem, -+ au_lc_key + AuLcNonDir_DIINFO); -+ } -+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)) -+ vfsub_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 = vfsub_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 = vfsub_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) && d_unlinked(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); -+ di_write_lock_child(dentry); -+ /* no d_unlinked(), to set UDBA_NONE for root */ -+ if (d_unhashed(dentry)) -+ a->udba = AuOpt_UDBA_NONE; -+ 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, 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, vfsub_ia_uid(ia), -+ vfsub_ia_gid(ia)); -+ 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) -+{ -+ unsigned int n; -+ -+ inode->i_mode = st->mode; -+ i_uid_write(inode, st->uid); -+ i_gid_write(inode, 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)) { -+ n = inode->i_nlink; -+ n -= nlink; -+ n += st->nlink; -+ /* 0 can happen */ -+ set_nlink(inode, n); -+ } -+ -+ 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 (!d_unlinked(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 = (void *)__get_free_page(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: -+ free_page((unsigned long)buf.k); -+out: -+ AuTraceErr(err); -+ return ERR_PTR(err); -+} -+ -+static void aufs_put_link(struct dentry *dentry __maybe_unused, -+ struct nameidata *nd, void *cookie __maybe_unused) -+{ -+ char *p; -+ -+ p = nd_get_link(nd); -+ if (!IS_ERR_OR_NULL(p)) -+ free_page((unsigned long)p); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags) -+{ -+ int err; -+ struct super_block *sb; -+ struct inode *h_inode; -+ -+ sb = inode->i_sb; -+ /* mmap_sem might be acquired already, cf. aufs_mmap() */ -+ lockdep_off(); -+ si_read_lock(sb, AuLock_FLUSH); -+ ii_write_lock_child(inode); -+ lockdep_on(); -+ h_inode = au_h_iptr(inode, au_ibstart(inode)); -+ err = vfsub_update_time(h_inode, ts, flags); -+ lockdep_off(); -+ ii_write_unlock(inode); -+ si_read_unlock(sb); -+ lockdep_on(); -+ return err; -+} -+ -+/* ---------------------------------------------------------------------- */ -+ -+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, -+ -+ /* .update_time = aufs_update_time */ -+}; -+ -+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, -+ -+ .update_time = aufs_update_time -+ /* no support for atomic_open() */ -+}; -+ -+struct inode_operations aufs_iop = { -+ .permission = aufs_permission, -+ .setattr = aufs_setattr, -+ .getattr = aufs_getattr, -+ -+ .update_time = aufs_update_time -+}; ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/i_op_del.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,477 @@ -+/* -+ * Copyright (C) 2005-2013 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); -+ 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; -+ err = au_alive_dir(dentry); -+ if (unlikely(err)) -+ goto out_unlock; -+ inode = dentry->d_inode; -+ 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; -+} ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/i_op_ren.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,1026 @@ -+/* -+ * Copyright (C) 2005-2013 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 = vfsub_lkup_one(&a->src_dentry->d_name, -+ a->src_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("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 = vfsub_lkup_one(&a->dst_dentry->d_name, -+ a->dst_h_parent); -+ rerr = PTR_ERR(a->h_path.dentry); -+ if (IS_ERR(a->h_path.dentry)) { -+ RevertFailure("lkup one %.*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_warn("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 = -ENOTEMPTY; -+ if (unlikely(a->dst_h_dentry == a->h_trap)) -+ 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; -+ } -+ -+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)) -+ vfsub_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 = vfsub_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; -+ -+ /* -+ * is it possible? -+ * yes, it happend (in linux-3.3-rcN) but I don't know why. -+ * there may exist a problem somewhere else. -+ */ -+ err = -EINVAL; -+ if (unlikely(a->dst_parent->d_inode == a->src_dentry->d_inode)) -+ 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; -+} ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/Kconfig 2013-02-19 11:26:12.672456917 -0500 -@@ -0,0 +1,203 @@ -+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 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_PROC_MAP -+ bool "support for /proc/maps and lsof(1)" -+ depends on PROC_FS -+ help -+ When you issue mmap(2) in aufs, it is actually a direct mmap(2) -+ call to the file on the branch fs since the file in aufs is -+ purely virtual. And the file path printed in /proc/maps (and -+ others) will be the path on the branch fs. In most cases, it -+ does no harm. But some utilities like lsof(1) may confuse since -+ the utility or user may expect the file path in aufs to be -+ printed. -+ To address this issue, aufs provides a patch which introduces a -+ new member called vm_prfile into struct vm_are_struct. The patch -+ is meaningless without enabling this configuration since nobody -+ sets the new vm_prfile member. -+ If you don't apply the patch, then enabling this configuration -+ will cause a compile error. -+ This approach is fragile since if someone else make some changes -+ around vm_file, then vm_prfile may not work anymore. As a -+ workaround such case, aufs provides this configuration. If you -+ disable it, then lsof(1) may produce incorrect result but the -+ problem will be gone even if the aufs patch is applied (I hope). -+ -+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 ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/loop.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,133 @@ ++++ linux-3.9/fs/aufs/loop.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,135 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -18237,12 +18747,14 @@ +{ + int ret; + struct task_struct *tsk = current; ++ char c, comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { -+ const char c = tsk->comm[4]; ++ get_task_comm(comm, tsk); ++ c = comm[4]; + ret = ('0' <= c && c <= '9' -+ && !strncmp(tsk->comm, "loop", 4)); ++ && !strncmp(comm, "loop", 4)); + } + + return ret; @@ -18318,7 +18830,7 @@ + kfree(au_warn_loopback_array); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/loop.h 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/loop.h 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -18371,7 +18883,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_LOOP_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/magic.mk 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/magic.mk 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,54 @@ + +# defined in ${srctree}/fs/fuse/inode.c @@ -18428,53 +18940,8 @@ +ccflags-y += -DHFSPLUS_SUPER_MAGIC=0x482b +endif --- /dev/null -+++ linux-3.x-rcN/fs/aufs/Makefile 2013-02-19 11:26:12.672456917 -0500 -@@ -0,0 +1,42 @@ -+ -+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 requires the full pathname -+ifdef M -+ccflags-y += -include ${M}/../../include/linux/aufs_type.h -+else -+ccflags-y += -include ${srctree}/include/linux/aufs_type.h -+endif -+ -+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 ---- /dev/null -+++ linux-3.x-rcN/fs/aufs/module.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,202 @@ ++++ linux-3.9/fs/aufs/module.c 2013-05-13 17:13:17.217133960 +0200 +@@ -0,0 +1,203 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -18580,6 +19047,7 @@ +MODULE_DESCRIPTION(AUFS_NAME + " -- Advanced multi layered unification filesystem"); +MODULE_VERSION(AUFS_VERSION); ++MODULE_ALIAS_FS(AUFS_NAME); + +/* this module parameter has no meaning when SYSFS is disabled */ +int sysaufs_brs = 1; @@ -18678,7 +19146,7 @@ +module_init(aufs_init); +module_exit(aufs_exit); --- /dev/null -+++ linux-3.x-rcN/fs/aufs/module.h 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/module.h 2013-05-13 17:13:17.217133960 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -18786,8 +19254,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_MODULE_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/opts.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,1677 @@ ++++ linux-3.9/fs/aufs/opts.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,1697 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -18976,17 +19444,16 @@ + {0, NULL} +}; + -+static match_table_t brrattr = { ++static match_table_t brattr = { ++ {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, + {AuBrRAttr_WH, AUFS_BRRATTR_WH}, -+ {0, NULL} -+}; -+ -+static match_table_t brwattr = { + {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, + {0, NULL} +}; + -+#define AuBrStr_LONGEST AUFS_BRPERM_RW "+" AUFS_BRWATTR_NLWH ++#define AuBrStr_LONGEST AUFS_BRPERM_RW \ ++ "+" AUFS_BRATTR_UNPIN \ ++ "+" AUFS_BRWATTR_NLWH + +static int br_attr_val(char *str, match_table_t table, substring_t args[]) +{ @@ -19017,7 +19484,7 @@ +static int noinline_for_stack br_perm_val(char *perm) +{ + int val; -+ char *p; ++ char *p, *q; + substring_t args[MAX_OPT_ARGS]; + + p = strchr(perm, '+'); @@ -19034,13 +19501,33 @@ + if (!p) + goto out; + -+ switch (val) { ++ p++; ++ while (1) { ++ q = strchr(p, '+'); ++ if (q) ++ *q = 0; ++ val |= br_attr_val(p, brattr, args); ++ if (q) { ++ *q = '+'; ++ p = q + 1; ++ } else ++ break; ++ } ++ switch (val & AuBrPerm_Mask) { + case AuBrPerm_RO: + case AuBrPerm_RR: -+ val |= br_attr_val(p + 1, brrattr, args); ++ if (unlikely(val & AuBrWAttr_NoLinkWH)) { ++ pr_warn("ignored branch attribute %s\n", ++ AUFS_BRWATTR_NLWH); ++ val &= ~AuBrWAttr_NoLinkWH; ++ } + break; + case AuBrPerm_RW: -+ val |= br_attr_val(p + 1, brwattr, args); ++ if (unlikely(val & AuBrRAttr_WH)) { ++ pr_warn("ignored branch attribute %s\n", ++ AUFS_BRRATTR_WH); ++ val &= ~AuBrRAttr_WH; ++ } + break; + } + @@ -19083,6 +19570,7 @@ + AuDebugOn(1); + } + ++ AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN); + AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH); + AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH); + @@ -20308,7 +20796,7 @@ + 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); ++ err = au_wh_init(br, sb); + if (wbr) + wbr_wh_write_unlock(wbr); + au_hn_imtx_unlock(hdir); @@ -20466,7 +20954,7 @@ + return au_mntflags(sb) & AuOptMask_UDBA; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/opts.h 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/opts.h 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -20678,8 +21166,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_OPTS_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/plink.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,511 @@ ++++ linux-3.9/fs/aufs/plink.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,520 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -20801,19 +21289,12 @@ + +/* ---------------------------------------------------------------------- */ + -+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) +{ ++ int i; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; + struct pseudo_link *plink; + + SiMustAnyLock(sb); @@ -20822,20 +21303,22 @@ + 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(); ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ rcu_read_lock(); ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) ++ 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; ++ int found, i; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; + struct pseudo_link *plink; + + sbinfo = au_sbi(inode->i_sb); @@ -20844,9 +21327,10 @@ + AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); + + found = 0; -+ plink_list = &sbinfo->si_plink.head; ++ i = au_plink_hash(inode->i_ino); ++ plink_hlist = &sbinfo->si_plink[i].head; + rcu_read_lock(); -+ list_for_each_entry_rcu(plink, plink_list, list) ++ hlist_for_each_entry_rcu(plink, plink_hlist, hlist) + if (plink->inode == inode) { + found = 1; + break; @@ -20942,7 +21426,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + struct inode *h_dir; + @@ -21025,7 +21509,7 @@ +static void do_put_plink(struct pseudo_link *plink, int do_del) +{ + if (do_del) -+ list_del(&plink->list); ++ hlist_del(&plink->hlist); + iput(plink->inode); + kfree(plink); +} @@ -21048,30 +21532,23 @@ +{ + struct super_block *sb; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; ++ struct hlist_head *plink_hlist; + struct pseudo_link *plink, *tmp; -+ int found, err, cnt; ++ struct au_sphlhead *sphl; ++ int found, err, cnt, i; + + 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(); ++ found = au_plink_test(inode); + if (found) + return; + ++ i = au_plink_hash(inode->i_ino); ++ sphl = sbinfo->si_plink + i; ++ plink_hlist = &sphl->head; + tmp = kmalloc(sizeof(*plink), GFP_NOFS); + if (tmp) + tmp->inode = au_igrab(inode); @@ -21080,20 +21557,22 @@ + goto out; + } + -+ spin_lock(&sbinfo->si_plink.spin); -+ list_for_each_entry(plink, plink_list, list) { ++ spin_lock(&sphl->spin); ++ hlist_for_each_entry(plink, plink_hlist, hlist) { + if (plink->inode == inode) { + found = 1; + break; + } + } + if (!found) -+ list_add_rcu(&tmp->list, plink_list); -+ spin_unlock(&sbinfo->si_plink.spin); ++ hlist_add_head_rcu(&tmp->hlist, plink_hlist); ++ spin_unlock(&sphl->spin); + if (!found) { -+ cnt++; -+ WARN_ONCE(cnt > AUFS_PLINK_WARN, -+ "unexpectedly many pseudo links, %d\n", cnt); ++ cnt = au_sphl_count(sphl); ++#define msg "unexpectedly unblanced or too many pseudo-links" ++ if (cnt > AUFS_PLINK_WARN) ++ AuWarn1(msg ", %d\n", cnt); ++#undef msg + err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); + } else { + do_put_plink(tmp, 0); @@ -21104,7 +21583,7 @@ + if (unlikely(err)) { + pr_warn("err %d, damaged pseudo link.\n", err); + if (tmp) { -+ au_spl_del_rcu(&tmp->list, &sbinfo->si_plink); ++ au_sphl_del_rcu(&tmp->hlist, sphl); + call_rcu(&tmp->rcu, do_put_plink_rcu); + } + } @@ -21113,9 +21592,11 @@ +/* free all plinks */ +void au_plink_put(struct super_block *sb, int verbose) +{ ++ int i, warned; + struct au_sbinfo *sbinfo; -+ struct list_head *plink_list; -+ struct pseudo_link *plink, *tmp; ++ struct hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; + + SiMustWriteLock(sb); + @@ -21123,12 +21604,18 @@ + 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); ++ warned = 0; ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ if (!warned && verbose && !hlist_empty(plink_hlist)) { ++ pr_warn("pseudo-link is not flushed"); ++ warned = 1; ++ } ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) ++ do_put_plink(plink, 0); ++ INIT_HLIST_HEAD(plink_hlist); ++ } +} + +void au_plink_clean(struct super_block *sb, int verbose) @@ -21142,15 +21629,44 @@ + aufs_write_unlock(root); +} + ++static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) ++{ ++ int do_put; ++ aufs_bindex_t bstart, bend, bindex; ++ ++ do_put = 0; ++ 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; ++ } ++ if (do_put) ++ for (bindex = bstart; bindex <= bend; bindex++) ++ if (au_h_iptr(inode, bindex)) { ++ do_put = 0; ++ break; ++ } ++ } else ++ do_put = 1; ++ ++ return do_put; ++} ++ +/* 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 hlist_head *plink_hlist; ++ struct hlist_node *tmp; ++ struct pseudo_link *plink; + struct inode *inode; -+ aufs_bindex_t bstart, bend, bindex; -+ unsigned char do_put; ++ int i, do_put; + + SiMustWriteLock(sb); + @@ -21158,41 +21674,22 @@ + 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; -+ } ++ for (i = 0; i < AuPlink_NHASH; i++) { ++ plink_hlist = &sbinfo->si_plink[i].head; ++ hlist_for_each_entry_safe(plink, tmp, plink_hlist, hlist) { ++ inode = au_igrab(plink->inode); ++ ii_write_lock_child(inode); ++ do_put = au_plink_do_half_refresh(inode, br_id); + if (do_put) + do_put_plink(plink, 1); ++ ii_write_unlock(inode); ++ iput(inode); + } -+ ii_write_unlock(inode); -+ iput(inode); + } +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/poll.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/poll.c 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -21251,7 +21748,7 @@ + return mask; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/procfs.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/procfs.c 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2010-2013 Junjiro R. Okajima @@ -21424,7 +21921,7 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/rdu.c 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/rdu.c 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -21811,7 +22308,7 @@ +} +#endif --- /dev/null -+++ linux-3.x-rcN/fs/aufs/rwsem.h 2013-02-19 11:26:12.679123764 -0500 ++++ linux-3.9/fs/aufs/rwsem.h 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -22002,8 +22499,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_RWSEM_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/sbinfo.c 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,343 @@ ++++ linux-3.9/fs/aufs/sbinfo.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,346 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -22033,11 +22530,13 @@ + */ +void au_si_free(struct kobject *kobj) +{ ++ int i; + 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)); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); + AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); + + au_rw_write_lock(&sbinfo->si_rwsem); @@ -22059,7 +22558,7 @@ + +int au_si_alloc(struct super_block *sb) +{ -+ int err; ++ int err, i; + struct au_sbinfo *sbinfo; + static struct lock_class_key aufs_si; + @@ -22112,7 +22611,8 @@ + sbinfo->si_rdhash = AUFS_RDHASH_DEF; + sbinfo->si_dirwh = AUFS_DIRWH_DEF; + -+ au_spl_init(&sbinfo->si_plink); ++ for (i = 0; i < AuPlink_NHASH; i++) ++ au_sphl_init(sbinfo->si_plink + i); + init_waitqueue_head(&sbinfo->si_plink_wq); + spin_lock_init(&sbinfo->si_plink_maint_lock); + @@ -22348,8 +22848,8 @@ + spin_unlock(&sbinfo->au_si_pid.tree_lock); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/spl.h 2013-02-19 11:26:12.679123764 -0500 -@@ -0,0 +1,62 @@ ++++ linux-3.9/fs/aufs/spl.h 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -22410,11 +22910,61 @@ + spin_unlock(&spl->spin); +} + ++/* ---------------------------------------------------------------------- */ ++ ++struct au_sphlhead { ++ spinlock_t spin; ++ struct hlist_head head; ++}; ++ ++static inline void au_sphl_init(struct au_sphlhead *sphl) ++{ ++ spin_lock_init(&sphl->spin); ++ INIT_HLIST_HEAD(&sphl->head); ++} ++ ++static inline void au_sphl_add(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_add_head(hlist, &sphl->head); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline void au_sphl_del_rcu(struct hlist_node *hlist, ++ struct au_sphlhead *sphl) ++{ ++ spin_lock(&sphl->spin); ++ hlist_del_rcu(hlist); ++ spin_unlock(&sphl->spin); ++} ++ ++static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) ++{ ++ unsigned long cnt; ++ struct hlist_node *pos; ++ ++ cnt = 0; ++ spin_lock(&sphl->spin); ++ hlist_for_each(pos, &sphl->head) ++ cnt++; ++ spin_unlock(&sphl->spin); ++ return cnt; ++} ++ +#endif /* __KERNEL__ */ +#endif /* __AUFS_SPL_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/super.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,993 @@ ++++ linux-3.9/fs/aufs/super.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,992 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -22521,7 +23071,7 @@ + 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.mnt = au_br_mnt(br); + path.dentry = hdp[bindex].hd_dentry; + err = au_seq_path(seq, &path); + if (err > 0) { @@ -23400,17 +23950,16 @@ + +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 */ ++ /* a race between rename and others */ ++ .fs_flags = FS_RENAME_DOES_D_MOVE, + .mount = aufs_mount, + .kill_sb = aufs_kill_sb, + /* no need to __module_get() and module_put(). */ + .owner = THIS_MODULE, +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/super.h 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,546 @@ ++++ linux-3.9/fs/aufs/super.h 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,555 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -23468,6 +24017,20 @@ + unsigned long long mfsrr_watermark; +}; + ++struct pseudo_link { ++ union { ++ struct hlist_node hlist; ++ struct rcu_head rcu; ++ }; ++ struct inode *inode; ++}; ++ ++#define AuPlink_NHASH 100 ++static inline int au_plink_hash(ino_t ino) ++{ ++ return ino % AuPlink_NHASH; ++} ++ +struct au_branch; +struct au_sbinfo { + /* nowait tasks in the system-wide workqueue */ @@ -23558,7 +24121,7 @@ + /* int si_rendir; */ + + /* pseudo_link list */ -+ struct au_splhead si_plink; ++ struct au_sphlhead si_plink[AuPlink_NHASH]; + wait_queue_head_t si_plink_wq; + spinlock_t si_plink_maint_lock; + pid_t si_plink_maint_pid; @@ -23571,7 +24134,9 @@ + */ + struct kobject si_kobj; +#ifdef CONFIG_DEBUG_FS -+ struct dentry *si_dbgaufs, *si_dbgaufs_xib; ++ struct dentry *si_dbgaufs; ++ struct dentry *si_dbgaufs_plink; ++ struct dentry *si_dbgaufs_xib; +#ifdef CONFIG_AUFS_EXPORT + struct dentry *si_dbgaufs_xigen; +#endif @@ -23679,16 +24244,8 @@ +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_EXPORT ++int au_test_nfsd(void); +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); @@ -23701,8 +24258,8 @@ + return -ESTALE; +} +#else -+AuStubVoid(au_export_init, struct super_block *sb) +AuStubInt0(au_test_nfsd, void) ++AuStubVoid(au_export_init, struct super_block *sb) +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) @@ -23770,6 +24327,7 @@ + /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ +#ifdef CONFIG_DEBUG_FS + sbinfo->si_dbgaufs = NULL; ++ sbinfo->si_dbgaufs_plink = NULL; + sbinfo->si_dbgaufs_xib = NULL; +#ifdef CONFIG_AUFS_EXPORT + sbinfo->si_dbgaufs_xigen = NULL; @@ -23958,7 +24516,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_SUPER_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/sysaufs.c 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/fs/aufs/sysaufs.c 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -24066,7 +24624,7 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/sysaufs.h 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/fs/aufs/sysaufs.h 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -24173,7 +24731,7 @@ +#endif /* __KERNEL__ */ +#endif /* __SYSAUFS_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/sysfs.c 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/fs/aufs/sysfs.c 2013-05-13 17:13:17.220468929 +0200 @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -24269,7 +24827,7 @@ + root = sb->s_root; + di_read_lock_parent(root, !AuLock_IR); + br = au_sbr(sb, bindex); -+ path.mnt = br->br_mnt; ++ path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(root, bindex); + au_seq_path(seq, &path); + di_read_unlock(root, !AuLock_IR); @@ -24433,8 +24991,8 @@ + } +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/sysrq.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,148 @@ ++++ linux-3.9/fs/aufs/sysrq.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,151 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -24472,16 +25030,18 @@ + plevel = au_plevel; + au_plevel = KERN_WARNING; + -+ sbinfo = au_sbi(sb); + /* since we define pr_fmt, call printk directly */ ++#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) ++ ++ sbinfo = au_sbi(sb); + printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); -+ printk(KERN_WARNING AUFS_NAME ": superblock\n"); ++ pr("superblock\n"); + au_dpri_sb(sb); + +#if 0 -+ printk(KERN_WARNING AUFS_NAME ": root dentry\n"); ++ pr("root dentry\n"); + au_dpri_dentry(sb->s_root); -+ printk(KERN_WARNING AUFS_NAME ": root inode\n"); ++ pr("root inode\n"); + au_dpri_inode(sb->s_root->d_inode); +#endif + @@ -24509,7 +25069,7 @@ +#if 1 + { + struct inode *i; -+ printk(KERN_WARNING AUFS_NAME ": isolated inode\n"); ++ pr("isolated inode\n"); + spin_lock(&inode_sb_list_lock); + list_for_each_entry(i, &sb->s_inodes, i_sb_list) { + spin_lock(&i->i_lock); @@ -24520,17 +25080,18 @@ + spin_unlock(&inode_sb_list_lock); + } +#endif -+ printk(KERN_WARNING AUFS_NAME ": files\n"); ++ pr("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; ++ mode = file_inode(file)->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"); ++ pr("done\n"); + ++#undef pr + au_plevel = plevel; +} + @@ -24584,8 +25145,8 @@ + pr_err("err %d (ignored)\n", err); +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/vdir.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,885 @@ ++++ linux-3.9/fs/aufs/vdir.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,878 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -24693,24 +25254,20 @@ + +static void au_nhash_wh_do_free(struct hlist_head *head) +{ -+ struct au_vdir_wh *tpos; -+ struct hlist_node *pos, *node; ++ struct au_vdir_wh *pos; ++ struct hlist_node *node; + -+ hlist_for_each_entry_safe(tpos, pos, node, head, wh_hash) { -+ /* hlist_del(pos); */ -+ kfree(tpos); -+ } ++ hlist_for_each_entry_safe(pos, node, head, wh_hash) ++ kfree(pos); +} + +static void au_nhash_de_do_free(struct hlist_head *head) +{ -+ struct au_vdir_dehstr *tpos; -+ struct hlist_node *pos, *node; ++ struct au_vdir_dehstr *pos; ++ struct hlist_node *node; + -+ hlist_for_each_entry_safe(tpos, pos, node, head, hash) { -+ /* hlist_del(pos); */ -+ au_cache_free_vdir_dehstr(tpos); -+ } ++ hlist_for_each_entry_safe(pos, node, head, hash) ++ au_cache_free_vdir_dehstr(pos); +} + +static void au_nhash_do_free(struct au_nhash *nhash, @@ -24749,15 +25306,14 @@ + int num; + unsigned int u, n; + struct hlist_head *head; -+ struct au_vdir_wh *tpos; -+ struct hlist_node *pos; ++ struct au_vdir_wh *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) ++ hlist_for_each_entry(pos, head, wh_hash) ++ if (pos->wh_bindex == btgt && ++num > limit) + return 1; + return 0; +} @@ -24789,13 +25345,12 @@ +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_wh *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; ++ hlist_for_each_entry(pos, head, wh_hash) { ++ str = &pos->wh_str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; @@ -24807,13 +25362,12 @@ +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_dehstr *pos; + struct au_vdir_destr *str; + + head = au_name_hash(delist, name, nlen); -+ hlist_for_each_entry(tpos, pos, head, hash) { -+ str = tpos->str; ++ hlist_for_each_entry(pos, head, hash) { ++ str = pos->str; + AuDbg("%.*s\n", str->len, str->name); + if (au_nhash_test_name(str, name, nlen)) + return 1; @@ -25102,8 +25656,8 @@ + int err; + unsigned int nh, u; + struct hlist_head *head; -+ struct au_vdir_wh *tpos; -+ struct hlist_node *pos, *n; ++ struct au_vdir_wh *pos; ++ struct hlist_node *n; + char *p, *o; + struct au_vdir_destr *destr; + @@ -25120,11 +25674,11 @@ + 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; ++ hlist_for_each_entry_safe(pos, n, head, wh_hash) { ++ destr = &pos->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); ++ pos->wh_ino, pos->wh_type, delist); + if (unlikely(err)) + break; + } @@ -25220,7 +25774,7 @@ + struct au_vdir *vdir, *allocated; + + err = 0; -+ inode = file->f_dentry->d_inode; ++ inode = file_inode(file); + IMustLock(inode); + SiMustAnyLock(inode->i_sb); + @@ -25344,7 +25898,7 @@ + } else + return 0; /* success */ + -+ inode = file->f_dentry->d_inode; ++ inode = file_inode(file); + err = copy_vdir(vdir_cache, au_ivdir(inode)); + if (!err) { + file->f_version = inode->i_version; @@ -25472,8 +26026,8 @@ + return 0; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/vfsub.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,777 @@ ++++ linux-3.9/fs/aufs/vfsub.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,769 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -25517,7 +26071,7 @@ + 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); ++ err = vfs_getattr(h_path, &st); + + return err; +} @@ -26016,23 +26570,18 @@ +{ + int err; + struct inode *h_inode; ++ struct super_block *h_sb; + -+ h_inode = h_path->dentry->d_inode; + if (!h_file) { -+ err = vfsub_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 = vfsub_truncate(h_path, length); ++ goto out; + } + ++ h_inode = h_path->dentry->d_inode; ++ h_sb = h_inode->i_sb; ++ lockdep_off(); ++ sb_start_write(h_sb); ++ lockdep_on(); + err = locks_verify_truncate(h_inode, h_file, length); + if (!err) + err = security_path_truncate(h_path); @@ -26041,13 +26590,10 @@ + err = do_truncate(h_path->dentry, length, attr, h_file); + lockdep_on(); + } ++ lockdep_off(); ++ sb_end_write(h_sb); ++ lockdep_on(); + -+out_inode: -+ if (!h_file) -+ put_write_access(h_inode); -+out_mnt: -+ if (!h_file) -+ vfsub_mnt_drop_write(h_path->mnt); +out: + return err; +} @@ -26252,8 +26798,8 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/vfsub.h 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,284 @@ ++++ linux-3.9/fs/aufs/vfsub.h 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,294 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -26289,6 +26835,7 @@ +/* copied from linux/fs/internal.h */ +/* todo: BAD approach!! */ +extern struct lglock vfsmount_lock; ++extern void __mnt_drop_write(struct vfsmount *); +extern spinlock_t inode_sb_list_lock; + +/* copied from linux/fs/file_table.c */ @@ -26358,19 +26905,6 @@ + +/* ---------------------------------------------------------------------- */ + -+/* cf. i_[ug]id_read() in linux/include/fs.h */ -+static inline uid_t vfsub_ia_uid(struct iattr *ia) -+{ -+ return from_kuid(&init_user_ns, ia->ia_uid); -+} -+ -+static inline gid_t vfsub_ia_gid(struct iattr *ia) -+{ -+ return from_kgid(&init_user_ns, ia->ia_gid); -+} -+ -+/* ---------------------------------------------------------------------- */ -+ +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); @@ -26411,6 +26945,13 @@ + lockdep_on(); +} + ++static inline void vfsub_mnt_drop_write_file(struct file *file) ++{ ++ lockdep_off(); ++ mnt_drop_write_file(file); ++ lockdep_on(); ++} ++ +/* ---------------------------------------------------------------------- */ + +struct au_hinode; @@ -26444,6 +26985,11 @@ +int vfsub_flush(struct file *file, fl_owner_t id); +int vfsub_readdir(struct file *file, filldir_t filldir, void *arg); + ++static inline loff_t vfsub_f_size_read(struct file *file) ++{ ++ return i_size_read(file_inode(file)); ++} ++ +static inline unsigned int vfsub_file_flags(struct file *file) +{ + unsigned int flags; @@ -26484,6 +27030,16 @@ + unsigned int flags); +long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, + loff_t *ppos, size_t len, unsigned int flags); ++ ++static inline long vfsub_truncate(struct path *path, loff_t length) ++{ ++ long err; ++ lockdep_off(); ++ err = vfs_truncate(path, length); ++ lockdep_on(); ++ return err; ++} ++ +int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, + struct file *h_file); +int vfsub_fsync(struct file *file, struct path *path, int datasync); @@ -26539,8 +27095,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_VFSUB_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/wbr_policy.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,700 @@ ++++ linux-3.9/fs/aufs/wbr_policy.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,701 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -26580,7 +27136,7 @@ + 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); ++ au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); + err = vfsub_sio_notify_change(h_path, &ia); + + /* is this nfs only? */ @@ -26640,7 +27196,7 @@ + + err = 0; + if (h_path.dentry->d_inode) { -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, + dentry); + } @@ -26651,6 +27207,7 @@ +} + +static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, ++ struct au_pin *pin, + struct dentry *h_parent, void *arg) +{ + int err, rerr; @@ -26668,7 +27225,7 @@ + AuDebugOn(h_dir != au_h_iptr(dir, bdst)); + IMustLock(h_dir); + -+ err = au_lkup_neg(dentry, bdst); ++ err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (unlikely(err < 0)) + goto out; + h_path.dentry = au_h_dptr(dentry, bdst); @@ -26974,7 +27531,7 @@ + continue; + + /* sb->s_root for NFS is unreliable */ -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + h_path.dentry = h_path.mnt->mnt_root; + err = vfs_statfs(&h_path, st); + if (unlikely(err)) { @@ -27242,8 +27799,8 @@ + } +}; --- /dev/null -+++ linux-3.x-rcN/fs/aufs/whout.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,1042 @@ ++++ linux-3.9/fs/aufs/whout.c 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,1022 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -27420,7 +27977,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + struct inode *h_dir; + struct dentry *h_parent; @@ -27479,7 +28036,7 @@ +{ + int err; + struct path h_path = { -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + + err = 0; @@ -27509,14 +28066,10 @@ + if (!whpath->dentry->d_inode) + return; + -+ err = vfsub_mnt_want_write(whpath->mnt); -+ if (!err) { -+ if (isdir) -+ err = vfsub_rmdir(h_dir, whpath); -+ else -+ err = vfsub_unlink(h_dir, whpath, /*force*/0); -+ vfsub_mnt_drop_write(whpath->mnt); -+ } ++ if (isdir) ++ err = vfsub_rmdir(h_dir, whpath); ++ else ++ err = vfsub_unlink(h_dir, whpath, /*force*/0); + if (unlikely(err)) + pr_warn("failed removing %.*s (%d), ignored.\n", + AuDLNPair(whpath->dentry), err); @@ -27545,11 +28098,7 @@ + + if (au_test_nfs(path->dentry->d_sb)) + mode |= S_IXUGO; -+ err = vfsub_mnt_want_write(path->mnt); -+ if (!err) { -+ err = vfsub_mkdir(h_dir, path, mode); -+ vfsub_mnt_drop_write(path->mnt); -+ } ++ err = vfsub_mkdir(h_dir, path, mode); + } else if (S_ISDIR(path->dentry->d_inode->i_mode)) + err = 0; + else @@ -27643,13 +28192,8 @@ + err = -EEXIST; + h_dir = h_root->d_inode; + if (!base[AuBrWh_BASE].dentry->d_inode) { -+ err = vfsub_mnt_want_write(h_path->mnt); -+ if (!err) { -+ h_path->dentry = base[AuBrWh_BASE].dentry; -+ err = vfsub_create(h_dir, h_path, WH_MASK, -+ /*want_excl*/true); -+ vfsub_mnt_drop_write(h_path->mnt); -+ } ++ h_path->dentry = base[AuBrWh_BASE].dentry; ++ err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); + } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) + err = 0; + else @@ -27681,16 +28225,14 @@ +/* + * 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 au_wh_init(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 path path = br->br_path; ++ struct dentry *h_root = path.dentry; + struct au_wbr *wbr = br->br_wbr; + static const struct qstr base_name[] = { + [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, @@ -27804,19 +28346,16 @@ + dir = a->sb->s_root->d_inode; + hdir = au_hi(dir, bindex); + h_root = au_h_dptr(a->sb->s_root, bindex); ++ AuDebugOn(h_root != au_br_dentry(a->br)); + + 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 = vfsub_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); -+ vfsub_mnt_drop_write(a->br->br_mnt); -+ } ++ h_path.dentry = wbr->wbr_whbase; ++ h_path.mnt = au_br_mnt(a->br); ++ err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); + } else { + pr_warn("%.*s is moved, ignored\n", + AuDLNPair(wbr->wbr_whbase)); @@ -27825,7 +28364,7 @@ + dput(wbr->wbr_whbase); + wbr->wbr_whbase = NULL; + if (!err) -+ err = au_wh_init(h_root, a->br, a->sb); ++ err = au_wh_init(a->br, a->sb); + wbr_wh_write_unlock(wbr); + au_hn_imtx_unlock(hdir); + di_read_unlock(a->sb->s_root, AuLock_IR); @@ -27896,7 +28435,7 @@ + IMustLock(h_dir); + + br = au_sbr(sb, bindex); -+ h_path.mnt = br->br_mnt; ++ h_path.mnt = au_br_mnt(br); + wbr = br->br_wbr; + wbr_wh_read_lock(wbr); + if (wbr->wbr_whbase) { @@ -27945,7 +28484,7 @@ + } else { + struct path tmp = { + .dentry = opq_dentry, -+ .mnt = br->br_mnt ++ .mnt = au_br_mnt(br) + }; + err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); + if (!err) @@ -28056,8 +28595,7 @@ + struct qstr wh_name; + char *p; + struct hlist_head *head; -+ struct au_vdir_wh *tpos; -+ struct hlist_node *pos; ++ struct au_vdir_wh *pos; + struct au_vdir_destr *str; + + err = -ENOMEM; @@ -28072,11 +28610,11 @@ + 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) ++ hlist_for_each_entry(pos, head, wh_hash) { ++ if (pos->wh_bindex != bindex) + continue; + -+ str = &tpos->wh_str; ++ str = &pos->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; @@ -28197,7 +28735,7 @@ + + if (!err) { + h_tmp.dentry = wh_dentry; -+ h_tmp.mnt = br->br_mnt; ++ h_tmp.mnt = au_br_mnt(br); + err = vfsub_rmdir(h_dir, &h_tmp); + } + @@ -28241,21 +28779,20 @@ + h_parent = dget_parent(a->wh_dentry); + h_dir = h_parent->d_inode; + hdir = au_hi(a->dir, bindex); ++ err = vfsub_mnt_want_write(au_br_mnt(a->br)); ++ if (unlikely(err)) ++ goto out_mnt; + 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 = vfsub_mnt_want_write(a->br->br_mnt); -+ if (!err) { -+ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, -+ &a->whlist); -+ vfsub_mnt_drop_write(a->br->br_mnt); -+ } -+ } ++ if (!err) ++ err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); + au_hn_imtx_unlock(hdir); ++ vfsub_mnt_drop_write(au_br_mnt(a->br)); ++ ++out_mnt: + dput(h_parent); + ii_write_unlock(a->dir); -+ +out: + /* mutex_unlock(&a->dir->i_mutex); */ + au_whtmp_rmdir_free(a); @@ -28287,8 +28824,8 @@ + } +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/whout.h 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,88 @@ ++++ linux-3.9/fs/aufs/whout.h 2013-05-13 17:13:17.220468929 +0200 +@@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -28329,8 +28866,7 @@ +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); ++int au_wh_init(struct au_branch *br, struct super_block *sb); + +/* diropq flags */ +#define AuDiropq_CREATE 1 @@ -28378,7 +28914,7 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_WHOUT_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/wkq.c 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/fs/aufs/wkq.c 2013-05-13 17:13:17.223803898 +0200 @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -28595,7 +29131,7 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/fs/aufs/wkq.h 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/fs/aufs/wkq.h 2013-05-13 17:13:17.223803898 +0200 @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima @@ -28690,8 +29226,8 @@ +#endif /* __KERNEL__ */ +#endif /* __AUFS_WKQ_H__ */ --- /dev/null -+++ linux-3.x-rcN/fs/aufs/xino.c 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,1265 @@ ++++ linux-3.9/fs/aufs/xino.c 2013-05-13 17:13:17.223803898 +0200 +@@ -0,0 +1,1264 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -28859,7 +29395,7 @@ + goto out_dput; + } + -+ path.mnt = base_file->f_vfsmnt; ++ path.mnt = base_file->f_path.mnt; + file = vfsub_dentry_open(&path, + O_RDWR | O_CREAT | O_EXCL | O_LARGEFILE + /* | __FMODE_NONOTIFY */); @@ -28876,8 +29412,7 @@ + + 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)); ++ err = au_copy_file(file, copy_src, vfsub_f_size_read(copy_src)); + if (unlikely(err)) { + pr_err("%.*s copy err %d\n", AuLNPair(name), err); + goto out_fput; @@ -28963,12 +29498,12 @@ + fput(file); + br->br_xino.xi_file = new_xino; + -+ h_sb = br->br_mnt->mnt_sb; ++ h_sb = au_br_sb(br); + for (bi = 0; bi <= bend; bi++) { + if (unlikely(bi == bindex)) + continue; + br = au_sbr(sb, bi); -+ if (br->br_mnt->mnt_sb != h_sb) ++ if (au_br_sb(br) != h_sb) + continue; + + fput(br->br_xino.xi_file); @@ -29004,7 +29539,7 @@ + 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 ++ && file_inode(br->br_xino.xi_file)->i_blocks + >= br->br_xino_upper) + br->br_xino_upper += AUFS_XINO_TRUNC_STEP; + @@ -29024,7 +29559,7 @@ + struct xino_do_trunc_args *args; + int wkq_err; + -+ if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks ++ if (file_inode(br->br_xino.xi_file)->i_blocks + < br->br_xino_upper) + return; + @@ -29103,7 +29638,7 @@ + h_ino, ino); + if (!err) { + if (au_opt_test(mnt_flags, TRUNC_XINO) -+ && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) ++ && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + return 0; /* success */ + } @@ -29161,7 +29696,7 @@ + + pos = pindex; + pos *= PAGE_SIZE; -+ if (i_size_read(xib->f_dentry->d_inode) >= pos + PAGE_SIZE) ++ if (vfsub_f_size_read(xib) >= pos + PAGE_SIZE) + sz = xino_fread(sbinfo->si_xread, xib, p, PAGE_SIZE, &pos); + else { + memset(p, 0, PAGE_SIZE); @@ -29256,7 +29791,7 @@ + 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)) ++ && au_test_fs_trunc_xino(au_br_sb(br))) + xino_try_trunc(sb, br); + } +} @@ -29294,7 +29829,7 @@ + } + + file = sbinfo->si_xib; -+ pend = i_size_read(file->f_dentry->d_inode) / PAGE_SIZE; ++ pend = vfsub_f_size_read(file) / PAGE_SIZE; + for (ul = pindex + 1; ul <= pend; ul++) { + err = xib_pindex(sb, ul); + if (unlikely(err)) @@ -29347,7 +29882,7 @@ + pos *= sizeof(*ino); + + file = au_sbr(sb, bindex)->br_xino.xi_file; -+ if (i_size_read(file->f_dentry->d_inode) < pos + sizeof(*ino)) ++ if (vfsub_f_size_read(file) < pos + sizeof(*ino)) + return 0; /* no ino */ + + sz = xino_fread(sbinfo->si_xread, file, ino, sizeof(*ino), &pos); @@ -29463,10 +29998,10 @@ + shared_br = NULL; + bend = au_sbend(sb); + if (do_test) { -+ tgt_sb = br->br_mnt->mnt_sb; ++ tgt_sb = au_br_sb(br); + for (bindex = 0; bindex <= bend; bindex++) { + b = au_sbr(sb, bindex); -+ if (tgt_sb == b->br_mnt->mnt_sb) { ++ if (tgt_sb == au_br_sb(b)) { + shared_br = b; + break; + } @@ -29522,7 +30057,7 @@ + MtxMustLock(&sbinfo->si_xib_mtx); + p = sbinfo->si_xib_buf; + func = sbinfo->si_xread; -+ pend = i_size_read(file->f_dentry->d_inode); ++ pend = vfsub_f_size_read(file); + pos = 0; + while (pos < pend) { + sz = xino_fread(func, file, page, PAGE_SIZE, &pos); @@ -29592,7 +30127,7 @@ + goto out; + + file = sbinfo->si_xib; -+ if (i_size_read(file->f_dentry->d_inode) <= PAGE_SIZE) ++ if (vfsub_f_size_read(file) <= PAGE_SIZE) + goto out; + + au_xino_lock_dir(sb, file, &ldir); @@ -29702,7 +30237,7 @@ + + sbinfo->si_xib_last_pindex = 0; + sbinfo->si_xib_next_bit = 0; -+ if (i_size_read(file->f_dentry->d_inode) < PAGE_SIZE) { ++ if (vfsub_f_size_read(file) < PAGE_SIZE) { + pos = 0; + err = xino_fwrite(sbinfo->si_xwrite, file, sbinfo->si_xib_buf, + PAGE_SIZE, &pos); @@ -29895,7 +30430,7 @@ + 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)) { ++ && !au_test_fs_bad_xino(au_br_sb(br))) { + bwr = bindex; + break; + } @@ -29906,7 +30441,7 @@ + page = (void *)__get_free_page(GFP_NOFS); + if (unlikely(!page)) + goto out; -+ path.mnt = br->br_mnt; ++ path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(sb->s_root, bwr); + p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); + file = (void *)p; @@ -29958,7 +30493,7 @@ + return err; +} --- /dev/null -+++ linux-3.x-rcN/include/linux/aufs_type.h 2013-02-19 11:26:12.682457188 -0500 ++++ linux-3.9/include/linux/aufs_type.h 2013-05-13 17:13:17.223803898 +0200 @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012-2013 Junjiro R. Okajima @@ -29980,8 +30515,8 @@ + +#include --- /dev/null -+++ linux-3.x-rcN/include/uapi/linux/aufs_type.h 2013-02-19 11:26:12.682457188 -0500 -@@ -0,0 +1,233 @@ ++++ linux-3.9/include/uapi/linux/aufs_type.h 2013-05-13 17:13:17.223803898 +0200 +@@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * @@ -30014,8 +30549,9 @@ +#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ +#include +#undef pr_fmt -+#define pr_fmt(fmt) AUFS_NAME " %s:%d:%s[%d]: " fmt, \ -+ __func__, __LINE__, current->comm, current->pid ++#define pr_fmt(fmt) \ ++ AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ ++ (int)sizeof(current->comm), current->comm, current->pid +#else +#include +#include @@ -30023,7 +30559,7 @@ + +#include + -+#define AUFS_VERSION "3.x-rcN-20130204" ++#define AUFS_VERSION "3.9-20130513" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') @@ -30060,7 +30596,7 @@ +#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 */ ++/* a limit for rmdir/rename a dir and copyup */ +#define AUFS_MAX_NAMELEN (NAME_MAX \ + - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ + - 1 /* dot */\ @@ -30077,7 +30613,7 @@ +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ -+#define AUFS_PLINK_WARN 100 /* number of plinks */ ++#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ + +/* pseudo-link maintenace under /proc */ +#define AUFS_PLINK_MAINT_NAME "plink_maint" @@ -30102,6 +30638,7 @@ +#define AUFS_BRPERM_RR "rr" +#define AUFS_BRRATTR_WH "wh" +#define AUFS_BRWATTR_NLWH "nolwh" ++#define AUFS_BRATTR_UNPIN "unpin" + +/* ---------------------------------------------------------------------- */ + diff --git a/linux/config.x86_64 b/linux/config.x86_64 index 8d6be68b4..1def1cf3d 100644 --- a/linux/config.x86_64 +++ b/linux/config.x86_64 @@ -1,6 +1,6 @@ # # Automatically generated file; DO NOT EDIT. -# Linux/x86_64 3.9.0-1 Kernel Configuration +# Linux/x86_64 3.9.2-1 Kernel Configuration # CONFIG_64BIT=y CONFIG_X86_64=y diff --git a/linux/linux.install b/linux/linux.install index 43bb7420e..b86e52230 100644 --- a/linux/linux.install +++ b/linux/linux.install @@ -2,7 +2,7 @@ # arg 2: the old package version KERNEL_NAME= -KERNEL_VERSION=3.9.0-1-CHAKRA +KERNEL_VERSION=3.9.2-1-CHAKRA # set a sane PATH to ensure that critical utils like depmod will be found export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' @@ -49,4 +49,4 @@ post_remove() { # also remove the compat symlinks rm -f boot/{initramfs-linux,kernel26}${KERNEL_NAME}.img rm -f boot/{initramfs-linux,kernel26}${KERNEL_NAME}-fallback.img -} \ No newline at end of file +} diff --git a/ndiswrapper/PKGBUILD b/ndiswrapper/PKGBUILD index 97f34fe0a..77e648f10 100644 --- a/ndiswrapper/PKGBUILD +++ b/ndiswrapper/PKGBUILD @@ -6,25 +6,31 @@ _kver="$(cat /lib/modules/${_extramodules}/version)" pkgname=ndiswrapper pkgver=1.58 -pkgrel=2 +pkgrel=1 pkgdesc="Module for NDIS (Windows Network Drivers) drivers supplied by vendors. For linux-testing." license=('GPL') arch=('x86_64') url="http://ndiswrapper.sourceforge.net" install="ndiswrapper.install" -depends=('linux>=3.9' 'linux<4.0') +depends=('linux>=3.9' 'linux<3.10') provides=("ndiswrapper-utils=$pkgver") replaces=('ndiswrapper-utils') conflicts=('ndiswrapper-utils') makedepends=('linux-headers') -source=("http://downloads.sourceforge.net/sourceforge/ndiswrapper/ndiswrapper-$pkgver.tar.gz") -md5sums=('ba6f57e5eb2f9e94cb07c7f151b2afcd') +source=("http://downloads.sourceforge.net/sourceforge/ndiswrapper/ndiswrapper-$pkgver.tar.gz" + ndiswrapper-1.58-add_taint.patch) +sha1sums=('a256812b3136648ed93e04146d2276a3ca70957c' + 'f2c8bd7553f87a647b2e221b01fbd5d0d07eff8c') build() { cd "$srcdir/$pkgname-$pkgver" + # https://bugs.gentoo.org/show_bug.cgi?id=467956 3.9 kernels + patch -Np1 -i "$srcdir/ndiswrapper-1.58-add_taint.patch" + sed -i "/modinfo/s/s/usr\//" driver/Makefile sed -i "s|/include/linux/version.h|/include/generated/uapi/linux/version.h|g" driver/Makefile + make KVERS=$_kver } diff --git a/ndiswrapper/ndiswrapper-1.58-add_taint.patch b/ndiswrapper/ndiswrapper-1.58-add_taint.patch new file mode 100644 index 000000000..87dc3a0e7 --- /dev/null +++ b/ndiswrapper/ndiswrapper-1.58-add_taint.patch @@ -0,0 +1,24 @@ +diff -ur ndiswrapper-1.58.orig/driver/loader.c ndiswrapper-1.58/driver/loader.c +--- ndiswrapper-1.58.orig/driver/loader.c 2013-05-01 09:48:34.910000000 +0900 ++++ ndiswrapper-1.58/driver/loader.c 2013-05-01 09:48:59.870000000 +0900 +@@ -575,7 +575,7 @@ + } else { + printk(KERN_INFO "%s: driver %s (%s) loaded\n", + DRIVER_NAME, wrap_driver->name, wrap_driver->version); +- add_taint(TAINT_PROPRIETARY_MODULE); ++ add_taint(TAINT_PROPRIETARY_MODULE, LOCKDEP_STILL_OK); + EXIT1(return 0); + } + } +diff -ur ndiswrapper-1.58.orig/driver/wrapper.c ndiswrapper-1.58/driver/wrapper.c +--- ndiswrapper-1.58.orig/driver/wrapper.c 2013-02-20 03:00:37.000000000 +0900 ++++ ndiswrapper-1.58/driver/wrapper.c 2013-05-01 09:54:30.140000000 +0900 +@@ -72,7 +72,7 @@ + static int __init wrapper_init(void) + { + #ifdef TAINT_OOT_MODULE +- add_taint(TAINT_OOT_MODULE); ++ add_taint(TAINT_OOT_MODULE, LOCKDEP_STILL_OK); + #endif + printk(KERN_INFO "%s version %s loaded (smp=%s, preempt=%s)\n", + DRIVER_NAME, DRIVER_VERSION,