[packages/kernel/LINUX_4_9] - up to 4.9.94
arekm
arekm at pld-linux.org
Sun Apr 15 09:01:14 CEST 2018
commit ae9dfd79ace7e0a2a98099f2c18227fc16643bf3
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Sun Apr 15 09:01:04 2018 +0200
- up to 4.9.94
kernel-aufs4.patch | 4222 ++++++++++++++++++++++++++++++++++++-------------
kernel-patches.config | 1 +
kernel.spec | 4 +-
3 files changed, 3120 insertions(+), 1107 deletions(-)
---
diff --git a/kernel.spec b/kernel.spec
index 82fa32e7..c7ecbcc9 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -73,7 +73,7 @@
%define rel 1
%define basever 4.9
-%define postver .93
+%define postver .94
# define this to '-%{basever}' for longterm branch
%define versuffix -%{basever}
@@ -125,7 +125,7 @@ Source0: https://www.kernel.org/pub/linux/kernel/v4.x/linux-%{basever}.tar.xz
# Source0-md5: 0a68ef3615c64bd5ee54a3320e46667d
%if "%{postver}" != ".0"
Patch0: https://www.kernel.org/pub/linux/kernel/v4.x/patch-%{version}.xz
-# Patch0-md5: 677b70806e24b607152f47b5d75bcd56
+# Patch0-md5: 866dbf79661ef17708f5722b9fab7623
%endif
Source1: kernel.sysconfig
diff --git a/kernel-aufs4.patch b/kernel-aufs4.patch
index 16f65cba..c117b634 100644
--- a/kernel-aufs4.patch
+++ b/kernel-aufs4.patch
@@ -127,7 +127,7 @@ diff --git a/fs/inode.c b/fs/inode.c
index 88110fd..9a9ba3a 100644
--- a/fs/inode.c
+++ b/fs/inode.c
-@@ -1642,7 +1642,7 @@ int generic_update_time(struct inode *inode, struct timespec *time, int flags)
+@@ -1642,7 +1642,7 @@ EXPORT_SYMBOL(generic_update_time);
* This does the actual work of updating an inodes time or version. Must have
* had called mnt_want_write() before calling this.
*/
@@ -136,6 +136,23 @@ index 88110fd..9a9ba3a 100644
{
int (*update_time)(struct inode *, struct timespec *, int);
+diff --git a/fs/namespace.c b/fs/namespace.c
+index e6c234b..db0b1ac 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -787,6 +787,12 @@ static inline int check_mnt(struct mount *mnt)
+ return mnt->mnt_ns == current->nsproxy->mnt_ns;
+ }
+
++/* for aufs, CONFIG_AUFS_BR_FUSE */
++int is_current_mnt_ns(struct vfsmount *mnt)
++{
++ return check_mnt(real_mount(mnt));
++}
++
+ /*
+ * vfsmount lock must be held for write
+ */
diff --git a/fs/read_write.c b/fs/read_write.c
index 190e0d36..4052813 100644
--- a/fs/read_write.c
@@ -173,7 +190,7 @@ diff --git a/fs/splice.c b/fs/splice.c
index 5a7750b..28160a7 100644
--- a/fs/splice.c
+++ b/fs/splice.c
-@@ -855,8 +855,8 @@ ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
+@@ -855,8 +855,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
/*
* Attempt to initiate a splice from pipe to file.
*/
@@ -197,11 +214,24 @@ index 5a7750b..28160a7 100644
{
ssize_t (*splice_read)(struct file *, loff_t *,
struct pipe_inode_info *, size_t, unsigned int);
+diff --git a/fs/sync.c b/fs/sync.c
+index 2a54c1f..7a5fa3f 100644
+--- a/fs/sync.c
++++ b/fs/sync.c
+@@ -27,7 +27,7 @@
+ * wait == 1 case since in that case write_inode() functions do
+ * sync_dirty_buffer() and thus effectively write one block at a time.
+ */
+-static int __sync_filesystem(struct super_block *sb, int wait)
++int __sync_filesystem(struct super_block *sb, int wait)
+ {
+ if (wait)
+ sync_inodes_sb(sb);
diff --git a/include/linux/file.h b/include/linux/file.h
index 7444f5f..bdac0be 100644
--- a/include/linux/file.h
+++ b/include/linux/file.h
-@@ -19,6 +19,7 @@
+@@ -19,6 +19,7 @@ struct dentry;
struct path;
extern struct file *alloc_file(struct path *, fmode_t mode,
const struct file_operations *fop);
@@ -210,10 +240,10 @@ index 7444f5f..bdac0be 100644
static inline void fput_light(struct file *file, int fput_needed)
{
diff --git a/include/linux/fs.h b/include/linux/fs.h
-index dc0478c..27c05e7 100644
+index dc0478c..a02be40d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
-@@ -1291,6 +1291,7 @@ struct fasync_struct {
+@@ -1291,6 +1291,7 @@ extern void fasync_free(struct fasync_struct *);
/* can be called from interrupts */
extern void kill_fasync(struct fasync_struct **, int, int);
@@ -242,7 +272,7 @@ index dc0478c..27c05e7 100644
extern ssize_t __vfs_read(struct file *, char __user *, size_t, loff_t *);
extern ssize_t __vfs_write(struct file *, const char __user *, size_t, loff_t *);
extern ssize_t vfs_read(struct file *, char __user *, size_t, loff_t *);
-@@ -2140,6 +2148,7 @@ extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
+@@ -2140,6 +2148,7 @@ extern int current_umask(void);
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
extern int generic_update_time(struct inode *, struct timespec *, int);
@@ -250,11 +280,38 @@ index dc0478c..27c05e7 100644
/* /sys/fs */
extern struct kobject *fs_kobj;
+@@ -2419,6 +2428,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
+ return false;
+ }
+ #endif
++extern int __sync_filesystem(struct super_block *, int);
+ extern int sync_filesystem(struct super_block *);
+ extern const struct file_operations def_blk_fops;
+ extern const struct file_operations def_chr_fops;
+diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h
+index 12b2ab5..8b810d1 100644
+--- a/include/linux/mnt_namespace.h
++++ b/include/linux/mnt_namespace.h
+@@ -5,11 +5,14 @@
+ struct mnt_namespace;
+ struct fs_struct;
+ struct user_namespace;
++struct vfsmount;
+
+ extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *,
+ struct user_namespace *, struct fs_struct *);
+ extern void put_mnt_ns(struct mnt_namespace *ns);
+
++extern int is_current_mnt_ns(struct vfsmount *mnt);
++
+ extern const struct file_operations proc_mounts_operations;
+ extern const struct file_operations proc_mountinfo_operations;
+ extern const struct file_operations proc_mountstats_operations;
diff --git a/include/linux/splice.h b/include/linux/splice.h
index 00a2116..1f0a4a2 100644
--- a/include/linux/splice.h
+++ b/include/linux/splice.h
-@@ -86,4 +86,10 @@ extern ssize_t splice_direct_to_actor(struct file *, struct splice_desc *,
+@@ -86,4 +86,10 @@ extern void spd_release_page(struct splice_pipe_desc *, unsigned int);
extern const struct pipe_buf_operations page_cache_pipe_buf_ops;
extern const struct pipe_buf_operations default_pipe_buf_ops;
@@ -300,7 +357,7 @@ diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index 35b92d8..5b981db 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
-@@ -291,7 +291,10 @@ static int is_stack(struct proc_maps_private *priv,
+@@ -291,7 +291,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
const char *name = NULL;
if (file) {
@@ -492,7 +549,7 @@ index 1af87c1..95b0ff4 100644
unlink_anon_vmas(new);
out_free_mpol:
mpol_put(vma_policy(new));
-@@ -2703,7 +2703,7 @@ int vm_munmap(unsigned long start, size_t len)
+@@ -2703,7 +2703,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
struct vm_area_struct *vma;
unsigned long populate = 0;
unsigned long ret = -EINVAL;
@@ -501,7 +558,7 @@ index 1af87c1..95b0ff4 100644
pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n",
current->comm, current->pid);
-@@ -2778,10 +2778,27 @@ int vm_munmap(unsigned long start, size_t len)
+@@ -2778,10 +2778,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
}
}
@@ -585,18 +642,18 @@ index 8b8faaf..5d26ed94 100644
diff --git a/mm/prfile.c b/mm/prfile.c
new file mode 100644
-index 0000000..b323b8a
+index 0000000..86e01bd
--- /dev/null
+++ b/mm/prfile.c
-@@ -0,0 +1,86 @@
+@@ -0,0 +1,85 @@
+/*
-+ * Mainly for aufs which mmap(2) diffrent file and wants to print different path
-+ * in /proc/PID/maps.
++ * Mainly for aufs which mmap(2) different 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-2018 Junjro R. Okajima
+ * Copyright (c) 2014 Ian Campbell
+ */
+
@@ -610,8 +667,7 @@ index 0000000..b323b8a
+{
+#ifdef PRFILE_TRACE
+ if (pr)
-+ pr_info("%s:%d: %s, %s\n", func, line, func2,
-+ f ? (char *)f->f_path.dentry->d_name.name : "(null)");
++ pr_info("%s:%d: %s, %pD2\n", func, line, func2, f);
+#endif
+}
+
@@ -758,7 +814,7 @@ index ad17e05..ae9f267 100644
void __init files_init(void)
{
diff --git a/fs/inode.c b/fs/inode.c
-index 9a9ba3a..a3a18d8 100644
+index 9a9ba3a..a3a18d83 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -1651,6 +1651,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags)
@@ -770,7 +826,7 @@ index 9a9ba3a..a3a18d8 100644
/**
* touch_atime - update the access time
diff --git a/fs/namespace.c b/fs/namespace.c
-index e6c234b..8d13f7b 100644
+index db0b1ac..2d1e8ff 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -466,6 +466,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
@@ -781,7 +837,15 @@ index e6c234b..8d13f7b 100644
/**
* mnt_drop_write - give up write access to a mount
-@@ -1823,6 +1824,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
+@@ -792,6 +793,7 @@ int is_current_mnt_ns(struct vfsmount *mnt)
+ {
+ return check_mnt(real_mount(mnt));
+ }
++EXPORT_SYMBOL_GPL(is_current_mnt_ns);
+
+ /*
+ * vfsmount lock must be held for write
+@@ -1829,6 +1831,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
}
return 0;
}
@@ -921,11 +985,23 @@ index 28160a7..98c1902 100644
/**
* splice_direct_to_actor - splices data directly between two non-pipes
+diff --git a/fs/sync.c b/fs/sync.c
+index 7a5fa3f..c9b9d46 100644
+--- a/fs/sync.c
++++ b/fs/sync.c
+@@ -38,6 +38,7 @@ int __sync_filesystem(struct super_block *sb, int wait)
+ sb->s_op->sync_fs(sb, wait);
+ return __sync_blockdev(sb->s_bdev, wait);
+ }
++EXPORT_SYMBOL_GPL(__sync_filesystem);
+
+ /*
+ * Write out and wait upon all dirty data associated with this
diff --git a/fs/xattr.c b/fs/xattr.c
index 2d13b4e..41c2bcd 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
-@@ -296,6 +296,7 @@ int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
+@@ -296,6 +296,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
*xattr_value = value;
return error;
}
@@ -1067,7 +1143,7 @@ index f825304..8dd441d 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 2016-10-09 16:55:36.476034536 +0200
++++ linux/Documentation/ABI/testing/debugfs-aufs 2017-07-29 12:14:25.893041746 +0200
@@ -0,0 +1,50 @@
+What: /debug/aufs/si_<id>/
+Date: March 2009
@@ -1121,7 +1197,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 2016-10-09 16:55:36.476034536 +0200
++++ linux/Documentation/ABI/testing/sysfs-aufs 2017-07-29 12:14:25.893041746 +0200
@@ -0,0 +1,31 @@
+What: /sys/fs/aufs/si_<id>/
+Date: March 2009
@@ -1156,10 +1232,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 2016-10-09 16:55:36.479367956 +0200
-@@ -0,0 +1,170 @@
++++ linux/Documentation/filesystems/aufs/design/01intro.txt 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,171 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -1177,12 +1253,13 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/01intro.txt lin
+Introduction
+----------------------------------------
+
-+aufs [ei ju: ef es] | [a u f s]
++aufs [ei ju: ef es] | /ey-yoo-ef-es/ | [a u f s]
+1. abbrev. for "advanced multi-layered unification filesystem".
+2. abbrev. for "another unionfs".
+3. abbrev. for "auf das" in German which means "on the" in English.
+ Ex. "Butter aufs Brot"(G) means "butter onto bread"(E).
+ But "Filesystem aufs Filesystem" is hard to understand.
++4. abbrev. for "African Urban Fashion Show".
+
+AUFS is a filesystem with features:
+- multi layered stackable unification filesystem, the member directory
@@ -1330,10 +1407,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 2016-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/02struct.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,258 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -1592,10 +1669,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/02struct.txt li
+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-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/03atomic_open.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,85 @@
+
-+# Copyright (C) 2015-2016 Junjiro R. Okajima
++# Copyright (C) 2015-2018 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
@@ -1681,10 +1758,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03atomic_open.t
+ 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 2016-10-09 16:55:36.479367956 +0200
++++ linux/Documentation/filesystems/aufs/design/03lookup.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,113 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -1798,10 +1875,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/03lookup.txt li
+ 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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/04branch.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,74 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -1876,10 +1953,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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/05wbr_policy.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,64 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -1942,12 +2019,153 @@ 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/06dirren.dot linux/Documentation/filesystems/aufs/design/06dirren.dot
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.dot 1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06dirren.dot 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,31 @@
++
++// to view this graph, run dot(1) command in GRAPHVIZ.
++
++digraph G {
++node [shape=box];
++whinfo [label="detailed info file\n(lower_brid_root-hinum, h_inum, namelen, old name)"];
++
++node [shape=oval];
++
++aufs_rename -> whinfo [label="store/remove"];
++
++node [shape=oval];
++inode_list [label="h_inum list in branch\ncache"];
++
++node [shape=box];
++whinode [label="h_inum list file"];
++
++node [shape=oval];
++brmgmt [label="br_add/del/mod/umount"];
++
++brmgmt -> inode_list [label="create/remove"];
++brmgmt -> whinode [label="load/store"];
++
++inode_list -> whinode [style=dashed,dir=both];
++
++aufs_rename -> inode_list [label="add/del"];
++
++aufs_lookup -> inode_list [label="search"];
++
++aufs_lookup -> whinfo [label="load/remove"];
++}
+diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt linux/Documentation/filesystems/aufs/design/06dirren.txt
+--- /usr/share/empty/Documentation/filesystems/aufs/design/06dirren.txt 1970-01-01 01:00:00.000000000 +0100
++++ linux/Documentation/filesystems/aufs/design/06dirren.txt 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,102 @@
++
++# Copyright (C) 2017-2018 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/>.
++
++Special handling for renaming a directory (DIRREN)
++----------------------------------------------------------------------
++First, let's assume we have a simple usecase.
++
++- /u = /rw + /ro
++- /rw/dirA exists
++- /ro/dirA and /ro/dirA/file exist too
++- there is no dirB on both branches
++- a user issues rename("dirA", "dirB")
++
++Now, what should aufs behave against this rename(2)?
++There are a few possible cases.
++
++A. returns EROFS.
++ since dirA exists on a readonly branch which cannot be renamed.
++B. returns EXDEV.
++ it is possible to copy-up dirA (only the dir itself), but the child
++ entries ("file" in this case) should not be. it must be a bad
++ approach to copy-up recursively.
++C. returns a success.
++ even the branch /ro is readonly, aufs tries renaming it. Obviously it
++ is a violation of aufs' policy.
++D. construct an extra information which indicates that /ro/dirA should
++ be handled as the name of dirB.
++ overlayfs has a similar feature called REDIRECT.
++
++Until now, aufs implements the case B only which returns EXDEV, and
++expects the userspace application behaves like mv(1) which tries
++issueing rename(2) recursively.
++
++A new aufs feature called DIRREN is introduced which implements the case
++D. There are several "extra information" added.
++
++1. detailed info per renamed directory
++ path: /rw/dirB/$AUFS_WH_DR_INFO_PFX.<lower branch-id>
++2. the inode-number list of directories on a branch
++ path: /rw/dirB/$AUFS_WH_DR_BRHINO
++
++The filename of "detailed info per directory" represents the lower
++branch, and its format is
++- a type of the branch id
++ one of these.
++ + uuid (not implemented yet)
++ + fsid
++ + dev
++- the inode-number of the branch root dir
++
++And it contains these info in a single regular file.
++- magic number
++- branch's inode-number of the logically renamed dir
++- the name of the before-renamed dir
++
++The "detailed info per directory" file is created in aufs rename(2), and
++loaded in any lookup.
++The info is considered in lookup for the matching case only. Here
++"matching" means that the root of branch (in the info filename) is same
++to the current looking-up branch. After looking-up the before-renamed
++name, the inode-number is compared. And the matched dentry is used.
++
++The "inode-number list of directories" is a regular file which contains
++simply the inode-numbers on the branch. The file is created or updated
++in removing the branch, and loaded in adding the branch. Its lifetime is
++equal to the branch.
++The list is refered in lookup, and when the current target inode is
++found in the list, the aufs tries loading the "detailed info per
++directory" and get the changed and valid name of the dir.
++
++Theoretically these "extra informaiton" may be able to be put into XATTR
++in the dir inode. But aufs doesn't choose this way because
++1. XATTR may not be supported by the branch (or its configuration)
++2. XATTR may have its size limit.
++3. XATTR may be less easy to convert than a regular file, when the
++ format of the info is changed in the future.
++At the same time, I agree that the regular file approach is much slower
++than XATTR approach. So, in the future, aufs may take the XATTR or other
++better approach.
++
++This DIRREN feature is enabled by aufs configuration, and is activated
++by a new mount option.
++
++For the more complicated case, there is a work with UDBA option, which
++is to dected the direct access to the branches (by-passing aufs) and to
++maintain the cashes in aufs. Since a single cached aufs dentry may
++contains two names, before- and after-rename, the name comparision in
++UDBA handler may not work correctly. In this case, the behaviour will be
++equivalen to udba=reval case.
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-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06fhsm.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,120 @@
+
-+# Copyright (C) 2011-2016 Junjiro R. Okajima
++# Copyright (C) 2011-2018 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
@@ -2068,10 +2286,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06fhsm.txt linu
+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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06mmap.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,72 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -2144,10 +2362,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06mmap.txt linu
+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-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/06xattr.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,96 @@
+
-+# Copyright (C) 2014-2016 Junjiro R. Okajima
++# Copyright (C) 2014-2018 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
@@ -2244,10 +2462,10 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/06xattr.txt lin
+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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/07export.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,58 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -2306,10 +2524,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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/08shwh.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,52 @@
+
-+# Copyright (C) 2005-2016 Junjiro R. Okajima
++# Copyright (C) 2005-2018 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
@@ -2362,10 +2580,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 2016-10-09 16:55:36.482701377 +0200
++++ linux/Documentation/filesystems/aufs/design/10dynop.txt 2018-04-15 08:49:13.394483860 +0200
@@ -0,0 +1,47 @@
+
-+# Copyright (C) 2010-2016 Junjiro R. Okajima
++# Copyright (C) 2010-2018 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
@@ -2413,7 +2631,7 @@ diff -urN /usr/share/empty/Documentation/filesystems/aufs/design/10dynop.txt lin
+regular files only.
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 2016-12-17 12:28:17.595211562 +0100
++++ linux/Documentation/filesystems/aufs/README 2017-07-29 12:14:25.893041746 +0200
@@ -0,0 +1,393 @@
+
+Aufs4 -- advanced multi layered unification filesystem version 4.x
@@ -2810,10 +3028,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 2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,59 @@
++++ linux/fs/aufs/aufs.h 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,60 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -2854,15 +3072,16 @@ diff -urN /usr/share/empty/fs/aufs/aufs.h linux/fs/aufs/aufs.h
+#include "dbgaufs.h"
+#include "dentry.h"
+#include "dir.h"
++#include "dirren.h"
+#include "dynop.h"
+#include "file.h"
+#include "fstype.h"
++#include "hbl.h"
+#include "inode.h"
+#include "loop.h"
+#include "module.h"
+#include "opts.h"
+#include "rwsem.h"
-+#include "spl.h"
+#include "super.h"
+#include "sysaufs.h"
+#include "vfsub.h"
@@ -2873,10 +3092,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 2016-10-09 16:55:38.886097714 +0200
-@@ -0,0 +1,1412 @@
++++ linux/fs/aufs/branch.c 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,1432 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -2910,10 +3129,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ struct au_dykey **key;
+
+ au_hnotify_fin_br(br);
++ /* always, regardless the mount option */
++ au_dr_hino_free(&br->br_dirren);
+
+ if (br->br_xino.xi_file)
+ fput(br->br_xino.xi_file);
-+ mutex_destroy(&br->br_xino.xi_nondir_mtx);
++ for (i = br->br_xino.xi_nondir.total - 1; i >= 0; i--)
++ AuDebugOn(br->br_xino.xi_nondir.array[i]);
++ kfree(br->br_xino.xi_nondir.array);
+
+ AuDebugOn(au_br_count(br));
+ au_br_count_fin(br);
@@ -2928,7 +3151,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+
+ if (br->br_fhsm) {
+ au_br_fhsm_fin(br->br_fhsm);
-+ au_delayed_kfree(br->br_fhsm);
++ kfree(br->br_fhsm);
+ }
+
+ key = br->br_dykey;
@@ -2942,9 +3165,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ lockdep_off();
+ path_put(&br->br_path);
+ lockdep_on();
-+ if (wbr)
-+ au_delayed_kfree(wbr);
-+ au_delayed_kfree(br);
++ kfree(wbr);
++ kfree(br);
+}
+
+/*
@@ -3008,14 +3230,19 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ int err;
+
+ err = -ENOMEM;
-+ root = sb->s_root;
+ add_branch = kzalloc(sizeof(*add_branch), GFP_NOFS);
+ if (unlikely(!add_branch))
+ goto out;
++ add_branch->br_xino.xi_nondir.total = 8; /* initial size */
++ add_branch->br_xino.xi_nondir.array
++ = kcalloc(add_branch->br_xino.xi_nondir.total, sizeof(ino_t),
++ GFP_NOFS);
++ if (unlikely(!add_branch->br_xino.xi_nondir.array))
++ goto out_br;
+
+ err = au_hnotify_init_br(add_branch, perm);
+ if (unlikely(err))
-+ goto out_br;
++ goto out_xinondir;
+
+ if (au_br_writable(perm)) {
+ /* may be freed separately at changing the branch permission */
@@ -3031,23 +3258,26 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ goto out_wbr;
+ }
+
++ root = sb->s_root;
+ err = au_sbr_realloc(au_sbi(sb), new_nbranch, /*may_shrink*/0);
+ if (!err)
+ err = au_di_realloc(au_di(root), new_nbranch, /*may_shrink*/0);
+ if (!err) {
+ inode = d_inode(root);
-+ err = au_hinode_realloc(au_ii(inode), new_nbranch, /*may_shrink*/0);
++ err = au_hinode_realloc(au_ii(inode), new_nbranch,
++ /*may_shrink*/0);
+ }
+ if (!err)
+ return add_branch; /* success */
+
+out_wbr:
-+ if (add_branch->br_wbr)
-+ au_delayed_kfree(add_branch->br_wbr);
++ kfree(add_branch->br_wbr);
+out_hnotify:
+ au_hnotify_fin_br(add_branch);
++out_xinondir:
++ kfree(add_branch->br_xino.xi_nondir.array);
+out_br:
-+ au_delayed_kfree(add_branch);
++ kfree(add_branch);
+out:
+ return ERR_PTR(err);
+}
@@ -3213,7 +3443,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ br->br_perm = old_perm;
+
+ if (!err && wbr && !au_br_writable(new_perm)) {
-+ au_delayed_kfree(wbr);
++ kfree(wbr);
+ br->br_wbr = NULL;
+ }
+
@@ -3259,7 +3489,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ struct inode *h_inode;
+
+ err = 0;
-+ mutex_init(&br->br_xino.xi_nondir_mtx);
++ spin_lock_init(&br->br_xino.xi_nondir.spin);
++ init_waitqueue_head(&br->br_xino.xi_nondir.wqh);
+ br->br_perm = add->perm;
+ br->br_path = add->path; /* set first, path_get() later */
+ spin_lock_init(&br->br_dykey_lock);
@@ -3268,6 +3499,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ br->br_id = au_new_br_id(sb);
+ AuDebugOn(br->br_id < 0);
+
++ /* always, regardless the given option */
++ err = au_dr_br_init(sb, br, &add->path);
++ if (unlikely(err))
++ goto out_err;
++
+ if (au_br_writable(add->perm)) {
+ err = au_wbr_init(br, sb, add->perm);
+ if (unlikely(err))
@@ -3434,14 +3670,15 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+{
+ unsigned long long n;
+ struct file **p, *f;
-+ struct au_sphlhead *files;
++ struct hlist_bl_head *files;
++ struct hlist_bl_node *pos;
+ struct au_finfo *finfo;
+
+ n = 0;
+ p = a;
+ files = &au_sbi(sb)->si_files;
-+ spin_lock(&files->spin);
-+ hlist_for_each_entry(finfo, &files->head, fi_hlist) {
++ hlist_bl_lock(files);
++ hlist_bl_for_each_entry(finfo, pos, files, fi_hlist) {
+ f = finfo->fi_file;
+ if (file_count(f)
+ && !special_file(file_inode(f)->i_mode)) {
@@ -3451,7 +3688,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ AuDebugOn(n > max);
+ }
+ }
-+ spin_unlock(&files->spin);
++ hlist_bl_unlock(files);
+
+ return n;
+}
@@ -3854,6 +4091,9 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ au_br_do_del_hip(au_ii(inode), bindex, bbot);
+ au_sbilist_unlock();
+
++ /* ignore an error */
++ au_dr_br_fin(sb, br); /* always, regardless the mount option */
++
+ dput(h_root);
+ iput(h_inode);
+ au_br_do_free(br);
@@ -4239,7 +4479,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ if (br->br_wbr) {
+ err = au_wbr_init(br, sb, mod->perm);
+ if (unlikely(err)) {
-+ au_delayed_kfree(br->br_wbr);
++ kfree(br->br_wbr);
+ br->br_wbr = NULL;
+ }
+ }
@@ -4251,7 +4491,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ if (!au_br_fhsm(mod->perm)) {
+ /* fhsm --> non-fhsm */
+ au_br_fhsm_fin(br->br_fhsm);
-+ au_delayed_kfree(br->br_fhsm);
++ kfree(br->br_fhsm);
+ br->br_fhsm = NULL;
+ }
+ } else if (au_br_fhsm(mod->perm))
@@ -4263,8 +4503,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+ goto out; /* success */
+
+out_bf:
-+ if (bf)
-+ au_delayed_kfree(bf);
++ kfree(bf);
+out:
+ AuTraceErr(err);
+ return err;
@@ -4289,10 +4528,10 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
+}
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 2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,309 @@
++++ linux/fs/aufs/branch.h 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,324 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -4318,6 +4557,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
+#ifdef __KERNEL__
+
+#include <linux/mount.h>
++#include "dirren.h"
+#include "dynop.h"
+#include "rwsem.h"
+#include "super.h"
@@ -4327,7 +4567,14 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
+/* a xino file */
+struct au_xino_file {
+ struct file *xi_file;
-+ struct mutex xi_nondir_mtx;
++ struct {
++ spinlock_t spin;
++ ino_t *array;
++ int total;
++ /* reserved for future use */
++ /* unsigned long *bitmap; */
++ wait_queue_head_t wqh;
++ } xi_nondir;
+
+ /* todo: make xino files an array to support huge inode number */
+
@@ -4408,6 +4655,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
+ /* entries under sysfs per mount-point */
+ struct au_brsysfs br_sysfs[AuBrSysfs_Last];
+#endif
++
++ struct au_dr_br br_dirren;
+};
+
+/* ---------------------------------------------------------------------- */
@@ -4526,6 +4775,11 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
+struct file *au_xino_def(struct super_block *sb);
+int au_xino_path(struct seq_file *seq, struct file *file);
+
++void au_xinondir_leave(struct super_block *sb, aufs_bindex_t bindex,
++ ino_t h_ino, int idx);
++int au_xinondir_enter(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
++ int *idx);
++
+/* ---------------------------------------------------------------------- */
+
+/* Superblock to branch */
@@ -4602,8 +4856,8 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
+#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 2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,38 @@
++++ linux/fs/aufs/conf.mk 2018-04-15 08:49:13.394483860 +0200
+@@ -0,0 +1,39 @@
+
+AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
+
@@ -4620,6 +4874,7 @@ diff -urN /usr/share/empty/fs/aufs/conf.mk linux/fs/aufs/conf.mk
+ XATTR \
+ FHSM \
+ RDU \
++ DIRREN \
+ SHWH \
+ BR_RAMFS \
+ BR_FUSE POLL \
@@ -4644,10 +4899,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 2016-12-17 12:28:17.595211562 +0100
-@@ -0,0 +1,1394 @@
++++ linux/fs/aufs/cpup.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1414 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -5005,9 +5260,9 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ dst->f_pos = 0;
+ err = au_do_copy_file(dst, src, len, buf, blksize);
+ if (do_kfree)
-+ au_delayed_kfree(buf);
++ kfree(buf);
+ else
-+ au_delayed_free_page((unsigned long)buf);
++ free_page((unsigned long)buf);
+
+out:
+ return err;
@@ -5041,7 +5296,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ .label = &&out_src
+ }
+ };
-+ struct super_block *sb;
++ struct super_block *sb, *h_src_sb;
+ struct inode *h_src_inode;
+ struct task_struct *tsk = current;
+
@@ -5059,16 +5314,35 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+
+ /* try stopping to update while we copyup */
+ h_src_inode = d_inode(file[SRC].dentry);
-+ if (!au_test_nfs(h_src_inode->i_sb))
++ h_src_sb = h_src_inode->i_sb;
++ if (!au_test_nfs(h_src_sb))
+ IMustLock(h_src_inode);
-+ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
++
++ if (h_src_sb != file_inode(file[DST].file)->i_sb
++ || !file[DST].file->f_op->clone_file_range)
++ err = au_copy_file(file[DST].file, file[SRC].file, cpg->len);
++ else {
++ if (!au_test_nfs(h_src_sb)) {
++ inode_unlock_shared(h_src_inode);
++ err = vfsub_clone_file_range(file[SRC].file,
++ file[DST].file, cpg->len);
++ vfsub_inode_lock_shared_nested(h_src_inode,
++ AuLsc_I_CHILD);
++ } else
++ err = vfsub_clone_file_range(file[SRC].file,
++ file[DST].file, cpg->len);
++ if (unlikely(err == -EOPNOTSUPP && au_test_nfs(h_src_sb)))
++ /* the backend fs on NFS may not support cloning */
++ err = au_copy_file(file[DST].file, file[SRC].file,
++ cpg->len);
++ AuTraceErr(err);
++ }
+
+ /* i wonder if we had O_NO_DELAY_FPUT flag */
+ if (tsk->flags & PF_KTHREAD)
+ __fput_sync(file[DST].file);
+ else {
-+ WARN(1, "%pD\nPlease report this warning to aufs-users ML",
-+ file[DST].file);
++ /* it happend actually */
+ fput(file[DST].file);
+ /*
+ * too bad.
@@ -5102,7 +5376,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ cpg->len = l;
+ if (cpg->len) {
+ /* try stopping to update while we are referencing */
-+ inode_lock_nested(h_src_inode, AuLsc_I_CHILD);
++ vfsub_inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
+ au_pin_hdir_unlock(cpg->pin);
+
+ h_path.dentry = au_h_dptr(cpg->dentry, cpg->bsrc);
@@ -5111,20 +5385,21 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ if (!au_test_nfs(h_src_inode->i_sb))
+ err = vfs_getattr(&h_path, &h_src_attr->st);
+ else {
-+ inode_unlock(h_src_inode);
++ inode_unlock_shared(h_src_inode);
+ err = vfs_getattr(&h_path, &h_src_attr->st);
-+ inode_lock_nested(h_src_inode, AuLsc_I_CHILD);
++ vfsub_inode_lock_shared_nested(h_src_inode,
++ AuLsc_I_CHILD);
+ }
+ if (unlikely(err)) {
-+ inode_unlock(h_src_inode);
++ inode_unlock_shared(h_src_inode);
+ goto out;
+ }
+ h_src_attr->valid = 1;
+ if (!au_test_nfs(h_src_inode->i_sb)) {
+ err = au_cp_regular(cpg);
-+ inode_unlock(h_src_inode);
++ inode_unlock_shared(h_src_inode);
+ } else {
-+ inode_unlock(h_src_inode);
++ inode_unlock_shared(h_src_inode);
+ err = au_cp_regular(cpg);
+ }
+ rerr = au_pin_hdir_relock(cpg->pin);
@@ -5175,7 +5450,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ sym.k[symlen] = 0;
+ err = vfsub_symlink(h_dir, h_path, sym.k);
+ }
-+ au_delayed_free_page((unsigned long)sym.k);
++ free_page((unsigned long)sym.k);
+
+out:
+ return err;
@@ -5549,7 +5824,7 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
+ }
+out_parent:
+ dput(dst_parent);
-+ au_delayed_kfree(a);
++ kfree(a);
+out:
+ return err;
+}
@@ -6042,10 +6317,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 2016-10-09 16:55:36.486034798 +0200
-@@ -0,0 +1,94 @@
++++ linux/fs/aufs/cpup.h 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,99 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6105,6 +6380,11 @@ diff -urN /usr/share/empty/fs/aufs/cpup.h linux/fs/aufs/cpup.h
+#define AuCpup_RWDST (1 << 5) /* force write target even if
+ the branch is marked as RO */
+
++#ifndef CONFIG_AUFS_BR_HFSPLUS
++#undef AuCpup_HOPEN
++#define AuCpup_HOPEN 0
++#endif
++
+#define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name)
+#define au_fset_cpup(flags, name) \
+ do { (flags) |= AuCpup_##name; } while (0)
@@ -6140,10 +6420,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 2016-10-09 16:55:38.886097714 +0200
-@@ -0,0 +1,438 @@
++++ linux/fs/aufs/dbgaufs.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,437 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6185,7 +6465,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
+static int dbgaufs_xi_release(struct inode *inode __maybe_unused,
+ struct file *file)
+{
-+ au_delayed_kfree(file->private_data);
++ kfree(file->private_data);
+ return 0;
+}
+
@@ -6247,7 +6527,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
+static int dbgaufs_plink_release(struct inode *inode __maybe_unused,
+ struct file *file)
+{
-+ au_delayed_free_page((unsigned long)file->private_data);
++ free_page((unsigned long)file->private_data);
+ return 0;
+}
+
@@ -6258,7 +6538,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
+ struct dbgaufs_plink_arg *p;
+ struct au_sbinfo *sbinfo;
+ struct super_block *sb;
-+ struct au_sphlhead *sphl;
++ struct hlist_bl_head *hbl;
+
+ err = -ENOMEM;
+ p = (void *)get_zeroed_page(GFP_NOFS);
@@ -6278,10 +6558,9 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
+ limit -= n;
+
+ sum = 0;
-+ for (i = 0, sphl = sbinfo->si_plink;
-+ i < AuPlink_NHASH;
-+ i++, sphl++) {
-+ n = au_sphl_count(sphl);
++ for (i = 0, hbl = sbinfo->si_plink; i < AuPlink_NHASH;
++ i++, hbl++) {
++ n = au_hbl_count(hbl);
+ sum += n;
+
+ n = snprintf(p->a + p->n, limit, "%lu ", n);
@@ -6311,7 +6590,7 @@ diff -urN /usr/share/empty/fs/aufs/dbgaufs.c linux/fs/aufs/dbgaufs.c
+ goto out; /* success */
+
+out_free:
-+ au_delayed_free_page((unsigned long)p);
++ free_page((unsigned long)p);
+out:
+ return err;
+}
@@ -6582,10 +6861,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 2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/dbgaufs.h 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,48 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6634,10 +6913,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 2016-10-09 16:55:38.886097714 +0200
++++ linux/fs/aufs/dcsub.c 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,225 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -6667,7 +6946,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
+ p = dpage->dentries;
+ for (i = 0; i < dpage->ndentry; i++)
+ dput(*p++);
-+ au_delayed_free_page((unsigned long)dpage->dentries);
++ free_page((unsigned long)dpage->dentries);
+}
+
+int au_dpages_init(struct au_dcsub_pages *dpages, gfp_t gfp)
@@ -6690,7 +6969,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
+ return 0; /* success */
+
+out_dpages:
-+ au_delayed_kfree(dpages->dpages);
++ kfree(dpages->dpages);
+out:
+ return err;
+}
@@ -6703,7 +6982,7 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.c linux/fs/aufs/dcsub.c
+ p = dpages->dpages;
+ for (i = 0; i < dpages->ndpage; i++)
+ au_dpage_free(p++);
-+ au_delayed_kfree(dpages->dpages);
++ kfree(dpages->dpages);
+}
+
+static int au_dpages_append(struct au_dcsub_pages *dpages,
@@ -6863,10 +7142,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 2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/dcsub.h 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,136 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7003,10 +7282,10 @@ diff -urN /usr/share/empty/fs/aufs/dcsub.h linux/fs/aufs/dcsub.h
+#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 2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/debug.c 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,440 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7343,7 +7622,7 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
+ au_br_count_init(&a->fake);
+ err = do_pri_br(-1, &a->fake);
+ au_br_count_fin(&a->fake);
-+ au_delayed_kfree(a);
++ kfree(a);
+ dpri("dev 0x%x\n", sb->s_dev);
+ if (err || !au_test_aufs(sb))
+ return;
@@ -7447,10 +7726,10 @@ diff -urN /usr/share/empty/fs/aufs/debug.c linux/fs/aufs/debug.c
+}
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 2016-10-09 16:55:36.486034798 +0200
++++ linux/fs/aufs/debug.h 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,225 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7676,10 +7955,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 2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,1130 @@
++++ linux/fs/aufs/dentry.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1152 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -7702,19 +7981,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+#include <linux/namei.h>
+#include "aufs.h"
+
-+struct au_do_lookup_args {
-+ unsigned int flags;
-+ mode_t type;
-+};
-+
+/*
+ * returns positive/negative dentry, NULL or an error.
+ * NULL means whiteout-ed or not-found.
+ */
+static struct dentry*
+au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
-+ aufs_bindex_t bindex, struct qstr *wh_name,
-+ struct au_do_lookup_args *args)
++ aufs_bindex_t bindex, struct au_do_lookup_args *args)
+{
+ struct dentry *h_dentry;
+ struct inode *h_inode;
@@ -7729,7 +8002,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ 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, ignore_perm);
++ wh_found = au_wh_test(h_parent, &args->whname, ignore_perm);
+ h_dentry = ERR_PTR(wh_found);
+ if (!wh_found)
+ goto real_lookup;
@@ -7744,9 +8017,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+
+real_lookup:
+ if (!ignore_perm)
-+ h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
++ h_dentry = vfsub_lkup_one(args->name, h_parent);
+ else
-+ h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
++ h_dentry = au_sio_lkup_one(args->name, h_parent);
+ if (IS_ERR(h_dentry)) {
+ if (PTR_ERR(h_dentry) == -ENAMETOOLONG
+ && !allow_neg)
@@ -7761,6 +8034,13 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ } else if (wh_found
+ || (args->type && args->type != (h_inode->i_mode & S_IFMT)))
+ goto out_neg;
++ else if (au_ftest_lkup(args->flags, DIRREN)
++ /* && h_inode */
++ && !au_dr_lkup_h_ino(args, bindex, h_inode->i_ino)) {
++ AuDbg("b%d %pd ignored hi%llu\n", bindex, h_dentry,
++ (unsigned long long)h_inode->i_ino);
++ goto out_neg;
++ }
+
+ if (au_dbbot(dentry) <= bindex)
+ au_set_dbbot(dentry, bindex);
@@ -7773,9 +8053,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ || (d_really_is_positive(dentry) && !d_is_dir(dentry)))
+ goto out; /* success */
+
-+ inode_lock_nested(h_inode, AuLsc_I_CHILD);
++ vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
+ opq = au_diropq_test(h_dentry);
-+ inode_unlock(h_inode);
++ inode_unlock_shared(h_inode);
+ if (opq > 0)
+ au_set_dbdiropq(dentry, bindex);
+ else if (unlikely(opq < 0)) {
@@ -7809,26 +8089,28 @@ 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, dirperm1;
-+ struct qstr whname;
++ unsigned char isdir, dirperm1, dirren;
+ struct au_do_lookup_args args = {
-+ .flags = flags
++ .flags = flags,
++ .name = &dentry->d_name
+ };
-+ const struct qstr *name = &dentry->d_name;
+ struct dentry *parent;
+ struct super_block *sb;
+
+ sb = dentry->d_sb;
-+ err = au_test_shwh(sb, name);
++ err = au_test_shwh(sb, args.name);
+ if (unlikely(err))
+ goto out;
+
-+ err = au_wh_name_alloc(&whname, name);
++ err = au_wh_name_alloc(&args.whname, args.name);
+ if (unlikely(err))
+ goto out;
+
+ isdir = !!d_is_dir(dentry);
+ dirperm1 = !!au_opt_test(au_mntflags(sb), DIRPERM1);
++ dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
++ if (dirren)
++ au_fset_lkup(args.flags, DIRREN);
+
+ npositive = 0;
+ parent = dget_parent(dentry);
@@ -7836,6 +8118,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ for (bindex = btop; bindex <= btail; bindex++) {
+ struct dentry *h_parent, *h_dentry;
+ struct inode *h_inode, *h_dir;
++ struct au_branch *br;
+
+ h_dentry = au_h_dptr(dentry, bindex);
+ if (h_dentry) {
@@ -7847,11 +8130,17 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ if (!h_parent || !d_is_dir(h_parent))
+ continue;
+
++ if (dirren) {
++ /* if the inum matches, then use the prepared name */
++ err = au_dr_lkup_name(&args, bindex);
++ if (unlikely(err))
++ goto out_parent;
++ }
++
+ h_dir = d_inode(h_parent);
-+ inode_lock_nested(h_dir, AuLsc_I_PARENT);
-+ h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
-+ &args);
-+ inode_unlock(h_dir);
++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
++ h_dentry = au_do_lookup(h_parent, dentry, bindex, &args);
++ inode_unlock_shared(h_dir);
+ err = PTR_ERR(h_dentry);
+ if (IS_ERR(h_dentry))
+ goto out_parent;
@@ -7878,6 +8167,15 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ if (bdiropq >= 0 && bdiropq <= bindex)
+ break;
+ }
++ br = au_sbr(sb, bindex);
++ if (dirren
++ && au_dr_hino_test_add(&br->br_dirren, h_inode->i_ino,
++ /*add_ent*/NULL)) {
++ /* prepare next name to lookup */
++ err = au_dr_lkup(&args, dentry, bindex);
++ if (unlikely(err))
++ goto out_parent;
++ }
+ }
+
+ if (npositive) {
@@ -7894,7 +8192,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+
+out_parent:
+ dput(parent);
-+ au_delayed_kfree(whname.name);
++ kfree(args.whname.name);
++ if (dirren)
++ au_dr_lkup_fin(&args);
+out:
+ return err;
+}
@@ -8503,7 +8803,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+
+/* todo: remove this */
+static int h_d_revalidate(struct dentry *dentry, struct inode *inode,
-+ unsigned int flags, int do_udba)
++ unsigned int flags, int do_udba, int dirren)
+{
+ int err;
+ umode_t mode, h_mode;
@@ -8554,7 +8854,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ && !is_root
+ && ((!h_nfs
+ && (unhashed != !!d_unhashed(h_dentry)
-+ || (!tmpfile
++ || (!tmpfile && !dirren
+ && !au_qstreq(name, h_name))
+ ))
+ || (h_nfs
@@ -8695,7 +8995,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+{
+ int valid, err;
+ unsigned int sigen;
-+ unsigned char do_udba;
++ unsigned char do_udba, dirren;
+ struct super_block *sb;
+ struct inode *inode;
+
@@ -8768,7 +9068,8 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
+ }
+ }
+
-+ err = h_d_revalidate(dentry, inode, flags, do_udba);
++ dirren = !!au_opt_test(au_mntflags(sb), DIRREN);
++ err = h_d_revalidate(dentry, inode, flags, do_udba, dirren);
+ if (unlikely(!err && do_udba && au_dbtop(dentry) < 0)) {
+ err = -EIO;
+ AuDbg("both of real entry and whiteout found, %p, err %d\n",
@@ -8810,10 +9111,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 2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,255 @@
++++ linux/fs/aufs/dentry.h 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,266 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -8839,6 +9140,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
+#ifdef __KERNEL__
+
+#include <linux/dcache.h>
++#include "dirren.h"
+#include "rwsem.h"
+
+struct au_hdentry {
@@ -8852,10 +9154,7 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
+ struct au_rwsem di_rwsem;
+ aufs_bindex_t di_btop, di_bbot, di_bwh, di_bdiropq;
+ unsigned char di_tmpfile; /* to allow the different name */
-+ union {
-+ struct au_hdentry *di_hdentry;
-+ struct llist_node di_lnode; /* delayed free */
-+ };
++ struct au_hdentry *di_hdentry;
+} ____cacheline_aligned_in_smp;
+
+/* ---------------------------------------------------------------------- */
@@ -8863,12 +9162,25 @@ diff -urN /usr/share/empty/fs/aufs/dentry.h linux/fs/aufs/dentry.h
+/* flags for au_lkup_dentry() */
+#define AuLkup_ALLOW_NEG 1
+#define AuLkup_IGNORE_PERM (1 << 1)
++#define AuLkup_DIRREN (1 << 2)
+#define au_ftest_lkup(flags, name) ((flags) & AuLkup_##name)
+#define au_fset_lkup(flags, name) \
+ do { (flags) |= AuLkup_##name; } while (0)
+#define au_fclr_lkup(flags, name) \
+ do { (flags) &= ~AuLkup_##name; } while (0)
+
++#ifndef CONFIG_AUFS_DIRREN
++#undef AuLkup_DIRREN
++#define AuLkup_DIRREN 0
++#endif
++
++struct au_do_lookup_args {
++ unsigned int flags;
++ mode_t type;
++ struct qstr whname, *name;
++ struct au_dr_lookup dirren;
++};
++
+/* ---------------------------------------------------------------------- */
+
+/* dentry.c */
@@ -9069,10 +9381,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 2016-10-09 16:55:38.889431135 +0200
++++ linux/fs/aufs/dinfo.c 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,553 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -9126,7 +9438,7 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
+ goto out;
+ }
+
-+ au_cache_dfree_dinfo(dinfo);
++ au_cache_free_dinfo(dinfo);
+ dinfo = NULL;
+
+out:
@@ -9146,8 +9458,8 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
+ while (bindex++ <= bbot)
+ au_hdput(p++);
+ }
-+ au_delayed_kfree(dinfo->di_hdentry);
-+ au_cache_dfree_dinfo(dinfo);
++ kfree(dinfo->di_hdentry);
++ au_cache_free_dinfo(dinfo);
+}
+
+void au_di_swap(struct au_dinfo *a, struct au_dinfo *b)
@@ -9349,11 +9661,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
+ || d_inode(d1) == d_inode(d2)
+ || d1->d_sb != d2->d_sb);
+
-+ if (isdir && au_test_subdir(d1, d2)) {
++ if ((isdir && au_test_subdir(d1, d2))
++ || d1 < d2) {
+ di_write_lock_child(d1);
+ di_write_lock_child2(d2);
+ } else {
-+ /* there should be no races */
+ di_write_lock_child(d2);
+ di_write_lock_child2(d1);
+ }
@@ -9365,11 +9677,11 @@ diff -urN /usr/share/empty/fs/aufs/dinfo.c linux/fs/aufs/dinfo.c
+ || d_inode(d1) == d_inode(d2)
+ || d1->d_sb != d2->d_sb);
+
-+ if (isdir && au_test_subdir(d1, d2)) {
++ if ((isdir && au_test_subdir(d1, d2))
++ || d1 < d2) {
+ di_write_lock_parent(d1);
+ di_write_lock_parent2(d2);
+ } else {
-+ /* there should be no races */
+ di_write_lock_parent(d2);
+ di_write_lock_parent2(d1);
+ }
@@ -9626,10 +9938,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 2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,762 @@
++++ linux/fs/aufs/dir.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,759 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -9786,7 +10098,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+out:
+ dput(a->dentry);
+ au_nwt_done(&au_sbi(sb)->si_nowait);
-+ au_delayed_kfree(arg);
++ kfree(arg);
+}
+
+void au_dir_ts(struct inode *dir, aufs_bindex_t bindex)
@@ -9822,7 +10134,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ if (unlikely(wkq_err)) {
+ pr_err("wkq %d\n", wkq_err);
+ dput(dentry);
-+ au_delayed_kfree(arg);
++ kfree(arg);
+ }
+
+out:
@@ -9941,7 +10253,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ };
+ err = au_do_open(file, &args);
+ if (unlikely(err))
-+ au_delayed_kfree(fidir);
++ kfree(fidir);
+ }
+ si_read_unlock(sb);
+ return err;
@@ -9955,21 +10267,18 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ struct au_fidir *fidir;
+ struct au_hfile *hf;
+ aufs_bindex_t bindex, bbot;
-+ int execed, delayed;
+
-+ delayed = (current->flags & PF_KTHREAD) || in_interrupt();
+ finfo = au_fi(file);
+ fidir = finfo->fi_hdir;
+ if (fidir) {
-+ au_sphl_del(&finfo->fi_hlist,
-+ &au_sbi(file->f_path.dentry->d_sb)->si_files);
++ au_hbl_del(&finfo->fi_hlist,
++ &au_sbi(file->f_path.dentry->d_sb)->si_files);
+ vdir_cache = fidir->fd_vdir_cache; /* lock-free */
+ if (vdir_cache)
-+ au_vdir_free(vdir_cache, delayed);
++ au_vdir_free(vdir_cache);
+
+ bindex = finfo->fi_btop;
+ if (bindex >= 0) {
-+ execed = vfsub_file_execed(file);
+ hf = fidir->fd_hfile + bindex;
+ /*
+ * calls fput() instead of filp_close(),
@@ -9978,12 +10287,12 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ bbot = fidir->fd_bbot;
+ for (; bindex <= bbot; bindex++, hf++)
+ if (hf->hf_file)
-+ au_hfput(hf, execed);
++ au_hfput(hf, /*execed*/0);
+ }
-+ au_delayed_kfree(fidir);
++ kfree(fidir);
+ finfo->fi_hdir = NULL;
+ }
-+ au_finfo_fin(file, delayed);
++ au_finfo_fin(file);
+ return 0;
+}
+
@@ -10048,7 +10357,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ struct super_block *sb;
+ struct inode *inode;
+
-+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
+ if (unlikely(err))
+ goto out;
+
@@ -10117,7 +10426,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+
+ sb = dentry->d_sb;
+ si_read_lock(sb, AuLock_FLUSH);
-+ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1);
++ err = au_reval_and_lock_fdi(file, reopen_dir, /*wlock*/1, /*fi_lsc*/0);
+ if (unlikely(err))
+ goto out;
+ err = au_alive_dir(dentry);
@@ -10270,9 +10579,9 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+ h_dentry = au_h_dptr(dentry, arg->bindex);
+ h_inode = d_inode(h_dentry);
+ /* todo: i_mode changes anytime? */
-+ inode_lock_nested(h_inode, AuLsc_I_CHILD);
++ vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
+ err = au_test_h_perm_sio(h_inode, MAY_EXEC | MAY_READ);
-+ inode_unlock(h_inode);
++ inode_unlock_shared(h_inode);
+ if (!err)
+ err = do_test_empty(dentry, arg);
+ else {
@@ -10392,10 +10701,10 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
+};
diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
--- /usr/share/empty/fs/aufs/dir.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dir.h 2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,137 @@
++++ linux/fs/aufs/dir.h 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,131 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -10438,10 +10747,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
+
+struct au_vdir_dehstr {
+ struct hlist_node hash;
-+ union {
-+ struct au_vdir_destr *str;
-+ struct llist_node lnode; /* delayed free */
-+ };
++ struct au_vdir_destr *str;
+} ____cacheline_aligned_in_smp;
+
+struct au_vdir_de {
@@ -10479,10 +10785,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
+
+ unsigned long vd_version;
+ unsigned int vd_deblk_sz;
-+ union {
-+ unsigned long vd_jiffy;
-+ struct llist_node vd_lnode; /* delayed free */
-+ };
++ unsigned long vd_jiffy;
+} ____cacheline_aligned_in_smp;
+
+/* ---------------------------------------------------------------------- */
@@ -10506,7 +10809,7 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
+int au_nhash_append_wh(struct au_nhash *whlist, char *name, int nlen, ino_t ino,
+ unsigned int d_type, aufs_bindex_t bindex,
+ unsigned char shwh);
-+void au_vdir_free(struct au_vdir *vdir, int atonce);
++void au_vdir_free(struct au_vdir *vdir);
+int au_vdir_init(struct file *file);
+int au_vdir_fill_de(struct file *file, struct dir_context *ctx);
+
@@ -10531,12 +10834,1479 @@ diff -urN /usr/share/empty/fs/aufs/dir.h linux/fs/aufs/dir.h
+
+#endif /* __KERNEL__ */
+#endif /* __AUFS_DIR_H__ */
+diff -urN /usr/share/empty/fs/aufs/dirren.c linux/fs/aufs/dirren.c
+--- /usr/share/empty/fs/aufs/dirren.c 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/aufs/dirren.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,1314 @@
++/*
++ * Copyright (C) 2017-2018 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * special handling in renaming a directoy
++ * in order to support looking-up the before-renamed name on the lower readonly
++ * branches
++ */
++
++#include <linux/byteorder/generic.h>
++#include "aufs.h"
++
++static void au_dr_hino_del(struct au_dr_br *dr, struct au_dr_hino *ent)
++{
++ int idx;
++
++ idx = au_dr_ihash(ent->dr_h_ino);
++ au_hbl_del(&ent->dr_hnode, dr->dr_h_ino + idx);
++}
++
++static int au_dr_hino_test_empty(struct au_dr_br *dr)
++{
++ int ret, i;
++ struct hlist_bl_head *hbl;
++
++ ret = 1;
++ for (i = 0; ret && i < AuDirren_NHASH; i++) {
++ hbl = dr->dr_h_ino + i;
++ hlist_bl_lock(hbl);
++ ret &= hlist_bl_empty(hbl);
++ hlist_bl_unlock(hbl);
++ }
++
++ return ret;
++}
++
++static struct au_dr_hino *au_dr_hino_find(struct au_dr_br *dr, ino_t ino)
++{
++ struct au_dr_hino *found, *ent;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *pos;
++ int idx;
++
++ found = NULL;
++ idx = au_dr_ihash(ino);
++ hbl = dr->dr_h_ino + idx;
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
++ if (ent->dr_h_ino == ino) {
++ found = ent;
++ break;
++ }
++ hlist_bl_unlock(hbl);
++
++ return found;
++}
++
++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t ino,
++ struct au_dr_hino *add_ent)
++{
++ int found, idx;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *pos;
++ struct au_dr_hino *ent;
++
++ found = 0;
++ idx = au_dr_ihash(ino);
++ hbl = dr->dr_h_ino + idx;
++#if 0
++ {
++ struct hlist_bl_node *tmp;
++
++ hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
++ AuDbg("hi%llu\n", (unsigned long long)ent->dr_h_ino);
++ }
++#endif
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(ent, pos, hbl, dr_hnode)
++ if (ent->dr_h_ino == ino) {
++ found = 1;
++ break;
++ }
++ if (!found && add_ent)
++ hlist_bl_add_head(&add_ent->dr_hnode, hbl);
++ hlist_bl_unlock(hbl);
++
++ if (!found && add_ent)
++ AuDbg("i%llu added\n", (unsigned long long)add_ent->dr_h_ino);
++
++ return found;
++}
++
++void au_dr_hino_free(struct au_dr_br *dr)
++{
++ int i;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *pos, *tmp;
++ struct au_dr_hino *ent;
++
++ /* SiMustWriteLock(sb); */
++
++ for (i = 0; i < AuDirren_NHASH; i++) {
++ hbl = dr->dr_h_ino + i;
++ /* no spinlock since sbinfo must be write-locked */
++ hlist_bl_for_each_entry_safe(ent, pos, tmp, hbl, dr_hnode)
++ kfree(ent);
++ INIT_HLIST_BL_HEAD(hbl);
++ }
++}
++
++/* returns the number of inodes or an error */
++static int au_dr_hino_store(struct super_block *sb, struct au_branch *br,
++ struct file *hinofile)
++{
++ int err, i;
++ ssize_t ssz;
++ loff_t pos, oldsize;
++ uint64_t u64;
++ struct inode *hinoinode;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *n1, *n2;
++ struct au_dr_hino *ent;
++
++ SiMustWriteLock(sb);
++ AuDebugOn(!au_br_writable(br->br_perm));
++
++ hinoinode = file_inode(hinofile);
++ oldsize = i_size_read(hinoinode);
++
++ err = 0;
++ pos = 0;
++ hbl = br->br_dirren.dr_h_ino;
++ for (i = 0; !err && i < AuDirren_NHASH; i++, hbl++) {
++ /* no bit-lock since sbinfo must be write-locked */
++ hlist_bl_for_each_entry_safe(ent, n1, n2, hbl, dr_hnode) {
++ AuDbg("hi%llu, %pD2\n",
++ (unsigned long long)ent->dr_h_ino, hinofile);
++ u64 = cpu_to_be64(ent->dr_h_ino);
++ ssz = vfsub_write_k(hinofile, &u64, sizeof(u64), &pos);
++ if (ssz == sizeof(u64))
++ continue;
++
++ /* write error */
++ pr_err("ssz %zd, %pD2\n", ssz, hinofile);
++ err = -ENOSPC;
++ if (ssz < 0)
++ err = ssz;
++ break;
++ }
++ }
++ /* regardless the error */
++ if (pos < oldsize) {
++ err = vfsub_trunc(&hinofile->f_path, pos, /*attr*/0, hinofile);
++ AuTraceErr(err);
++ }
++
++ AuTraceErr(err);
++ return err;
++}
++
++static int au_dr_hino_load(struct au_dr_br *dr, struct file *hinofile)
++{
++ int err, hidx;
++ ssize_t ssz;
++ size_t sz, n;
++ loff_t pos;
++ uint64_t u64;
++ struct au_dr_hino *ent;
++ struct inode *hinoinode;
++ struct hlist_bl_head *hbl;
++
++ err = 0;
++ pos = 0;
++ hbl = dr->dr_h_ino;
++ hinoinode = file_inode(hinofile);
++ sz = i_size_read(hinoinode);
++ AuDebugOn(sz % sizeof(u64));
++ n = sz / sizeof(u64);
++ while (n--) {
++ ssz = vfsub_read_k(hinofile, &u64, sizeof(u64), &pos);
++ if (unlikely(ssz != sizeof(u64))) {
++ pr_err("ssz %zd, %pD2\n", ssz, hinofile);
++ err = -EINVAL;
++ if (ssz < 0)
++ err = ssz;
++ goto out_free;
++ }
++
++ ent = kmalloc(sizeof(*ent), GFP_NOFS);
++ if (!ent) {
++ err = -ENOMEM;
++ AuTraceErr(err);
++ goto out_free;
++ }
++ ent->dr_h_ino = be64_to_cpu(u64);
++ AuDbg("hi%llu, %pD2\n",
++ (unsigned long long)ent->dr_h_ino, hinofile);
++ hidx = au_dr_ihash(ent->dr_h_ino);
++ au_hbl_add(&ent->dr_hnode, hbl + hidx);
++ }
++ goto out; /* success */
++
++out_free:
++ au_dr_hino_free(dr);
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++/*
++ * @bindex/@br is a switch to distinguish whether suspending hnotify or not.
++ * @path is a switch to distinguish load and store.
++ */
++static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex,
++ struct au_branch *br, const struct path *path)
++{
++ int err, flags;
++ unsigned char load, suspend;
++ struct file *hinofile;
++ struct au_hinode *hdir;
++ struct inode *dir, *delegated;
++ struct path hinopath;
++ struct qstr hinoname = QSTR_INIT(AUFS_WH_DR_BRHINO,
++ sizeof(AUFS_WH_DR_BRHINO) - 1);
++
++ AuDebugOn(bindex < 0 && !br);
++ AuDebugOn(bindex >= 0 && br);
++
++ err = -EINVAL;
++ suspend = !br;
++ if (suspend)
++ br = au_sbr(sb, bindex);
++ load = !!path;
++ if (!load) {
++ path = &br->br_path;
++ AuDebugOn(!au_br_writable(br->br_perm));
++ if (unlikely(!au_br_writable(br->br_perm)))
++ goto out;
++ }
++
++ hdir = NULL;
++ if (suspend) {
++ dir = d_inode(sb->s_root);
++ hdir = au_hinode(au_ii(dir), bindex);
++ dir = hdir->hi_inode;
++ au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD);
++ } else {
++ dir = d_inode(path->dentry);
++ inode_lock_nested(dir, AuLsc_I_CHILD);
++ }
++ hinopath.dentry = vfsub_lkup_one(&hinoname, path->dentry);
++ err = PTR_ERR(hinopath.dentry);
++ if (IS_ERR(hinopath.dentry))
++ goto out_unlock;
++
++ err = 0;
++ flags = O_RDONLY;
++ if (load) {
++ if (d_is_negative(hinopath.dentry))
++ goto out_dput; /* success */
++ } else {
++ if (au_dr_hino_test_empty(&br->br_dirren)) {
++ if (d_is_positive(hinopath.dentry)) {
++ delegated = NULL;
++ err = vfsub_unlink(dir, &hinopath, &delegated,
++ /*force*/0);
++ AuTraceErr(err);
++ if (unlikely(err))
++ pr_err("ignored err %d, %pd2\n",
++ err, hinopath.dentry);
++ if (unlikely(err == -EWOULDBLOCK))
++ iput(delegated);
++ err = 0;
++ }
++ goto out_dput;
++ } else if (!d_is_positive(hinopath.dentry)) {
++ err = vfsub_create(dir, &hinopath, 0600,
++ /*want_excl*/false);
++ AuTraceErr(err);
++ if (unlikely(err))
++ goto out_dput;
++ }
++ flags = O_WRONLY;
++ }
++ hinopath.mnt = path->mnt;
++ hinofile = vfsub_dentry_open(&hinopath, flags);
++ if (suspend)
++ au_hn_inode_unlock(hdir);
++ else
++ inode_unlock(dir);
++ dput(hinopath.dentry);
++ AuTraceErrPtr(hinofile);
++ if (IS_ERR(hinofile)) {
++ err = PTR_ERR(hinofile);
++ goto out;
++ }
++
++ if (load)
++ err = au_dr_hino_load(&br->br_dirren, hinofile);
++ else
++ err = au_dr_hino_store(sb, br, hinofile);
++ fput(hinofile);
++ goto out;
++
++out_dput:
++ dput(hinopath.dentry);
++out_unlock:
++ if (suspend)
++ au_hn_inode_unlock(hdir);
++ else
++ inode_unlock(dir);
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_dr_brid_init(struct au_dr_brid *brid, const struct path *path)
++{
++ int err;
++ struct kstatfs kstfs;
++ dev_t dev;
++ struct dentry *dentry;
++ struct super_block *sb;
++
++ err = vfs_statfs((void *)path, &kstfs);
++ AuTraceErr(err);
++ if (unlikely(err))
++ goto out;
++
++ /* todo: support for UUID */
++
++ if (kstfs.f_fsid.val[0] || kstfs.f_fsid.val[1]) {
++ brid->type = AuBrid_FSID;
++ brid->fsid = kstfs.f_fsid;
++ } else {
++ dentry = path->dentry;
++ sb = dentry->d_sb;
++ dev = sb->s_dev;
++ if (dev) {
++ brid->type = AuBrid_DEV;
++ brid->dev = dev;
++ }
++ }
++
++out:
++ return err;
++}
++
++int au_dr_br_init(struct super_block *sb, struct au_branch *br,
++ const struct path *path)
++{
++ int err, i;
++ struct au_dr_br *dr;
++ struct hlist_bl_head *hbl;
++
++ dr = &br->br_dirren;
++ hbl = dr->dr_h_ino;
++ for (i = 0; i < AuDirren_NHASH; i++, hbl++)
++ INIT_HLIST_BL_HEAD(hbl);
++
++ err = au_dr_brid_init(&dr->dr_brid, path);
++ if (unlikely(err))
++ goto out;
++
++ if (au_opt_test(au_mntflags(sb), DIRREN))
++ err = au_dr_hino(sb, /*bindex*/-1, br, path);
++
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++int au_dr_br_fin(struct super_block *sb, struct au_branch *br)
++{
++ int err;
++
++ err = 0;
++ if (au_br_writable(br->br_perm))
++ err = au_dr_hino(sb, /*bindex*/-1, br, /*path*/NULL);
++ if (!err)
++ au_dr_hino_free(&br->br_dirren);
++
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static int au_brid_str(struct au_dr_brid *brid, struct inode *h_inode,
++ char *buf, size_t sz)
++{
++ int err;
++ unsigned int major, minor;
++ char *p;
++
++ p = buf;
++ err = snprintf(p, sz, "%d_", brid->type);
++ AuDebugOn(err > sz);
++ p += err;
++ sz -= err;
++ switch (brid->type) {
++ case AuBrid_Unset:
++ return -EINVAL;
++ case AuBrid_UUID:
++ err = snprintf(p, sz, "%pU", brid->uuid.__u_bits);
++ break;
++ case AuBrid_FSID:
++ err = snprintf(p, sz, "%08x-%08x",
++ brid->fsid.val[0], brid->fsid.val[1]);
++ break;
++ case AuBrid_DEV:
++ major = MAJOR(brid->dev);
++ minor = MINOR(brid->dev);
++ if (major <= 0xff && minor <= 0xff)
++ err = snprintf(p, sz, "%02x%02x", major, minor);
++ else
++ err = snprintf(p, sz, "%03x:%05x", major, minor);
++ break;
++ }
++ AuDebugOn(err > sz);
++ p += err;
++ sz -= err;
++ err = snprintf(p, sz, "_%llu", (unsigned long long)h_inode->i_ino);
++ AuDebugOn(err > sz);
++ p += err;
++ sz -= err;
++
++ return p - buf;
++}
++
++static int au_drinfo_name(struct au_branch *br, char *name, int len)
++{
++ int rlen;
++ struct dentry *br_dentry;
++ struct inode *br_inode;
++
++ br_dentry = au_br_dentry(br);
++ br_inode = d_inode(br_dentry);
++ rlen = au_brid_str(&br->br_dirren.dr_brid, br_inode, name, len);
++ AuDebugOn(rlen >= AUFS_DIRREN_ENV_VAL_SZ);
++ AuDebugOn(rlen > len);
++
++ return rlen;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/*
++ * from the given @h_dentry, construct drinfo at @*fdata.
++ * when the size of @*fdata is not enough, reallocate and return new @fdata and
++ * @allocated.
++ */
++static int au_drinfo_construct(struct au_drinfo_fdata **fdata,
++ struct dentry *h_dentry,
++ unsigned char *allocated)
++{
++ int err, v;
++ struct au_drinfo_fdata *f, *p;
++ struct au_drinfo *drinfo;
++ struct inode *h_inode;
++ struct qstr *qname;
++
++ err = 0;
++ f = *fdata;
++ h_inode = d_inode(h_dentry);
++ qname = &h_dentry->d_name;
++ drinfo = &f->drinfo;
++ drinfo->ino = cpu_to_be64(h_inode->i_ino);
++ drinfo->oldnamelen = qname->len;
++ if (*allocated < sizeof(*f) + qname->len) {
++ v = roundup_pow_of_two(*allocated + qname->len);
++ p = au_krealloc(f, v, GFP_NOFS, /*may_shrink*/0);
++ if (unlikely(!p)) {
++ err = -ENOMEM;
++ AuTraceErr(err);
++ goto out;
++ }
++ f = p;
++ *fdata = f;
++ *allocated = v;
++ drinfo = &f->drinfo;
++ }
++ memcpy(drinfo->oldname, qname->name, qname->len);
++ AuDbg("i%llu, %.*s\n",
++ be64_to_cpu(drinfo->ino), drinfo->oldnamelen, drinfo->oldname);
++
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++/* callers have to free the return value */
++static struct au_drinfo *au_drinfo_read_k(struct file *file, ino_t h_ino)
++{
++ struct au_drinfo *ret, *drinfo;
++ struct au_drinfo_fdata fdata;
++ int len;
++ loff_t pos;
++ ssize_t ssz;
++
++ ret = ERR_PTR(-EIO);
++ pos = 0;
++ ssz = vfsub_read_k(file, &fdata, sizeof(fdata), &pos);
++ if (unlikely(ssz != sizeof(fdata))) {
++ AuIOErr("ssz %zd, %u, %pD2\n",
++ ssz, (unsigned int)sizeof(fdata), file);
++ goto out;
++ }
++
++ fdata.magic = ntohl(fdata.magic);
++ switch (fdata.magic) {
++ case AUFS_DRINFO_MAGIC_V1:
++ break;
++ default:
++ AuIOErr("magic-num 0x%x, 0x%x, %pD2\n",
++ fdata.magic, AUFS_DRINFO_MAGIC_V1, file);
++ goto out;
++ }
++
++ drinfo = &fdata.drinfo;
++ len = drinfo->oldnamelen;
++ if (!len) {
++ AuIOErr("broken drinfo %pD2\n", file);
++ goto out;
++ }
++
++ ret = NULL;
++ drinfo->ino = be64_to_cpu(drinfo->ino);
++ if (unlikely(h_ino && drinfo->ino != h_ino)) {
++ AuDbg("ignored i%llu, i%llu, %pD2\n",
++ (unsigned long long)drinfo->ino,
++ (unsigned long long)h_ino, file);
++ goto out; /* success */
++ }
++
++ ret = kmalloc(sizeof(*ret) + len, GFP_NOFS);
++ if (unlikely(!ret)) {
++ ret = ERR_PTR(-ENOMEM);
++ AuTraceErrPtr(ret);
++ goto out;
++ }
++
++ *ret = *drinfo;
++ ssz = vfsub_read_k(file, (void *)ret->oldname, len, &pos);
++ if (unlikely(ssz != len)) {
++ kfree(ret);
++ ret = ERR_PTR(-EIO);
++ AuIOErr("ssz %zd, %u, %pD2\n", ssz, len, file);
++ goto out;
++ }
++
++ AuDbg("oldname %.*s\n", ret->oldnamelen, ret->oldname);
++
++out:
++ return ret;
++}
++
++/* ---------------------------------------------------------------------- */
++
++/* in order to be revertible */
++struct au_drinfo_rev_elm {
++ int created;
++ struct dentry *info_dentry;
++ struct au_drinfo *info_last;
++};
++
++struct au_drinfo_rev {
++ unsigned char already;
++ aufs_bindex_t nelm;
++ struct au_drinfo_rev_elm elm[0];
++};
++
++/* todo: isn't it too large? */
++struct au_drinfo_store {
++ struct path h_ppath;
++ struct dentry *h_dentry;
++ struct au_drinfo_fdata *fdata;
++ char *infoname; /* inside of whname, just after PFX */
++ char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ];
++ aufs_bindex_t btgt, btail;
++ unsigned char no_sio,
++ allocated, /* current size of *fdata */
++ infonamelen, /* room size for p */
++ whnamelen, /* length of the genarated name */
++ renameback; /* renamed back */
++};
++
++/* on rename(2) error, the caller should revert it using @elm */
++static int au_drinfo_do_store(struct au_drinfo_store *w,
++ struct au_drinfo_rev_elm *elm)
++{
++ int err, len;
++ ssize_t ssz;
++ loff_t pos;
++ struct path infopath = {
++ .mnt = w->h_ppath.mnt
++ };
++ struct inode *h_dir, *h_inode, *delegated;
++ struct file *infofile;
++ struct qstr *qname;
++
++ AuDebugOn(elm
++ && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm)));
++
++ infopath.dentry = vfsub_lookup_one_len(w->whname, w->h_ppath.dentry,
++ w->whnamelen);
++ AuTraceErrPtr(infopath.dentry);
++ if (IS_ERR(infopath.dentry)) {
++ err = PTR_ERR(infopath.dentry);
++ goto out;
++ }
++
++ err = 0;
++ h_dir = d_inode(w->h_ppath.dentry);
++ if (elm && d_is_negative(infopath.dentry)) {
++ err = vfsub_create(h_dir, &infopath, 0600, /*want_excl*/true);
++ AuTraceErr(err);
++ if (unlikely(err))
++ goto out_dput;
++ elm->created = 1;
++ elm->info_dentry = dget(infopath.dentry);
++ }
++
++ infofile = vfsub_dentry_open(&infopath, O_RDWR);
++ AuTraceErrPtr(infofile);
++ if (IS_ERR(infofile)) {
++ err = PTR_ERR(infofile);
++ goto out_dput;
++ }
++
++ h_inode = d_inode(infopath.dentry);
++ if (elm && i_size_read(h_inode)) {
++ h_inode = d_inode(w->h_dentry);
++ elm->info_last = au_drinfo_read_k(infofile, h_inode->i_ino);
++ AuTraceErrPtr(elm->info_last);
++ if (IS_ERR(elm->info_last)) {
++ err = PTR_ERR(elm->info_last);
++ elm->info_last = NULL;
++ AuDebugOn(elm->info_dentry);
++ goto out_fput;
++ }
++ }
++
++ if (elm && w->renameback) {
++ delegated = NULL;
++ err = vfsub_unlink(h_dir, &infopath, &delegated, /*force*/0);
++ AuTraceErr(err);
++ if (unlikely(err == -EWOULDBLOCK))
++ iput(delegated);
++ goto out_fput;
++ }
++
++ pos = 0;
++ qname = &w->h_dentry->d_name;
++ len = sizeof(*w->fdata) + qname->len;
++ if (!elm)
++ len = sizeof(*w->fdata) + w->fdata->drinfo.oldnamelen;
++ ssz = vfsub_write_k(infofile, w->fdata, len, &pos);
++ if (ssz == len) {
++ AuDbg("hi%llu, %.*s\n", w->fdata->drinfo.ino,
++ w->fdata->drinfo.oldnamelen, w->fdata->drinfo.oldname);
++ goto out_fput; /* success */
++ } else {
++ err = -EIO;
++ if (ssz < 0)
++ err = ssz;
++ /* the caller should revert it using @elm */
++ }
++
++out_fput:
++ fput(infofile);
++out_dput:
++ dput(infopath.dentry);
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++struct au_call_drinfo_do_store_args {
++ int *errp;
++ struct au_drinfo_store *w;
++ struct au_drinfo_rev_elm *elm;
++};
++
++static void au_call_drinfo_do_store(void *args)
++{
++ struct au_call_drinfo_do_store_args *a = args;
++
++ *a->errp = au_drinfo_do_store(a->w, a->elm);
++}
++
++static int au_drinfo_store_sio(struct au_drinfo_store *w,
++ struct au_drinfo_rev_elm *elm)
++{
++ int err, wkq_err;
++
++ if (w->no_sio)
++ err = au_drinfo_do_store(w, elm);
++ else {
++ struct au_call_drinfo_do_store_args a = {
++ .errp = &err,
++ .w = w,
++ .elm = elm
++ };
++ wkq_err = au_wkq_wait(au_call_drinfo_do_store, &a);
++ if (unlikely(wkq_err))
++ err = wkq_err;
++ }
++ AuTraceErr(err);
++
++ return err;
++}
++
++static int au_drinfo_store_work_init(struct au_drinfo_store *w,
++ aufs_bindex_t btgt)
++{
++ int err;
++
++ memset(w, 0, sizeof(*w));
++ w->allocated = roundup_pow_of_two(sizeof(*w->fdata) + 40);
++ strcpy(w->whname, AUFS_WH_DR_INFO_PFX);
++ w->infoname = w->whname + sizeof(AUFS_WH_DR_INFO_PFX) - 1;
++ w->infonamelen = sizeof(w->whname) - sizeof(AUFS_WH_DR_INFO_PFX);
++ w->btgt = btgt;
++ w->no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID);
++
++ err = -ENOMEM;
++ w->fdata = kcalloc(1, w->allocated, GFP_NOFS);
++ if (unlikely(!w->fdata)) {
++ AuTraceErr(err);
++ goto out;
++ }
++ w->fdata->magic = htonl(AUFS_DRINFO_MAGIC_V1);
++ err = 0;
++
++out:
++ return err;
++}
++
++static void au_drinfo_store_work_fin(struct au_drinfo_store *w)
++{
++ kfree(w->fdata);
++}
++
++static void au_drinfo_store_rev(struct au_drinfo_rev *rev,
++ struct au_drinfo_store *w)
++{
++ struct au_drinfo_rev_elm *elm;
++ struct inode *h_dir, *delegated;
++ int err, nelm;
++ struct path infopath = {
++ .mnt = w->h_ppath.mnt
++ };
++
++ h_dir = d_inode(w->h_ppath.dentry);
++ IMustLock(h_dir);
++
++ err = 0;
++ elm = rev->elm;
++ for (nelm = rev->nelm; nelm > 0; nelm--, elm++) {
++ AuDebugOn(elm->created && elm->info_last);
++ if (elm->created) {
++ AuDbg("here\n");
++ delegated = NULL;
++ infopath.dentry = elm->info_dentry;
++ err = vfsub_unlink(h_dir, &infopath, &delegated,
++ !w->no_sio);
++ AuTraceErr(err);
++ if (unlikely(err == -EWOULDBLOCK))
++ iput(delegated);
++ dput(elm->info_dentry);
++ } else if (elm->info_last) {
++ AuDbg("here\n");
++ w->fdata->drinfo = *elm->info_last;
++ memcpy(w->fdata->drinfo.oldname,
++ elm->info_last->oldname,
++ elm->info_last->oldnamelen);
++ err = au_drinfo_store_sio(w, /*elm*/NULL);
++ kfree(elm->info_last);
++ }
++ if (unlikely(err))
++ AuIOErr("%d, %s\n", err, w->whname);
++ /* go on even if err */
++ }
++}
++
++/* caller has to call au_dr_rename_fin() later */
++static int au_drinfo_store(struct dentry *dentry, aufs_bindex_t btgt,
++ struct qstr *dst_name, void *_rev)
++{
++ int err, sz, nelm;
++ aufs_bindex_t bindex, btail;
++ struct au_drinfo_store work;
++ struct au_drinfo_rev *rev, **p;
++ struct au_drinfo_rev_elm *elm;
++ struct super_block *sb;
++ struct au_branch *br;
++ struct au_hinode *hdir;
++
++ err = au_drinfo_store_work_init(&work, btgt);
++ AuTraceErr(err);
++ if (unlikely(err))
++ goto out;
++
++ err = -ENOMEM;
++ btail = au_dbtaildir(dentry);
++ nelm = btail - btgt;
++ sz = sizeof(*rev) + sizeof(*elm) * nelm;
++ rev = kcalloc(1, sz, GFP_NOFS);
++ if (unlikely(!rev)) {
++ AuTraceErr(err);
++ goto out_args;
++ }
++ rev->nelm = nelm;
++ elm = rev->elm;
++ p = _rev;
++ *p = rev;
++
++ err = 0;
++ sb = dentry->d_sb;
++ work.h_ppath.dentry = au_h_dptr(dentry, btgt);
++ work.h_ppath.mnt = au_sbr_mnt(sb, btgt);
++ hdir = au_hi(d_inode(dentry), btgt);
++ au_hn_inode_lock_nested(hdir, AuLsc_I_CHILD);
++ for (bindex = btgt + 1; bindex <= btail; bindex++, elm++) {
++ work.h_dentry = au_h_dptr(dentry, bindex);
++ if (!work.h_dentry)
++ continue;
++
++ err = au_drinfo_construct(&work.fdata, work.h_dentry,
++ &work.allocated);
++ AuTraceErr(err);
++ if (unlikely(err))
++ break;
++
++ work.renameback = au_qstreq(&work.h_dentry->d_name, dst_name);
++ br = au_sbr(sb, bindex);
++ work.whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1;
++ work.whnamelen += au_drinfo_name(br, work.infoname,
++ work.infonamelen);
++ AuDbg("whname %.*s, i%llu, %.*s\n",
++ work.whnamelen, work.whname,
++ be64_to_cpu(work.fdata->drinfo.ino),
++ work.fdata->drinfo.oldnamelen,
++ work.fdata->drinfo.oldname);
++
++ err = au_drinfo_store_sio(&work, elm);
++ AuTraceErr(err);
++ if (unlikely(err))
++ break;
++ }
++ if (unlikely(err)) {
++ /* revert all drinfo */
++ au_drinfo_store_rev(rev, &work);
++ kfree(rev);
++ *p = NULL;
++ }
++ au_hn_inode_unlock(hdir);
++
++out_args:
++ au_drinfo_store_work_fin(&work);
++out:
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
++ struct qstr *dst_name, void *_rev)
++{
++ int err, already;
++ ino_t ino;
++ struct super_block *sb;
++ struct au_branch *br;
++ struct au_dr_br *dr;
++ struct dentry *h_dentry;
++ struct inode *h_inode;
++ struct au_dr_hino *ent;
++ struct au_drinfo_rev *rev, **p;
++
++ AuDbg("bindex %d\n", bindex);
++
++ err = -ENOMEM;
++ ent = kmalloc(sizeof(*ent), GFP_NOFS);
++ if (unlikely(!ent))
++ goto out;
++
++ sb = src->d_sb;
++ br = au_sbr(sb, bindex);
++ dr = &br->br_dirren;
++ h_dentry = au_h_dptr(src, bindex);
++ h_inode = d_inode(h_dentry);
++ ino = h_inode->i_ino;
++ ent->dr_h_ino = ino;
++ already = au_dr_hino_test_add(dr, ino, ent);
++ AuDbg("b%d, hi%llu, already %d\n",
++ bindex, (unsigned long long)ino, already);
++
++ err = au_drinfo_store(src, bindex, dst_name, _rev);
++ AuTraceErr(err);
++ if (!err) {
++ p = _rev;
++ rev = *p;
++ rev->already = already;
++ goto out; /* success */
++ }
++
++ /* revert */
++ if (!already)
++ au_dr_hino_del(dr, ent);
++ kfree(ent);
++
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *_rev)
++{
++ struct au_drinfo_rev *rev;
++ struct au_drinfo_rev_elm *elm;
++ int nelm;
++
++ rev = _rev;
++ elm = rev->elm;
++ for (nelm = rev->nelm; nelm > 0; nelm--, elm++) {
++ dput(elm->info_dentry);
++ kfree(elm->info_last);
++ }
++ kfree(rev);
++}
++
++void au_dr_rename_rev(struct dentry *src, aufs_bindex_t btgt, void *_rev)
++{
++ int err;
++ struct au_drinfo_store work;
++ struct au_drinfo_rev *rev = _rev;
++ struct super_block *sb;
++ struct au_branch *br;
++ struct inode *h_inode;
++ struct au_dr_br *dr;
++ struct au_dr_hino *ent;
++
++ err = au_drinfo_store_work_init(&work, btgt);
++ if (unlikely(err))
++ goto out;
++
++ sb = src->d_sb;
++ br = au_sbr(sb, btgt);
++ work.h_ppath.dentry = au_h_dptr(src, btgt);
++ work.h_ppath.mnt = au_br_mnt(br);
++ au_drinfo_store_rev(rev, &work);
++ au_drinfo_store_work_fin(&work);
++ if (rev->already)
++ goto out;
++
++ dr = &br->br_dirren;
++ h_inode = d_inode(work.h_ppath.dentry);
++ ent = au_dr_hino_find(dr, h_inode->i_ino);
++ BUG_ON(!ent);
++ au_dr_hino_del(dr, ent);
++ kfree(ent);
++
++out:
++ kfree(rev);
++ if (unlikely(err))
++ pr_err("failed to remove dirren info\n");
++}
++
++/* ---------------------------------------------------------------------- */
++
++static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath,
++ char *whname, int whnamelen,
++ struct dentry **info_dentry)
++{
++ struct au_drinfo *drinfo;
++ struct file *f;
++ struct inode *h_dir;
++ struct path infopath;
++ int unlocked;
++
++ AuDbg("%pd/%.*s\n", h_ppath->dentry, whnamelen, whname);
++
++ *info_dentry = NULL;
++ drinfo = NULL;
++ unlocked = 0;
++ h_dir = d_inode(h_ppath->dentry);
++ vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
++ infopath.dentry = vfsub_lookup_one_len(whname, h_ppath->dentry,
++ whnamelen);
++ if (IS_ERR(infopath.dentry)) {
++ drinfo = (void *)infopath.dentry;
++ goto out;
++ }
++
++ if (d_is_negative(infopath.dentry))
++ goto out_dput; /* success */
++
++ infopath.mnt = h_ppath->mnt;
++ f = vfsub_dentry_open(&infopath, O_RDONLY);
++ inode_unlock_shared(h_dir);
++ unlocked = 1;
++ if (IS_ERR(f)) {
++ drinfo = (void *)f;
++ goto out_dput;
++ }
++
++ drinfo = au_drinfo_read_k(f, /*h_ino*/0);
++ if (IS_ERR_OR_NULL(drinfo))
++ goto out_fput;
++
++ AuDbg("oldname %.*s\n", drinfo->oldnamelen, drinfo->oldname);
++ *info_dentry = dget(infopath.dentry); /* keep it alive */
++
++out_fput:
++ fput(f);
++out_dput:
++ dput(infopath.dentry);
++out:
++ if (!unlocked)
++ inode_unlock_shared(h_dir);
++ AuTraceErrPtr(drinfo);
++ return drinfo;
++}
++
++struct au_drinfo_do_load_args {
++ struct au_drinfo **drinfop;
++ struct path *h_ppath;
++ char *whname;
++ int whnamelen;
++ struct dentry **info_dentry;
++};
++
++static void au_call_drinfo_do_load(void *args)
++{
++ struct au_drinfo_do_load_args *a = args;
++
++ *a->drinfop = au_drinfo_do_load(a->h_ppath, a->whname, a->whnamelen,
++ a->info_dentry);
++}
++
++struct au_drinfo_load {
++ struct path h_ppath;
++ struct qstr *qname;
++ unsigned char no_sio;
++
++ aufs_bindex_t ninfo;
++ struct au_drinfo **drinfo;
++};
++
++static int au_drinfo_load(struct au_drinfo_load *w, aufs_bindex_t bindex,
++ struct au_branch *br)
++{
++ int err, wkq_err, whnamelen, e;
++ char whname[sizeof(AUFS_WH_DR_INFO_PFX) + AUFS_DIRREN_ENV_VAL_SZ]
++ = AUFS_WH_DR_INFO_PFX;
++ struct au_drinfo *drinfo;
++ struct qstr oldname;
++ struct inode *h_dir, *delegated;
++ struct dentry *info_dentry;
++ struct path infopath;
++
++ whnamelen = sizeof(AUFS_WH_DR_INFO_PFX) - 1;
++ whnamelen += au_drinfo_name(br, whname + whnamelen,
++ sizeof(whname) - whnamelen);
++ if (w->no_sio)
++ drinfo = au_drinfo_do_load(&w->h_ppath, whname, whnamelen,
++ &info_dentry);
++ else {
++ struct au_drinfo_do_load_args args = {
++ .drinfop = &drinfo,
++ .h_ppath = &w->h_ppath,
++ .whname = whname,
++ .whnamelen = whnamelen,
++ .info_dentry = &info_dentry
++ };
++ wkq_err = au_wkq_wait(au_call_drinfo_do_load, &args);
++ if (unlikely(wkq_err))
++ drinfo = ERR_PTR(wkq_err);
++ }
++ err = PTR_ERR(drinfo);
++ if (IS_ERR_OR_NULL(drinfo))
++ goto out;
++
++ err = 0;
++ oldname.len = drinfo->oldnamelen;
++ oldname.name = drinfo->oldname;
++ if (au_qstreq(w->qname, &oldname)) {
++ /* the name is renamed back */
++ kfree(drinfo);
++ drinfo = NULL;
++
++ infopath.dentry = info_dentry;
++ infopath.mnt = w->h_ppath.mnt;
++ h_dir = d_inode(w->h_ppath.dentry);
++ delegated = NULL;
++ inode_lock_nested(h_dir, AuLsc_I_PARENT);
++ e = vfsub_unlink(h_dir, &infopath, &delegated, !w->no_sio);
++ inode_unlock(h_dir);
++ if (unlikely(e))
++ AuIOErr("ignored %d, %pd2\n", e, &infopath.dentry);
++ if (unlikely(e == -EWOULDBLOCK))
++ iput(delegated);
++ }
++ kfree(w->drinfo[bindex]);
++ w->drinfo[bindex] = drinfo;
++ dput(info_dentry);
++
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++/* ---------------------------------------------------------------------- */
++
++static void au_dr_lkup_free(struct au_drinfo **drinfo, int n)
++{
++ struct au_drinfo **p = drinfo;
++
++ while (n-- > 0)
++ kfree(*drinfo++);
++ kfree(p);
++}
++
++int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
++ aufs_bindex_t btgt)
++{
++ int err, ninfo;
++ struct au_drinfo_load w;
++ aufs_bindex_t bindex, bbot;
++ struct au_branch *br;
++ struct inode *h_dir;
++ struct au_dr_hino *ent;
++ struct super_block *sb;
++
++ AuDbg("%.*s, name %.*s, whname %.*s, b%d\n",
++ AuLNPair(&dentry->d_name), AuLNPair(&lkup->dirren.dr_name),
++ AuLNPair(&lkup->whname), btgt);
++
++ sb = dentry->d_sb;
++ bbot = au_sbbot(sb);
++ w.ninfo = bbot + 1;
++ if (!lkup->dirren.drinfo) {
++ lkup->dirren.drinfo = kcalloc(w.ninfo,
++ sizeof(*lkup->dirren.drinfo),
++ GFP_NOFS);
++ if (unlikely(!lkup->dirren.drinfo)) {
++ err = -ENOMEM;
++ goto out;
++ }
++ lkup->dirren.ninfo = w.ninfo;
++ }
++ w.drinfo = lkup->dirren.drinfo;
++ w.no_sio = !!uid_eq(current_fsuid(), GLOBAL_ROOT_UID);
++ w.h_ppath.dentry = au_h_dptr(dentry, btgt);
++ AuDebugOn(!w.h_ppath.dentry);
++ w.h_ppath.mnt = au_sbr_mnt(sb, btgt);
++ w.qname = &dentry->d_name;
++
++ ninfo = 0;
++ for (bindex = btgt + 1; bindex <= bbot; bindex++) {
++ br = au_sbr(sb, bindex);
++ err = au_drinfo_load(&w, bindex, br);
++ if (unlikely(err))
++ goto out_free;
++ if (w.drinfo[bindex])
++ ninfo++;
++ }
++ if (!ninfo) {
++ br = au_sbr(sb, btgt);
++ h_dir = d_inode(w.h_ppath.dentry);
++ ent = au_dr_hino_find(&br->br_dirren, h_dir->i_ino);
++ AuDebugOn(!ent);
++ au_dr_hino_del(&br->br_dirren, ent);
++ kfree(ent);
++ }
++ goto out; /* success */
++
++out_free:
++ au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
++ lkup->dirren.ninfo = 0;
++ lkup->dirren.drinfo = NULL;
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++void au_dr_lkup_fin(struct au_do_lookup_args *lkup)
++{
++ au_dr_lkup_free(lkup->dirren.drinfo, lkup->dirren.ninfo);
++}
++
++int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt)
++{
++ int err;
++ struct au_drinfo *drinfo;
++
++ err = 0;
++ if (!lkup->dirren.drinfo)
++ goto out;
++ AuDebugOn(lkup->dirren.ninfo < btgt + 1);
++ drinfo = lkup->dirren.drinfo[btgt + 1];
++ if (!drinfo)
++ goto out;
++
++ kfree(lkup->whname.name);
++ lkup->whname.name = NULL;
++ lkup->dirren.dr_name.len = drinfo->oldnamelen;
++ lkup->dirren.dr_name.name = drinfo->oldname;
++ lkup->name = &lkup->dirren.dr_name;
++ err = au_wh_name_alloc(&lkup->whname, lkup->name);
++ if (!err)
++ AuDbg("name %.*s, whname %.*s, b%d\n",
++ AuLNPair(lkup->name), AuLNPair(&lkup->whname),
++ btgt);
++
++out:
++ AuTraceErr(err);
++ return err;
++}
++
++int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
++ ino_t h_ino)
++{
++ int match;
++ struct au_drinfo *drinfo;
++
++ match = 1;
++ if (!lkup->dirren.drinfo)
++ goto out;
++ AuDebugOn(lkup->dirren.ninfo < bindex + 1);
++ drinfo = lkup->dirren.drinfo[bindex + 1];
++ if (!drinfo)
++ goto out;
++
++ match = (drinfo->ino == h_ino);
++ AuDbg("match %d\n", match);
++
++out:
++ return match;
++}
++
++/* ---------------------------------------------------------------------- */
++
++int au_dr_opt_set(struct super_block *sb)
++{
++ int err;
++ aufs_bindex_t bindex, bbot;
++ struct au_branch *br;
++
++ err = 0;
++ bbot = au_sbbot(sb);
++ for (bindex = 0; !err && bindex <= bbot; bindex++) {
++ br = au_sbr(sb, bindex);
++ err = au_dr_hino(sb, bindex, /*br*/NULL, &br->br_path);
++ }
++
++ return err;
++}
++
++int au_dr_opt_flush(struct super_block *sb)
++{
++ int err;
++ aufs_bindex_t bindex, bbot;
++ struct au_branch *br;
++
++ err = 0;
++ bbot = au_sbbot(sb);
++ for (bindex = 0; !err && bindex <= bbot; bindex++) {
++ br = au_sbr(sb, bindex);
++ if (au_br_writable(br->br_perm))
++ err = au_dr_hino(sb, bindex, /*br*/NULL, /*path*/NULL);
++ }
++
++ return err;
++}
++
++int au_dr_opt_clr(struct super_block *sb, int no_flush)
++{
++ int err;
++ aufs_bindex_t bindex, bbot;
++ struct au_branch *br;
++
++ err = 0;
++ if (!no_flush) {
++ err = au_dr_opt_flush(sb);
++ if (unlikely(err))
++ goto out;
++ }
++
++ bbot = au_sbbot(sb);
++ for (bindex = 0; bindex <= bbot; bindex++) {
++ br = au_sbr(sb, bindex);
++ au_dr_hino_free(&br->br_dirren);
++ }
++
++out:
++ return err;
++}
+diff -urN /usr/share/empty/fs/aufs/dirren.h linux/fs/aufs/dirren.h
+--- /usr/share/empty/fs/aufs/dirren.h 1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/aufs/dirren.h 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,145 @@
++/*
++ * Copyright (C) 2017-2018 Junjiro R. Okajima
++ *
++ * This program, aufs is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program. If not, see <http://www.gnu.org/licenses/>.
++ */
++
++/*
++ * renamed dir info
++ */
++
++#ifndef __AUFS_DIRREN_H__
++#define __AUFS_DIRREN_H__
++
++#ifdef __KERNEL__
++
++#include <linux/dcache.h>
++#include <linux/statfs.h>
++#include "hbl.h"
++
++#define AuDirren_NHASH 100
++
++#ifdef CONFIG_AUFS_DIRREN
++/* copied from linux/fs/xfs/uuid.h */
++typedef struct {
++ unsigned char __u_bits[16];
++} uuid_t;
++
++#define __UUID_TMPLT "01234567-0123-4567-0123-456701234567"
++
++enum au_brid_type {
++ AuBrid_Unset,
++ AuBrid_UUID,
++ AuBrid_FSID,
++ AuBrid_DEV
++};
++
++struct au_dr_brid {
++ enum au_brid_type type;
++ union {
++ uuid_t uuid; /* unimplemented yet */
++ fsid_t fsid;
++ dev_t dev;
++ };
++};
++
++/* 20 is the max digits length of ulong 64 */
++/* brid-type "_" uuid "_" inum */
++#define AUFS_DIRREN_FNAME_SZ (1 + 1 + sizeof(__UUID_TMPLT) + 20)
++#define AUFS_DIRREN_ENV_VAL_SZ (AUFS_DIRREN_FNAME_SZ + 1 + 20)
++
++struct au_dr_hino {
++ struct hlist_bl_node dr_hnode;
++ ino_t dr_h_ino;
++};
++
++struct au_dr_br {
++ struct hlist_bl_head dr_h_ino[AuDirren_NHASH];
++ struct au_dr_brid dr_brid;
++};
++
++struct au_dr_lookup {
++ /* dr_name is pointed by struct au_do_lookup_args.name */
++ struct qstr dr_name; /* subset of dr_info */
++ aufs_bindex_t ninfo;
++ struct au_drinfo **drinfo;
++};
++#else
++struct au_dr_hino;
++/* empty */
++struct au_dr_br { };
++struct au_dr_lookup { };
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++struct au_branch;
++struct au_do_lookup_args;
++struct au_hinode;
++#ifdef CONFIG_AUFS_DIRREN
++int au_dr_hino_test_add(struct au_dr_br *dr, ino_t h_ino,
++ struct au_dr_hino *add_ent);
++void au_dr_hino_free(struct au_dr_br *dr);
++int au_dr_br_init(struct super_block *sb, struct au_branch *br,
++ const struct path *path);
++int au_dr_br_fin(struct super_block *sb, struct au_branch *br);
++int au_dr_rename(struct dentry *src, aufs_bindex_t bindex,
++ struct qstr *dst_name, void *_rev);
++void au_dr_rename_fin(struct dentry *src, aufs_bindex_t btgt, void *rev);
++void au_dr_rename_rev(struct dentry *src, aufs_bindex_t bindex, void *rev);
++int au_dr_lkup(struct au_do_lookup_args *lkup, struct dentry *dentry,
++ aufs_bindex_t bindex);
++int au_dr_lkup_name(struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
++int au_dr_lkup_h_ino(struct au_do_lookup_args *lkup, aufs_bindex_t bindex,
++ ino_t h_ino);
++void au_dr_lkup_fin(struct au_do_lookup_args *lkup);
++int au_dr_opt_set(struct super_block *sb);
++int au_dr_opt_flush(struct super_block *sb);
++int au_dr_opt_clr(struct super_block *sb, int no_flush);
++#else
++AuStubInt0(au_dr_hino_test_add, struct au_dr_br *dr, ino_t h_ino,
++ struct au_dr_hino *add_ent);
++AuStubVoid(au_dr_hino_free, struct au_dr_br *dr);
++AuStubInt0(au_dr_br_init, struct super_block *sb, struct au_branch *br,
++ const struct path *path);
++AuStubInt0(au_dr_br_fin, struct super_block *sb, struct au_branch *br);
++AuStubInt0(au_dr_rename, struct dentry *src, aufs_bindex_t bindex,
++ struct qstr *dst_name, void *_rev);
++AuStubVoid(au_dr_rename_fin, struct dentry *src, aufs_bindex_t btgt, void *rev);
++AuStubVoid(au_dr_rename_rev, struct dentry *src, aufs_bindex_t bindex,
++ void *rev);
++AuStubInt0(au_dr_lkup, struct au_do_lookup_args *lkup, struct dentry *dentry,
++ aufs_bindex_t bindex);
++AuStubInt0(au_dr_lkup_name, struct au_do_lookup_args *lkup, aufs_bindex_t btgt);
++AuStubInt0(au_dr_lkup_h_ino, struct au_do_lookup_args *lkup,
++ aufs_bindex_t bindex, ino_t h_ino);
++AuStubVoid(au_dr_lkup_fin, struct au_do_lookup_args *lkup);
++AuStubInt0(au_dr_opt_set, struct super_block *sb);
++AuStubInt0(au_dr_opt_flush, struct super_block *sb);
++AuStubInt0(au_dr_opt_clr, struct super_block *sb, int no_flush);
++#endif
++
++/* ---------------------------------------------------------------------- */
++
++#ifdef CONFIG_AUFS_DIRREN
++static inline int au_dr_ihash(ino_t h_ino)
++{
++ return h_ino % AuDirren_NHASH;
++}
++#else
++AuStubInt0(au_dr_ihash, ino_t h_ino);
++#endif
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_DIRREN_H__ */
diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
--- /usr/share/empty/fs/aufs/dynop.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dynop.c 2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,371 @@
++++ linux/fs/aufs/dynop.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,369 @@
+/*
-+ * Copyright (C) 2010-2016 Junjiro R. Okajima
++ * Copyright (C) 2010-2018 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
@@ -10564,23 +12334,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+ * How large will these lists be?
+ * Usually just a few elements, 20-30 at most for each, I guess.
+ */
-+static struct au_sphlhead dynop[AuDyLast];
++static struct hlist_bl_head dynop[AuDyLast];
+
-+static struct au_dykey *dy_gfind_get(struct au_sphlhead *sphl, const void *h_op)
++static struct au_dykey *dy_gfind_get(struct hlist_bl_head *hbl,
++ const void *h_op)
+{
+ struct au_dykey *key, *tmp;
-+ struct hlist_head *head;
++ struct hlist_bl_node *pos;
+
+ key = NULL;
-+ head = &sphl->head;
-+ rcu_read_lock();
-+ hlist_for_each_entry_rcu(tmp, head, dk_hnode)
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
+ if (tmp->dk_op.dy_hop == h_op) {
+ key = tmp;
+ kref_get(&key->dk_kref);
+ break;
+ }
-+ rcu_read_unlock();
++ hlist_bl_unlock(hbl);
+
+ return key;
+}
@@ -10621,24 +12391,23 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+}
+
+/* kref_get() if @key is already added */
-+static struct au_dykey *dy_gadd(struct au_sphlhead *sphl, struct au_dykey *key)
++static struct au_dykey *dy_gadd(struct hlist_bl_head *hbl, struct au_dykey *key)
+{
+ struct au_dykey *tmp, *found;
-+ struct hlist_head *head;
++ struct hlist_bl_node *pos;
+ const void *h_op = key->dk_op.dy_hop;
+
+ found = NULL;
-+ head = &sphl->head;
-+ spin_lock(&sphl->spin);
-+ hlist_for_each_entry(tmp, head, dk_hnode)
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(tmp, pos, hbl, dk_hnode)
+ if (tmp->dk_op.dy_hop == h_op) {
+ kref_get(&tmp->dk_kref);
+ found = tmp;
+ break;
+ }
+ if (!found)
-+ hlist_add_head_rcu(&key->dk_hnode, head);
-+ spin_unlock(&sphl->spin);
++ hlist_bl_add_head(&key->dk_hnode, hbl);
++ hlist_bl_unlock(hbl);
+
+ if (!found)
+ DyPrSym(key);
@@ -10651,17 +12420,17 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+
+ key = container_of(rcu, struct au_dykey, dk_rcu);
+ DyPrSym(key);
-+ kfree(key); /* not delayed */
++ kfree(key);
+}
+
+static void dy_free(struct kref *kref)
+{
+ struct au_dykey *key;
-+ struct au_sphlhead *sphl;
++ struct hlist_bl_head *hbl;
+
+ key = container_of(kref, struct au_dykey, dk_kref);
-+ sphl = dynop + key->dk_op.dy_type;
-+ au_sphl_del_rcu(&key->dk_hnode, sphl);
++ hbl = dynop + key->dk_op.dy_type;
++ au_hbl_del(&key->dk_hnode, hbl);
+ call_rcu(&key->dk_rcu, dy_free_rcu);
+}
+
@@ -10748,7 +12517,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br)
+{
+ struct au_dykey *key, *old;
-+ struct au_sphlhead *sphl;
++ struct hlist_bl_head *hbl;
+ struct op {
+ unsigned int sz;
+ void (*set)(struct au_dykey *key, const void *h_op,
@@ -10762,8 +12531,8 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+ };
+ const struct op *p;
+
-+ sphl = dynop + op->dy_type;
-+ key = dy_gfind_get(sphl, op->dy_hop);
++ hbl = dynop + op->dy_type;
++ key = dy_gfind_get(hbl, op->dy_hop);
+ if (key)
+ goto out_add; /* success */
+
@@ -10777,9 +12546,9 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+ key->dk_op.dy_hop = op->dy_hop;
+ kref_init(&key->dk_kref);
+ p->set(key, op->dy_hop, au_br_sb(br));
-+ old = dy_gadd(sphl, key);
++ old = dy_gadd(hbl, key);
+ if (old) {
-+ au_delayed_kfree(key);
++ kfree(key);
+ key = old;
+ }
+
@@ -10874,16 +12643,15 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+
+void au_dy_arefresh(int do_dx)
+{
-+ struct au_sphlhead *sphl;
-+ struct hlist_head *head;
++ struct hlist_bl_head *hbl;
++ struct hlist_bl_node *pos;
+ struct au_dykey *key;
+
-+ sphl = dynop + AuDy_AOP;
-+ head = &sphl->head;
-+ spin_lock(&sphl->spin);
-+ hlist_for_each_entry(key, head, dk_hnode)
++ hbl = dynop + AuDy_AOP;
++ hlist_bl_lock(hbl);
++ hlist_bl_for_each_entry(key, pos, hbl, dk_hnode)
+ dy_adx((void *)key, do_dx);
-+ spin_unlock(&sphl->spin);
++ hlist_bl_unlock(hbl);
+}
+
+/* ---------------------------------------------------------------------- */
@@ -10896,7 +12664,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+ BUILD_BUG_ON(offsetof(struct au_dyaop, da_key));
+
+ for (i = 0; i < AuDyLast; i++)
-+ au_sphl_init(dynop + i);
++ INIT_HLIST_BL_HEAD(dynop + i);
+}
+
+void au_dy_fin(void)
@@ -10904,14 +12672,14 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
+ int i;
+
+ for (i = 0; i < AuDyLast; i++)
-+ WARN_ON(!hlist_empty(&dynop[i].head));
++ WARN_ON(!hlist_bl_empty(dynop + i));
+}
diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
--- /usr/share/empty/fs/aufs/dynop.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/dynop.h 2016-10-09 16:55:36.489368218 +0200
++++ linux/fs/aufs/dynop.h 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,74 @@
+/*
-+ * Copyright (C) 2010-2016 Junjiro R. Okajima
++ * Copyright (C) 2010-2018 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
@@ -10951,7 +12719,7 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
+
+struct au_dykey {
+ union {
-+ struct hlist_node dk_hnode;
++ struct hlist_bl_node dk_hnode;
+ struct rcu_head dk_rcu;
+ };
+ struct au_dynop dk_op;
@@ -10986,10 +12754,10 @@ diff -urN /usr/share/empty/fs/aufs/dynop.h linux/fs/aufs/dynop.h
+#endif /* __AUFS_DYNOP_H__ */
diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
--- /usr/share/empty/fs/aufs/export.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/export.c 2016-12-17 12:28:17.595211562 +0100
++++ linux/fs/aufs/export.c 2018-04-15 08:49:13.397817296 +0200
@@ -0,0 +1,836 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -11406,7 +13174,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
+ }
+
+out_name:
-+ au_delayed_free_page((unsigned long)arg.name);
++ free_page((unsigned long)arg.name);
+out_file:
+ fput(file);
+out:
@@ -11560,7 +13328,7 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
+ dentry = ERR_PTR(-ESTALE);
+ }
+out_pathname:
-+ au_delayed_free_page((unsigned long)pathname);
++ free_page((unsigned long)pathname);
+out_h_parent:
+ dput(h_parent);
+out:
@@ -11826,10 +13594,10 @@ diff -urN /usr/share/empty/fs/aufs/export.c linux/fs/aufs/export.c
+}
diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
--- /usr/share/empty/fs/aufs/fhsm.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/fhsm.c 2016-10-09 16:55:36.489368218 +0200
-@@ -0,0 +1,426 @@
++++ linux/fs/aufs/fhsm.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,427 @@
+/*
-+ * Copyright (C) 2011-2016 Junjiro R. Okajima
++ * Copyright (C) 2011-2018 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
@@ -12013,7 +13781,8 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
+ if (atomic_read(&fhsm->fhsm_readable))
+ mask = POLLIN /* | POLLRDNORM */;
+
-+ AuTraceErr((int)mask);
++ if (!mask)
++ AuDbg("mask 0x%x\n", mask);
+ return mask;
+}
+
@@ -12256,10 +14025,10 @@ diff -urN /usr/share/empty/fs/aufs/fhsm.c linux/fs/aufs/fhsm.c
+}
diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
--- /usr/share/empty/fs/aufs/file.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/file.c 2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,857 @@
++++ linux/fs/aufs/file.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,848 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -12368,7 +14137,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+
+static int au_cmoo(struct dentry *dentry)
+{
-+ int err, cmoo;
++ int err, cmoo, matched;
+ unsigned int udba;
+ struct path h_path;
+ struct au_pin pin;
@@ -12403,9 +14172,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ sbinfo = au_sbi(sb);
+ fhsm = &sbinfo->si_fhsm;
+ pid = au_fhsm_pid(fhsm);
-+ if (pid
-+ && (current->pid == pid
-+ || current->real_parent->pid == pid))
++ rcu_read_lock();
++ matched = (pid
++ && (current->pid == pid
++ || rcu_dereference(current->real_parent)->pid == pid));
++ rcu_read_unlock();
++ if (matched)
+ goto out;
+
+ br = au_sbr(sb, cpg.bsrc);
@@ -12482,11 +14254,11 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+
+int au_do_open(struct file *file, struct au_do_open_args *args)
+{
-+ int err, no_lock = args->no_lock;
++ int err, aopen = args->aopen;
+ struct dentry *dentry;
+ struct au_finfo *finfo;
+
-+ if (!no_lock)
++ if (!aopen)
+ err = au_finfo_init(file, args->fidir);
+ else {
+ lockdep_off();
@@ -12498,33 +14270,20 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+
+ dentry = file->f_path.dentry;
+ AuDebugOn(IS_ERR_OR_NULL(dentry));
-+ if (!no_lock) {
-+ di_write_lock_child(dentry);
-+ err = au_cmoo(dentry);
-+ di_downgrade_lock(dentry, AuLock_IR);
-+ if (!err)
-+ err = args->open(file, vfsub_file_flags(file), NULL);
-+ di_read_unlock(dentry, AuLock_IR);
-+ } else {
-+ err = au_cmoo(dentry);
-+ if (!err)
-+ err = args->open(file, vfsub_file_flags(file),
-+ args->h_file);
-+ if (!err && au_fbtop(file) != au_dbtop(dentry))
-+ /*
-+ * cmoo happens after h_file was opened.
-+ * need to refresh file later.
-+ */
-+ atomic_dec(&au_fi(file)->fi_generation);
-+ }
++ di_write_lock_child(dentry);
++ err = au_cmoo(dentry);
++ di_downgrade_lock(dentry, AuLock_IR);
++ if (!err)
++ err = args->open(file, vfsub_file_flags(file), NULL);
++ di_read_unlock(dentry, AuLock_IR);
+
+ finfo = au_fi(file);
+ if (!err) {
+ finfo->fi_file = file;
-+ au_sphl_add(&finfo->fi_hlist,
-+ &au_sbi(file->f_path.dentry->d_sb)->si_files);
++ au_hbl_add(&finfo->fi_hlist,
++ &au_sbi(file->f_path.dentry->d_sb)->si_files);
+ }
-+ if (!no_lock)
++ if (!aopen)
+ fi_write_unlock(file);
+ else {
+ lockdep_off();
@@ -12533,7 +14292,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ }
+ if (unlikely(err)) {
+ finfo->fi_hdir = NULL;
-+ au_finfo_fin(file, /*atonce*/0);
++ au_finfo_fin(file);
+ }
+
+out:
@@ -12853,7 +14612,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+
+static void au_do_refresh_dir(struct file *file)
+{
-+ int execed;
+ aufs_bindex_t bindex, bbot, new_bindex, brid;
+ struct au_hfile *p, tmp, *q;
+ struct au_finfo *finfo;
@@ -12892,7 +14650,6 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ }
+ }
+
-+ execed = vfsub_file_execed(file);
+ p = fidir->fd_hfile;
+ if (!au_test_mmapped(file) && !d_unlinked(file->f_path.dentry)) {
+ bbot = au_sbbot(sb);
@@ -12901,14 +14658,14 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ if (p->hf_file) {
+ if (file_inode(p->hf_file))
+ break;
-+ au_hfput(p, execed);
++ au_hfput(p, /*execed*/0);
+ }
+ } else {
+ bbot = au_br_index(sb, brid);
+ for (finfo->fi_btop = 0; finfo->fi_btop < bbot;
+ finfo->fi_btop++, p++)
+ if (p->hf_file)
-+ au_hfput(p, execed);
++ au_hfput(p, /*execed*/0);
+ bbot = au_sbbot(sb);
+ }
+
@@ -12918,7 +14675,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ if (p->hf_file) {
+ if (file_inode(p->hf_file))
+ break;
-+ au_hfput(p, execed);
++ au_hfput(p, /*execed*/0);
+ }
+ AuDebugOn(fidir->fd_bbot < finfo->fi_btop);
+}
@@ -12980,7 +14737,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+
+/* common function to regular file and dir */
+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
-+ int wlock)
++ int wlock, unsigned int fi_lsc)
+{
+ int err;
+ unsigned int sigen, figen;
@@ -12993,9 +14750,12 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+ dentry = file->f_path.dentry;
+ inode = d_inode(dentry);
+ sigen = au_sigen(dentry->d_sb);
-+ fi_write_lock(file);
++ fi_write_lock_nested(file, fi_lsc);
+ figen = au_figen(file);
-+ di_write_lock_child(dentry);
++ if (!fi_lsc)
++ di_write_lock_child(dentry);
++ else
++ di_write_lock_child2(dentry);
+ btop = au_dbtop(dentry);
+ pseudo_link = (btop != au_ibtop(inode));
+ if (sigen == figen && !pseudo_link && au_fbtop(file) == btop) {
@@ -13117,10 +14877,10 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
+};
diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
--- /usr/share/empty/fs/aufs/file.h 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/file.h 2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,294 @@
++++ linux/fs/aufs/file.h 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,330 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13183,11 +14943,8 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+ };
+ struct au_fidir *fi_hdir; /* for dir only */
+
-+ struct hlist_node fi_hlist;
-+ union {
-+ struct file *fi_file; /* very ugly */
-+ struct llist_node fi_lnode; /* delayed free */
-+ };
++ struct hlist_bl_node fi_hlist;
++ struct file *fi_file; /* very ugly */
+} ____cacheline_aligned_in_smp;
+
+/* ---------------------------------------------------------------------- */
@@ -13198,7 +14955,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags,
+ struct file *file, int force_wr);
+struct au_do_open_args {
-+ int no_lock;
++ int aopen;
+ int (*open)(struct file *file, int flags,
+ struct file *h_file);
+ struct au_fidir *fidir;
@@ -13209,7 +14966,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+struct au_pin;
+int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin);
+int au_reval_and_lock_fdi(struct file *file, int (*reopen)(struct file *file),
-+ int wlock);
++ int wlock, unsigned int fi_lsc);
+int au_do_flush(struct file *file, fl_owner_t id,
+ int (*flush)(struct file *file, fl_owner_t id));
+
@@ -13235,7 +14992,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+extern const struct file_operations aufs_file_fop;
+int au_do_open_nondir(struct file *file, int flags, struct file *h_file);
+int aufs_release_nondir(struct inode *inode __maybe_unused, struct file *file);
-+struct file *au_read_pre(struct file *file, int keep_fi);
++struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc);
+
+/* finfo.c */
+void au_hfput(struct au_hfile *hf, int execed);
@@ -13247,7 +15004,7 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+int au_fidir_realloc(struct au_finfo *finfo, int nbr, int may_shrink);
+
+void au_fi_init_once(void *_fi);
-+void au_finfo_fin(struct file *file, int atonce);
++void au_finfo_fin(struct file *file);
+int au_finfo_init(struct file *file, struct au_fidir *fidir);
+
+/* ioctl.c */
@@ -13274,6 +15031,45 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+ */
+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
+
++/* lock subclass for finfo */
++enum {
++ AuLsc_FI_1,
++ AuLsc_FI_2
++};
++
++static inline void fi_read_lock_nested(struct file *f, unsigned int lsc)
++{
++ au_rw_read_lock_nested(&au_fi(f)->fi_rwsem, lsc);
++}
++
++static inline void fi_write_lock_nested(struct file *f, unsigned int lsc)
++{
++ au_rw_write_lock_nested(&au_fi(f)->fi_rwsem, lsc);
++}
++
++/*
++ * fi_read_lock_1, fi_write_lock_1,
++ * fi_read_lock_2, fi_write_lock_2
++ */
++#define AuReadLockFunc(name) \
++static inline void fi_read_lock_##name(struct file *f) \
++{ fi_read_lock_nested(f, AuLsc_FI_##name); }
++
++#define AuWriteLockFunc(name) \
++static inline void fi_write_lock_##name(struct file *f) \
++{ fi_write_lock_nested(f, AuLsc_FI_##name); }
++
++#define AuRWLockFuncs(name) \
++ AuReadLockFunc(name) \
++ AuWriteLockFunc(name)
++
++AuRWLockFuncs(1);
++AuRWLockFuncs(2);
++
++#undef AuReadLockFunc
++#undef AuWriteLockFunc
++#undef AuRWLockFuncs
++
+#define FiMustNoWaiters(f) AuRwMustNoWaiters(&au_fi(f)->fi_rwsem)
+#define FiMustAnyLock(f) AuRwMustAnyLock(&au_fi(f)->fi_rwsem)
+#define FiMustWriteLock(f) AuRwMustWriteLock(&au_fi(f)->fi_rwsem)
@@ -13415,10 +15211,10 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
+#endif /* __AUFS_FILE_H__ */
diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
--- /usr/share/empty/fs/aufs/finfo.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/finfo.c 2016-10-09 16:55:38.889431135 +0200
-@@ -0,0 +1,151 @@
++++ linux/fs/aufs/finfo.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,148 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13521,7 +15317,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
+
+/* ---------------------------------------------------------------------- */
+
-+void au_finfo_fin(struct file *file, int atonce)
++void au_finfo_fin(struct file *file)
+{
+ struct au_finfo *finfo;
+
@@ -13530,10 +15326,7 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
+ finfo = au_fi(file);
+ AuDebugOn(finfo->fi_hdir);
+ AuRwDestroy(&finfo->fi_rwsem);
-+ if (!atonce)
-+ au_cache_dfree_finfo(finfo);
-+ else
-+ au_cache_free_finfo(finfo);
++ au_cache_free_finfo(finfo);
+}
+
+void au_fi_init_once(void *_finfo)
@@ -13570,10 +15363,10 @@ diff -urN /usr/share/empty/fs/aufs/finfo.c linux/fs/aufs/finfo.c
+}
diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
--- /usr/share/empty/fs/aufs/f_op.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/f_op.c 2016-12-17 12:28:17.595211562 +0100
-@@ -0,0 +1,723 @@
++++ linux/fs/aufs/f_op.c 2018-04-15 08:49:13.397817296 +0200
+@@ -0,0 +1,817 @@
+/*
-+ * Copyright (C) 2005-2016 Junjiro R. Okajima
++ * Copyright (C) 2005-2018 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
@@ -13673,17 +15466,15 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+{
+ struct au_finfo *finfo;
+ aufs_bindex_t bindex;
-+ int delayed;
+
+ finfo = au_fi(file);
-+ au_sphl_del(&finfo->fi_hlist,
-+ &au_sbi(file->f_path.dentry->d_sb)->si_files);
++ au_hbl_del(&finfo->fi_hlist,
++ &au_sbi(file->f_path.dentry->d_sb)->si_files);
+ bindex = finfo->fi_btop;
+ if (bindex >= 0)
+ au_set_h_fptr(file, bindex, NULL);
+
-+ delayed = (current->flags & PF_KTHREAD) || in_interrupt();
-+ au_finfo_fin(file, delayed);
++ au_finfo_fin(file);
+ return 0;
+}
+
@@ -13716,12 +15507,12 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ */
+
+/* Callers should call au_read_post() or fput() in the end */
-+struct file *au_read_pre(struct file *file, int keep_fi)
++struct file *au_read_pre(struct file *file, int keep_fi, unsigned int lsc)
+{
+ struct file *h_file;
+ int err;
+
-+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0);
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/0, lsc);
+ if (!err) {
+ di_read_unlock(file->f_path.dentry, AuLock_IR);
+ h_file = au_hf_top(file);
@@ -13742,6 +15533,10 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+}
+
+struct au_write_pre {
++ /* input */
++ unsigned int lsc;
++
++ /* output */
+ blkcnt_t blks;
+ aufs_bindex_t btop;
+};
@@ -13757,9 +15552,13 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ struct file *h_file;
+ struct dentry *dentry;
+ int err;
++ unsigned int lsc;
+ struct au_pin pin;
+
-+ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1);
++ lsc = 0;
++ if (wpre)
++ lsc = wpre->lsc;
++ err = au_reval_and_lock_fdi(file, au_reopen_nondir, /*wlock*/1, lsc);
+ h_file = ERR_PTR(err);
+ if (unlikely(err))
+ goto out;
@@ -13801,12 +15600,11 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ h_inode = file_inode(h_file);
+ inode->i_mode = h_inode->i_mode;
+ ii_write_unlock(inode);
-+ fput(h_file);
-+
+ /* AuDbg("blks %llu, %llu\n", (u64)blks, (u64)h_inode->i_blocks); */
+ if (written > 0)
+ au_fhsm_wrote(inode->i_sb, wpre->btop,
+ /*force*/h_inode->i_blocks > wpre->blks);
++ fput(h_file);
+}
+
+static ssize_t aufs_read(struct file *file, char __user *buf, size_t count,
@@ -13821,7 +15619,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ sb = inode->i_sb;
+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ h_file = au_read_pre(file, /*keep_fi*/0);
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out;
@@ -13871,6 +15669,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ inode = file_inode(file);
+ au_mtx_and_read_lock(inode);
+
++ wpre.lsc = 0;
+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
@@ -13930,7 +15729,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ sb = inode->i_sb;
+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ h_file = au_read_pre(file, /*keep_fi*/1);
++ h_file = au_read_pre(file, /*keep_fi*/1, /*lsc*/0);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out;
@@ -13965,6 +15764,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ inode = file_inode(file);
+ au_mtx_and_read_lock(inode);
+
++ wpre.lsc = 0;
+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
@@ -13992,7 +15792,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ sb = inode->i_sb;
+ si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLMW);
+
-+ h_file = au_read_pre(file, /*keep_fi*/0);
++ h_file = au_read_pre(file, /*keep_fi*/0, /*lsc*/0);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
+ goto out;
@@ -14019,6 +15819,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ inode = file_inode(file);
+ au_mtx_and_read_lock(inode);
+
++ wpre.lsc = 0;
+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
@@ -14044,6 +15845,7 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ inode = file_inode(file);
+ au_mtx_and_read_lock(inode);
+
++ wpre.lsc = 0;
+ h_file = au_write_pre(file, /*do_ready*/1, &wpre);
+ err = PTR_ERR(h_file);
+ if (IS_ERR(h_file))
@@ -14060,6 +15862,88 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
+ return err;
+}
+
++static ssize_t aufs_copy_file_range(struct file *src, loff_t src_pos,
++ struct file *dst, loff_t dst_pos,
++ size_t len, unsigned int flags)
++{
++ ssize_t err;
++ struct au_write_pre wpre;
++ enum { SRC, DST };
++ struct {
++ struct inode *inode;
++ struct file *h_file;
++ struct super_block *h_sb;
++ } a[2];
++#define a_src a[SRC]
++#define a_dst a[DST]
++
++ err = -EINVAL;
++ a_src.inode = file_inode(src);
++ if (unlikely(!S_ISREG(a_src.inode->i_mode)))
++ goto out;
++ a_dst.inode = file_inode(dst);
++ if (unlikely(!S_ISREG(a_dst.inode->i_mode)))
++ goto out;
++
++ au_mtx_and_read_lock(a_dst.inode);
++ /*
++ * in order to match the order in di_write_lock2_{child,parent}(),
++ * use f_path.dentry for this comparision.
++ */
++ if (src->f_path.dentry < dst->f_path.dentry) {
++ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_1);
++ err = PTR_ERR(a_src.h_file);
++ if (IS_ERR(a_src.h_file))
++ goto out_si;
++
++ wpre.lsc = AuLsc_FI_2;
++ a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre);
++ err = PTR_ERR(a_dst.h_file);
++ if (IS_ERR(a_dst.h_file)) {
++ au_read_post(a_src.inode, a_src.h_file);
++ goto out_si;
++ }
++ } else {
++ wpre.lsc = AuLsc_FI_1;
++ a_dst.h_file = au_write_pre(dst, /*do_ready*/1, &wpre);
++ err = PTR_ERR(a_dst.h_file);
++ if (IS_ERR(a_dst.h_file))
++ goto out_si;
++
++ a_src.h_file = au_read_pre(src, /*keep_fi*/1, AuLsc_FI_2);
++ err = PTR_ERR(a_src.h_file);
++ if (IS_ERR(a_src.h_file)) {
<Skipped 3743 lines>
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/kernel.git/commitdiff/ae9dfd79ace7e0a2a98099f2c18227fc16643bf3
More information about the pld-cvs-commit
mailing list