[packages/kernel/LINUX_3_14] - 3.14.62 - updated aufs patch

baggins baggins at pld-linux.org
Sun Feb 28 13:59:38 CET 2016


commit 5e15af1bd4e185defb21463f5bba76871b437913
Author: Jan Rękorajski <baggins at pld-linux.org>
Date:   Sun Feb 28 13:59:14 2016 +0100

    - 3.14.62
    - updated aufs patch

 kernel-aufs3.patch    | 8771 +++++++++++++++++++++++++++++++------------------
 kernel-patches.config |    2 +
 kernel.spec           |    4 +-
 3 files changed, 5667 insertions(+), 3110 deletions(-)
---
diff --git a/kernel.spec b/kernel.spec
index 862a9ed..73f2459 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -70,7 +70,7 @@
 
 %define		rel		1
 %define		basever		3.14
-%define		postver		.61
+%define		postver		.62
 
 %define		versuffix	-%{basever}
 
@@ -115,7 +115,7 @@ Source0:	http://www.kernel.org/pub/linux/kernel/v3.x/linux-%{basever}.tar.xz
 # Source0-md5:	b621207b3f6ecbb67db18b13258f8ea8
 %if "%{postver}" != ".0"
 Patch0:		http://www.kernel.org/pub/linux/kernel/v3.x/patch-%{version}.xz
-# Patch0-md5:	bb15aae18d16cfddb4d4a8b89c4743ea
+# Patch0-md5:	ecd58d393390073de704735e61002b4b
 %endif
 Source1:	kernel.sysconfig
 
diff --git a/kernel-aufs3.patch b/kernel-aufs3.patch
index 2e59ec7..28fde09 100644
--- a/kernel-aufs3.patch
+++ b/kernel-aufs3.patch
@@ -1,4 +1,4 @@
-aufs3.14 kbuild patch
+aufs3.14.40+ kbuild patch
 
 diff --git a/fs/Kconfig b/fs/Kconfig
 index 7385e54..d5c769c 100644
@@ -33,8 +33,33 @@ index 3ce25b5..9faebdc 100644
  header-y += auto_fs.h
  header-y += auto_fs4.h
  header-y += auxvec.h
-aufs3.14 base patch
+aufs3.14.40+ base patch
 
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 900d98e..28c4a56 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1629,6 +1629,20 @@ F:	include/linux/audit.h
+ F:	include/uapi/linux/audit.h
+ F:	kernel/audit*
+ 
++AUFS (advanced multi layered unification filesystem) FILESYSTEM
++M:	"J. R. Okajima" <hooanon05g at gmail.com>
++L:	linux-unionfs at vger.kernel.org
++L:	aufs-users at lists.sourceforge.net (members only)
++W:	http://aufs.sourceforge.net
++T:	git://git.code.sf.net/p/aufs/aufs3-linux
++T:	git://github.com/sfjro/aufs3-linux.git
++S:	Supported
++F:	Documentation/filesystems/aufs/
++F:	Documentation/ABI/testing/debugfs-aufs
++F:	Documentation/ABI/testing/sysfs-aufs
++F:	fs/aufs/
++F:	include/uapi/linux/aufs_type.h
++
+ AUXILIARY DISPLAY DRIVERS
+ M:	Miguel Ojeda Sandonis <miguel.ojeda.sandonis at gmail.com>
+ W:	http://miguelojeda.es/auxdisplay.htm
 diff --git a/drivers/block/loop.c b/drivers/block/loop.c
 index 66e8c3b..ec278ac 100644
 --- a/drivers/block/loop.c
@@ -64,8 +89,21 @@ index 66e8c3b..ec278ac 100644
  /* loop sysfs attributes */
  
  static ssize_t loop_attr_show(struct device *dev, char *page,
+diff --git a/fs/dcache.c b/fs/dcache.c
+index c345f5f..3214ccd 100644
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -1060,7 +1060,7 @@ enum d_walk_ret {
+  *
+  * The @enter() and @finish() callbacks are called with d_lock held.
+  */
+-static void d_walk(struct dentry *parent, void *data,
++void d_walk(struct dentry *parent, void *data,
+ 		   enum d_walk_ret (*enter)(void *, struct dentry *),
+ 		   void (*finish)(void *))
+ {
 diff --git a/fs/inode.c b/fs/inode.c
-index 4bcdad3..bc83168 100644
+index e846a32..49960c6 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
 @@ -1497,7 +1497,7 @@ static int relatime_need_update(struct vfsmount *mnt, struct inode *inode,
@@ -105,6 +143,18 @@ index 12028fa..f26cbaf 100644
  {
  	ssize_t (*splice_read)(struct file *, loff_t *,
  			       struct pipe_inode_info *, size_t, unsigned int);
+diff --git a/include/linux/file.h b/include/linux/file.h
+index 4d69123..62cffc0 100644
+--- a/include/linux/file.h
++++ b/include/linux/file.h
+@@ -19,6 +19,7 @@ struct dentry;
+ struct path;
+ extern struct file *alloc_file(struct path *, fmode_t mode,
+ 	const struct file_operations *fop);
++extern struct file *get_empty_filp(void);
+ 
+ static inline void fput_light(struct file *file, int fput_needed)
+ {
 diff --git a/include/linux/fs.h b/include/linux/fs.h
 index 23b2a35..f3f635c 100644
 --- a/include/linux/fs.h
@@ -132,13 +182,13 @@ index 0e43906..304169e 100644
 +			 struct pipe_inode_info *pipe, size_t len,
 +			 unsigned int flags);
  #endif
-aufs3.14 mmap patch
+aufs3.14.40+ mmap patch
 
 diff --git a/fs/buffer.c b/fs/buffer.c
-index 27265a8..75427a6 100644
+index eef21c6..37f4df4 100644
 --- a/fs/buffer.c
 +++ b/fs/buffer.c
-@@ -2448,7 +2448,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
+@@ -2470,7 +2470,7 @@ int block_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf,
  	 * Update file times before taking page lock. We may end up failing the
  	 * fault so this update may be superfluous but who really cares...
  	 */
@@ -147,37 +197,52 @@ index 27265a8..75427a6 100644
  
  	ret = __block_page_mkwrite(vma, vmf, get_block);
  	sb_end_pagefault(sb);
+diff --git a/fs/proc/base.c b/fs/proc/base.c
+index 489ba8c..115cba0 100644
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -1828,7 +1828,7 @@ static int proc_map_files_get_link(struct dentry *dentry, struct path *path)
+ 	down_read(&mm->mmap_sem);
+ 	vma = find_exact_vma(mm, vm_start, vm_end);
+ 	if (vma && vma->vm_file) {
+-		*path = vma->vm_file->f_path;
++		*path = vma_pr_or_file(vma)->f_path;
+ 		path_get(path);
+ 		rc = 0;
+ 	}
 diff --git a/fs/proc/nommu.c b/fs/proc/nommu.c
-index d4a3574..e44a744 100644
+index d4a3574..1397181 100644
 --- a/fs/proc/nommu.c
 +++ b/fs/proc/nommu.c
-@@ -45,7 +45,9 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
+@@ -45,7 +45,10 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
  	file = region->vm_file;
  
  	if (file) {
 -		struct inode *inode = file_inode(region->vm_file);
 +		struct inode *inode;
++
 +		file = vmr_pr_or_file(region);
 +		inode = file_inode(file);
  		dev = inode->i_sb->s_dev;
  		ino = inode->i_ino;
  	}
 diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
-index fb52b54..1aca72e 100644
+index eaa7374..f422b1a 100644
 --- a/fs/proc/task_mmu.c
 +++ b/fs/proc/task_mmu.c
-@@ -264,7 +264,9 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
+@@ -265,7 +265,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
  	const char *name = NULL;
  
  	if (file) {
 -		struct inode *inode = file_inode(vma->vm_file);
 +		struct inode *inode;
++
 +		file = vma_pr_or_file(vma);
 +		inode = file_inode(file);
  		dev = inode->i_sb->s_dev;
  		ino = inode->i_ino;
  		pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
-@@ -1407,6 +1409,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
+@@ -1434,6 +1437,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
  	seq_printf(m, "%08lx %s", vma->vm_start, buffer);
  
  	if (file) {
@@ -186,112 +251,35 @@ index fb52b54..1aca72e 100644
  		seq_path(m, &file->f_path, "\n\t= ");
  	} else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) {
 diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
-index 678455d..ad0ce45 100644
+index 678455d..ebd34ba 100644
 --- a/fs/proc/task_nommu.c
 +++ b/fs/proc/task_nommu.c
-@@ -141,7 +141,9 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
+@@ -141,7 +141,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
  	file = vma->vm_file;
  
  	if (file) {
 -		struct inode *inode = file_inode(vma->vm_file);
 +		struct inode *inode;
-+		file = vma_pr_or_file(file);
++
++		file = vma_pr_or_file(vma);
 +		inode = file_inode(file);
  		dev = inode->i_sb->s_dev;
  		ino = inode->i_ino;
  		pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
 diff --git a/include/linux/mm.h b/include/linux/mm.h
-index c1b7414..02036e0 100644
+index a7b311d..c823224 100644
 --- a/include/linux/mm.h
 +++ b/include/linux/mm.h
-@@ -18,6 +18,9 @@
- #include <linux/pfn.h>
- #include <linux/bit_spinlock.h>
- #include <linux/shrinker.h>
-+#include <linux/dcache.h>
-+#include <linux/file.h>
-+#include <linux/fs.h>
- 
- struct mempolicy;
- struct anon_vma;
-@@ -1152,6 +1155,87 @@ static inline int fixup_user_fault(struct task_struct *tsk,
+@@ -1163,6 +1163,28 @@ static inline int fixup_user_fault(struct task_struct *tsk,
  }
  #endif
  
-+/*
-+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
-+ * in /proc/PID/maps.
-+ */
-+/* #define AUFS_DEBUG_MMAP */
-+static inline void aufs_trace(struct file *f, struct file *pr,
-+			      const char func[], int line, const char func2[])
-+{
-+#ifdef AUFS_DEBUG_MMAP
-+	if (pr)
-+		pr_info("%s:%d: %s, %p\n", func, line, func2,
-+			f ? (char *)f->f_dentry->d_name.name : "(null)");
-+#endif
-+}
-+
-+static inline struct file *vmr_do_pr_or_file(struct vm_region *region,
-+					     const char func[], int line)
-+{
-+	struct file *f = region->vm_file, *pr = region->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	return (f && pr) ? pr : f;
-+}
-+
-+static inline void vmr_do_fput(struct vm_region *region,
-+			       const char func[], int line)
-+{
-+	struct file *f = region->vm_file, *pr = region->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	fput(f);
-+	if (f && pr)
-+		fput(pr);
-+}
-+
-+static inline void vma_do_file_update_time(struct vm_area_struct *vma,
-+					   const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	file_update_time(f);
-+	if (f && pr)
-+		file_update_time(pr);
-+}
-+
-+static inline struct file *vma_do_pr_or_file(struct vm_area_struct *vma,
-+					     const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	return (f && pr) ? pr : f;
-+}
++extern void vma_do_file_update_time(struct vm_area_struct *, const char[], int);
++extern struct file *vma_do_pr_or_file(struct vm_area_struct *, const char[],
++				      int);
++extern void vma_do_get_file(struct vm_area_struct *, const char[], int);
++extern void vma_do_fput(struct vm_area_struct *, const char[], int);
 +
-+static inline void vma_do_get_file(struct vm_area_struct *vma,
-+				   const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	get_file(f);
-+	if (f && pr)
-+		get_file(pr);
-+}
-+
-+static inline void vma_do_fput(struct vm_area_struct *vma,
-+			       const char func[], int line)
-+{
-+	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
-+	aufs_trace(f, pr, func, line, __func__);
-+	fput(f);
-+	if (f && pr)
-+		fput(pr);
-+}
-+
-+#define vmr_pr_or_file(region)		vmr_do_pr_or_file(region, __func__, \
-+							  __LINE__)
-+#define vmr_fput(region)		vmr_do_fput(region, __func__, __LINE__)
 +#define vma_file_update_time(vma)	vma_do_file_update_time(vma, __func__, \
 +								__LINE__)
 +#define vma_pr_or_file(vma)		vma_do_pr_or_file(vma, __func__, \
@@ -299,11 +287,20 @@ index c1b7414..02036e0 100644
 +#define vma_get_file(vma)		vma_do_get_file(vma, __func__, __LINE__)
 +#define vma_fput(vma)			vma_do_fput(vma, __func__, __LINE__)
 +
++#ifndef CONFIG_MMU
++extern struct file *vmr_do_pr_or_file(struct vm_region *, const char[], int);
++extern void vmr_do_fput(struct vm_region *, const char[], int);
++
++#define vmr_pr_or_file(region)		vmr_do_pr_or_file(region, __func__, \
++							  __LINE__)
++#define vmr_fput(region)		vmr_do_fput(region, __func__, __LINE__)
++#endif /* !CONFIG_MMU */
++
  extern int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, int len, int write);
  extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
  		void *buf, int len, int write);
 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
-index 290901a..c21588b 100644
+index 2b58d19..ca020f3 100644
 --- a/include/linux/mm_types.h
 +++ b/include/linux/mm_types.h
 @@ -231,6 +231,7 @@ struct vm_region {
@@ -323,10 +320,10 @@ index 290901a..c21588b 100644
  
  #ifndef CONFIG_MMU
 diff --git a/kernel/fork.c b/kernel/fork.c
-index a17621c..40d9f6a 100644
+index e2c6853..8fa0a96 100644
 --- a/kernel/fork.c
 +++ b/kernel/fork.c
-@@ -412,7 +412,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
+@@ -414,7 +414,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
  			struct inode *inode = file_inode(file);
  			struct address_space *mapping = file->f_mapping;
  
@@ -335,11 +332,24 @@ index a17621c..40d9f6a 100644
  			if (tmp->vm_flags & VM_DENYWRITE)
  				atomic_dec(&inode->i_writecount);
  			mutex_lock(&mapping->i_mmap_mutex);
+diff --git a/mm/Makefile b/mm/Makefile
+index c561f1f..c52f298 100644
+--- a/mm/Makefile
++++ b/mm/Makefile
+@@ -17,7 +17,7 @@ obj-y			:= filemap.o mempool.o oom_kill.o fadvise.o \
+ 			   util.o mmzone.o vmstat.o backing-dev.o \
+ 			   mm_init.o mmu_context.o percpu.o slab_common.o \
+ 			   compaction.o balloon_compaction.o vmacache.o \
+-			   interval_tree.o list_lru.o $(mmu-y)
++			   interval_tree.o list_lru.o prfile.o $(mmu-y)
+ 
+ obj-y += init-mm.o
+ 
 diff --git a/mm/filemap.c b/mm/filemap.c
-index 7a13f6a..f1805df 100644
+index 217cfd3..58502d1 100644
 --- a/mm/filemap.c
 +++ b/mm/filemap.c
-@@ -1733,7 +1733,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
+@@ -1939,7 +1939,7 @@ int filemap_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
  	int ret = VM_FAULT_LOCKED;
  
  	sb_start_pagefault(inode->i_sb);
@@ -349,43 +359,45 @@ index 7a13f6a..f1805df 100644
  	if (page->mapping != inode->i_mapping) {
  		unlock_page(page);
 diff --git a/mm/fremap.c b/mm/fremap.c
-index 34feba6..d857364 100644
+index 34feba6..5397350 100644
 --- a/mm/fremap.c
 +++ b/mm/fremap.c
-@@ -227,7 +227,9 @@ get_write_lock:
+@@ -223,16 +223,28 @@ get_write_lock:
+ 		 */
+ 		if (mapping_cap_account_dirty(mapping)) {
+ 			unsigned long addr;
+-			struct file *file = get_file(vma->vm_file);
++			struct file *file = vma->vm_file,
++				*prfile = vma->vm_prfile;
++
  			/* mmap_region may free vma; grab the info now */
  			vm_flags = vma->vm_flags;
  
 +			vma_get_file(vma);
  			addr = mmap_region(file, start, size, vm_flags, pgoff);
+-			fput(file);
 +			vma_fput(vma);
- 			fput(file);
  			if (IS_ERR_VALUE(addr)) {
  				err = addr;
-diff --git a/mm/madvise.c b/mm/madvise.c
-index 539eeb9..5e700b1 100644
---- a/mm/madvise.c
-+++ b/mm/madvise.c
-@@ -327,12 +327,12 @@ static long madvise_remove(struct vm_area_struct *vma,
- 	 * vma's reference to the file) can go away as soon as we drop
- 	 * mmap_sem.
- 	 */
--	get_file(f);
-+	vma_get_file(vma);
- 	up_read(&current->mm->mmap_sem);
- 	error = do_fallocate(f,
- 				FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
- 				offset, end - start);
--	fput(f);
-+	vma_fput(vma);
- 	down_read(&current->mm->mmap_sem);
- 	return error;
- }
+ 			} else {
+ 				BUG_ON(addr != start);
++				if (prfile) {
++					struct vm_area_struct *new_vma;
++
++					new_vma = find_vma(mm, addr);
++					if (!new_vma->vm_prfile)
++						new_vma->vm_prfile = prfile;
++					if (new_vma != vma)
++						get_file(prfile);
++				}
+ 				err = 0;
+ 			}
+ 			goto out_freed;
 diff --git a/mm/memory.c b/mm/memory.c
-index 22dfa61..81813d9 100644
+index 749e1c6..d46df34 100644
 --- a/mm/memory.c
 +++ b/mm/memory.c
-@@ -2755,7 +2755,7 @@ reuse:
+@@ -2761,7 +2761,7 @@ reuse:
  			set_page_dirty_balance(dirty_page, page_mkwrite);
  			/* file_update_time outside page_lock */
  			if (vma->vm_file)
@@ -394,7 +406,7 @@ index 22dfa61..81813d9 100644
  		}
  		put_page(dirty_page);
  		if (page_mkwrite) {
-@@ -3467,7 +3467,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
+@@ -3473,7 +3473,7 @@ static int __do_fault(struct mm_struct *mm, struct vm_area_struct *vma,
  
  		/* file_update_time outside page_lock */
  		if (vma->vm_file && !page_mkwrite)
@@ -404,10 +416,10 @@ index 22dfa61..81813d9 100644
  		unlock_page(vmf.page);
  		if (anon)
 diff --git a/mm/mmap.c b/mm/mmap.c
-index 20ff0c3..f743033 100644
+index d4c97ba..837741e 100644
 --- a/mm/mmap.c
 +++ b/mm/mmap.c
-@@ -249,7 +249,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
+@@ -250,7 +250,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
  	if (vma->vm_ops && vma->vm_ops->close)
  		vma->vm_ops->close(vma);
  	if (vma->vm_file)
@@ -416,7 +428,7 @@ index 20ff0c3..f743033 100644
  	mpol_put(vma_policy(vma));
  	kmem_cache_free(vm_area_cachep, vma);
  	return next;
-@@ -859,7 +859,7 @@ again:			remove_next = 1 + (end > next->vm_end);
+@@ -864,7 +864,7 @@ again:			remove_next = 1 + (end > next->vm_end);
  	if (remove_next) {
  		if (file) {
  			uprobe_munmap(next, next->vm_start, next->vm_end);
@@ -425,7 +437,7 @@ index 20ff0c3..f743033 100644
  		}
  		if (next->anon_vma)
  			anon_vma_merge(vma, next);
-@@ -1639,8 +1639,8 @@ out:
+@@ -1644,8 +1644,8 @@ out:
  unmap_and_free_vma:
  	if (vm_flags & VM_DENYWRITE)
  		allow_write_access(file);
@@ -435,7 +447,7 @@ index 20ff0c3..f743033 100644
  
  	/* Undo any partial mapping done by a device driver. */
  	unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
-@@ -2429,7 +2429,7 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+@@ -2439,7 +2439,7 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
  		goto out_free_mpol;
  
  	if (new->vm_file)
@@ -444,7 +456,7 @@ index 20ff0c3..f743033 100644
  
  	if (new->vm_ops && new->vm_ops->open)
  		new->vm_ops->open(new);
-@@ -2448,7 +2448,7 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+@@ -2458,7 +2458,7 @@ static int __split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
  	if (new->vm_ops && new->vm_ops->close)
  		new->vm_ops->close(new);
  	if (new->vm_file)
@@ -453,7 +465,7 @@ index 20ff0c3..f743033 100644
  	unlink_anon_vmas(new);
   out_free_mpol:
  	mpol_put(vma_policy(new));
-@@ -2837,7 +2837,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
+@@ -2847,7 +2847,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
  			if (anon_vma_clone(new_vma, vma))
  				goto out_free_mempol;
  			if (new_vma->vm_file)
@@ -462,28 +474,11 @@ index 20ff0c3..f743033 100644
  			if (new_vma->vm_ops && new_vma->vm_ops->open)
  				new_vma->vm_ops->open(new_vma);
  			vma_link(mm, new_vma, prev, rb_link, rb_parent);
-diff --git a/mm/msync.c b/mm/msync.c
-index 632df45..02d770e 100644
---- a/mm/msync.c
-+++ b/mm/msync.c
-@@ -80,10 +80,10 @@ SYSCALL_DEFINE3(msync, unsigned long, start, size_t, len, int, flags)
- 		start = vma->vm_end;
- 		if ((flags & MS_SYNC) && file &&
- 				(vma->vm_flags & VM_SHARED)) {
--			get_file(file);
-+			vma_get_file(vma);
- 			up_read(&mm->mmap_sem);
- 			error = vfs_fsync(file, 0);
--			fput(file);
-+			vma_fput(vma);
- 			if (error || start >= end)
- 				goto out;
- 			down_read(&mm->mmap_sem);
 diff --git a/mm/nommu.c b/mm/nommu.c
-index 8740213..ea7e336 100644
+index 76b3f90..244dd6a 100644
 --- a/mm/nommu.c
 +++ b/mm/nommu.c
-@@ -653,7 +653,7 @@ static void __put_nommu_region(struct vm_region *region)
+@@ -654,7 +654,7 @@ static void __put_nommu_region(struct vm_region *region)
  		up_write(&nommu_region_sem);
  
  		if (region->vm_file)
@@ -492,7 +487,7 @@ index 8740213..ea7e336 100644
  
  		/* IO memory and memory shared directly out of the pagecache
  		 * from ramfs/tmpfs mustn't be released here */
-@@ -811,7 +811,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
+@@ -819,7 +819,7 @@ static void delete_vma(struct mm_struct *mm, struct vm_area_struct *vma)
  	if (vma->vm_ops && vma->vm_ops->close)
  		vma->vm_ops->close(vma);
  	if (vma->vm_file)
@@ -501,7 +496,7 @@ index 8740213..ea7e336 100644
  	put_nommu_region(vma->vm_region);
  	kmem_cache_free(vm_area_cachep, vma);
  }
-@@ -1377,7 +1377,7 @@ unsigned long do_mmap_pgoff(struct file *file,
+@@ -1385,7 +1385,7 @@ unsigned long do_mmap_pgoff(struct file *file,
  					goto error_just_free;
  				}
  			}
@@ -510,7 +505,7 @@ index 8740213..ea7e336 100644
  			kmem_cache_free(vm_region_jar, region);
  			region = pregion;
  			result = start;
-@@ -1453,10 +1453,10 @@ error_just_free:
+@@ -1461,10 +1461,10 @@ error_just_free:
  	up_write(&nommu_region_sem);
  error:
  	if (region->vm_file)
@@ -523,10 +518,134 @@ index 8740213..ea7e336 100644
  	kmem_cache_free(vm_area_cachep, vma);
  	kleave(" = %d", ret);
  	return ret;
-aufs3.14 standalone patch
+diff --git a/mm/prfile.c b/mm/prfile.c
+new file mode 100644
+index 0000000..532e518
+--- /dev/null
++++ b/mm/prfile.c
+@@ -0,0 +1,86 @@
++/*
++ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
++ * in /proc/PID/maps.
++ * Call these functions via macros defined in linux/mm.h.
++ *
++ * See Documentation/filesystems/aufs/design/06mmap.txt
++ *
++ * Copyright (c) 2014 Junjro R. Okajima
++ * Copyright (c) 2014 Ian Campbell
++ */
++
++#include <linux/mm.h>
++#include <linux/file.h>
++#include <linux/fs.h>
++
++/* #define PRFILE_TRACE */
++static inline void prfile_trace(struct file *f, struct file *pr,
++			      const char func[], int line, const char func2[])
++{
++#ifdef PRFILE_TRACE
++	if (pr)
++		pr_info("%s:%d: %s, %s\n", func, line, func2,
++			f ? (char *)f->f_dentry->d_name.name : "(null)");
++#endif
++}
++
++void vma_do_file_update_time(struct vm_area_struct *vma, const char func[],
++			     int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	file_update_time(f);
++	if (f && pr)
++		file_update_time(pr);
++}
++
++struct file *vma_do_pr_or_file(struct vm_area_struct *vma, const char func[],
++			       int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	return (f && pr) ? pr : f;
++}
++
++void vma_do_get_file(struct vm_area_struct *vma, const char func[], int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	get_file(f);
++	if (f && pr)
++		get_file(pr);
++}
++
++void vma_do_fput(struct vm_area_struct *vma, const char func[], int line)
++{
++	struct file *f = vma->vm_file, *pr = vma->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	fput(f);
++	if (f && pr)
++		fput(pr);
++}
++
++#ifndef CONFIG_MMU
++struct file *vmr_do_pr_or_file(struct vm_region *region, const char func[],
++			       int line)
++{
++	struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	return (f && pr) ? pr : f;
++}
++
++void vmr_do_fput(struct vm_region *region, const char func[], int line)
++{
++	struct file *f = region->vm_file, *pr = region->vm_prfile;
++
++	prfile_trace(f, pr, func, line, __func__);
++	fput(f);
++	if (f && pr)
++		fput(pr);
++}
++#endif /* !CONFIG_MMU */
+aufs3.14.40+ standalone patch
 
+diff --git a/fs/dcache.c b/fs/dcache.c
+index 3214ccd..92e5f72 100644
+--- a/fs/dcache.c
++++ b/fs/dcache.c
+@@ -1164,6 +1164,7 @@ rename_retry:
+ 	seq = 1;
+ 	goto again;
+ }
++EXPORT_SYMBOL(d_walk);
+ 
+ /*
+  * Search for at least 1 mount point in the dentry's subdirs.
+diff --git a/fs/file_table.c b/fs/file_table.c
+index 79ecae6..15ca9fc 100644
+--- a/fs/file_table.c
++++ b/fs/file_table.c
+@@ -148,6 +148,7 @@ over:
+ 	}
+ 	return ERR_PTR(-ENFILE);
+ }
++EXPORT_SYMBOL(get_empty_filp);
+ 
+ /**
+  * alloc_file - allocate and initialize a 'struct file'
+@@ -344,6 +345,7 @@ void put_filp(struct file *file)
+ 		file_free(file);
+ 	}
+ }
++EXPORT_SYMBOL(put_filp);
+ 
+ void __init files_init(unsigned long mempages)
+ { 
 diff --git a/fs/inode.c b/fs/inode.c
-index bc83168..6dd1207 100644
+index 49960c6..3e6463a 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
 @@ -57,6 +57,7 @@ static struct hlist_head *inode_hashtable __read_mostly;
@@ -546,7 +665,7 @@ index bc83168..6dd1207 100644
  /**
   *	touch_atime	-	update the access time
 diff --git a/fs/namespace.c b/fs/namespace.c
-index 2ffc5a2..785a51f 100644
+index 039f380..778cb1b 100644
 --- a/fs/namespace.c
 +++ b/fs/namespace.c
 @@ -455,6 +455,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
@@ -557,7 +676,7 @@ index 2ffc5a2..785a51f 100644
  
  /**
   * mnt_drop_write - give up write access to a mount
-@@ -1555,6 +1556,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
+@@ -1600,6 +1601,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
  	}
  	return 0;
  }
@@ -638,7 +757,7 @@ index 923fe4a..176b435 100644
  static int fsnotify_mark_destroy(void *ignored)
  {
 diff --git a/fs/open.c b/fs/open.c
-index b9ed8b2..3ea66972 100644
+index 2ed7325..671e923 100644
 --- a/fs/open.c
 +++ b/fs/open.c
 @@ -62,6 +62,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
@@ -649,6 +768,22 @@ index b9ed8b2..3ea66972 100644
  
  long vfs_truncate(struct path *path, loff_t length)
  {
+@@ -279,6 +280,7 @@ int do_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
+ 	sb_end_write(inode->i_sb);
+ 	return ret;
+ }
++EXPORT_SYMBOL(do_fallocate);
+ 
+ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
+ {
+@@ -662,6 +664,7 @@ int open_check_o_direct(struct file *f)
+ 	}
+ 	return 0;
+ }
++EXPORT_SYMBOL(open_check_o_direct);
+ 
+ static int do_dentry_open(struct file *f,
+ 			  int (*open)(struct inode *, struct file *),
 diff --git a/fs/splice.c b/fs/splice.c
 index f26cbaf..ac02366 100644
 --- a/fs/splice.c
@@ -669,11 +804,23 @@ index f26cbaf..ac02366 100644
  
  /**
   * splice_direct_to_actor - splices data directly between two non-pipes
+diff --git a/fs/xattr.c b/fs/xattr.c
+index 3377dff..1de03c8 100644
+--- a/fs/xattr.c
++++ b/fs/xattr.c
+@@ -207,6 +207,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
+ 	*xattr_value = value;
+ 	return error;
+ }
++EXPORT_SYMBOL(vfs_getxattr_alloc);
+ 
+ /* Compare an extended attribute value with the given value */
+ int vfs_xattr_cmp(struct dentry *dentry, const char *xattr_name,
 diff --git a/security/commoncap.c b/security/commoncap.c
-index b9d613e..ba3b618 100644
+index 963dc59..65a8e53 100644
 --- a/security/commoncap.c
 +++ b/security/commoncap.c
-@@ -988,9 +988,11 @@ int cap_mmap_addr(unsigned long addr)
+@@ -991,9 +991,11 @@ int cap_mmap_addr(unsigned long addr)
  	}
  	return ret;
  }
@@ -686,7 +833,7 @@ index b9d613e..ba3b618 100644
  }
 +EXPORT_SYMBOL(cap_mmap_file);
 diff --git a/security/device_cgroup.c b/security/device_cgroup.c
-index d3b6d2c..5076912 100644
+index 6e4e6eb..ffd4259 100644
 --- a/security/device_cgroup.c
 +++ b/security/device_cgroup.c
 @@ -7,6 +7,7 @@
@@ -697,7 +844,7 @@ index d3b6d2c..5076912 100644
  #include <linux/list.h>
  #include <linux/uaccess.h>
  #include <linux/seq_file.h>
-@@ -744,6 +745,7 @@ int __devcgroup_inode_permission(struct inode *inode, int mask)
+@@ -861,6 +862,7 @@ int __devcgroup_inode_permission(struct inode *inode, int mask)
  	return __devcgroup_check_permission(type, imajor(inode), iminor(inode),
  			access);
  }
@@ -791,7 +938,7 @@ index 919cad9..f9e9e17 100644
  {
 diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Documentation/ABI/testing/debugfs-aufs
 --- /usr/share/empty/Documentation/ABI/testing/debugfs-aufs	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/ABI/testing/debugfs-aufs	2014-04-24 22:11:10.471931783 +0200
++++ linux/Documentation/ABI/testing/debugfs-aufs	2016-02-28 11:27:09.947429195 +0100
 @@ -0,0 +1,50 @@
 +What:		/debug/aufs/si_<id>/
 +Date:		March 2009
@@ -845,7 +992,7 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/debugfs-aufs linux/Document
 +		will be empty. About XINO files, see the aufs manual.
 diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentation/ABI/testing/sysfs-aufs
 --- /usr/share/empty/Documentation/ABI/testing/sysfs-aufs	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/ABI/testing/sysfs-aufs	2014-04-24 22:11:10.471931783 +0200
++++ linux/Documentation/ABI/testing/sysfs-aufs	2016-02-28 11:27:09.947429195 +0100
 @@ -0,0 +1,31 @@
 +What:		/sys/fs/aufs/si_<id>/
 +Date:		March 2009
@@ -880,10 +1027,10 @@ diff -urN /usr/share/empty/Documentation/ABI/testing/sysfs-aufs linux/Documentat
 +		will be empty. About XINO files, see the aufs manual.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt linux/Documentation/filesystems/aufs/design/01intro.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/01intro.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,161 @@
++++ linux/Documentation/filesystems/aufs/design/01intro.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,170 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -912,7 +1059,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +- multi layered stackable unification filesystem, the member directory
 +  is called as a branch.
 +- branch permission and attribute, 'readonly', 'real-readonly',
-+  'readwrite', 'whiteout-able', 'link-able whiteout' and their
++  'readwrite', 'whiteout-able', 'link-able whiteout', etc. and their
 +  combination.
 +- internal "file copy-on-write".
 +- logical deletion, whiteout.
@@ -963,11 +1110,16 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +- unnecessary copy-up may happen, for example mmap(MAP_PRIVATE) after
 +  open(O_RDWR).
 +
-+Unionfs has a longer history. When I started implementing a stacking filesystem
-+(Aug 2005), it already existed. It has virtual super_block, inode,
-+dentry and file objects and they have an array pointing lower same kind
-+objects. After contributing many patches for Unionfs, I re-started my
-+project AUFS (Jun 2006).
++In linux-3.18, "overlay" filesystem (formerly known as "overlayfs") was
++merged into mainline. This is another implementation of UnionMount as a
++separated filesystem. All the limitations and known problems which
++UnionMount are equally inherited to "overlay" filesystem.
++
++Unionfs has a longer history. When I started implementing a stackable
++filesystem (Aug 2005), it already existed. It has virtual super_block,
++inode, dentry and file objects and they have an array pointing lower
++same kind objects. After contributing many patches for Unionfs, I
++re-started my project AUFS (Jun 2006).
 +
 +In AUFS, the structure of filesystem resembles to Unionfs, but I
 +implemented my own ideas, approaches and enhancements and it became
@@ -989,11 +1141,14 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +- DM snapshot-merge may help a lot when users try merging. in the
 +  fs-layer union, users will use rsync(1).
 +
++You may want to read my old paper "Filesystems in LiveCD"
++(http://aufs.sourceforge.net/aufs2/report/sq/sq.pdf).
 +
-+Several characters/aspects of aufs
++
++Several characters/aspects/persona of aufs
 +----------------------------------------------------------------------
 +
-+Aufs has several characters or aspects.
++Aufs has several characters, aspects or persona.
 +1. a filesystem, callee of VFS helper
 +2. sub-VFS, caller of VFS helper for branches
 +3. a virtual filesystem which maintains persistent inode number
@@ -1024,10 +1179,11 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +numbers. This is particularly important to support exporting a
 +filesystem via NFS. Aufs is a virtual filesystem which doesn't have a
 +backend block device for its own. But some storage is necessary to
-+maintain inode number. It may be a large space and may not suit to keep
-+in memory. Aufs rents some space from its first writable branch
-+filesystem (by default) and creates file(s) on it. These files are
-+created by aufs internally and removed soon (currently) keeping opened.
++keep and maintain the inode numbers. It may be a large space and may not
++suit to keep in memory. Aufs rents some space from its first writable
++branch filesystem (by default) and creates file(s) on it. These files
++are created by aufs internally and removed soon (currently) keeping
++opened.
 +Note: Because these files are removed, they are totally gone after
 +      unmounting aufs. It means the inode numbers are not persistent
 +      across unmount or reboot. I have a plan to make them really
@@ -1045,10 +1201,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
 +about it. But currently I have implemented it in kernel space.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt linux/Documentation/filesystems/aufs/design/02struct.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/02struct.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,242 @@
++++ linux/Documentation/filesystems/aufs/design/02struct.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,258 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1078,18 +1234,18 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +
 +Aufs lookup operation finds /ro/fileA and gets dentry for that. These
 +pointers are stored in a aufs dentry. The array in aufs dentry will be,
-+- [0] = NULL
++- [0] = NULL (because /rw/fileA doesn't exist)
 +- [1] = /ro/fileA
 +
 +This style of an array is essentially same to the aufs
 +superblock/inode/dentry/file objects.
 +
 +Because aufs supports manipulating branches, ie. add/delete/change
-+dynamically, these objects has its own generation. When branches are
-+changed, the generation in aufs superblock is incremented. And a
-+generation in other object are compared when it is accessed.
-+When a generation in other objects are obsoleted, aufs refreshes the
-+internal array.
++branches dynamically, these objects has its own generation. When
++branches are changed, the generation in aufs superblock is
++incremented. And a generation in other object are compared when it is
++accessed. When a generation in other objects are obsoleted, aufs
++refreshes the internal array.
 +
 +
 +Superblock
@@ -1097,8 +1253,8 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +Additionally aufs superblock has some data for policies to select one
 +among multiple writable branches, XIB files, pseudo-links and kobject.
 +See below in detail.
-+About the policies which supports copy-down a directory, see policy.txt
-+too.
++About the policies which supports copy-down a directory, see
++wbr_policy.txt too.
 +
 +
 +Branch and XINO(External Inode Number Translation Table)
@@ -1118,13 +1274,20 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +might appear. If your branch filesystem consumes disk space for such
 +holes, then you should specify 'xino=' option at mounting aufs.
 +
++Aufs has a mount option to free the disk blocks for such holes in XINO
++files on tmpfs or ramdisk. But it is not so effective actually. If you
++meet a problem of disk shortage due to XINO files, then you should try
++"tmpfs-ino.patch" (and "vfs-ino.patch" too) in aufs4-standalone.git.
++The patch localizes the assignment inumbers per tmpfs-mount and avoid
++the holes in XINO files.
++
 +Also a writable branch has three kinds of "whiteout bases". All these
-+are existed when the branch is joined to aufs and the names are
++are existed when the branch is joined to aufs, and their names are
 +whiteout-ed doubly, so that users will never see their names in aufs
 +hierarchy.
-+1. a regular file which will be linked to all whiteouts.
++1. a regular file which will be hardlinked to all whiteouts.
 +2. a directory to store a pseudo-link.
-+3. a directory to store an "orphan-ed" file temporary.
++3. a directory to store an "orphan"-ed file temporary.
 +
 +1. Whiteout Base
 +   When you remove a file on a readonly branch, aufs handles it as a
@@ -1141,8 +1304,8 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +   creating a file under this dir, the file is unlinked.
 +
 +Because aufs supports manipulating branches, ie. add/delete/change
-+dynamically, a branch has its own id. When the branch order changes, aufs
-+finds the new index by searching the branch id.
++dynamically, a branch has its own id. When the branch order changes,
++aufs finds the new index by searching the branch id.
 +
 +
 +Pseudo-link
@@ -1155,7 +1318,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +simple list. If fileB is read after unlinking fileA, aufs returns
 +filedata from the pseudo-link instead of the lower readonly
 +branch. Because the pseudo-link is based upon the inode, to keep the
-+inode number by xino (see above) is important.
++inode number by xino (see above) is essentially necessary.
 +
 +All the hardlinks under the Pseudo-link Directory of the writable branch
 +should be restored in a proper location later. Aufs provides a utility
@@ -1173,12 +1336,13 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +XIB(external inode number bitmap)
 +----------------------------------------------------------------------
 +Addition to the xino file per a branch, aufs has an external inode number
-+bitmap in a superblock object. It is also a file such like a xino file.
++bitmap in a superblock object. It is also an internal file such like a
++xino file.
 +It is a simple bitmap to mark whether the aufs inode number is in-use or
 +not.
 +To reduce the file I/O, aufs prepares a single memory page to cache xib.
 +
-+Aufs implements a feature to truncate/refresh both of xino and xib to
++As well as XINO files, aufs has a feature to truncate/refresh XIB to
 +reduce the number of consumed disk blocks for these files.
 +
 +
@@ -1190,7 +1354,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +with eliminating the whiteout-ed ones, and sets it to file (dir)
 +object. So the file object has its entry list until it is closed. The
 +entry list will be updated when the file position is zero and becomes
-+old. This decision is made in aufs automatically.
++obsoleted. This decision is made in aufs automatically.
 +
 +The dynamically allocated memory block for the name of entries has a
 +unit of 512 bytes (by default) and stores the names contiguously (no
@@ -1229,9 +1393,8 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +its completion. This approach solves a problem of a signal handler
 +simply.
 +If aufs didn't adopt the workqueue and changed the privilege of the
-+process, and if the mkdir/write call arises SIGXFSZ or other signal,
-+then the user process might gain a privilege or the generated core file
-+was owned by a superuser.
++process, then the process may receive the unexpected SIGXFSZ or other
++signals.
 +
 +Also aufs uses the system global workqueue ("events" kernel thread) too
 +for asynchronous tasks, such like handling inotify/fsnotify, re-creating a
@@ -1249,7 +1412,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +
 +Basically the whiteout represents "logical deletion" which stops aufs to
 +lookup further, but also it represents "dir is opaque" which also stop
-+lookup.
++further lookup.
 +
 +In aufs, rmdir(2) and rename(2) for dir uses whiteout alternatively.
 +In order to make several functions in a single systemcall to be
@@ -1257,7 +1420,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +unique whiteouted name.
 +For example, in rename(2) dir where the target dir already existed, aufs
 +renames the target dir to a temporary unique whiteouted name before the
-+actual rename on a branch and then handles other actions (make it opaque,
++actual rename on a branch, and then handles other actions (make it opaque,
 +update the attributes, etc). If an error happens in these actions, aufs
 +simply renames the whiteouted name back and returns an error. If all are
 +succeeded, aufs registers a function to remove the whiteouted unique
@@ -1281,6 +1444,15 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +changes something about the file.
 +"Move-down" is an opposite action of copy-up. Basically this action is
 +ran manually instead of automatically and internally.
++For desgin and implementation, aufs has to consider these issues.
++- whiteout for the file may exist on the lower branch.
++- ancestor directories may not exist on the lower branch.
++- diropq for the ancestor directories may exist on the upper branch.
++- free space on the lower branch will reduce.
++- another access to the file may happen during moving-down, including
++  UDBA (see "Revalidate Dentry and UDBA").
++- the file should not be hard-linked nor pseudo-linked. they should be
++  handled by auplink utility later.
 +
 +Sometimes users want to move-down a file from the upper writable branch
 +to the lower readonly or writable branch. For instance,
@@ -1289,12 +1461,101 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
 +- etc.
 +
 +For this purpose, use "aumvdown" command in aufs-util.git.
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt linux/Documentation/filesystems/aufs/design/03atomic_open.txt
+--- /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.txt	1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,85 @@
++
++# Copyright (C) 2015 Junjiro R. Okajima
++# 
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# 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, see <http://www.gnu.org/licenses/>.
++
++Support for a branch who has its ->atomic_open()
++----------------------------------------------------------------------
++The filesystems who implement its ->atomic_open() are not majority. For
++example NFSv4 does, and aufs should call NFSv4 ->atomic_open,
++particularly for open(O_CREAT|O_EXCL, 0400) case. Other than
++->atomic_open(), NFSv4 returns an error for this open(2). While I am not
++sure whether all filesystems who have ->atomic_open() behave like this,
++but NFSv4 surely returns the error.
++
++In order to support ->atomic_open() for aufs, there are a few
++approaches.
++
++A. Introduce aufs_atomic_open()
++   - calls one of VFS:do_last(), lookup_open() or atomic_open() for
++     branch fs.
++B. Introduce aufs_atomic_open() calling create, open and chmod. this is
++   an aufs user Pip Cet's approach
++   - calls aufs_create(), VFS finish_open() and notify_change().
++   - pass fake-mode to finish_open(), and then correct the mode by
++     notify_change().
++C. Extend aufs_open() to call branch fs's ->atomic_open()
++   - no aufs_atomic_open().
++   - aufs_lookup() registers the TID to an aufs internal object.
++   - aufs_create() does nothing when the matching TID is registered, but
++     registers the mode.
++   - aufs_open() calls branch fs's ->atomic_open() when the matching
++     TID is registered.
++D. Extend aufs_open() to re-try branch fs's ->open() with superuser's
++   credential
++   - no aufs_atomic_open().
++   - aufs_create() registers the TID to an internal object. this info
++     represents "this process created this file just now."
++   - when aufs gets EACCES from branch fs's ->open(), then confirm the
++     registered TID and re-try open() with superuser's credential.
++
++Pros and cons for each approach.
++
++A.
++   - straightforward but highly depends upon VFS internal.
++   - the atomic behavaiour is kept.
++   - some of parameters such as nameidata are hard to reproduce for
++     branch fs.
++   - large overhead.
++B.
++   - easy to implement.
++   - the atomic behavaiour is lost.
++C.
++   - the atomic behavaiour is kept.
++   - dirty and tricky.
++   - VFS checks whether the file is created correctly after calling
++     ->create(), which means this approach doesn't work.
++D.
++   - easy to implement.
++   - the atomic behavaiour is lost.
++   - to open a file with superuser's credential and give it to a user
++     process is a bad idea, since the file object keeps the credential
++     in it. It may affect LSM or something. This approach doesn't work
++     either.
++
++The approach A is ideal, but it hard to implement. So here is a
++variation of A, which is to be implemented.
++
++A-1. Introduce aufs_atomic_open()
++     - calls branch fs ->atomic_open() if exists. otherwise calls
++       vfs_create() and finish_open().
++     - the demerit is that the several checks after branch fs
++       ->atomic_open() are lost. in the ordinary case, the checks are
++       done by VFS:do_last(), lookup_open() and atomic_open(). some can
++       be implemented in aufs, but not all I am afraid.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt linux/Documentation/filesystems/aufs/design/03lookup.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/03lookup.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,105 @@
++++ linux/Documentation/filesystems/aufs/design/03lookup.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,113 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1312,38 +1573,46 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt li
 +Lookup in a Branch
 +----------------------------------------------------------------------
 +Since aufs has a character of sub-VFS (see Introduction), it operates
-+lookup for branches as VFS does. It may be a heavy work. Generally
-+speaking struct nameidata is a bigger structure and includes many
-+information. But almost all lookup operation in aufs is the simplest
-+case, ie. lookup only an entry directly connected to its parent. Digging
-+down the directory hierarchy is unnecessary.
-+
-+VFS has a function lookup_one_len() for that use, but it is not usable
-+for a branch filesystem which requires struct nameidata. So aufs
-+implements a simple lookup wrapper function. When a branch filesystem
-+allows NULL as nameidata, it calls lookup_one_len(). Otherwise it builds
-+a simplest nameidata and calls lookup_hash().
-+Here aufs applies "a principle in NFSD", ie. if the filesystem supports
-+NFS-export, then it has to support NULL as a nameidata parameter for
-+->create(), ->lookup() and ->d_revalidate(). So the lookup wrapper in
-+aufs tests if ->s_export_op in the branch is NULL or not.
-+
-+When a branch is a remote filesystem, aufs basically trusts its
++lookup for branches as VFS does. It may be a heavy work. But almost all
++lookup operation in aufs is the simplest case, ie. lookup only an entry
++directly connected to its parent. Digging down the directory hierarchy
++is unnecessary. VFS has a function lookup_one_len() for that use, and
++aufs calls it.
++
++When a branch is a remote filesystem, aufs basically relies upon its
 +->d_revalidate(), also aufs forces the hardest revalidate tests for
 +them.
 +For d_revalidate, aufs implements three levels of revalidate tests. See
 +"Revalidate Dentry and UDBA" in detail.
 +
 +
-+Loopback Mount
++Test Only the Highest One for the Directory Permission (dirperm1 option)
 +----------------------------------------------------------------------
-+Basically aufs supports any type of filesystem and block device for a
-+branch (actually there are some exceptions). But it is prohibited to add
-+a loopback mounted one whose backend file exists in a filesystem which is
-+already added to aufs. The reason is to protect aufs from a recursive
-+lookup. If it was allowed, the aufs lookup operation might re-enter a
-+lookup for the loopback mounted branch in the same context, and will
-+cause a deadlock.
++Let's try case study.
++- aufs has two branches, upper readwrite and lower readonly.
++  /au = /rw + /ro
++- "dirA" exists under /ro, but /rw. and its mode is 0700.
++- user invoked "chmod a+rx /au/dirA"
++- the internal copy-up is activated and "/rw/dirA" is created and its
++  permission bits are set to world readable.
++- then "/au/dirA" becomes world readable?
++
++In this case, /ro/dirA is still 0700 since it exists in readonly branch,
++or it may be a natively readonly filesystem. If aufs respects the lower
++branch, it should not respond readdir request from other users. But user
++allowed it by chmod. Should really aufs rejects showing the entries
++under /ro/dirA?
++
++To be honest, I don't have a good solution for this case. So aufs
++implements 'dirperm1' and 'nodirperm1' mount options, and leave it to
++users.
++When dirperm1 is specified, aufs checks only the highest one for the
++directory permission, and shows the entries. Otherwise, as usual, checks
++every dir existing on all branches and rejects the request.
++
++As a side effect, dirperm1 option improves the performance of aufs
++because the number of permission check is reduced when the number of
++branch is many.
 +
 +
 +Revalidate Dentry and UDBA (User's Direct Branch Access)
@@ -1395,15 +1664,15 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt li
 +   itself.
 +3. No Extra Validation
 +   This is the simplest test and doesn't add any additional revalidation
-+   test, and skip therevalidatin in step 4. It is useful and improves
++   test, and skip the revalidation in step 4. It is useful and improves
 +   aufs performance when system surely hide the aufs branches from user,
 +   by over-mounting something (or another method).
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt linux/Documentation/filesystems/aufs/design/04branch.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/04branch.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,75 @@
++++ linux/Documentation/filesystems/aufs/design/04branch.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,74 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1427,8 +1696,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt li
 +Add a Branch
 +----------------------------------------------------------------------
 +o Confirm the adding dir exists outside of aufs, including loopback
-+  mount.
-+- and other various attributes...
++  mount, and its various attributes.
 +o Initialize the xino file and whiteout bases if necessary.
 +  See struct.txt.
 +
@@ -1443,7 +1711,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt li
 +  writable branch, and the writable branch is world-writable, then a
 +  malicious guy may create /etc/passwd on the writable branch directly
 +  and the infected file will be valid in aufs.
-+  I am afraid it can be a security issue, but nothing to do except
++  I am afraid it can be a security issue, but aufs can do nothing except
 +  producing a warning.
 +
 +
@@ -1479,10 +1747,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/04branch.txt li
 +    same named entry on the upper branch.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt linux/Documentation/filesystems/aufs/design/05wbr_policy.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt	2014-04-24 22:11:10.471931783 +0200
++++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt	2016-02-28 11:27:09.947429195 +0100
 @@ -0,0 +1,64 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1504,9 +1772,9 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.tx
 +writable branch which has the parent (or ancestor) dir of the target
 +file is chosen (top-down-parent policy).
 +By user's request, aufs implements some other policies to select the
-+writable branch, for file creation two policies, round-robin and
-+most-free-space policies. For copy-up three policies, top-down-parent,
-+bottom-up-parent and bottom-up policies.
++writable branch, for file creation several policies, round-robin,
++most-free-space, and other policies. For copy-up, top-down-parent,
++bottom-up-parent, bottom-up and others.
 +
 +As expected, the round-robin policy selects the branch in circular. When
 +you have two writable branches and creates 10 new files, 5 files will be
@@ -1545,12 +1813,136 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/05wbr_policy.tx
 +  where the source and the target exists and selects the higher
 +  one. If the selected branch is readonly, then aufs follows the
 +  copyup policy.
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linux/Documentation/filesystems/aufs/design/06fhsm.txt
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt	1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06fhsm.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,120 @@
++
++# Copyright (C) 2011-2015 Junjiro R. Okajima
++# 
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# 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-based Hierarchical Storage Management (FHSM)
++----------------------------------------------------------------------
++Hierarchical Storage Management (or HSM) is a well-known feature in the
++storage world. Aufs provides this feature as file-based with multiple
++writable branches, based upon the principle of "Colder, the Lower".
++Here the word "colder" means that the less used files, and "lower" means
++that the position in the order of the stacked branches vertically.
++These multiple writable branches are prioritized, ie. the topmost one
++should be the fastest drive and be used heavily.
++
++o Characters in aufs FHSM story
++- aufs itself and a new branch attribute.
++- a new ioctl interface to move-down and to establish a connection with
++  the daemon ("move-down" is a converse of "copy-up").
++- userspace tool and daemon.
++
++The userspace daemon establishes a connection with aufs and waits for
++the notification. The notified information is very similar to struct
++statfs containing the number of consumed blocks and inodes.
++When the consumed blocks/inodes of a branch exceeds the user-specified
++upper watermark, the daemon activates its move-down process until the
++consumed blocks/inodes reaches the user-specified lower watermark.
++
++The actual move-down is done by aufs based upon the request from
++user-space since we need to maintain the inode number and the internal
++pointer arrays in aufs.
++
++Currently aufs FHSM handles the regular files only. Additionally they
++must not be hard-linked nor pseudo-linked.
++
++
++o Cowork of aufs and the user-space daemon
++  During the userspace daemon established the connection, aufs sends a
++  small notification to it whenever aufs writes something into the
++  writable branch. But it may cost high since aufs issues statfs(2)
++  internally. So user can specify a new option to cache the
++  info. Actually the notification is controlled by these factors.
++  + the specified cache time.
++  + classified as "force" by aufs internally.
++  Until the specified time expires, aufs doesn't send the info
++  except the forced cases. When aufs decide forcing, the info is always
++  notified to userspace.
++  For example, the number of free inodes is generally large enough and
++  the shortage of it happens rarely. So aufs doesn't force the
++  notification when creating a new file, directory and others. This is
++  the typical case which aufs doesn't force.
++  When aufs writes the actual filedata and the files consumes any of new
++  blocks, the aufs forces notifying.
++
++
++o Interfaces in aufs
++- New branch attribute.
++  + fhsm
++    Specifies that the branch is managed by FHSM feature. In other word,
++    participant in the FHSM.
++    When nofhsm is set to the branch, it will not be the source/target
++    branch of the move-down operation. This attribute is set
++    independently from coo and moo attributes, and if you want full
++    FHSM, you should specify them as well.
++- New mount option.
++  + fhsm_sec
++    Specifies a second to suppress many less important info to be
++    notified.
++- New ioctl.
++  + AUFS_CTL_FHSM_FD
++    create a new file descriptor which userspace can read the notification
++    (a subset of struct statfs) from aufs.
++- Module parameter 'brs'
++  It has to be set to 1. Otherwise the new mount option 'fhsm' will not
++  be set.
++- mount helpers /sbin/mount.aufs and /sbin/umount.aufs
++  When there are two or more branches with fhsm attributes,
++  /sbin/mount.aufs invokes the user-space daemon and /sbin/umount.aufs
++  terminates it. As a result of remounting and branch-manipulation, the
++  number of branches with fhsm attribute can be one. In this case,
++  /sbin/mount.aufs will terminate the user-space daemon.
++
++
++Finally the operation is done as these steps in kernel-space.
++- make sure that,
++  + no one else is using the file.
++  + the file is not hard-linked.
++  + the file is not pseudo-linked.
++  + the file is a regular file.
++  + the parent dir is not opaqued.
++- find the target writable branch.
++- make sure the file is not whiteout-ed by the upper (than the target)
++  branch.
++- make the parent dir on the target branch.
++- mutex lock the inode on the branch.
++- unlink the whiteout on the target branch (if exists).
++- lookup and create the whiteout-ed temporary name on the target branch.
++- copy the file as the whiteout-ed temporary name on the target branch.
++- rename the whiteout-ed temporary name to the original name.
++- unlink the file on the source branch.
++- maintain the internal pointer array and the external inode number
++  table (XINO).
++- maintain the timestamps and other attributes of the parent dir and the
++  file.
++
++And of course, in every step, an error may happen. So the operation
++should restore the original file state after an error happens.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linux/Documentation/filesystems/aufs/design/06mmap.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/06mmap.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,46 @@
++++ linux/Documentation/filesystems/aufs/design/06mmap.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,72 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1571,7 +1963,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linu
 +interaction with aufs. It means aufs_mmap() calls the branch fs's
 +->mmap().
 +This approach is simple and good, but there is one problem.
-+Under /proc, several entries show the mmap-ped files by its path (with
++Under /proc, several entries show the mmapped files by its path (with
 +device and inode number), and the printed path will be the path on the
 +branch fs's instead of virtual aufs's.
 +This is not a problem in most cases, but some utilities lsof(1) (and its
@@ -1588,19 +1980,145 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linu
 +  incremented.
 +- merging vma maintains the ref count too.
 +
-+This is not a good approach. It just faking the printed path. But it
++This is not a good approach. It just fakes the printed path. But it
 +leaves all behaviour around f_mapping unchanged. This is surely an
 +advantage.
 +Actually aufs had adopted another complicated approach which calls
 +generic_file_mmap() and handles struct vm_operations_struct. In this
 +approach, aufs met a hard problem and I could not solve it without
 +switching the approach.
++
++There may be one more another approach which is
++- bind-mount the branch-root onto the aufs-root internally
++- grab the new vfsmount (ie. struct mount)
++- lazy-umount the branch-root internally
++- in open(2) the aufs-file, open the branch-file with the hidden
++  vfsmount (instead of the original branch's vfsmount)
++- ideally this "bind-mount and lazy-umount" should be done atomically,
++  but it may be possible from userspace by the mount helper.
++
++Adding the internal hidden vfsmount and using it in opening a file, the
++file path under /proc will be printed correctly. This approach looks
++smarter, but is not possible I am afraid.
++- aufs-root may be bind-mount later. when it happens, another hidden
++  vfsmount will be required.
++- it is hard to get the chance to bind-mount and lazy-umount
++  + in kernel-space, FS can have vfsmount in open(2) via
++    file->f_path, and aufs can know its vfsmount. But several locks are
++    already acquired, and if aufs tries to bind-mount and lazy-umount
++    here, then it may cause a deadlock.
++  + in user-space, bind-mount doesn't invoke the mount helper.
++- since /proc shows dev and ino, aufs has to give vma these info. it
++  means a new member vm_prinode will be necessary. this is essentially
++  equivalent to vm_prfile described above.
++
++I have to give up this "looks-smater" approach.
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt linux/Documentation/filesystems/aufs/design/06xattr.txt
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt	1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06xattr.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,96 @@
++
++# Copyright (C) 2014-2015 Junjiro R. Okajima
++#
++# This program is free software; you can redistribute it and/or modify
++# it under the terms of the GNU General Public License as published by
++# 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
++
++
++Listing XATTR/EA and getting the value
++----------------------------------------------------------------------
++For the inode standard attributes (owner, group, timestamps, etc.), aufs
++shows the values from the topmost existing file. This behaviour is good
++for the non-dir entries since the bahaviour exactly matches the shown
++information. But for the directories, aufs considers all the same named
++entries on the lower branches. Which means, if one of the lower entry
++rejects readdir call, then aufs returns an error even if the topmost
++entry allows it. This behaviour is necessary to respect the branch fs's
++security, but can make users confused since the user-visible standard
++attributes don't match the behaviour.
++To address this issue, aufs has a mount option called dirperm1 which
++checks the permission for the topmost entry only, and ignores the lower
++entry's permission.
++
++A similar issue can happen around XATTR.
++getxattr(2) and listxattr(2) families behave as if dirperm1 option is
++always set. Otherwise these very unpleasant situation would happen.
++- listxattr(2) may return the duplicated entries.
++- users may not be able to remove or reset the XATTR forever,
++
++
++XATTR/EA support in the internal (copy,move)-(up,down)
++----------------------------------------------------------------------
++Generally the extended attributes of inode are categorized as these.
++- "security" for LSM and capability.
++- "system" for posix ACL, 'acl' mount option is required for the branch
++  fs generally.
++- "trusted" for userspace, CAP_SYS_ADMIN is required.
++- "user" for userspace, 'user_xattr' mount option is required for the
++  branch fs generally.
++
++Moreover there are some other categories. Aufs handles these rather
++unpopular categories as the ordinary ones, ie. there is no special
++condition nor exception.
++
++In copy-up, the support for XATTR on the dst branch may differ from the
++src branch. In this case, the copy-up operation will get an error and
++the original user operation which triggered the copy-up will fail. It
++can happen that even all copy-up will fail.
++When both of src and dst branches support XATTR and if an error occurs
++during copying XATTR, then the copy-up should fail obviously. That is a
++good reason and aufs should return an error to userspace. But when only
++the src branch support that XATTR, aufs should not return an error.
++For example, the src branch supports ACL but the dst branch doesn't
++because the dst branch may natively un-support it or temporary
++un-support it due to "noacl" mount option. Of course, the dst branch fs
++may NOT return an error even if the XATTR is not supported. It is
++totally up to the branch fs.
++
++Anyway when the aufs internal copy-up gets an error from the dst branch
++fs, then aufs tries removing the just copied entry and returns the error
++to the userspace. The worst case of this situation will be all copy-up
++will fail.
++
++For the copy-up operation, there two basic approaches.
++- copy the specified XATTR only (by category above), and return the
++  error unconditionally if it happens.
++- copy all XATTR, and ignore the error on the specified category only.
++
++In order to support XATTR and to implement the correct behaviour, aufs
++chooses the latter approach and introduces some new branch attributes,
++"icexsec", "icexsys", "icextr", "icexusr", and "icexoth".
++They correspond to the XATTR namespaces (see above). Additionally, to be
++convenient, "icex" is also provided which means all "icex*" attributes
++are set (here the word "icex" stands for "ignore copy-error on XATTR").
++
++The meaning of these attributes is to ignore the error from setting
++XATTR on that branch.
++Note that aufs tries copying all XATTR unconditionally, and ignores the
++error from the dst branch according to the specified attributes.
++
++Some XATTR may have its default value. The default value may come from
++the parent dir or the environment. If the default value is set at the
++file creating-time, it will be overwritten by copy-up.
++Some contradiction may happen I am afraid.
++Do we need another attribute to stop copying XATTR? I am unsure. For
++now, aufs implements the branch attributes to ignore the error.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt linux/Documentation/filesystems/aufs/design/07export.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/07export.txt	2014-04-24 22:11:10.471931783 +0200
++++ linux/Documentation/filesystems/aufs/design/07export.txt	2016-02-28 11:27:09.947429195 +0100
 @@ -0,0 +1,58 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1639,10 +2157,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt li
 +  + find the index of a branch from its id in handle, and check it is
 +    still exist in aufs.
 +  + 1st level: get the inode number from handle and search it in cache.
-+  + 2nd level: if not found, get the parent inode number from handle and
-+    search it in cache. and then open the parent dir, find the matching
-+    inode number by vfs_readdir() and get its name, and call
-+    lookup_one_len() for the target dentry.
++  + 2nd level: if not found in cache, get the parent inode number from
++    the handle and search it in cache. and then open the found parent
++    dir, find the matching inode number by vfs_readdir() and get its
++    name, and call lookup_one_len() for the target dentry.
 +  + 3rd level: if the parent dir is not cached, call
 +    exportfs_decode_fh() for a branch and get the parent on a branch,
 +    build a pathname of it, convert it a pathname in aufs, call
@@ -1659,10 +2177,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/07export.txt li
 +  lookup_one_len(), vfs_getattr(), encode_fh() and others.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linux/Documentation/filesystems/aufs/design/08shwh.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/08shwh.txt	2014-04-24 22:11:10.471931783 +0200
++++ linux/Documentation/filesystems/aufs/design/08shwh.txt	2016-02-28 11:27:09.947429195 +0100
 @@ -0,0 +1,52 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1715,10 +2233,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/08shwh.txt linu
 +initramfs will use it to replace the old one at the next boot.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt linux/Documentation/filesystems/aufs/design/10dynop.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/10dynop.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,46 @@
++++ linux/Documentation/filesystems/aufs/design/10dynop.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,47 @@
 +
-+# Copyright (C) 2010-2014 Junjiro R. Okajima
++# Copyright (C) 2010-2015 Junjiro R. Okajima
 +#
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1741,34 +2259,35 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt lin
 +operation. Some FS have multiple sets of them. For instance, ext2 has
 +three sets, one for XIP, for NOBH, and for normal.
 +Since aufs overrides and redirects these operations, sometimes aufs has
-+to change its behaviour according to the branch FS type. More imporantly
++to change its behaviour according to the branch FS type. More importantly
 +VFS acts differently if a function (member in the struct) is set or
 +not. It means aufs should have several sets of operations and select one
 +among them according to the branch FS definition.
 +
-+In order to solve this problem and not to affect the behavour of VFS,
++In order to solve this problem and not to affect the behaviour of VFS,
 +aufs defines these operations dynamically. For instance, aufs defines
-+aio_read function for struct file_operations, but it may not be set to
-+the file_operations. When the branch FS doesn't have it, aufs doesn't
-+set it to its file_operations while the function definition itself is
-+still alive. So the behaviour of io_submit(2) will not change, and it
-+will return an error when aio_read is not defined.
++dummy direct_IO function for struct address_space_operations, but it may
++not be set to the address_space_operations actually. When the branch FS
++doesn't have it, aufs doesn't set it to its address_space_operations
++while the function definition itself is still alive. So the behaviour
++itself will not change, and it will return an error when direct_IO is
++not set.
 +
 +The lifetime of these dynamically generated operation object is
 +maintained by aufs branch object. When the branch is removed from aufs,
 +the reference counter of the object is decremented. When it reaches
 +zero, the dynamically generated operation object will be freed.
 +
-+This approach is designed to support AIO (io_submit), Direcit I/O and
-+XIP mainly.
-+Currently this approach is applied to file_operations and
-+vm_operations_struct for regular files only.
++This approach is designed to support AIO (io_submit), Direct I/O and
++XIP (DAX) mainly.
++Currently this approach is applied to address_space_operations for
++regular files only.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/99plan.txt linux/Documentation/filesystems/aufs/design/99plan.txt
 --- /usr/share/empty/Documentation/filesystems/aufs/design/99plan.txt	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/design/99plan.txt	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,95 @@
++++ linux/Documentation/filesystems/aufs/design/99plan.txt	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,57 @@
 +
-+# Copyright (C) 2005-2014 Junjiro R. Okajima
++# Copyright (C) 2005-2015 Junjiro R. Okajima
 +# 
 +# This program is free software; you can redistribute it and/or modify
 +# it under the terms of the GNU General Public License as published by
@@ -1790,48 +2309,11 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/99plan.txt linu
 +easier to be reviewed.
 +
 +
-+Test Only the Highest One for the Directory Permission (dirperm1 option)
++Being Another Aufs's Readonly Branch (robr)
 +----------------------------------------------------------------------
-+Let's try case study.
-+- aufs has two branches, upper readwrite and lower readonly.
-+  /au = /rw + /ro
-+- "dirA" exists under /ro, but /rw. and its mode is 0700.
-+- user invoked "chmod a+rx /au/dirA"
-+- then "dirA" becomes world readable?
-+
-+In this case, /ro/dirA is still 0700 since it exists in readonly branch,
-+or it may be a natively readonly filesystem. If aufs respects the lower
-+branch, it should not respond readdir request from other users. But user
-+allowed it by chmod. Should really aufs rejects showing the entries
-+under /ro/dirA?
-+
-+To be honest, I don't have a best solution for this case. So I
-+implemented 'dirperm1' and 'nodirperm1' option in aufs1, and leave it to
-+users.
-+When dirperm1 is specified, aufs checks only the highest one for the
-+directory permission, and shows the entries. Otherwise, as usual, checks
-+every dir existing on all branches and rejects the request.
-+
-+As a side effect, dirperm1 option improves the performance of aufs
-+because the number of permission check is reduced.
-+
-+
-+Being Another Aufs's Readonly Branch (robr)
-+----------------------------------------------------------------------
-+Aufs1 allows aufs to be another aufs's readonly branch.
-+This feature was developed by a user's request. But it may not be used
-+currecnly.
-+
-+
-+Copy-up on Open (coo=)
-+----------------------------------------------------------------------
-+By default the internal copy-up is executed when it is really necessary.
-+It is not done when a file is opened for writing, but when write(2) is
-+done. Users who have many (over 100) branches want to know and analyse
-+when and what file is copied-up. To insert a new upper branch which
-+contains such files only may improve the performance of aufs.
-+
-+Aufs1 implemented "coo=none | leaf | all" option.
++Aufs1 allows aufs to be another aufs's readonly branch.
++This feature was developed by a user's request. But it may not be used
++currently.
 +
 +
 +Refresh the Opened File (refrof)
@@ -1849,7 +2331,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/99plan.txt linu
 +- user opened "/au/fileA".
 +- he or someone else inserts a branch (/new) between /rw and /ro.
 +  /au = /rw + /new + /ro
-+- the new branch has "fileA".
++- the new branch contains "fileA".
 +- user reads from the opened "fileA"
 +- which filedata should aufs return, from /ro or /new?
 +
@@ -1860,12 +2342,11 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/99plan.txt linu
 +Here again I don't have a best and final answer. I got an idea to
 +implement 'refrof' and 'norefrof' option. When 'refrof' (REFResh the
 +Opened File) is specified (by default), aufs returns the filedata from
-+/new.
-+Otherwise from /new.
++/new. Otherwise from /ro.
 diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documentation/filesystems/aufs/README
 --- /usr/share/empty/Documentation/filesystems/aufs/README	1970-01-01 01:00:00.000000000 +0100
-+++ linux/Documentation/filesystems/aufs/README	2014-04-24 22:11:10.471931783 +0200
-@@ -0,0 +1,344 @@
++++ linux/Documentation/filesystems/aufs/README	2016-02-28 11:27:09.947429195 +0100
+@@ -0,0 +1,384 @@
 +
 +Aufs3 -- advanced multi layered unification filesystem version 3.x
 +http://aufs.sf.net
@@ -1875,7 +2356,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +0. Introduction
 +----------------------------------------
 +In the early days, aufs was entirely re-designed and re-implemented
-+Unionfs Version 1.x series. After many original ideas, approaches,
++Unionfs Version 1.x series. Adding 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
@@ -1888,10 +2369,16 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +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.
++      According to Christoph Hellwig, linux rejects all union-type
++      filesystems but UnionMount.
 +<http://marc.info/?l=linux-kernel&m=123938533724484&w=2>
 +
++PS. Al Viro seems have a plan to merge aufs as well as overlayfs and
++    UnionMount, and he pointed out an issue around a directory mutex
++    lock and aufs addressed it. But it is still unsure whether aufs will
++    be merged (or any other union solution).
++<http://marc.info/?l=linux-kernel&m=136312705029295&w=1>
++
 +
 +1. Features
 +----------------------------------------
@@ -1904,7 +2391,12 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +- dynamic branch manipulation, add, del.
 +- etc...
 +
-+Also there are many enhancements in aufs1, such as:
++Also there are many enhancements in aufs, such as:
++- test only the highest one for the directory permission (dirperm1)
++- copyup on open (coo=)
++- 'move' policy for copy-up between two writable branches, after
++  checking free space.
++- xattr, acl
 +- readdir(3) in userspace.
 +- keep inode number by external inode number table
 +- keep the timestamps of file/dir in internal copyup operation
@@ -1936,28 +2428,18 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +
 +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
@@ -1972,9 +2454,13 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +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.
++Several extra patches are not included in this tree. Only
++aufs3-standalong tree contains them. They are describe in the later
++section "Configuration and Compilation."
 +
 +On the other hand, the aufs3-standalone tree has only aufs source files
 +and necessary patches, and you can select CONFIG_AUFS_FS=m.
++But you need to apply all aufs patches manually.
 +
 +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,
@@ -1983,12 +2469,26 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +
 +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
++	git://git.code.sf.net/p/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
 +
++Or You may want to directly git-pull aufs into your linux GIT tree, and
++leave the patch-work to GIT.
++$ cd /your/linux/git/tree
++$ git remote add aufs3 https://github.com/sfjro/aufs3-linux.git
++- aufs3-linux.git tree also exists on github.
++$ git fetch aufs3
++$ git checkout -b my3.14 v3.14
++$ (add your change...)
++$ git pull aufs3 aufs3.14
++- now you have v3.14 + your_changes + aufs3.14 in you my3.14 branch.
++- you may need to solve some conflicts between your_changes and
++  aufs3.14. in this case, git-rerere is recommended so that you can
++  solve the similar confilicts automatically when you upgrade to 3.15 or
++  later in the future.
++
 +o aufs3-standalone tree
 +$ git clone git://git.code.sf.net/p/aufs/aufs3-standalone \
 +	aufs3-standalone.git
@@ -2096,6 +2596,23 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +  then run "make install_ulib" too. And refer to the aufs manual in
 +  detail.
 +
++There several other patches in aufs3-standalone.git. They are all
++optional. When you meet some problems, they will help you.
++- aufs3-loopback.patch
++  Supports a nested loopback mount in a branch-fs. This patch is
++  unnecessary until aufs produces a message like "you may want to try
++  another patch for loopback file".
++- vfs-ino.patch
++  Modifies a system global kernel internal function get_next_ino() in
++  order to stop assigning 0 for an inode-number. Not directly related to
++  aufs, but recommended generally.
++- tmpfs-idr.patch
++  Keeps the tmpfs inode number as the lowest value. Effective to reduce
++  the size of aufs XINO files for tmpfs branch. Also it prevents the
++  duplication of inode number, which is important for backup tools and
++  other utilities. When you find aufs XINO files for tmpfs branch
++  growing too much, try this patch.
++
 +
 +4. Usage
 +----------------------------------------
@@ -2193,6 +2710,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +"pemasu from Finland" made a donation (2013/7).
 +The Parted Magic Project made a donation (2013/9 and 11).
 +Pavel Barta made a donation (2013/10).
++Nikolay Pertsev made a donation (2014/5).
++James B made a donation (2014/7 and 2015/7).
++Stefano Di Biase made a donation (2014/8).
++Daniel Epellei made a donation (2015/1).
 +
 +Thank you very much.
 +Donations are always, including future donations, very important and
@@ -2212,10 +2733,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/README linux/Documenta
 +# End: ;
 diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
 --- /usr/share/empty/fs/aufs/aufs.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/aufs.h	2014-04-24 22:11:10.848602379 +0200
++++ linux/fs/aufs/aufs.h	2016-02-28 11:27:09.950762598 +0100
 @@ -0,0 +1,59 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -2275,10 +2796,10 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
 +#endif /* __AUFS_H__ */
 diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 --- /usr/share/empty/fs/aufs/branch.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/branch.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,1219 @@
++++ linux/fs/aufs/branch.c	2016-02-28 13:27:46.147416184 +0100
+@@ -0,0 +1,1442 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -2355,6 +2876,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		AuRwDestroy(&wbr->wbr_wh_rwsem);
 +	}
 +
++	if (br->br_fhsm) {
++		au_br_fhsm_fin(br->br_fhsm);
++		kfree(br->br_fhsm);
++	}
++
 +	key = br->br_dykey;
 +	for (i = 0; i < AuBrDynOp; i++, key++)
 +		if (*key)
@@ -2450,6 +2976,13 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +			goto out_hnotify;
 +	}
 +
++	add_branch->br_fhsm = NULL;
++	if (au_br_fhsm(perm)) {
++		err = au_fhsm_br_alloc(add_branch);
++		if (unlikely(err))
++			goto out_wbr;
++	}
++
 +	err = au_sbr_realloc(au_sbi(sb), new_nbranch);
 +	if (!err)
 +		err = au_di_realloc(au_di(root), new_nbranch);
@@ -2458,8 +2991,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	if (!err)
 +		return add_branch; /* success */
 +
++out_wbr:
 +	kfree(add_branch->br_wbr);
-+
 +out_hnotify:
 +	au_hnotify_fin_br(add_branch);
 +out_br:
@@ -2660,7 +3193,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	return err;
 +}
 +
-+/* intialize a new branch */
++/* initialize a new branch */
 +static int au_br_init(struct au_branch *br, struct super_block *sb,
 +		      struct au_opt_add *add)
 +{
@@ -2842,6 +3375,54 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +
 +/* ---------------------------------------------------------------------- */
 +
++static unsigned long long au_farray_cb(void *a,
++				       unsigned long long max __maybe_unused,
++				       void *arg)
++{
++	unsigned long long n;
++	struct file **p, *f;
++	struct au_sphlhead *files;
++	struct au_finfo *finfo;
++	struct super_block *sb = arg;
++
++	n = 0;
++	p = a;
++	files = &au_sbi(sb)->si_files;
++	spin_lock(&files->spin);
++	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
++		f = finfo->fi_file;
++		if (file_count(f)
++		    && !special_file(file_inode(f)->i_mode)) {
++			get_file(f);
++			*p++ = f;
++			n++;
++			AuDebugOn(n > max);
++		}
++	}
++	spin_unlock(&files->spin);
++
++	return n;
++}
++
++static struct file **au_farray_alloc(struct super_block *sb,
++				     unsigned long long *max)
++{
++	*max = atomic_long_read(&au_sbi(sb)->si_nfiles);
++	return au_array_alloc(max, au_farray_cb, sb);
++}
++
++static void au_farray_free(struct file **a, unsigned long long max)
++{
++	unsigned long long ull;
++
++	for (ull = 0; ull < max; ull++)
++		if (a[ull])
++			fput(a[ull]);
++	au_array_free(a);
++}
++
++/* ---------------------------------------------------------------------- */
++
 +/*
 + * delete a branch
 + */
@@ -2888,7 +3469,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		ndentry = dpage->ndentry;
 +		for (j = 0; !err && j < ndentry; j++) {
 +			d = dpage->dentries[j];
-+			AuDebugOn(!d_count(d));
++			AuDebugOn(au_dcount(d) <= 0);
 +			if (!au_digen_test(d, sigen)) {
 +				di_read_lock_child(d, AuLock_IR);
 +				if (unlikely(au_dbrange_test(d))) {
@@ -2948,6 +3529,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	AuDbg("b%d\n", bindex);
 +	for (ull = 0; !err && ull < max; ull++) {
 +		i = array[ull];
++		if (unlikely(!i))
++			break;
 +		if (i->i_ino == AUFS_ROOT_INO)
 +			continue;
 +
@@ -3002,6 +3585,135 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	return err;
 +}
 +
++static int test_dir_busy(struct file *file, aufs_bindex_t br_id,
++			 struct file **to_free, int *idx)
++{
++	int err;
++	unsigned char matched, root;
++	aufs_bindex_t bindex, bend;
++	struct au_fidir *fidir;
++	struct au_hfile *hfile;
++
++	err = 0;
++	root = IS_ROOT(file->f_dentry);
++	if (root) {
++		get_file(file);
++		to_free[*idx] = file;
++		(*idx)++;
++		goto out;
++	}
++
++	matched = 0;
++	fidir = au_fi(file)->fi_hdir;
++	AuDebugOn(!fidir);
++	bend = au_fbend_dir(file);
++	for (bindex = au_fbstart(file); bindex <= bend; bindex++) {
++		hfile = fidir->fd_hfile + bindex;
++		if (!hfile->hf_file)
++			continue;
++
++		if (hfile->hf_br->br_id == br_id) {
++			matched = 1;
++			break;
++		}
++	}
++	if (matched)
++		err = -EBUSY;
++
++out:
++	return err;
++}
++
++static int test_file_busy(struct super_block *sb, aufs_bindex_t br_id,
++			  struct file **to_free, int opened)
++{
++	int err, idx;
++	unsigned long long ull, max;
++	aufs_bindex_t bstart;
++	struct file *file, **array;
++	struct dentry *root;
++	struct au_hfile *hfile;
++
++	array = au_farray_alloc(sb, &max);
++	err = PTR_ERR(array);
++	if (IS_ERR(array))
++		goto out;
++
++	err = 0;
++	idx = 0;
++	root = sb->s_root;
++	di_write_unlock(root);
++	for (ull = 0; ull < max; ull++) {
++		file = array[ull];
++		if (unlikely(!file))
++			break;
++
++		/* AuDbg("%pD\n", file); */
++		fi_read_lock(file);
++		bstart = au_fbstart(file);
++		if (!d_is_dir(file->f_path.dentry)) {
++			hfile = &au_fi(file)->fi_htop;
++			if (hfile->hf_br->br_id == br_id)
++				err = -EBUSY;
++		} else
++			err = test_dir_busy(file, br_id, to_free, &idx);
++		fi_read_unlock(file);
++		if (unlikely(err))
++			break;
++	}
++	di_write_lock_child(root);
++	au_farray_free(array, max);
++	AuDebugOn(idx > opened);
++
++out:
++	return err;
++}
++
++static void br_del_file(struct file **to_free, unsigned long long opened,
++			  aufs_bindex_t br_id)
++{
++	unsigned long long ull;
++	aufs_bindex_t bindex, bstart, bend, bfound;
++	struct file *file;
++	struct au_fidir *fidir;
++	struct au_hfile *hfile;
++
++	for (ull = 0; ull < opened; ull++) {
++		file = to_free[ull];
++		if (unlikely(!file))
++			break;
++
++		/* AuDbg("%pD\n", file); */
++		AuDebugOn(!d_is_dir(file->f_path.dentry));
++		bfound = -1;
++		fidir = au_fi(file)->fi_hdir;
++		AuDebugOn(!fidir);
++		fi_write_lock(file);
++		bstart = au_fbstart(file);
++		bend = au_fbend_dir(file);
++		for (bindex = bstart; bindex <= bend; bindex++) {
++			hfile = fidir->fd_hfile + bindex;
++			if (!hfile->hf_file)
++				continue;
++
++			if (hfile->hf_br->br_id == br_id) {
++				bfound = bindex;
++				break;
++			}
++		}
++		AuDebugOn(bfound < 0);
++		au_set_h_fptr(file, bfound, NULL);
++		if (bfound == bstart) {
++			for (bstart++; bstart <= bend; bstart++)
++				if (au_hf_dir(file, bstart)) {
++					au_set_fbstart(file, bstart);
++					break;
++				}
++		}
++		fi_write_unlock(file);
++	}
++}
++
 +static void au_br_do_del_brp(struct au_sbinfo *sbinfo,
 +			     const aufs_bindex_t bindex,
 +			     const aufs_bindex_t bend)
@@ -3094,17 +3806,29 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	au_br_do_free(br);
 +}
 +
++static unsigned long long empty_cb(void *array, unsigned long long max,
++				   void *arg)
++{
++	return max;
++}
++
 +int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
 +{
 +	int err, rerr, i;
++	unsigned long long opened;
 +	unsigned int mnt_flags;
 +	aufs_bindex_t bindex, bend, br_id;
 +	unsigned char do_wh, verbose;
 +	struct au_branch *br;
 +	struct au_wbr *wbr;
++	struct dentry *root;
++	struct file **to_free;
 +
 +	err = 0;
-+	bindex = au_find_dbindex(sb->s_root, del->h_path.dentry);
++	opened = 0;
++	to_free = NULL;
++	root = sb->s_root;
++	bindex = au_find_dbindex(root, del->h_path.dentry);
 +	if (bindex < 0) {
 +		if (remount)
 +			goto out; /* success */
@@ -3124,10 +3848,20 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	}
 +	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);
-+		goto out;
++
++	br_id = br->br_id;
++	opened = atomic_read(&br->br_count);
++	if (unlikely(opened)) {
++		to_free = au_array_alloc(&opened, empty_cb, NULL);
++		err = PTR_ERR(to_free);
++		if (IS_ERR(to_free))
++			goto out;
++
++		err = test_file_busy(sb, br_id, to_free, opened);
++		if (unlikely(err)) {
++			AuVerbose(verbose, "%llu file(s) opened\n", opened);
++			goto out;
++		}
 +	}
 +
 +	wbr = br->br_wbr;
@@ -3141,7 +3875,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		}
 +	}
 +
-+	err = test_children_busy(sb->s_root, bindex, verbose);
++	err = test_children_busy(root, bindex, verbose);
 +	if (unlikely(err)) {
 +		if (do_wh)
 +			goto out_wh;
@@ -3149,7 +3883,16 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	}
 +
 +	err = 0;
-+	br_id = br->br_id;
++	if (to_free) {
++		/*
++		 * now we confirmed the branch is deletable.
++		 * let's free the remaining opened dirs on the branch.
++		 */
++		di_write_unlock(root);
++		br_del_file(to_free, opened, br_id);
++		di_write_lock_child(root);
++	}
++
 +	if (!remount)
 +		au_br_do_del(sb, bindex, br);
 +	else {
@@ -3159,10 +3902,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	}
 +
 +	if (!bindex) {
-+		au_cpup_attr_all(sb->s_root->d_inode, /*force*/1);
++		au_cpup_attr_all(root->d_inode, /*force*/1);
 +		sb->s_maxbytes = au_sbr_sb(sb, 0)->s_maxbytes;
 +	} else
-+		au_sub_nlink(sb->s_root->d_inode, del->h_path.dentry->d_inode);
++		au_sub_nlink(root->d_inode, del->h_path.dentry->d_inode);
 +	if (au_opt_test(mnt_flags, PLINK))
 +		au_plink_half_refresh(sb, br_id);
 +
@@ -3177,6 +3920,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		pr_warn("failed re-creating base whiteout, %s. (%d)\n",
 +			del->pathname, rerr);
 +out:
++	if (to_free)
++		au_farray_free(to_free, opened);
 +	return err;
 +}
 +
@@ -3276,52 +4021,6 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +		|| do_need_sigen_inc(new, old);
 +}
 +
-+static unsigned long long au_farray_cb(void *a,
-+				       unsigned long long max __maybe_unused,
-+				       void *arg)
-+{
-+	unsigned long long n;
-+	struct file **p, *f;
-+	struct au_sphlhead *files;
-+	struct au_finfo *finfo;
-+	struct super_block *sb = arg;
-+
-+	n = 0;
-+	p = a;
-+	files = &au_sbi(sb)->si_files;
-+	spin_lock(&files->spin);
-+	hlist_for_each_entry(finfo, &files->head, fi_hlist) {
-+		f = finfo->fi_file;
-+		if (file_count(f)
-+		    && !special_file(file_inode(f)->i_mode)) {
-+			get_file(f);
-+			*p++ = f;
-+			n++;
-+			AuDebugOn(n > max);
-+		}
-+	}
-+	spin_unlock(&files->spin);
-+
-+	return n;
-+}
-+
-+static struct file **au_farray_alloc(struct super_block *sb,
-+				     unsigned long long *max)
-+{
-+	*max = atomic_long_read(&au_sbi(sb)->si_nfiles);
-+	return au_array_alloc(max, au_farray_cb, sb);
-+}
-+
-+static void au_farray_free(struct file **a, unsigned long long max)
-+{
-+	unsigned long long ull;
-+
-+	for (ull = 0; ull < max; ull++)
-+		if (a[ull])
-+			fput(a[ull]);
-+	au_array_free(a);
-+}
-+
 +static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex)
 +{
 +	int err, do_warn;
@@ -3345,6 +4044,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	br_id = au_sbr_id(sb, bindex);
 +	for (ull = 0; ull < max; ull++) {
 +		file = array[ull];
++		if (unlikely(!file))
++			break;
 +
 +		/* AuDbg("%pD\n", file); */
 +		fi_read_lock(file);
@@ -3419,6 +4120,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	aufs_bindex_t bindex;
 +	struct dentry *root;
 +	struct au_branch *br;
++	struct au_br_fhsm *bf;
 +
 +	root = sb->s_root;
 +	bindex = au_find_dbindex(root, mod->h_root);
@@ -3440,11 +4142,21 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +	if (br->br_perm == mod->perm)
 +		return 0; /* success */
 +
++	/* pre-allocate for non-fhsm --> fhsm */
++	bf = NULL;
++	if (!au_br_fhsm(br->br_perm) && au_br_fhsm(mod->perm)) {
++		err = au_fhsm_br_alloc(br);
++		if (unlikely(err))
++			goto out;
++		bf = br->br_fhsm;
++		br->br_fhsm = NULL;
++	}
++
 +	if (au_br_writable(br->br_perm)) {
 +		/* remove whiteout base */
 +		err = au_br_init_wh(sb, br, mod->perm);
 +		if (unlikely(err))
-+			goto out;
++			goto out_bf;
 +
 +		if (!au_br_writable(mod->perm)) {
 +			/* rw --> ro, file might be mmapped */
@@ -3480,28 +4192,60 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +			}
 +		}
 +	}
++	if (unlikely(err))
++		goto out_bf;
++
++	if (au_br_fhsm(br->br_perm)) {
++		if (!au_br_fhsm(mod->perm)) {
++			/* fhsm --> non-fhsm */
++			au_br_fhsm_fin(br->br_fhsm);
++			kfree(br->br_fhsm);
++			br->br_fhsm = NULL;
++		}
++	} else if (au_br_fhsm(mod->perm))
++		/* non-fhsm --> fhsm */
++		br->br_fhsm = bf;
++
++	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;
++	goto out; /* success */
++
++out_bf:
++	kfree(bf);
++out:
++	AuTraceErr(err);
++	return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs)
++{
++	int err;
++	struct kstatfs kstfs;
 +
++	err = vfs_statfs(&br->br_path, &kstfs);
 +	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;
++		stfs->f_blocks = kstfs.f_blocks;
++		stfs->f_bavail = kstfs.f_bavail;
++		stfs->f_files = kstfs.f_files;
++		stfs->f_ffree = kstfs.f_ffree;
 +	}
 +
-+out:
-+	AuTraceErr(err);
 +	return err;
 +}
 diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 --- /usr/share/empty/fs/aufs/branch.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/branch.h	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,264 @@
++++ linux/fs/aufs/branch.h	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,280 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -3545,8 +4289,18 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +#endif
 +};
 +
-+/* members for writable branch only */
-+enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
++/* File-based Hierarchical Storage Management */
++struct au_br_fhsm {
++#ifdef CONFIG_AUFS_FHSM
++	struct mutex		bf_lock;
++	unsigned long		bf_jiffy;
++	struct aufs_stfs	bf_stfs;
++	int			bf_readable;
++#endif
++};
++
++/* members for writable branch only */
++enum {AuBrWh_BASE, AuBrWh_PLINK, AuBrWh_ORPH, AuBrWh_Last};
 +struct au_wbr {
 +	struct au_rwsem		wbr_wh_rwsem;
 +	struct dentry		*wbr_wh[AuBrWh_Last];
@@ -3595,6 +4349,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +	atomic_t		br_count;
 +
 +	struct au_wbr		*br_wbr;
++	struct au_br_fhsm	*br_fhsm;
 +
 +	/* xino truncation */
 +	atomic_t		br_xino_running;
@@ -3626,34 +4381,6 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +	return au_br_mnt(br)->mnt_sb;
 +}
 +
-+/* branch permissions and attributes */
-+#define AuBrPerm_RW		1		/* writable, hardlinkable wh */
-+#define AuBrPerm_RO		(1 << 1)	/* readonly */
-+#define AuBrPerm_RR		(1 << 2)	/* natively readonly */
-+#define AuBrPerm_Mask		(AuBrPerm_RW | AuBrPerm_RO | AuBrPerm_RR)
-+
-+#define AuBrRAttr_WH		(1 << 3)	/* whiteout-able */
-+
-+#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;
-+}
-+
-+static inline int au_br_whable(int brperm)
-+{
-+	return brperm & (AuBrPerm_RW | AuBrRAttr_WH);
-+}
-+
-+static inline int au_br_wh_linkable(int brperm)
-+{
-+	return !(brperm & AuBrWAttr_NoLinkWH);
-+}
-+
 +static inline int au_br_rdonly(struct au_branch *br)
 +{
 +	return ((au_br_sb(br)->s_flags & MS_RDONLY)
@@ -3670,6 +4397,18 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +#endif
 +}
 +
++static inline int au_br_test_oflag(int oflag, struct au_branch *br)
++{
++	int err, exec_flag;
++
++	err = 0;
++	exec_flag = oflag & __FMODE_EXEC;
++	if (unlikely(exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC)))
++		err = -EACCES;
++
++	return err;
++}
++
 +/* ---------------------------------------------------------------------- */
 +
 +/* branch.c */
@@ -3687,6 +4426,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +struct au_opt_mod;
 +int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount,
 +	      int *do_refresh);
++struct aufs_stfs;
++int au_br_stfs(struct au_branch *br, struct aufs_stfs *stfs);
 +
 +/* xino.c */
 +static const loff_t au_loff_max = LLONG_MAX;
@@ -3762,12 +4503,31 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +#define WbrWhMustAnyLock(wbr)	AuRwMustAnyLock(&wbr->wbr_wh_rwsem)
 +#define WbrWhMustWriteLock(wbr)	AuRwMustWriteLock(&wbr->wbr_wh_rwsem)
 +
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_FHSM
++static inline void au_br_fhsm_init(struct au_br_fhsm *brfhsm)
++{
++	mutex_init(&brfhsm->bf_lock);
++	brfhsm->bf_jiffy = 0;
++	brfhsm->bf_readable = 0;
++}
++
++static inline void au_br_fhsm_fin(struct au_br_fhsm *brfhsm)
++{
++	mutex_destroy(&brfhsm->bf_lock);
++}
++#else
++AuStubVoid(au_br_fhsm_init, struct au_br_fhsm *brfhsm)
++AuStubVoid(au_br_fhsm_fin, struct au_br_fhsm *brfhsm)
++#endif
++
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_BRANCH_H__ */
 diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 --- /usr/share/empty/fs/aufs/conf.mk	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/conf.mk	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,37 @@
++++ linux/fs/aufs/conf.mk	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,38 @@
 +
 +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
 +
@@ -3781,8 +4541,9 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 +	SBILIST \
 +	HNOTIFY HFSNOTIFY \
 +	EXPORT INO_T_64 \
++	XATTR \
++	FHSM \
 +	RDU \
-+	SP_IATTR \
 +	SHWH \
 +	BR_RAMFS \
 +	BR_FUSE POLL \
@@ -3807,10 +4568,10 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
 +-include ${srctree}/${src}/conf_priv.mk
 diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 --- /usr/share/empty/fs/aufs/cpup.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,1277 @@
++++ linux/fs/aufs/cpup.c	2016-02-28 13:27:46.147416184 +0100
+@@ -0,0 +1,1308 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -3872,8 +4633,11 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +
 +	/*
 +	 * 0 can happen in revalidating.
-+	 * h_inode->i_mutex is not held, but it is harmless since once i_nlink
-+	 * reaches 0, it will never become positive.
++	 * h_inode->i_mutex may not be held here, but it is harmless since once
++	 * i_nlink reaches 0, it will never become positive except O_TMPFILE
++	 * case.
++	 * todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause
++	 *	 the incorrect link count.
 +	 */
 +	set_nlink(inode, h_inode->i_nlink);
 +
@@ -3973,15 +4737,19 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +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;
++	int err, sbits, icex;
++	unsigned int mnt_flags;
++	unsigned char verbose;
 +	struct iattr ia;
 +	struct path h_path;
 +	struct inode *h_isrc, *h_idst;
 +	struct kstat *h_st;
++	struct au_branch *br;
 +
 +	h_path.dentry = au_h_dptr(dst, bindex);
 +	h_idst = h_path.dentry->d_inode;
-+	h_path.mnt = au_sbr_mnt(dst->d_sb, bindex);
++	br = au_sbr(dst->d_sb, bindex);
++	h_path.mnt = au_br_mnt(br);
 +	h_isrc = h_src->d_inode;
 +	ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID
 +		| ATTR_ATIME | ATTR_MTIME
@@ -4022,6 +4790,13 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		err = vfsub_notify_change(&h_path, &ia, /*delegated*/NULL);
 +	}
 +
++	icex = br->br_perm & AuBrAttr_ICEX;
++	if (!err) {
++		mnt_flags = au_mntflags(dst->d_sb);
++		verbose = !!au_opt_test(mnt_flags, VERBOSE);
++		err = au_cpup_xattr(h_path.dentry, h_src, icex, verbose);
++	}
++
 +	return err;
 +}
 +
@@ -4223,7 +4998,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	int err, rerr;
 +	loff_t l;
 +	struct path h_path;
-+	struct inode *h_src_inode;
++	struct inode *h_src_inode, *h_dst_inode;
 +
 +	err = 0;
 +	h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc);
@@ -4250,6 +5025,13 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		if (!err && rerr)
 +			err = rerr;
 +	}
++	if (!err && (h_src_inode->i_state & I_LINKABLE)) {
++		h_path.dentry = au_h_dptr(cpg->dentry, cpg->bdst);
++		h_dst_inode = h_path.dentry->d_inode;
++		spin_lock(&h_dst_inode->i_lock);
++		h_dst_inode->i_state |= I_LINKABLE;
++		spin_unlock(&h_dst_inode->i_lock);
++	}
 +
 +out:
 +	return err;
@@ -4298,7 +5080,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	int err;
 +	umode_t mode;
 +	unsigned int mnt_flags;
-+	unsigned char isdir;
++	unsigned char isdir, isreg, force;
 +	const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME);
 +	struct au_dtime dt;
 +	struct path h_path;
@@ -4329,10 +5111,12 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	}
 +	h_path.dentry = h_dst;
 +
++	isreg = 0;
 +	isdir = 0;
 +	mode = h_inode->i_mode;
 +	switch (mode & S_IFMT) {
 +	case S_IFREG:
++		isreg = 1;
 +		err = vfsub_create(h_dir, &h_path, mode | S_IWUSR,
 +				   /*want_excl*/true);
 +		if (!err)
@@ -4372,7 +5156,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	if (!au_opt_test(mnt_flags, UDBA_NONE)
 +	    && !isdir
 +	    && au_opt_test(mnt_flags, XINO)
-+	    && h_inode->i_nlink == 1
++	    && (h_inode->i_nlink == 1
++		|| (h_inode->i_state & I_LINKABLE))
 +	    /* todo: unnecessary? */
 +	    /* && cpg->dentry->d_inode->i_nlink == 1 */
 +	    && cpg->bdst < cpg->bsrc
@@ -4380,6 +5165,16 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0);
 +		/* ignore this error */
 +
++	if (!err) {
++		force = 0;
++		if (isreg) {
++			force = !!cpg->len;
++			if (cpg->len == -1)
++				force = !!i_size_read(h_inode);
++		}
++		au_fhsm_wrote(sb, cpg->bdst, force);
++	}
++
 +	if (do_dt)
 +		au_dtime_revert(&dt);
 +	return err;
@@ -4564,7 +5359,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		goto out_rev;
 +
 +	if (!isdir
-+	    && h_src->d_inode->i_nlink > 1
++	    && (h_src->d_inode->i_nlink > 1
++		|| h_src->d_inode->i_state & I_LINKABLE)
 +	    && plink)
 +		au_plink_append(inode, cpg->bdst, h_dst);
 +
@@ -4601,7 +5397,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	return err;
 +}
 +
-+#if 0 /* unused */
++#if 0 /* reserved */
 +struct au_cpup_single_args {
 +	int *errp;
 +	struct au_cp_generic *cpg;
@@ -4657,7 +5453,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +	return do_sio;
 +}
 +
-+#if 0 /* unused */
++#if 0 /* reserved */
 +int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
 +{
 +	int err, wkq_err;
@@ -4868,7 +5664,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +
 +	dget(wh_dentry);
 +	h_path.dentry = wh_dentry;
-+	if (!S_ISDIR(wh_dentry->d_inode->i_mode)) {
++	if (!d_is_dir(wh_dentry)) {
 +		/* no delegation since it is just created */
 +		err = vfsub_unlink(h_parent->d_inode, &h_path,
 +				   /*delegated*/NULL, /*force*/0);
@@ -4908,7 +5704,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +{
 +	int err, wkq_err;
 +	aufs_bindex_t bdst;
-+	struct dentry *dentry, *parent, *h_orph, *h_parent, *h_dentry;
++	struct dentry *dentry, *parent, *h_orph, *h_parent;
 +	struct inode *dir, *h_dir, *h_tmpdir;
 +	struct au_wbr *wbr;
 +	struct au_pin wh_pin, *pin_orig;
@@ -4931,10 +5727,6 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +		h_tmpdir = h_orph->d_inode;
 +		au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0);
 +
-+		if (file)
-+			h_dentry = au_hf_top(file)->f_dentry;
-+		else
-+			h_dentry = au_h_dptr(dentry, au_dbstart(dentry));
 +		mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3);
 +		/* todo: au_h_open_pre()? */
 +
@@ -5044,7 +5836,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +
 +static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst,
 +		       struct au_pin *pin,
-+		       struct dentry *h_parent __maybe_unused ,
++		       struct dentry *h_parent __maybe_unused,
 +		       void *arg __maybe_unused)
 +{
 +	struct au_cp_generic cpg = {
@@ -5088,10 +5880,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +}
 diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
 --- /usr/share/empty/fs/aufs/cpup.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/cpup.h	2014-04-24 22:11:10.848602379 +0200
++++ linux/fs/aufs/cpup.h	2016-02-28 11:27:09.950762598 +0100
 @@ -0,0 +1,94 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -5186,10 +5978,10 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
 +#endif /* __AUFS_CPUP_H__ */
 diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 --- /usr/share/empty/fs/aufs/dbgaufs.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dbgaufs.c	2014-04-24 22:11:10.848602379 +0200
++++ linux/fs/aufs/dbgaufs.c	2016-02-28 11:27:09.950762598 +0100
 @@ -0,0 +1,432 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -5521,7 +6313,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +	int err;
 +
 +	/*
-+	 * This function is a dynamic '__init' fucntion actually,
++	 * This function is a dynamic '__init' function actually,
 +	 * so the tiny check for si_rwsem is unnecessary.
 +	 */
 +	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
@@ -5547,7 +6339,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +void dbgaufs_si_fin(struct au_sbinfo *sbinfo)
 +{
 +	/*
-+	 * This function is a dynamic '__init' fucntion actually,
++	 * This function is a dynamic '__fin' function actually,
 +	 * so the tiny check for si_rwsem is unnecessary.
 +	 */
 +	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
@@ -5563,7 +6355,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +	char name[SysaufsSiNameLen];
 +
 +	/*
-+	 * This function is a dynamic '__init' fucntion actually,
++	 * This function is a dynamic '__init' function actually,
 +	 * so the tiny check for si_rwsem is unnecessary.
 +	 */
 +	/* AuRwMustWriteLock(&sbinfo->si_rwsem); */
@@ -5622,10 +6414,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h
 --- /usr/share/empty/fs/aufs/dbgaufs.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dbgaufs.h	2014-04-24 22:11:10.848602379 +0200
++++ linux/fs/aufs/dbgaufs.h	2016-02-28 11:27:09.950762598 +0100
 @@ -0,0 +1,48 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -5674,10 +6466,10 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.h linux/fs/aufs/dbgaufs.h
 +#endif /* __DBGAUFS_H__ */
 diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 --- /usr/share/empty/fs/aufs/dcsub.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dcsub.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,243 @@
++++ linux/fs/aufs/dcsub.c	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,224 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -5775,7 +6567,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +		dpages->ndpage++;
 +	}
 +
-+	AuDebugOn(!d_count(dentry));
++	AuDebugOn(au_dcount(dentry) <= 0);
 +	dpage->dentries[dpage->ndentry++] = dget_dlock(dentry);
 +	return 0; /* success */
 +
@@ -5783,79 +6575,60 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	return err;
 +}
 +
-+/* try d_walk() in linux/fs/dcache.c */
-+int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
-+		   au_dpages_test test, void *arg)
-+{
++/* todo: BAD approach */
++/* copied from linux/fs/dcache.c */
++enum d_walk_ret {
++	D_WALK_CONTINUE,
++	D_WALK_QUIT,
++	D_WALK_NORETRY,
++	D_WALK_SKIP,
++};
++
++extern void d_walk(struct dentry *parent, void *data,
++		   enum d_walk_ret (*enter)(void *, struct dentry *),
++		   void (*finish)(void *));
++
++struct ac_dpages_arg {
 +	int err;
-+	struct dentry *this_parent;
-+	struct list_head *next;
-+	struct super_block *sb = root->d_sb;
++	struct au_dcsub_pages *dpages;
++	struct super_block *sb;
++	au_dpages_test test;
++	void *arg;
++};
 +
-+	err = 0;
-+	write_seqlock(&rename_lock);
-+	this_parent = root;
-+	spin_lock(&this_parent->d_lock);
-+repeat:
-+	next = this_parent->d_subdirs.next;
-+resume:
-+	if (this_parent->d_sb == sb
-+	    && !IS_ROOT(this_parent)
-+	    && au_di(this_parent)
-+	    && d_count(this_parent)
-+	    && (!test || test(this_parent, arg))) {
-+		err = au_dpages_append(dpages, this_parent, GFP_ATOMIC);
-+		if (unlikely(err))
-+			goto out;
-+	}
++static enum d_walk_ret au_call_dpages_append(void *_arg, struct dentry *dentry)
++{
++	enum d_walk_ret ret;
++	struct ac_dpages_arg *arg = _arg;
 +
-+	while (next != &this_parent->d_subdirs) {
-+		struct list_head *tmp = next;
-+		struct dentry *dentry = list_entry(tmp, struct dentry,
-+						   d_child);
-+
-+		next = tmp->next;
-+		spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
-+		if (d_count(dentry)) {
-+			if (!list_empty(&dentry->d_subdirs)) {
-+				spin_unlock(&this_parent->d_lock);
-+				spin_release(&dentry->d_lock.dep_map, 1,
-+					     _RET_IP_);
-+				this_parent = dentry;
-+				spin_acquire(&this_parent->d_lock.dep_map, 0, 1,
-+					     _RET_IP_);
-+				goto repeat;
-+			}
-+			if (dentry->d_sb == sb
-+			    && au_di(dentry)
-+			    && (!test || test(dentry, arg)))
-+				err = au_dpages_append(dpages, dentry,
-+						       GFP_ATOMIC);
-+		}
-+		spin_unlock(&dentry->d_lock);
-+		if (unlikely(err))
-+			goto out;
++	ret = D_WALK_CONTINUE;
++	if (dentry->d_sb == arg->sb
++	    && !IS_ROOT(dentry)
++	    && au_dcount(dentry) > 0
++	    && au_di(dentry)
++	    && (!arg->test || arg->test(dentry, arg->arg))) {
++		arg->err = au_dpages_append(arg->dpages, dentry, GFP_ATOMIC);
++		if (unlikely(arg->err))
++			ret = D_WALK_QUIT;
 +	}
 +
-+	if (this_parent != root) {
-+		struct dentry *tmp;
-+		struct dentry *child;
++	return ret;
++}
 +
-+		tmp = this_parent->d_parent;
-+		rcu_read_lock();
-+		spin_unlock(&this_parent->d_lock);
-+		child = this_parent;
-+		this_parent = tmp;
-+		spin_lock(&this_parent->d_lock);
-+		rcu_read_unlock();
-+		next = child->d_child.next;
-+		goto resume;
-+	}
++int au_dcsub_pages(struct au_dcsub_pages *dpages, struct dentry *root,
++		   au_dpages_test test, void *arg)
++{
++	struct ac_dpages_arg args = {
++		.err	= 0,
++		.dpages	= dpages,
++		.sb	= root->d_sb,
++		.test	= test,
++		.arg	= arg
++	};
 +
-+out:
-+	spin_unlock(&this_parent->d_lock);
-+	write_sequnlock(&rename_lock);
-+	return err;
++	d_walk(root, &args, au_call_dpages_append, NULL);
++
++	return args.err;
 +}
 +
 +int au_dcsub_pages_rev(struct au_dcsub_pages *dpages, struct dentry *dentry,
@@ -5867,7 +6640,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	write_seqlock(&rename_lock);
 +	spin_lock(&dentry->d_lock);
 +	if (do_include
-+	    && d_count(dentry)
++	    && au_dcount(dentry) > 0
 +	    && (!test || test(dentry, arg)))
 +		err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
 +	spin_unlock(&dentry->d_lock);
@@ -5881,7 +6654,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +	while (!IS_ROOT(dentry)) {
 +		dentry = dentry->d_parent; /* rename_lock is locked */
 +		spin_lock(&dentry->d_lock);
-+		if (d_count(dentry)
++		if (au_dcount(dentry) > 0
 +		    && (!test || test(dentry, arg)))
 +			err = au_dpages_append(dpages, dentry, GFP_ATOMIC);
 +		spin_unlock(&dentry->d_lock);
@@ -5921,10 +6694,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 --- /usr/share/empty/fs/aufs/dcsub.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dcsub.h	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,98 @@
++++ linux/fs/aufs/dcsub.h	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,123 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -5952,8 +6725,6 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 +#include <linux/dcache.h>
 +#include <linux/fs.h>
 +
-+struct dentry;
-+
 +struct au_dpage {
 +	int ndentry;
 +	struct dentry **dentries;
@@ -5989,16 +6760,31 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 +{
 +	int err;
 +	struct inode *inode = d->d_inode;
++
 +	err = 0;
 +	if (unlikely(d_unhashed(d) || !inode || !inode->i_nlink))
 +		err = -ENOENT;
 +	return err;
 +}
 +
++static inline int au_d_linkable(struct dentry *d)
++{
++	int err;
++	struct inode *inode = d->d_inode;
++
++	err = au_d_hashed_positive(d);
++	if (err
++	    && inode
++	    && (inode->i_state & I_LINKABLE))
++		err = 0;
++	return err;
++}
++
 +static inline int au_d_alive(struct dentry *d)
 +{
 +	int err;
 +	struct inode *inode;
++
 +	err = 0;
 +	if (!IS_ROOT(d))
 +		err = au_d_hashed_positive(d);
@@ -6013,20 +6799,32 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
 +static inline int au_alive_dir(struct dentry *d)
 +{
 +	int err;
++
 +	err = au_d_alive(d);
 +	if (unlikely(err || IS_DEADDIR(d->d_inode)))
 +		err = -ENOENT;
 +	return err;
 +}
 +
++static inline int au_qstreq(struct qstr *a, struct qstr *b)
++{
++	return a->len == b->len
++		&& !memcmp(a->name, b->name, a->len);
++}
++
++static inline int au_dcount(struct dentry *d)
++{
++	return (int)d_count(d);
++}
++
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_DCSUB_H__ */
 diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 --- /usr/share/empty/fs/aufs/debug.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/debug.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,517 @@
++++ linux/fs/aufs/debug.c	2016-02-28 13:27:46.147416184 +0100
+@@ -0,0 +1,438 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -6046,7 +6844,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 + * debug print functions
 + */
 +
-+#include <linux/vt_kern.h>
 +#include "aufs.h"
 +
 +/* Returns 0, or -errno.  arg is in kp->arg. */
@@ -6083,6 +6880,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +MODULE_PARM_DESC(debug, "debug print");
 +module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP);
 +
++DEFINE_MUTEX(au_dbg_mtx);	/* just to serialize the dbg msgs */
 +char *au_plevel = KERN_DEBUG;
 +#define dpri(fmt, ...) do {					\
 +	if ((au_plevel						\
@@ -6204,6 +7002,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +{
 +	struct dentry *wh = NULL;
 +	int hn;
++	struct au_iinfo *iinfo;
 +
 +	if (!dentry || IS_ERR(dentry)) {
 +		dpri("d%d: err %ld\n", bindex, PTR_ERR(dentry));
@@ -6214,11 +7013,11 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	dpri("d%d: %p, %pd2?, %s, cnt %d, flags 0x%x, %shashed\n",
 +	     bindex, dentry, dentry,
 +	     dentry->d_sb ? au_sbtype(dentry->d_sb) : "??",
-+	     d_count(dentry), dentry->d_flags,
++	     au_dcount(dentry), dentry->d_flags,
 +	     d_unhashed(dentry) ? "un" : "");
 +	hn = -1;
 +	if (bindex >= 0 && dentry->d_inode && au_test_aufs(dentry->d_sb)) {
-+		struct au_iinfo *iinfo = au_ii(dentry->d_inode);
++		iinfo = au_ii(dentry->d_inode);
 +		if (iinfo) {
 +			hn = !!au_hn(iinfo->ii_hinode + bindex);
 +			wh = iinfo->ii_hinode[0 + bindex].hi_whdentry;
@@ -6242,9 +7041,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	dinfo = au_di(dentry);
 +	if (!dinfo)
 +		return;
-+	dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d\n",
++	dpri("d-1: bstart %d, bend %d, bwh %d, bdiropq %d, gen %d, tmp %d\n",
 +	     dinfo->di_bstart, dinfo->di_bend,
-+	     dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry));
++	     dinfo->di_bwh, dinfo->di_bdiropq, au_digen(dentry),
++	     dinfo->di_tmpfile);
 +	if (dinfo->di_bstart < 0)
 +		return;
 +	hdp = dinfo->di_hdentry;
@@ -6262,7 +7062,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	}
 +	a[0] = 0;
 +	if (bindex < 0
-+	    && file->f_dentry
++	    && !IS_ERR_OR_NULL(file->f_dentry)
 +	    && au_test_aufs(file->f_dentry->d_sb)
 +	    && au_fi(file))
 +		snprintf(a, sizeof(a), ", gen %d, mmapped %d",
@@ -6270,7 +7070,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	dpri("f%d: mode 0x%x, flags 0%o, cnt %ld, v %llu, pos %llu%s\n",
 +	     bindex, file->f_mode, file->f_flags, (long)file_count(file),
 +	     file->f_version, file->f_pos, a);
-+	if (file->f_dentry)
++	if (!IS_ERR_OR_NULL(file->f_dentry))
 +		do_pri_dentry(bindex, file->f_dentry);
 +	return 0;
 +}
@@ -6284,7 +7084,9 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	int err;
 +
 +	err = do_pri_file(-1, file);
-+	if (err || !file->f_dentry || !au_test_aufs(file->f_dentry->d_sb))
++	if (err
++	    || IS_ERR_OR_NULL(file->f_dentry)
++	    || !au_test_aufs(file->f_dentry->d_sb))
 +		return;
 +
 +	finfo = au_fi(file);
@@ -6374,42 +7176,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +
 +/* ---------------------------------------------------------------------- */
 +
-+void au_dbg_sleep_jiffy(int jiffy)
-+{
-+	while (jiffy)
-+		jiffy = schedule_timeout_uninterruptible(jiffy);
-+}
-+
-+void au_dbg_iattr(struct iattr *ia)
-+{
-+#define AuBit(name)					\
-+	do {						\
-+		if (ia->ia_valid & ATTR_ ## name)	\
-+			dpri(#name "\n");		\
-+	} while (0)
-+	AuBit(MODE);
-+	AuBit(UID);
-+	AuBit(GID);
-+	AuBit(SIZE);
-+	AuBit(ATIME);
-+	AuBit(MTIME);
-+	AuBit(CTIME);
-+	AuBit(ATIME_SET);
-+	AuBit(MTIME_SET);
-+	AuBit(FORCE);
-+	AuBit(ATTR_FLAG);
-+	AuBit(KILL_SUID);
-+	AuBit(KILL_SGID);
-+	AuBit(FILE);
-+	AuBit(KILL_PRIV);
-+	AuBit(OPEN);
-+	AuBit(TIMES_SET);
-+#undef	AuBit
-+	dpri("ia_file %p\n", ia->ia_file);
-+}
-+
-+/* ---------------------------------------------------------------------- */
-+
 +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line)
 +{
 +	struct inode *h_inode, *inode = dentry->d_inode;
@@ -6444,29 +7210,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	}
 +}
 +
-+void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen)
-+{
-+	struct dentry *parent;
-+
-+	parent = dget_parent(dentry);
-+	AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
-+	AuDebugOn(IS_ROOT(dentry));
-+	AuDebugOn(au_digen_test(parent, sigen));
-+	dput(parent);
-+}
-+
-+void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen)
-+{
-+	struct dentry *parent;
-+	struct inode *inode;
-+
-+	parent = dget_parent(dentry);
-+	inode = dentry->d_inode;
-+	AuDebugOn(inode && S_ISDIR(dentry->d_inode->i_mode));
-+	AuDebugOn(au_digen_test(parent, sigen));
-+	dput(parent);
-+}
-+
 +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen)
 +{
 +	int err, i, j;
@@ -6501,26 +7244,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +
 +/* ---------------------------------------------------------------------- */
 +
-+void au_debug_sbinfo_init(struct au_sbinfo *sbinfo __maybe_unused)
-+{
-+#ifdef AuForceNoPlink
-+	au_opt_clr(sbinfo->si_mntflags, PLINK);
-+#endif
-+#ifdef AuForceNoXino
-+	au_opt_clr(sbinfo->si_mntflags, XINO);
-+#endif
-+#ifdef AuForceNoRefrof
-+	au_opt_clr(sbinfo->si_mntflags, REFROF);
-+#endif
-+#ifdef AuForceHnotify
-+	au_opt_set_udba(sbinfo->si_mntflags, UDBA_HNOTIFY);
-+#endif
-+#ifdef AuForceRd0
-+	sbinfo->si_rdblk = 0;
-+	sbinfo->si_rdhash = 0;
-+#endif
-+}
-+
 +int __init au_debug_init(void)
 +{
 +	aufs_bindex_t bindex;
@@ -6536,18 +7259,14 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
 +	pr_warn("CONFIG_4KSTACKS is defined.\n");
 +#endif
 +
-+#ifdef AuForceNoBrs
-+	sysaufs_brs = 0;
-+#endif
-+
 +	return 0;
 +}
 diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 --- /usr/share/empty/fs/aufs/debug.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/debug.h	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,247 @@
++++ linux/fs/aufs/debug.h	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,228 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -6654,10 +7373,9 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +
 +/* ---------------------------------------------------------------------- */
 +
-+struct au_sbinfo;
-+struct au_finfo;
 +struct dentry;
 +#ifdef CONFIG_AUFS_DEBUG
++extern struct mutex au_dbg_mtx;
 +extern char *au_plevel;
 +struct au_nhash;
 +void au_dpri_whlist(struct au_nhash *whlist);
@@ -6672,67 +7390,60 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +struct super_block;
 +void au_dpri_sb(struct super_block *sb);
 +
-+void au_dbg_sleep_jiffy(int jiffy);
-+struct iattr;
-+void au_dbg_iattr(struct iattr *ia);
-+
 +#define au_dbg_verify_dinode(d) __au_dbg_verify_dinode(d, __func__, __LINE__)
 +void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line);
-+void au_dbg_verify_dir_parent(struct dentry *dentry, unsigned int sigen);
-+void au_dbg_verify_nondir_parent(struct dentry *dentry, unsigned int sigen);
 +void au_dbg_verify_gen(struct dentry *parent, unsigned int sigen);
 +void au_dbg_verify_kthread(void);
 +
 +int __init au_debug_init(void);
-+void au_debug_sbinfo_init(struct au_sbinfo *sbinfo);
++
 +#define AuDbgWhlist(w) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#w "\n"); \
 +	au_dpri_whlist(w); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgVdir(v) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#v "\n"); \
 +	au_dpri_vdir(v); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgInode(i) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#i "\n"); \
 +	au_dpri_inode(i); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgDAlias(i) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#i "\n"); \
 +	au_dpri_dalias(i); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgDentry(d) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#d "\n"); \
 +	au_dpri_dentry(d); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgFile(f) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#f "\n"); \
 +	au_dpri_file(f); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgSb(sb) do { \
++	mutex_lock(&au_dbg_mtx); \
 +	AuDbg(#sb "\n"); \
 +	au_dpri_sb(sb); \
-+} while (0)
-+
-+#define AuDbgSleep(sec) do { \
-+	AuDbg("sleep %d sec\n", sec); \
-+	ssleep(sec); \
-+} while (0)
-+
-+#define AuDbgSleepJiffy(jiffy) do { \
-+	AuDbg("sleep %d jiffies\n", jiffy); \
-+	au_dbg_sleep_jiffy(jiffy); \
-+} while (0)
-+
-+#define AuDbgIAttr(ia) do { \
-+	AuDbg("ia_valid 0x%x\n", (ia)->ia_valid); \
-+	au_dbg_iattr(ia); \
++	mutex_unlock(&au_dbg_mtx); \
 +} while (0)
 +
 +#define AuDbgSym(addr) do {				\
@@ -6740,12 +7451,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +	sprint_symbol(sym, (unsigned long)addr);	\
 +	AuDbg("%s\n", sym);				\
 +} while (0)
-+
-+#define AuInfoSym(addr) do {				\
-+	char sym[KSYM_SYMBOL_LEN];			\
-+	sprint_symbol(sym, (unsigned long)addr);	\
-+	AuInfo("%s\n", sym);				\
-+} while (0)
 +#else
 +AuStubVoid(au_dbg_verify_dinode, struct dentry *dentry)
 +AuStubVoid(au_dbg_verify_dir_parent, struct dentry *dentry, unsigned int sigen)
@@ -6754,7 +7459,6 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +AuStubVoid(au_dbg_verify_gen, struct dentry *parent, unsigned int sigen)
 +AuStubVoid(au_dbg_verify_kthread, void)
 +AuStubInt0(__init au_debug_init, void)
-+AuStubVoid(au_debug_sbinfo_init, struct au_sbinfo *sbinfo)
 +
 +#define AuDbgWhlist(w)		do {} while (0)
 +#define AuDbgVdir(v)		do {} while (0)
@@ -6763,11 +7467,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +#define AuDbgDentry(d)		do {} while (0)
 +#define AuDbgFile(f)		do {} while (0)
 +#define AuDbgSb(sb)		do {} while (0)
-+#define AuDbgSleep(sec)		do {} while (0)
-+#define AuDbgSleepJiffy(jiffy)	do {} while (0)
-+#define AuDbgIAttr(ia)		do {} while (0)
 +#define AuDbgSym(addr)		do {} while (0)
-+#define AuInfoSym(addr)		do {} while (0)
 +#endif /* CONFIG_AUFS_DEBUG */
 +
 +/* ---------------------------------------------------------------------- */
@@ -6795,10 +7495,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.h linux/fs/aufs/debug.h
 +#endif /* __AUFS_DEBUG_H__ */
 diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 --- /usr/share/empty/fs/aufs/dentry.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dentry.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,1081 @@
++++ linux/fs/aufs/dentry.c	2016-02-28 13:27:46.147416184 +0100
+@@ -0,0 +1,1098 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -6822,6 +7522,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +#include "aufs.h"
 +
 +#define AuLkup_ALLOW_NEG	1
++#define AuLkup_IGNORE_PERM	(1 << 1)
 +#define au_ftest_lkup(flags, name)	((flags) & AuLkup_##name)
 +#define au_fset_lkup(flags, name) \
 +	do { (flags) |= AuLkup_##name; } while (0)
@@ -6843,17 +7544,19 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	     struct au_do_lookup_args *args)
 +{
 +	struct dentry *h_dentry;
-+	struct inode *h_inode, *inode;
++	struct inode *h_inode;
 +	struct au_branch *br;
 +	int wh_found, opq;
 +	unsigned char wh_able;
 +	const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
++	const unsigned char ignore_perm = !!au_ftest_lkup(args->flags,
++							  IGNORE_PERM);
 +
 +	wh_found = 0;
 +	br = au_sbr(dentry->d_sb, bindex);
 +	wh_able = !!au_br_whable(br->br_perm);
 +	if (wh_able)
-+		wh_found = au_wh_test(h_parent, wh_name, br, /*try_sio*/0);
++		wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0);
 +	h_dentry = ERR_PTR(wh_found);
 +	if (!wh_found)
 +		goto real_lookup;
@@ -6867,9 +7570,16 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		return NULL; /* success */
 +
 +real_lookup:
-+	h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
-+	if (IS_ERR(h_dentry))
++	if (!ignore_perm)
++		h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
++	else
++		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
++	if (IS_ERR(h_dentry)) {
++		if (PTR_ERR(h_dentry) == -ENAMETOOLONG
++		    && !allow_neg)
++			h_dentry = NULL;
 +		goto out;
++	}
 +
 +	h_inode = h_dentry->d_inode;
 +	if (!h_inode) {
@@ -6885,13 +7595,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		au_set_dbstart(dentry, bindex);
 +	au_set_h_dptr(dentry, bindex, h_dentry);
 +
-+	inode = dentry->d_inode;
-+	if (!h_inode || !S_ISDIR(h_inode->i_mode) || !wh_able
-+	    || (inode && !S_ISDIR(inode->i_mode)))
++	if (!d_is_dir(h_dentry)
++	    || !wh_able
++	    || (d_is_positive(dentry) && !d_is_dir(dentry)))
 +		goto out; /* success */
 +
 +	mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD);
-+	opq = au_diropq_test(h_dentry, br);
++	opq = au_diropq_test(h_dentry);
 +	mutex_unlock(&h_inode->i_mutex);
 +	if (opq > 0)
 +		au_set_dbdiropq(dentry, bindex);
@@ -6925,7 +7635,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +{
 +	int npositive, err;
 +	aufs_bindex_t bindex, btail, bdiropq;
-+	unsigned char isdir;
++	unsigned char isdir, dirperm1;
 +	struct qstr whname;
 +	struct au_do_lookup_args args = {
 +		.flags		= 0,
@@ -6934,8 +7644,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	const struct qstr *name = &dentry->d_name;
 +	struct dentry *parent;
 +	struct inode *inode;
++	struct super_block *sb;
 +
-+	err = au_test_shwh(dentry->d_sb, name);
++	sb = dentry->d_sb;
++	err = au_test_shwh(sb, name);
 +	if (unlikely(err))
 +		goto out;
 +
@@ -6944,9 +7656,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		goto out;
 +
 +	inode = dentry->d_inode;
-+	isdir = !!(inode && S_ISDIR(inode->i_mode));
++	isdir = !!d_is_dir(dentry);
 +	if (!type)
 +		au_fset_lkup(args.flags, ALLOW_NEG);
++	dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);
 +
 +	npositive = 0;
 +	parent = dget_parent(dentry);
@@ -6964,12 +7677,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +			continue;
 +		}
 +		h_parent = au_h_dptr(parent, bindex);
-+		if (!h_parent)
-+			continue;
-+		h_dir = h_parent->d_inode;
-+		if (!h_dir || !S_ISDIR(h_dir->i_mode))
++		if (!h_parent || !d_is_dir(h_parent))
 +			continue;
 +
++		h_dir = h_parent->d_inode;
 +		mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT);
 +		h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
 +					&args);
@@ -6977,7 +7688,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		err = PTR_ERR(h_dentry);
 +		if (IS_ERR(h_dentry))
 +			goto out_parent;
-+		au_fclr_lkup(args.flags, ALLOW_NEG);
++		if (h_dentry)
++			au_fclr_lkup(args.flags, ALLOW_NEG);
++		if (dirperm1)
++			au_fset_lkup(args.flags, IGNORE_PERM);
 +
 +		if (au_dbwh(dentry) >= 0)
 +			break;
@@ -7004,7 +7718,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		au_update_dbstart(dentry);
 +	}
 +	err = npositive;
-+	if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), UDBA_NONE)
++	if (unlikely(!au_opt_test(au_mntflags(sb), UDBA_NONE)
 +		     && au_dbstart(dentry) < 0)) {
 +		err = -EIO;
 +		AuIOErr("both of real entry and whiteout found, %pd, err %d\n",
@@ -7018,8 +7732,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	return err;
 +}
 +
-+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
-+			       struct au_branch *br)
++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent)
 +{
 +	struct dentry *dentry;
 +	int wkq_err;
@@ -7056,7 +7769,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	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);
++		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
 +	err = PTR_ERR(h_dentry);
 +	if (IS_ERR(h_dentry))
 +		goto out;
@@ -7311,11 +8024,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +static void au_hide(struct dentry *dentry)
 +{
 +	int err;
-+	struct inode *inode;
 +
 +	AuDbgDentry(dentry);
-+	inode = dentry->d_inode;
-+	if (inode && S_ISDIR(inode->i_mode)) {
++	if (d_is_dir(dentry)) {
 +		/* shrink_dcache_parent(dentry); */
 +		err = au_hide_children(dentry);
 +		if (unlikely(err))
@@ -7508,7 +8219,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	if (!ebrange)
 +		ebrange = au_do_refresh_hdentry(dentry, parent);
 +
-+	if (d_unhashed(dentry) || ebrange) {
++	if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) {
 +		AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0);
 +		if (inode)
 +			err = au_refresh_hinode_self(inode);
@@ -7594,7 +8305,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	int err;
 +	umode_t mode, h_mode;
 +	aufs_bindex_t bindex, btail, bstart, ibs, ibe;
-+	unsigned char plus, unhashed, is_root, h_plus, h_nfs;
++	unsigned char plus, unhashed, is_root, h_plus, h_nfs, tmpfile;
 +	struct inode *h_inode, *h_cached_inode;
 +	struct dentry *h_dentry;
 +	struct qstr *name, *h_name;
@@ -7607,6 +8318,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	unhashed = !!d_unhashed(dentry);
 +	is_root = !!IS_ROOT(dentry);
 +	name = &dentry->d_name;
++	tmpfile = au_di(dentry)->di_tmpfile;
 +
 +	/*
 +	 * Theoretically, REVAL test should be unnecessary in case of
@@ -7640,18 +8352,20 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +			     && !is_root
 +			     && ((!h_nfs
 +				  && (unhashed != !!d_unhashed(h_dentry)
-+				      || name->len != h_name->len
-+				      || memcmp(name->name, h_name->name,
-+						name->len)))
++				      || (!tmpfile
++					  && !au_qstreq(name, h_name))
++					  ))
 +				 || (h_nfs
 +				     && !(flags & LOOKUP_OPEN)
 +				     && (h_dentry->d_flags
 +					 & DCACHE_NFSFS_RENAMED)))
 +			    )) {
-+			AuDbg("unhash 0x%x 0x%x, %pd %pd\n",
-+			      unhashed, d_unhashed(h_dentry),
-+			      dentry, h_dentry);
++			int h_unhashed;
++
++			h_unhashed = d_unhashed(h_dentry);
 +			spin_unlock(&h_dentry->d_lock);
++			AuDbg("unhash 0x%x 0x%x, %pd %pd\n",
++			      unhashed, h_unhashed, dentry, h_dentry);
 +			goto err;
 +		}
 +		spin_unlock(&h_dentry->d_lock);
@@ -7681,7 +8395,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +			h_cached_inode = au_h_iptr(inode, bindex);
 +
 +		if (!h_nfs) {
-+			if (unlikely(plus != h_plus))
++			if (unlikely(plus != h_plus && !tmpfile))
 +				goto err;
 +		} else {
 +			if (unlikely(!(h_dentry->d_flags & DCACHE_NFSFS_RENAMED)
@@ -7791,10 +8505,6 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	if (unlikely(!au_di(dentry)))
 +		goto out;
 +
-+	inode = dentry->d_inode;
-+	if (inode && is_bad_inode(inode))
-+		goto out;
-+
 +	valid = 1;
 +	sb = dentry->d_sb;
 +	/*
@@ -7808,6 +8518,12 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +		AuTraceErr(err);
 +		goto out;
 +	}
++	inode = dentry->d_inode;
++	if (unlikely(inode && is_bad_inode(inode))) {
++		err = -EINVAL;
++		AuTraceErr(err);
++		goto out_dgrade;
++	}
 +	if (unlikely(au_dbrange_test(dentry))) {
 +		err = -EINVAL;
 +		AuTraceErr(err);
@@ -7826,8 +8542,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +	di_downgrade_lock(dentry, AuLock_IR);
 +
 +	err = -EINVAL;
-+	if (!(flags & LOOKUP_OPEN)
++	if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY))
 +	    && inode
++	    && !(inode->i_state && I_LINKABLE)
 +	    && (IS_DEADDIR(inode) || !inode->i_nlink))
 +		goto out_inval;
 +
@@ -7880,10 +8597,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +};
 diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 --- /usr/share/empty/fs/aufs/dentry.h	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dentry.h	2014-04-24 22:11:10.848602379 +0200
++++ linux/fs/aufs/dentry.h	2016-02-28 11:27:09.950762598 +0100
 @@ -0,0 +1,233 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -7921,6 +8638,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +
 +	struct au_rwsem		di_rwsem;
 +	aufs_bindex_t		di_bstart, di_bend, di_bwh, di_bdiropq;
++	unsigned char		di_tmpfile; /* to allow the different name */
 +	struct au_hdentry	*di_hdentry;
 +} ____cacheline_aligned_in_smp;
 +
@@ -7929,8 +8647,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +/* dentry.c */
 +extern const struct dentry_operations aufs_dop;
 +struct au_branch;
-+struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent,
-+			       struct au_branch *br);
++struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent);
 +int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
 +		struct dentry *h_parent, struct au_branch *br);
 +
@@ -8117,10 +8834,10 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
 +#endif /* __AUFS_DENTRY_H__ */
 diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 --- /usr/share/empty/fs/aufs/dinfo.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dinfo.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,542 @@
++++ linux/fs/aufs/dinfo.c	2016-02-28 11:27:09.950762598 +0100
+@@ -0,0 +1,544 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -8170,6 +8887,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		dinfo->di_bend = -1;
 +		dinfo->di_bwh = -1;
 +		dinfo->di_bdiropq = -1;
++		dinfo->di_tmpfile = 0;
 +		for (i = 0; i < nbr; i++)
 +			dinfo->di_hdentry[i].hd_id = -1;
 +		goto out;
@@ -8438,13 +9156,14 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +		return NULL;
 +	AuDebugOn(bindex < 0);
 +	d = au_di(dentry)->di_hdentry[0 + bindex].hd_dentry;
-+	AuDebugOn(d && d_count(d) <= 0);
++	AuDebugOn(d && au_dcount(d) <= 0);
 +	return d;
 +}
 +
 +/*
 + * extended version of au_h_dptr().
-+ * returns a hashed and positive h_dentry in bindex, NULL, or error.
++ * returns a hashed and positive (or linkable) h_dentry in bindex, NULL, or
++ * error.
 + */
 +struct dentry *au_h_d_alias(struct dentry *dentry, aufs_bindex_t bindex)
 +{
@@ -8458,7 +9177,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +	if (au_dbstart(dentry) <= bindex
 +	    && bindex <= au_dbend(dentry))
 +		h_dentry = au_h_dptr(dentry, bindex);
-+	if (h_dentry && !au_d_hashed_positive(h_dentry)) {
++	if (h_dentry && !au_d_linkable(h_dentry)) {
 +		dget(h_dentry);
 +		goto out; /* success */
 +	}
@@ -8469,7 +9188,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +	h_dentry = d_find_alias(h_inode);
 +	if (h_dentry) {
 +		if (!IS_ERR(h_dentry)) {
-+			if (!au_d_hashed_positive(h_dentry))
++			if (!au_d_linkable(h_dentry))
 +				goto out; /* success */
 +			dput(h_dentry);
 +		} else
@@ -8663,10 +9382,10 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
 +}
 diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 --- /usr/share/empty/fs/aufs/dir.c	1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dir.c	2014-04-24 22:11:10.848602379 +0200
-@@ -0,0 +1,639 @@
++++ linux/fs/aufs/dir.c	2016-02-28 13:27:46.147416184 +0100
+@@ -0,0 +1,751 @@
 +/*
-+ * Copyright (C) 2005-2014 Junjiro R. Okajima
++ * Copyright (C) 2005-2015 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
@@ -8728,8 +9447,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +
 +	sz = 0;
 +	if (file) {
-+		AuDebugOn(!file_inode(file));
-+		AuDebugOn(!S_ISDIR(file_inode(file)->i_mode));
++		AuDebugOn(!d_is_dir(file->f_path.dentry));
 +
 +		bend = au_fbend_dir(file);
 +		for (bindex = au_fbstart(file);
@@ -8741,8 +9459,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +		}
 +	} else {
 +		AuDebugOn(!dentry);
-+		AuDebugOn(!dentry->d_inode);
-+		AuDebugOn(!S_ISDIR(dentry->d_inode->i_mode));
++		AuDebugOn(!d_is_dir(dentry));
 +
 +		bend = au_dbtaildir(dentry);
 +		for (bindex = au_dbstart(dentry);
@@ -8764,60 +9481,164 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +	return sz;
 +}
 +
-+/* ---------------------------------------------------------------------- */
++struct au_dir_ts_arg {
++	struct dentry *dentry;
++	aufs_bindex_t brid;
++};
 +
-+static int reopen_dir(struct file *file)
++static void au_do_dir_ts(void *arg)
 +{
++	struct au_dir_ts_arg *a = arg;
++	struct au_dtime dt;
++	struct path h_path;
++	struct inode *dir, *h_dir;
++	struct super_block *sb;
++	struct au_branch *br;
++	struct au_hinode *hdir;
 +	int err;
-+	unsigned int flags;
-+	aufs_bindex_t bindex, btail, bstart;
-+	struct dentry *dentry, *h_dentry;
-+	struct file *h_file;
++	aufs_bindex_t bstart, bindex;
 +
-+	/* open all lower dirs */
-+	dentry = file->f_dentry;
-+	bstart = au_dbstart(dentry);
-+	for (bindex = au_fbstart(file); bindex < bstart; bindex++)
-+		au_set_h_fptr(file, bindex, NULL);
-+	au_set_fbstart(file, bstart);
++	sb = a->dentry->d_sb;
++	dir = a->dentry->d_inode;
++	if (!dir)
++		goto out;
++	/* no dir->i_mutex lock */
++	aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */
 +
-+	btail = au_dbtaildir(dentry);
-+	for (bindex = au_fbend_dir(file); btail < bindex; bindex--)
-+		au_set_h_fptr(file, bindex, NULL);
-+	au_set_fbend_dir(file, btail);
++	bstart = au_ibstart(dir);
++	bindex = au_br_index(sb, a->brid);
++	if (bindex < bstart)
++		goto out_unlock;
 +
-+	flags = vfsub_file_flags(file);
-+	for (bindex = bstart; bindex <= btail; bindex++) {
-+		h_dentry = au_h_dptr(dentry, bindex);
-+		if (!h_dentry)
-+			continue;
-+		h_file = au_hf_dir(file, bindex);
-+		if (h_file)
-+			continue;
++	br = au_sbr(sb, bindex);
++	h_path.dentry = au_h_dptr(a->dentry, bindex);
++	if (!h_path.dentry)
++		goto out_unlock;
++	h_path.mnt = au_br_mnt(br);
++	au_dtime_store(&dt, a->dentry, &h_path);
 +
-+		h_file = au_h_open(dentry, bindex, flags, file, /*force_wr*/0);
-+		err = PTR_ERR(h_file);
-+		if (IS_ERR(h_file))
-+			goto out; /* close all? */
-+		au_set_h_fptr(file, bindex, h_file);
++	br = au_sbr(sb, bstart);
++	if (!au_br_writable(br->br_perm))
++		goto out_unlock;
++	h_path.dentry = au_h_dptr(a->dentry, bstart);
++	h_path.mnt = au_br_mnt(br);
++	err = vfsub_mnt_want_write(h_path.mnt);
++	if (err)
++		goto out_unlock;
++	hdir = au_hi(dir, bstart);
++	au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT);
++	h_dir = au_h_iptr(dir, bstart);
++	if (h_dir->i_nlink
++	    && timespec_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) {
++		dt.dt_h_path = h_path;
++		au_dtime_revert(&dt);
 +	}
-+	au_update_figen(file);
-+	/* todo: necessary? */
-+	/* file->f_ra = h_file->f_ra; */
-+	err = 0;
++	au_hn_imtx_unlock(hdir);
++	vfsub_mnt_drop_write(h_path.mnt);
++	au_cpup_attr_timesizes(dir);
 +
++out_unlock:
++	aufs_read_unlock(a->dentry, AuLock_DW);
 +out:
-+	return err;
++	dput(a->dentry);
++	au_nwt_done(&au_sbi(sb)->si_nowait);
++	kfree(arg);
 +}
 +
-+static int do_open_dir(struct file *file, int flags)
++void au_dir_ts(struct inode *dir, aufs_bindex_t bindex)
++{
++	int perm, wkq_err;
++	aufs_bindex_t bstart;
++	struct au_dir_ts_arg *arg;
<Skipped 9358 lines>
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/kernel.git/commitdiff/5e15af1bd4e185defb21463f5bba76871b437913



More information about the pld-cvs-commit mailing list