]> git.pld-linux.org Git - packages/kernel.git/commitdiff
- initial 4.14.0
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Sun, 12 Nov 2017 22:03:17 +0000 (23:03 +0100)
committerArkadiusz Miśkiewicz <arekm@maven.pl>
Sun, 12 Nov 2017 22:03:17 +0000 (23:03 +0100)
17 files changed:
0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch [moved from 0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch with 100% similarity]
0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch [deleted file]
0003-apparmor-Fix-logical-error-in-verify_header.patch [deleted file]
0004-apparmor-Fix-an-error-code-in-aafs_create.patch [deleted file]
0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch [deleted file]
0006-apparmor-add-the-ability-to-mediate-signals.patch [deleted file]
0007-apparmor-add-mount-mediation.patch [deleted file]
0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch [deleted file]
0009-apparmor-add-support-for-absolute-root-view-based-la.patch [deleted file]
0010-apparmor-make-policy_unpack-able-to-audit-different-.patch [deleted file]
0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch [deleted file]
0013-apparmor-move-new_null_profile-to-after-profile-look.patch [deleted file]
0014-apparmor-fix-race-condition-in-null-profile-creation.patch [deleted file]
0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch [deleted file]
0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch [deleted file]
kernel-aufs4.patch
kernel.spec

diff --git a/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch b/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch
deleted file mode 100644 (file)
index 4d9836d..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-From c6cad5e65a23dcafa1821ca381901297664d9c64 Mon Sep 17 00:00:00 2001
-From: Geert Uytterhoeven <geert@linux-m68k.org>
-Date: Thu, 6 Jul 2017 10:56:21 +0200
-Subject: [PATCH 02/17] apparmor: Fix shadowed local variable in
- unpack_trans_table()
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-with W=2:
-
-    security/apparmor/policy_unpack.c: In function ‘unpack_trans_table’:
-    security/apparmor/policy_unpack.c:469: warning: declaration of ‘pos’ shadows a previous local
-    security/apparmor/policy_unpack.c:451: warning: shadowed declaration is here
-
-Rename the old "pos" to "saved_pos" to fix this.
-
-Fixes: 5379a3312024a8be ("apparmor: support v7 transition format compatible with label_parse")
-Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
-Reviewed-by: Serge Hallyn <serge@hallyn.com>
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit 966d631935a578fadb5770f17a957ee1a969d868)
----
- security/apparmor/policy_unpack.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
-index c600f4dd1783..2d5a1a007b06 100644
---- a/security/apparmor/policy_unpack.c
-+++ b/security/apparmor/policy_unpack.c
-@@ -448,7 +448,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e)
-  */
- static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
- {
--      void *pos = e->pos;
-+      void *saved_pos = e->pos;
-       /* exec table is optional */
-       if (unpack_nameX(e, AA_STRUCT, "xtable")) {
-@@ -511,7 +511,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile)
- fail:
-       aa_free_domain_entries(&profile->file.trans);
--      e->pos = pos;
-+      e->pos = saved_pos;
-       return 0;
- }
--- 
-2.11.0
-
diff --git a/0003-apparmor-Fix-logical-error-in-verify_header.patch b/0003-apparmor-Fix-logical-error-in-verify_header.patch
deleted file mode 100644 (file)
index be4f854..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-From 9934296cba701d429a0fc0cf071a40c8c3a1587e Mon Sep 17 00:00:00 2001
-From: Christos Gkekas <chris.gekas@gmail.com>
-Date: Sat, 8 Jul 2017 20:50:21 +0100
-Subject: [PATCH 03/17] apparmor: Fix logical error in verify_header()
-
-verify_header() is currently checking whether interface version is less
-than 5 *and* greater than 7, which always evaluates to false. Instead it
-should check whether it is less than 5 *or* greater than 7.
-
-Signed-off-by: Christos Gkekas <chris.gekas@gmail.com>
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit c54a2175e3a6bf6c697d249bba1aa729e06c7ba8)
----
- security/apparmor/policy_unpack.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
-index 2d5a1a007b06..bda0dce3b582 100644
---- a/security/apparmor/policy_unpack.c
-+++ b/security/apparmor/policy_unpack.c
-@@ -832,7 +832,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
-        * if not specified use previous version
-        * Mask off everything that is not kernel abi version
-        */
--      if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) {
-+      if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) {
-               audit_iface(NULL, NULL, NULL, "unsupported interface version",
-                           e, error);
-               return error;
--- 
-2.11.0
-
diff --git a/0004-apparmor-Fix-an-error-code-in-aafs_create.patch b/0004-apparmor-Fix-an-error-code-in-aafs_create.patch
deleted file mode 100644 (file)
index 4e937e0..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-From 8b3851c7b83f32f2be9d4b48371ddf033afedf62 Mon Sep 17 00:00:00 2001
-From: Dan Carpenter <dan.carpenter@oracle.com>
-Date: Thu, 13 Jul 2017 10:39:20 +0300
-Subject: [PATCH 04/17] apparmor: Fix an error code in aafs_create()
-
-We accidentally forgot to set the error code on this path.  It means we
-return NULL instead of an error pointer.  I looked through a bunch of
-callers and I don't think it really causes a big issue, but the
-documentation says we're supposed to return error pointers here.
-
-Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
-Acked-by: Serge Hallyn <serge@hallyn.com>
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit aee58bf341db52a3a3563c6b972bfd4fc2d41e46)
----
- security/apparmor/apparmorfs.c | 4 +++-
- 1 file changed, 3 insertions(+), 1 deletion(-)
-
-diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
-index 853c2ec8e0c9..2caeb748070c 100644
---- a/security/apparmor/apparmorfs.c
-+++ b/security/apparmor/apparmorfs.c
-@@ -248,8 +248,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode,
-       inode_lock(dir);
-       dentry = lookup_one_len(name, parent, strlen(name));
--      if (IS_ERR(dentry))
-+      if (IS_ERR(dentry)) {
-+              error = PTR_ERR(dentry);
-               goto fail_lock;
-+      }
-       if (d_really_is_positive(dentry)) {
-               error = -EEXIST;
--- 
-2.11.0
-
diff --git a/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch b/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch
deleted file mode 100644 (file)
index beea3de..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-From 4b56e146905bbad2c79ea92e3f49e210ca527572 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Mon, 31 Jul 2017 23:44:37 -0700
-Subject: [PATCH 05/17] apparmor: Redundant condition: prev_ns. in
- [label.c:1498]
-
-Reported-by: David Binderman <dcb314@hotmail.com>
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit d323d2c17cfcc54b6845bfc1d13bca5cef210fc7)
----
- security/apparmor/label.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/security/apparmor/label.c b/security/apparmor/label.c
-index e052eaba1cf6..e324f4df3e34 100644
---- a/security/apparmor/label.c
-+++ b/security/apparmor/label.c
-@@ -1495,7 +1495,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view,
-               view = profiles_ns(profile);
-       if (view != profile->ns &&
--          (!prev_ns || (prev_ns && *prev_ns != profile->ns))) {
-+          (!prev_ns || (*prev_ns != profile->ns))) {
-               if (prev_ns)
-                       *prev_ns = profile->ns;
-               ns_name = aa_ns_name(view, profile->ns,
--- 
-2.11.0
-
diff --git a/0006-apparmor-add-the-ability-to-mediate-signals.patch b/0006-apparmor-add-the-ability-to-mediate-signals.patch
deleted file mode 100644 (file)
index ee3672d..0000000
+++ /dev/null
@@ -1,397 +0,0 @@
-From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Tue, 18 Jul 2017 22:56:22 -0700
-Subject: [PATCH 06/17] apparmor: add the ability to mediate signals
-
-Add signal mediation where the signal can be mediated based on the
-signal, direction, or the label or the peer/target. The signal perms
-are verified on a cross check to ensure policy consistency in the case
-of incremental policy load/replacement.
-
-The optimization of skipping the cross check when policy is guaranteed
-to be consistent (single compile unit) remains to be done.
-
-policy rules have the form of
-  SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ]
-                [ SIGNAL SET ] [ SIGNAL PEER ]
-
-  SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST
-
-  SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL
-                           ACCESS ')'
-
-  SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' |
-                    'receive' )
-
-  SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')'
-
-  SIGNAL LIST = Comma or space separated list of SIGNALS
-
-  SIGNALS = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' |
-              'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' |
-             'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' |
-             'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' |
-             'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' |
-             'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32'
-            )
-
-  SIGNAL PEER = 'peer' '=' AARE
-
-eg.
-  signal,                                 # allow all signals
-  signal send set=(hup, kill) peer=foo,
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44)
----
- security/apparmor/apparmorfs.c        |  7 +++
- security/apparmor/include/apparmor.h  |  1 +
- security/apparmor/include/audit.h     |  2 +
- security/apparmor/include/ipc.h       |  6 +++
- security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++
- security/apparmor/ipc.c               | 99 +++++++++++++++++++++++++++++++++++
- security/apparmor/lsm.c               | 21 ++++++++
- 7 files changed, 231 insertions(+)
- create mode 100644 security/apparmor/include/sig_names.h
-
-diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
-index 2caeb748070c..a5f9e1aa51f7 100644
---- a/security/apparmor/apparmorfs.c
-+++ b/security/apparmor/apparmorfs.c
-@@ -32,6 +32,7 @@
- #include "include/audit.h"
- #include "include/context.h"
- #include "include/crypto.h"
-+#include "include/ipc.h"
- #include "include/policy_ns.h"
- #include "include/label.h"
- #include "include/policy.h"
-@@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = {
-       { }
- };
-+static struct aa_sfs_entry aa_sfs_entry_signal[] = {
-+      AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK),
-+      { }
-+};
-+
- static struct aa_sfs_entry aa_sfs_entry_domain[] = {
-       AA_SFS_FILE_BOOLEAN("change_hat",       1),
-       AA_SFS_FILE_BOOLEAN("change_hatv",      1),
-@@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
-       AA_SFS_DIR("rlimit",                    aa_sfs_entry_rlimit),
-       AA_SFS_DIR("caps",                      aa_sfs_entry_caps),
-       AA_SFS_DIR("ptrace",                    aa_sfs_entry_ptrace),
-+      AA_SFS_DIR("signal",                    aa_sfs_entry_signal),
-       AA_SFS_DIR("query",                     aa_sfs_entry_query),
-       { }
- };
-diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
-index aaf893f4e4f5..962a20a75e01 100644
---- a/security/apparmor/include/apparmor.h
-+++ b/security/apparmor/include/apparmor.h
-@@ -28,6 +28,7 @@
- #define AA_CLASS_RLIMITS      5
- #define AA_CLASS_DOMAIN               6
- #define AA_CLASS_PTRACE               9
-+#define AA_CLASS_SIGNAL               10
- #define AA_CLASS_LABEL                16
- #define AA_CLASS_LAST         AA_CLASS_LABEL
-diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
-index c68839a44351..d9a156ae11b9 100644
---- a/security/apparmor/include/audit.h
-+++ b/security/apparmor/include/audit.h
-@@ -86,6 +86,7 @@ enum audit_type {
- #define OP_SHUTDOWN "socket_shutdown"
- #define OP_PTRACE "ptrace"
-+#define OP_SIGNAL "signal"
- #define OP_EXEC "exec"
-@@ -126,6 +127,7 @@ struct apparmor_audit_data {
-                       long pos;
-                       const char *ns;
-               } iface;
-+              int signal;
-               struct {
-                       int rlim;
-                       unsigned long max;
-diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h
-index 656fdb81c8a0..5ffc218d1e74 100644
---- a/security/apparmor/include/ipc.h
-+++ b/security/apparmor/include/ipc.h
-@@ -27,8 +27,14 @@ struct aa_profile;
- #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \
-                            AA_MAY_BE_READ | AA_MAY_BE_TRACED)
-+#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE)
-+
-+#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \
-+      "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \
-+      "xcpu xfsz vtalrm prof winch io pwr sys emt lost"
- int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
-                 u32 request);
-+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig);
- #endif /* __AA_IPC_H */
-diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h
-new file mode 100644
-index 000000000000..0d4395f231ca
---- /dev/null
-+++ b/security/apparmor/include/sig_names.h
-@@ -0,0 +1,95 @@
-+#include <linux/signal.h>
-+
-+#define SIGUNKNOWN 0
-+#define MAXMAPPED_SIG 35
-+/* provide a mapping of arch signal to internal signal # for mediation
-+ * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO
-+ * map to the same entry those that may/or may not get a separate entry
-+ */
-+static const int sig_map[MAXMAPPED_SIG] = {
-+      [0] = MAXMAPPED_SIG,    /* existence test */
-+      [SIGHUP] = 1,
-+      [SIGINT] = 2,
-+      [SIGQUIT] = 3,
-+      [SIGILL] = 4,
-+      [SIGTRAP] = 5,          /* -, 5, - */
-+      [SIGABRT] = 6,          /*  SIGIOT: -, 6, - */
-+      [SIGBUS] = 7,           /* 10, 7, 10 */
-+      [SIGFPE] = 8,
-+      [SIGKILL] = 9,
-+      [SIGUSR1] = 10,         /* 30, 10, 16 */
-+      [SIGSEGV] = 11,
-+      [SIGUSR2] = 12,         /* 31, 12, 17 */
-+      [SIGPIPE] = 13,
-+      [SIGALRM] = 14,
-+      [SIGTERM] = 15,
-+      [SIGSTKFLT] = 16,       /* -, 16, - */
-+      [SIGCHLD] = 17,         /* 20, 17, 18.  SIGCHLD -, -, 18 */
-+      [SIGCONT] = 18,         /* 19, 18, 25 */
-+      [SIGSTOP] = 19,         /* 17, 19, 23 */
-+      [SIGTSTP] = 20,         /* 18, 20, 24 */
-+      [SIGTTIN] = 21,         /* 21, 21, 26 */
-+      [SIGTTOU] = 22,         /* 22, 22, 27 */
-+      [SIGURG] = 23,          /* 16, 23, 21 */
-+      [SIGXCPU] = 24,         /* 24, 24, 30 */
-+      [SIGXFSZ] = 25,         /* 25, 25, 31 */
-+      [SIGVTALRM] = 26,       /* 26, 26, 28 */
-+      [SIGPROF] = 27,         /* 27, 27, 29 */
-+      [SIGWINCH] = 28,        /* 28, 28, 20 */
-+      [SIGIO] = 29,           /* SIGPOLL: 23, 29, 22 */
-+      [SIGPWR] = 30,          /* 29, 30, 19.  SIGINFO 29, -, - */
-+#ifdef SIGSYS
-+      [SIGSYS] = 31,          /* 12, 31, 12. often SIG LOST/UNUSED */
-+#endif
-+#ifdef SIGEMT
-+      [SIGEMT] = 32,          /* 7, - , 7 */
-+#endif
-+#if defined(SIGLOST) && SIGPWR != SIGLOST             /* sparc */
-+      [SIGLOST] = 33,         /* unused on Linux */
-+#endif
-+#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS
-+      [SIGUNUSED] = 34,       /* -, 31, - */
-+#endif
-+};
-+
-+/* this table is ordered post sig_map[sig] mapping */
-+static const char *const sig_names[MAXMAPPED_SIG + 1] = {
-+      "unknown",
-+      "hup",
-+      "int",
-+      "quit",
-+      "ill",
-+      "trap",
-+      "abrt",
-+      "bus",
-+      "fpe",
-+      "kill",
-+      "usr1",
-+      "segv",
-+      "usr2",
-+      "pipe",
-+      "alrm",
-+      "term",
-+      "stkflt",
-+      "chld",
-+      "cont",
-+      "stop",
-+      "stp",
-+      "ttin",
-+      "ttou",
-+      "urg",
-+      "xcpu",
-+      "xfsz",
-+      "vtalrm",
-+      "prof",
-+      "winch",
-+      "io",
-+      "pwr",
-+      "sys",
-+      "emt",
-+      "lost",
-+      "unused",
-+
-+      "exists",       /* always last existence test mapped to MAXMAPPED_SIG */
-+};
-+
-diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c
-index 11e66b5bbc42..66fb9ede9447 100644
---- a/security/apparmor/ipc.c
-+++ b/security/apparmor/ipc.c
-@@ -20,6 +20,7 @@
- #include "include/context.h"
- #include "include/policy.h"
- #include "include/ipc.h"
-+#include "include/sig_names.h"
- /**
-  * audit_ptrace_mask - convert mask to permission string
-@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee,
- }
-+static inline int map_signal_num(int sig)
-+{
-+      if (sig > SIGRTMAX)
-+              return SIGUNKNOWN;
-+      else if (sig >= SIGRTMIN)
-+              return sig - SIGRTMIN + 128;    /* rt sigs mapped to 128 */
-+      else if (sig <= MAXMAPPED_SIG)
-+              return sig_map[sig];
-+      return SIGUNKNOWN;
-+}
-+
-+/**
-+ * audit_file_mask - convert mask to permission string
-+ * @buffer: buffer to write string to (NOT NULL)
-+ * @mask: permission mask to convert
-+ */
-+static void audit_signal_mask(struct audit_buffer *ab, u32 mask)
-+{
-+      if (mask & MAY_READ)
-+              audit_log_string(ab, "receive");
-+      if (mask & MAY_WRITE)
-+              audit_log_string(ab, "send");
-+}
-+
-+/**
-+ * audit_cb - call back for signal specific audit fields
-+ * @ab: audit_buffer  (NOT NULL)
-+ * @va: audit struct to audit values of  (NOT NULL)
-+ */
-+static void audit_signal_cb(struct audit_buffer *ab, void *va)
-+{
-+      struct common_audit_data *sa = va;
-+
-+      if (aad(sa)->request & AA_SIGNAL_PERM_MASK) {
-+              audit_log_format(ab, " requested_mask=");
-+              audit_signal_mask(ab, aad(sa)->request);
-+              if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) {
-+                      audit_log_format(ab, " denied_mask=");
-+                      audit_signal_mask(ab, aad(sa)->denied);
-+              }
-+      }
-+      if (aad(sa)->signal <= MAXMAPPED_SIG)
-+              audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]);
-+      else
-+              audit_log_format(ab, " signal=rtmin+%d",
-+                               aad(sa)->signal - 128);
-+      audit_log_format(ab, " peer=");
-+      aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer,
-+                      FLAGS_NONE, GFP_ATOMIC);
-+}
-+
-+/* TODO: update to handle compound name&name2, conditionals */
-+static void profile_match_signal(struct aa_profile *profile, const char *label,
-+                               int signal, struct aa_perms *perms)
-+{
-+      unsigned int state;
-+
-+      /* TODO: secondary cache check <profile, profile, perm> */
-+      state = aa_dfa_next(profile->policy.dfa,
-+                          profile->policy.start[AA_CLASS_SIGNAL],
-+                          signal);
-+      state = aa_dfa_match(profile->policy.dfa, state, label);
-+      aa_compute_perms(profile->policy.dfa, state, perms);
-+}
-+
-+static int profile_signal_perm(struct aa_profile *profile,
-+                             struct aa_profile *peer, u32 request,
-+                             struct common_audit_data *sa)
-+{
-+      struct aa_perms perms;
-+
-+      if (profile_unconfined(profile) ||
-+          !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL))
-+              return 0;
-+
-+      aad(sa)->peer = &peer->label;
-+      profile_match_signal(profile, peer->base.hname, aad(sa)->signal,
-+                           &perms);
-+      aa_apply_modes_to_perms(profile, &perms);
-+      return aa_check_perms(profile, &perms, request, sa, audit_signal_cb);
-+}
-+
-+static int aa_signal_cross_perm(struct aa_profile *sender,
-+                              struct aa_profile *target,
-+                              struct common_audit_data *sa)
-+{
-+      return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa),
-+                    profile_signal_perm(target, sender, MAY_READ, sa));
-+}
-+
-+int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig)
-+{
-+      DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL);
-+
-+      aad(&sa)->signal = map_signal_num(sig);
-+      return xcheck_labels_profiles(sender, target, aa_signal_cross_perm,
-+                                    &sa);
-+}
-diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
-index 867bcd154c7e..af22f3dfbcce 100644
---- a/security/apparmor/lsm.c
-+++ b/security/apparmor/lsm.c
-@@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task,
-       return error;
- }
-+static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
-+                            int sig, u32 secid)
-+{
-+      struct aa_label *cl, *tl;
-+      int error;
-+
-+      if (secid)
-+              /* TODO: after secid to label mapping is done.
-+               *  Dealing with USB IO specific behavior
-+               */
-+              return 0;
-+      cl = __begin_current_label_crit_section();
-+      tl = aa_get_task_label(target);
-+      error = aa_may_signal(cl, tl, sig);
-+      aa_put_label(tl);
-+      __end_current_label_crit_section(cl);
-+
-+      return error;
-+}
-+
- static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
-       LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
-       LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
-@@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
-       LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec),
-       LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit),
-+      LSM_HOOK_INIT(task_kill, apparmor_task_kill),
- };
- /*
--- 
-2.11.0
-
diff --git a/0007-apparmor-add-mount-mediation.patch b/0007-apparmor-add-mount-mediation.patch
deleted file mode 100644 (file)
index 99dac5c..0000000
+++ /dev/null
@@ -1,1051 +0,0 @@
-From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Tue, 18 Jul 2017 23:04:47 -0700
-Subject: [PATCH 07/17] apparmor: add mount mediation
-
-Add basic mount mediation. That allows controlling based on basic
-mount parameters. It does not include special mount parameters for
-apparmor, super block labeling, or any triggers for apparmor namespace
-parameter modifications on pivot root.
-
-default userspace policy rules have the form of
-  MOUNT RULE = ( MOUNT | REMOUNT | UMOUNT )
-
-  MOUNT = [ QUALIFIERS ] 'mount' [ MOUNT CONDITIONS ] [ SOURCE FILEGLOB ]
-          [ '->' MOUNTPOINT FILEGLOB ]
-
-  REMOUNT = [ QUALIFIERS ] 'remount' [ MOUNT CONDITIONS ]
-            MOUNTPOINT FILEGLOB
-
-  UMOUNT = [ QUALIFIERS ] 'umount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB
-
-  MOUNT CONDITIONS = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' )
-                       MOUNT FSTYPE EXPRESSION ]
-                      [ 'options' ( '=' | 'in' ) MOUNT FLAGS EXPRESSION ]
-
-  MOUNT FSTYPE EXPRESSION = ( MOUNT FSTYPE LIST | MOUNT EXPRESSION )
-
-  MOUNT FSTYPE LIST = Comma separated list of valid filesystem and
-                      virtual filesystem types (eg ext4, debugfs, etc)
-
-  MOUNT FLAGS EXPRESSION = ( MOUNT FLAGS LIST | MOUNT EXPRESSION )
-
-  MOUNT FLAGS LIST = Comma separated list of MOUNT FLAGS.
-
-  MOUNT FLAGS = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' |
-                  'noexec' | 'exec' | 'sync' | 'async' | 'remount' |
-                 'mand' | 'nomand' | 'dirsync' | 'noatime' | 'atime' |
-                 'nodiratime' | 'diratime' | 'bind' | 'rbind' | 'move' |
-                 'verbose' | 'silent' | 'loud' | 'acl' | 'noacl' |
-                 'unbindable' | 'runbindable' | 'private' | 'rprivate' |
-                 'slave' | 'rslave' | 'shared' | 'rshared' |
-                 'relatime' | 'norelatime' | 'iversion' | 'noiversion' |
-                 'strictatime' | 'nouser' | 'user' )
-
-  MOUNT EXPRESSION = ( ALPHANUMERIC | AARE ) ...
-
-  PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ]
-                    [ NEW ROOT FILEGLOB ]
-
-  SOURCE FILEGLOB = FILEGLOB
-
-  MOUNTPOINT FILEGLOB = FILEGLOB
-
-eg.
-  mount,
-  mount /dev/foo,
-  mount options=ro /dev/foo -> /mnt/,
-  mount options in (ro,atime) /dev/foo -> /mnt/,
-  mount options=ro options=atime,
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit fa488437d0f95b2e5db1e624341fe0d5a233f729)
----
- security/apparmor/Makefile           |   2 +-
- security/apparmor/apparmorfs.c       |   8 +-
- security/apparmor/domain.c           |   4 +-
- security/apparmor/include/apparmor.h |   1 +
- security/apparmor/include/audit.h    |  11 +
- security/apparmor/include/domain.h   |   5 +
- security/apparmor/include/mount.h    |  54 +++
- security/apparmor/lsm.c              |  64 ++++
- security/apparmor/mount.c            | 696 +++++++++++++++++++++++++++++++++++
- 9 files changed, 841 insertions(+), 4 deletions(-)
- create mode 100644 security/apparmor/include/mount.h
- create mode 100644 security/apparmor/mount.c
-
-diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
-index a16b195274de..81a34426d024 100644
---- a/security/apparmor/Makefile
-+++ b/security/apparmor/Makefile
-@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o
- apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \
-               path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \
--              resource.o secid.o file.o policy_ns.o label.o
-+              resource.o secid.o file.o policy_ns.o label.o mount.o
- apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
- clean-files := capability_names.h rlim_names.h
-diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
-index a5f9e1aa51f7..8fa6c898c44b 100644
---- a/security/apparmor/apparmorfs.c
-+++ b/security/apparmor/apparmorfs.c
-@@ -2159,9 +2159,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
-       { }
- };
-+static struct aa_sfs_entry aa_sfs_entry_mount[] = {
-+      AA_SFS_FILE_STRING("mask", "mount umount pivot_root"),
-+      { }
-+};
-+
- static struct aa_sfs_entry aa_sfs_entry_ns[] = {
-       AA_SFS_FILE_BOOLEAN("profile",          1),
--      AA_SFS_FILE_BOOLEAN("pivot_root",       1),
-+      AA_SFS_FILE_BOOLEAN("pivot_root",       0),
-       { }
- };
-@@ -2180,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = {
-       AA_SFS_DIR("policy",                    aa_sfs_entry_policy),
-       AA_SFS_DIR("domain",                    aa_sfs_entry_domain),
-       AA_SFS_DIR("file",                      aa_sfs_entry_file),
-+      AA_SFS_DIR("mount",                     aa_sfs_entry_mount),
-       AA_SFS_DIR("namespaces",                aa_sfs_entry_ns),
-       AA_SFS_FILE_U64("capability",           VFS_CAP_FLAGS_MASK),
-       AA_SFS_DIR("rlimit",                    aa_sfs_entry_rlimit),
-diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
-index d0594446ae3f..ffc8c75a6785 100644
---- a/security/apparmor/domain.c
-+++ b/security/apparmor/domain.c
-@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name)
-  *
-  * Returns: refcounted label, or NULL on failure (MAYBE NULL)
-  */
--static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
--                                     const char **name)
-+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
-+                              const char **name)
- {
-       struct aa_label *label = NULL;
-       u32 xtype = xindex & AA_X_TYPE_MASK;
-diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
-index 962a20a75e01..829082c35faa 100644
---- a/security/apparmor/include/apparmor.h
-+++ b/security/apparmor/include/apparmor.h
-@@ -27,6 +27,7 @@
- #define AA_CLASS_NET          4
- #define AA_CLASS_RLIMITS      5
- #define AA_CLASS_DOMAIN               6
-+#define AA_CLASS_MOUNT                7
- #define AA_CLASS_PTRACE               9
- #define AA_CLASS_SIGNAL               10
- #define AA_CLASS_LABEL                16
-diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
-index d9a156ae11b9..c3fe1c5ef3bc 100644
---- a/security/apparmor/include/audit.h
-+++ b/security/apparmor/include/audit.h
-@@ -71,6 +71,10 @@ enum audit_type {
- #define OP_FMPROT "file_mprotect"
- #define OP_INHERIT "file_inherit"
-+#define OP_PIVOTROOT "pivotroot"
-+#define OP_MOUNT "mount"
-+#define OP_UMOUNT "umount"
-+
- #define OP_CREATE "create"
- #define OP_POST_CREATE "post_create"
- #define OP_BIND "bind"
-@@ -132,6 +136,13 @@ struct apparmor_audit_data {
-                       int rlim;
-                       unsigned long max;
-               } rlim;
-+              struct {
-+                      const char *src_name;
-+                      const char *type;
-+                      const char *trans;
-+                      const char *data;
-+                      unsigned long flags;
-+              } mnt;
-       };
- };
-diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h
-index bab5810b6e9a..db27403346c5 100644
---- a/security/apparmor/include/domain.h
-+++ b/security/apparmor/include/domain.h
-@@ -15,6 +15,8 @@
- #include <linux/binfmts.h>
- #include <linux/types.h>
-+#include "label.h"
-+
- #ifndef __AA_DOMAIN_H
- #define __AA_DOMAIN_H
-@@ -29,6 +31,9 @@ struct aa_domain {
- #define AA_CHANGE_ONEXEC  4
- #define AA_CHANGE_STACK 8
-+struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
-+                              const char **name);
-+
- int apparmor_bprm_set_creds(struct linux_binprm *bprm);
- int apparmor_bprm_secureexec(struct linux_binprm *bprm);
-diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h
-new file mode 100644
-index 000000000000..25d6067fa6ef
---- /dev/null
-+++ b/security/apparmor/include/mount.h
-@@ -0,0 +1,54 @@
-+/*
-+ * AppArmor security module
-+ *
-+ * This file contains AppArmor file mediation function definitions.
-+ *
-+ * Copyright 2017 Canonical Ltd.
-+ *
-+ * 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, version 2 of the
-+ * License.
-+ */
-+
-+#ifndef __AA_MOUNT_H
-+#define __AA_MOUNT_H
-+
-+#include <linux/fs.h>
-+#include <linux/path.h>
-+
-+#include "domain.h"
-+#include "policy.h"
-+
-+/* mount perms */
-+#define AA_MAY_PIVOTROOT      0x01
-+#define AA_MAY_MOUNT          0x02
-+#define AA_MAY_UMOUNT         0x04
-+#define AA_AUDIT_DATA         0x40
-+#define AA_MNT_CONT_MATCH     0x40
-+
-+#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN)
-+
-+int aa_remount(struct aa_label *label, const struct path *path,
-+             unsigned long flags, void *data);
-+
-+int aa_bind_mount(struct aa_label *label, const struct path *path,
-+                const char *old_name, unsigned long flags);
-+
-+
-+int aa_mount_change_type(struct aa_label *label, const struct path *path,
-+                       unsigned long flags);
-+
-+int aa_move_mount(struct aa_label *label, const struct path *path,
-+                const char *old_name);
-+
-+int aa_new_mount(struct aa_label *label, const char *dev_name,
-+               const struct path *path, const char *type, unsigned long flags,
-+               void *data);
-+
-+int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags);
-+
-+int aa_pivotroot(struct aa_label *label, const struct path *old_path,
-+               const struct path *new_path);
-+
-+#endif /* __AA_MOUNT_H */
-diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
-index af22f3dfbcce..4ad0b3a45142 100644
---- a/security/apparmor/lsm.c
-+++ b/security/apparmor/lsm.c
-@@ -38,6 +38,7 @@
- #include "include/policy.h"
- #include "include/policy_ns.h"
- #include "include/procattr.h"
-+#include "include/mount.h"
- /* Flag indicating whether initialization completed */
- int apparmor_initialized;
-@@ -511,6 +512,65 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma,
-                          !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0);
- }
-+static int apparmor_sb_mount(const char *dev_name, const struct path *path,
-+                           const char *type, unsigned long flags, void *data)
-+{
-+      struct aa_label *label;
-+      int error = 0;
-+
-+      /* Discard magic */
-+      if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
-+              flags &= ~MS_MGC_MSK;
-+
-+      flags &= ~AA_MS_IGNORE_MASK;
-+
-+      label = __begin_current_label_crit_section();
-+      if (!unconfined(label)) {
-+              if (flags & MS_REMOUNT)
-+                      error = aa_remount(label, path, flags, data);
-+              else if (flags & MS_BIND)
-+                      error = aa_bind_mount(label, path, dev_name, flags);
-+              else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE |
-+                                MS_UNBINDABLE))
-+                      error = aa_mount_change_type(label, path, flags);
-+              else if (flags & MS_MOVE)
-+                      error = aa_move_mount(label, path, dev_name);
-+              else
-+                      error = aa_new_mount(label, dev_name, path, type,
-+                                           flags, data);
-+      }
-+      __end_current_label_crit_section(label);
-+
-+      return error;
-+}
-+
-+static int apparmor_sb_umount(struct vfsmount *mnt, int flags)
-+{
-+      struct aa_label *label;
-+      int error = 0;
-+
-+      label = __begin_current_label_crit_section();
-+      if (!unconfined(label))
-+              error = aa_umount(label, mnt, flags);
-+      __end_current_label_crit_section(label);
-+
-+      return error;
-+}
-+
-+static int apparmor_sb_pivotroot(const struct path *old_path,
-+                               const struct path *new_path)
-+{
-+      struct aa_label *label;
-+      int error = 0;
-+
-+      label = aa_get_current_label();
-+      if (!unconfined(label))
-+              error = aa_pivotroot(label, old_path, new_path);
-+      aa_put_label(label);
-+
-+      return error;
-+}
-+
- static int apparmor_getprocattr(struct task_struct *task, char *name,
-                               char **value)
- {
-@@ -682,6 +742,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
-       LSM_HOOK_INIT(capget, apparmor_capget),
-       LSM_HOOK_INIT(capable, apparmor_capable),
-+      LSM_HOOK_INIT(sb_mount, apparmor_sb_mount),
-+      LSM_HOOK_INIT(sb_umount, apparmor_sb_umount),
-+      LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot),
-+
-       LSM_HOOK_INIT(path_link, apparmor_path_link),
-       LSM_HOOK_INIT(path_unlink, apparmor_path_unlink),
-       LSM_HOOK_INIT(path_symlink, apparmor_path_symlink),
-diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
-new file mode 100644
-index 000000000000..82a64b58041d
---- /dev/null
-+++ b/security/apparmor/mount.c
-@@ -0,0 +1,696 @@
-+/*
-+ * AppArmor security module
-+ *
-+ * This file contains AppArmor mediation of files
-+ *
-+ * Copyright (C) 1998-2008 Novell/SUSE
-+ * Copyright 2009-2017 Canonical Ltd.
-+ *
-+ * 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, version 2 of the
-+ * License.
-+ */
-+
-+#include <linux/fs.h>
-+#include <linux/mount.h>
-+#include <linux/namei.h>
-+
-+#include "include/apparmor.h"
-+#include "include/audit.h"
-+#include "include/context.h"
-+#include "include/domain.h"
-+#include "include/file.h"
-+#include "include/match.h"
-+#include "include/mount.h"
-+#include "include/path.h"
-+#include "include/policy.h"
-+
-+
-+static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
-+{
-+      if (flags & MS_RDONLY)
-+              audit_log_format(ab, "ro");
-+      else
-+              audit_log_format(ab, "rw");
-+      if (flags & MS_NOSUID)
-+              audit_log_format(ab, ", nosuid");
-+      if (flags & MS_NODEV)
-+              audit_log_format(ab, ", nodev");
-+      if (flags & MS_NOEXEC)
-+              audit_log_format(ab, ", noexec");
-+      if (flags & MS_SYNCHRONOUS)
-+              audit_log_format(ab, ", sync");
-+      if (flags & MS_REMOUNT)
-+              audit_log_format(ab, ", remount");
-+      if (flags & MS_MANDLOCK)
-+              audit_log_format(ab, ", mand");
-+      if (flags & MS_DIRSYNC)
-+              audit_log_format(ab, ", dirsync");
-+      if (flags & MS_NOATIME)
-+              audit_log_format(ab, ", noatime");
-+      if (flags & MS_NODIRATIME)
-+              audit_log_format(ab, ", nodiratime");
-+      if (flags & MS_BIND)
-+              audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind");
-+      if (flags & MS_MOVE)
-+              audit_log_format(ab, ", move");
-+      if (flags & MS_SILENT)
-+              audit_log_format(ab, ", silent");
-+      if (flags & MS_POSIXACL)
-+              audit_log_format(ab, ", acl");
-+      if (flags & MS_UNBINDABLE)
-+              audit_log_format(ab, flags & MS_REC ? ", runbindable" :
-+                               ", unbindable");
-+      if (flags & MS_PRIVATE)
-+              audit_log_format(ab, flags & MS_REC ? ", rprivate" :
-+                               ", private");
-+      if (flags & MS_SLAVE)
-+              audit_log_format(ab, flags & MS_REC ? ", rslave" :
-+                               ", slave");
-+      if (flags & MS_SHARED)
-+              audit_log_format(ab, flags & MS_REC ? ", rshared" :
-+                               ", shared");
-+      if (flags & MS_RELATIME)
-+              audit_log_format(ab, ", relatime");
-+      if (flags & MS_I_VERSION)
-+              audit_log_format(ab, ", iversion");
-+      if (flags & MS_STRICTATIME)
-+              audit_log_format(ab, ", strictatime");
-+      if (flags & MS_NOUSER)
-+              audit_log_format(ab, ", nouser");
-+}
-+
-+/**
-+ * audit_cb - call back for mount specific audit fields
-+ * @ab: audit_buffer  (NOT NULL)
-+ * @va: audit struct to audit values of  (NOT NULL)
-+ */
-+static void audit_cb(struct audit_buffer *ab, void *va)
-+{
-+      struct common_audit_data *sa = va;
-+
-+      if (aad(sa)->mnt.type) {
-+              audit_log_format(ab, " fstype=");
-+              audit_log_untrustedstring(ab, aad(sa)->mnt.type);
-+      }
-+      if (aad(sa)->mnt.src_name) {
-+              audit_log_format(ab, " srcname=");
-+              audit_log_untrustedstring(ab, aad(sa)->mnt.src_name);
-+      }
-+      if (aad(sa)->mnt.trans) {
-+              audit_log_format(ab, " trans=");
-+              audit_log_untrustedstring(ab, aad(sa)->mnt.trans);
-+      }
-+      if (aad(sa)->mnt.flags) {
-+              audit_log_format(ab, " flags=\"");
-+              audit_mnt_flags(ab, aad(sa)->mnt.flags);
-+              audit_log_format(ab, "\"");
-+      }
-+      if (aad(sa)->mnt.data) {
-+              audit_log_format(ab, " options=");
-+              audit_log_untrustedstring(ab, aad(sa)->mnt.data);
-+      }
-+}
-+
-+/**
-+ * audit_mount - handle the auditing of mount operations
-+ * @profile: the profile being enforced  (NOT NULL)
-+ * @op: operation being mediated (NOT NULL)
-+ * @name: name of object being mediated (MAYBE NULL)
-+ * @src_name: src_name of object being mediated (MAYBE_NULL)
-+ * @type: type of filesystem (MAYBE_NULL)
-+ * @trans: name of trans (MAYBE NULL)
-+ * @flags: filesystem idependent mount flags
-+ * @data: filesystem mount flags
-+ * @request: permissions requested
-+ * @perms: the permissions computed for the request (NOT NULL)
-+ * @info: extra information message (MAYBE NULL)
-+ * @error: 0 if operation allowed else failure error code
-+ *
-+ * Returns: %0 or error on failure
-+ */
-+static int audit_mount(struct aa_profile *profile, const char *op,
-+                     const char *name, const char *src_name,
-+                     const char *type, const char *trans,
-+                     unsigned long flags, const void *data, u32 request,
-+                     struct aa_perms *perms, const char *info, int error)
-+{
-+      int audit_type = AUDIT_APPARMOR_AUTO;
-+      DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op);
-+
-+      if (likely(!error)) {
-+              u32 mask = perms->audit;
-+
-+              if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
-+                      mask = 0xffff;
-+
-+              /* mask off perms that are not being force audited */
-+              request &= mask;
-+
-+              if (likely(!request))
-+                      return 0;
-+              audit_type = AUDIT_APPARMOR_AUDIT;
-+      } else {
-+              /* only report permissions that were denied */
-+              request = request & ~perms->allow;
-+
-+              if (request & perms->kill)
-+                      audit_type = AUDIT_APPARMOR_KILL;
-+
-+              /* quiet known rejects, assumes quiet and kill do not overlap */
-+              if ((request & perms->quiet) &&
-+                  AUDIT_MODE(profile) != AUDIT_NOQUIET &&
-+                  AUDIT_MODE(profile) != AUDIT_ALL)
-+                      request &= ~perms->quiet;
-+
-+              if (!request)
-+                      return error;
-+      }
-+
-+      aad(&sa)->name = name;
-+      aad(&sa)->mnt.src_name = src_name;
-+      aad(&sa)->mnt.type = type;
-+      aad(&sa)->mnt.trans = trans;
-+      aad(&sa)->mnt.flags = flags;
-+      if (data && (perms->audit & AA_AUDIT_DATA))
-+              aad(&sa)->mnt.data = data;
-+      aad(&sa)->info = info;
-+      aad(&sa)->error = error;
-+
-+      return aa_audit(audit_type, profile, &sa, audit_cb);
-+}
-+
-+/**
-+ * match_mnt_flags - Do an ordered match on mount flags
-+ * @dfa: dfa to match against
-+ * @state: state to start in
-+ * @flags: mount flags to match against
-+ *
-+ * Mount flags are encoded as an ordered match. This is done instead of
-+ * checking against a simple bitmask, to allow for logical operations
-+ * on the flags.
-+ *
-+ * Returns: next state after flags match
-+ */
-+static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state,
-+                                  unsigned long flags)
-+{
-+      unsigned int i;
-+
-+      for (i = 0; i <= 31 ; ++i) {
-+              if ((1 << i) & flags)
-+                      state = aa_dfa_next(dfa, state, i + 1);
-+      }
-+
-+      return state;
-+}
-+
-+/**
-+ * compute_mnt_perms - compute mount permission associated with @state
-+ * @dfa: dfa to match against (NOT NULL)
-+ * @state: state match finished in
-+ *
-+ * Returns: mount permissions
-+ */
-+static struct aa_perms compute_mnt_perms(struct aa_dfa *dfa,
-+                                         unsigned int state)
-+{
-+      struct aa_perms perms;
-+
-+      perms.kill = 0;
-+      perms.allow = dfa_user_allow(dfa, state);
-+      perms.audit = dfa_user_audit(dfa, state);
-+      perms.quiet = dfa_user_quiet(dfa, state);
-+      perms.xindex = dfa_user_xindex(dfa, state);
-+
-+      return perms;
-+}
-+
-+static const char * const mnt_info_table[] = {
-+      "match succeeded",
-+      "failed mntpnt match",
-+      "failed srcname match",
-+      "failed type match",
-+      "failed flags match",
-+      "failed data match"
-+};
-+
-+/*
-+ * Returns 0 on success else element that match failed in, this is the
-+ * index into the mnt_info_table above
-+ */
-+static int do_match_mnt(struct aa_dfa *dfa, unsigned int start,
-+                      const char *mntpnt, const char *devname,
-+                      const char *type, unsigned long flags,
-+                      void *data, bool binary, struct aa_perms *perms)
-+{
-+      unsigned int state;
-+
-+      AA_BUG(!dfa);
-+      AA_BUG(!perms);
-+
-+      state = aa_dfa_match(dfa, start, mntpnt);
-+      state = aa_dfa_null_transition(dfa, state);
-+      if (!state)
-+              return 1;
-+
-+      if (devname)
-+              state = aa_dfa_match(dfa, state, devname);
-+      state = aa_dfa_null_transition(dfa, state);
-+      if (!state)
-+              return 2;
-+
-+      if (type)
-+              state = aa_dfa_match(dfa, state, type);
-+      state = aa_dfa_null_transition(dfa, state);
-+      if (!state)
-+              return 3;
-+
-+      state = match_mnt_flags(dfa, state, flags);
-+      if (!state)
-+              return 4;
-+      *perms = compute_mnt_perms(dfa, state);
-+      if (perms->allow & AA_MAY_MOUNT)
-+              return 0;
-+
-+      /* only match data if not binary and the DFA flags data is expected */
-+      if (data && !binary && (perms->allow & AA_MNT_CONT_MATCH)) {
-+              state = aa_dfa_null_transition(dfa, state);
-+              if (!state)
-+                      return 4;
-+
-+              state = aa_dfa_match(dfa, state, data);
-+              if (!state)
-+                      return 5;
-+              *perms = compute_mnt_perms(dfa, state);
-+              if (perms->allow & AA_MAY_MOUNT)
-+                      return 0;
-+      }
-+
-+      /* failed at end of flags match */
-+      return 4;
-+}
-+
-+
-+static int path_flags(struct aa_profile *profile, const struct path *path)
-+{
-+      AA_BUG(!profile);
-+      AA_BUG(!path);
-+
-+      return profile->path_flags |
-+              (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0);
-+}
-+
-+/**
-+ * match_mnt_path_str - handle path matching for mount
-+ * @profile: the confining profile
-+ * @mntpath: for the mntpnt (NOT NULL)
-+ * @buffer: buffer to be used to lookup mntpath
-+ * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR)
-+ * @type: string for the dev type (MAYBE NULL)
-+ * @flags: mount flags to match
-+ * @data: fs mount data (MAYBE NULL)
-+ * @binary: whether @data is binary
-+ * @devinfo: error str if (IS_ERR(@devname))
-+ *
-+ * Returns: 0 on success else error
-+ */
-+static int match_mnt_path_str(struct aa_profile *profile,
-+                            const struct path *mntpath, char *buffer,
-+                            const char *devname, const char *type,
-+                            unsigned long flags, void *data, bool binary,
-+                            const char *devinfo)
-+{
-+      struct aa_perms perms = { };
-+      const char *mntpnt = NULL, *info = NULL;
-+      int pos, error;
-+
-+      AA_BUG(!profile);
-+      AA_BUG(!mntpath);
-+      AA_BUG(!buffer);
-+
-+      error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
-+                           &mntpnt, &info, profile->disconnected);
-+      if (error)
-+              goto audit;
-+      if (IS_ERR(devname)) {
-+              error = PTR_ERR(devname);
-+              devname = NULL;
-+              info = devinfo;
-+              goto audit;
-+      }
-+
-+      error = -EACCES;
-+      pos = do_match_mnt(profile->policy.dfa,
-+                         profile->policy.start[AA_CLASS_MOUNT],
-+                         mntpnt, devname, type, flags, data, binary, &perms);
-+      if (pos) {
-+              info = mnt_info_table[pos];
-+              goto audit;
-+      }
-+      error = 0;
-+
-+audit:
-+      return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL,
-+                         flags, data, AA_MAY_MOUNT, &perms, info, error);
-+}
-+
-+/**
-+ * match_mnt - handle path matching for mount
-+ * @profile: the confining profile
-+ * @mntpath: for the mntpnt (NOT NULL)
-+ * @buffer: buffer to be used to lookup mntpath
-+ * @devpath: path devname/src_name (MAYBE NULL)
-+ * @devbuffer: buffer to be used to lookup devname/src_name
-+ * @type: string for the dev type (MAYBE NULL)
-+ * @flags: mount flags to match
-+ * @data: fs mount data (MAYBE NULL)
-+ * @binary: whether @data is binary
-+ *
-+ * Returns: 0 on success else error
-+ */
-+static int match_mnt(struct aa_profile *profile, const struct path *path,
-+                   char *buffer, struct path *devpath, char *devbuffer,
-+                   const char *type, unsigned long flags, void *data,
-+                   bool binary)
-+{
-+      const char *devname = NULL, *info = NULL;
-+      int error = -EACCES;
-+
-+      AA_BUG(!profile);
-+      AA_BUG(devpath && !devbuffer);
-+
-+      if (devpath) {
-+              error = aa_path_name(devpath, path_flags(profile, devpath),
-+                                   devbuffer, &devname, &info,
-+                                   profile->disconnected);
-+              if (error)
-+                      devname = ERR_PTR(error);
-+      }
-+
-+      return match_mnt_path_str(profile, path, buffer, devname, type, flags,
-+                                data, binary, info);
-+}
-+
-+int aa_remount(struct aa_label *label, const struct path *path,
-+             unsigned long flags, void *data)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL;
-+      bool binary;
-+      int error;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!path);
-+
-+      binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA;
-+
-+      get_buffers(buffer);
-+      error = fn_for_each_confined(label, profile,
-+                      match_mnt(profile, path, buffer, NULL, NULL, NULL,
-+                                flags, data, binary));
-+      put_buffers(buffer);
-+
-+      return error;
-+}
-+
-+int aa_bind_mount(struct aa_label *label, const struct path *path,
-+                const char *dev_name, unsigned long flags)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL, *old_buffer = NULL;
-+      struct path old_path;
-+      int error;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!path);
-+
-+      if (!dev_name || !*dev_name)
-+              return -EINVAL;
-+
-+      flags &= MS_REC | MS_BIND;
-+
-+      error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
-+      if (error)
-+              return error;
-+
-+      get_buffers(buffer, old_buffer);
-+      error = fn_for_each_confined(label, profile,
-+                      match_mnt(profile, path, buffer, &old_path, old_buffer,
-+                                NULL, flags, NULL, false));
-+      put_buffers(buffer, old_buffer);
-+      path_put(&old_path);
-+
-+      return error;
-+}
-+
-+int aa_mount_change_type(struct aa_label *label, const struct path *path,
-+                       unsigned long flags)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL;
-+      int error;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!path);
-+
-+      /* These are the flags allowed by do_change_type() */
-+      flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
-+                MS_UNBINDABLE);
-+
-+      get_buffers(buffer);
-+      error = fn_for_each_confined(label, profile,
-+                      match_mnt(profile, path, buffer, NULL, NULL, NULL,
-+                                flags, NULL, false));
-+      put_buffers(buffer);
-+
-+      return error;
-+}
-+
-+int aa_move_mount(struct aa_label *label, const struct path *path,
-+                const char *orig_name)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL, *old_buffer = NULL;
-+      struct path old_path;
-+      int error;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!path);
-+
-+      if (!orig_name || !*orig_name)
-+              return -EINVAL;
-+
-+      error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path);
-+      if (error)
-+              return error;
-+
-+      get_buffers(buffer, old_buffer);
-+      error = fn_for_each_confined(label, profile,
-+                      match_mnt(profile, path, buffer, &old_path, old_buffer,
-+                                NULL, MS_MOVE, NULL, false));
-+      put_buffers(buffer, old_buffer);
-+      path_put(&old_path);
-+
-+      return error;
-+}
-+
-+int aa_new_mount(struct aa_label *label, const char *dev_name,
-+               const struct path *path, const char *type, unsigned long flags,
-+               void *data)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL, *dev_buffer = NULL;
-+      bool binary = true;
-+      int error;
-+      int requires_dev = 0;
-+      struct path tmp_path, *dev_path = NULL;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!path);
-+
-+      if (type) {
-+              struct file_system_type *fstype;
-+
-+              fstype = get_fs_type(type);
-+              if (!fstype)
-+                      return -ENODEV;
-+              binary = fstype->fs_flags & FS_BINARY_MOUNTDATA;
-+              requires_dev = fstype->fs_flags & FS_REQUIRES_DEV;
-+              put_filesystem(fstype);
-+
-+              if (requires_dev) {
-+                      if (!dev_name || !*dev_name)
-+                              return -ENOENT;
-+
-+                      error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path);
-+                      if (error)
-+                              return error;
-+                      dev_path = &tmp_path;
-+              }
-+      }
-+
-+      get_buffers(buffer, dev_buffer);
-+      if (dev_path) {
-+              error = fn_for_each_confined(label, profile,
-+                      match_mnt(profile, path, buffer, dev_path, dev_buffer,
-+                                type, flags, data, binary));
-+      } else {
-+              error = fn_for_each_confined(label, profile,
-+                      match_mnt_path_str(profile, path, buffer, dev_name,
-+                                         type, flags, data, binary, NULL));
-+      }
-+      put_buffers(buffer, dev_buffer);
-+      if (dev_path)
-+              path_put(dev_path);
-+
-+      return error;
-+}
-+
-+static int profile_umount(struct aa_profile *profile, struct path *path,
-+                        char *buffer)
-+{
-+      struct aa_perms perms = { };
-+      const char *name = NULL, *info = NULL;
-+      unsigned int state;
-+      int error;
-+
-+      AA_BUG(!profile);
-+      AA_BUG(!path);
-+
-+      error = aa_path_name(path, path_flags(profile, path), buffer, &name,
-+                           &info, profile->disconnected);
-+      if (error)
-+              goto audit;
-+
-+      state = aa_dfa_match(profile->policy.dfa,
-+                           profile->policy.start[AA_CLASS_MOUNT],
-+                           name);
-+      perms = compute_mnt_perms(profile->policy.dfa, state);
-+      if (AA_MAY_UMOUNT & ~perms.allow)
-+              error = -EACCES;
-+
-+audit:
-+      return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL,
-+                         AA_MAY_UMOUNT, &perms, info, error);
-+}
-+
-+int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags)
-+{
-+      struct aa_profile *profile;
-+      char *buffer = NULL;
-+      int error;
-+      struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
-+
-+      AA_BUG(!label);
-+      AA_BUG(!mnt);
-+
-+      get_buffers(buffer);
-+      error = fn_for_each_confined(label, profile,
-+                      profile_umount(profile, &path, buffer));
-+      put_buffers(buffer);
-+
-+      return error;
-+}
-+
-+/* helper fn for transition on pivotroot
-+ *
-+ * Returns: label for transition or ERR_PTR. Does not return NULL
-+ */
-+static struct aa_label *build_pivotroot(struct aa_profile *profile,
-+                                      const struct path *new_path,
-+                                      char *new_buffer,
-+                                      const struct path *old_path,
-+                                      char *old_buffer)
-+{
-+      const char *old_name, *new_name = NULL, *info = NULL;
-+      const char *trans_name = NULL;
-+      struct aa_perms perms = { };
-+      unsigned int state;
-+      int error;
-+
-+      AA_BUG(!profile);
-+      AA_BUG(!new_path);
-+      AA_BUG(!old_path);
-+
-+      if (profile_unconfined(profile))
-+              return aa_get_newest_label(&profile->label);
-+
-+      error = aa_path_name(old_path, path_flags(profile, old_path),
-+                           old_buffer, &old_name, &info,
-+                           profile->disconnected);
-+      if (error)
-+              goto audit;
-+      error = aa_path_name(new_path, path_flags(profile, new_path),
-+                           new_buffer, &new_name, &info,
-+                           profile->disconnected);
-+      if (error)
-+              goto audit;
-+
-+      error = -EACCES;
-+      state = aa_dfa_match(profile->policy.dfa,
-+                           profile->policy.start[AA_CLASS_MOUNT],
-+                           new_name);
-+      state = aa_dfa_null_transition(profile->policy.dfa, state);
-+      state = aa_dfa_match(profile->policy.dfa, state, old_name);
-+      perms = compute_mnt_perms(profile->policy.dfa, state);
-+
-+      if (AA_MAY_PIVOTROOT & perms.allow)
-+              error = 0;
-+
-+audit:
-+      error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name,
-+                          NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
-+                          &perms, info, error);
-+      if (error)
-+              return ERR_PTR(error);
-+
-+      return aa_get_newest_label(&profile->label);
-+}
-+
-+int aa_pivotroot(struct aa_label *label, const struct path *old_path,
-+               const struct path *new_path)
-+{
-+      struct aa_profile *profile;
-+      struct aa_label *target = NULL;
-+      char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
-+      int error;
-+
-+      AA_BUG(!label);
-+      AA_BUG(!old_path);
-+      AA_BUG(!new_path);
-+
-+      get_buffers(old_buffer, new_buffer);
-+      target = fn_label_build(label, profile, GFP_ATOMIC,
-+                      build_pivotroot(profile, new_path, new_buffer,
-+                                      old_path, old_buffer));
-+      if (!target) {
-+              info = "label build failed";
-+              error = -ENOMEM;
-+              goto fail;
-+      } else if (!IS_ERR(target)) {
-+              error = aa_replace_current_label(target);
-+              if (error) {
-+                      /* TODO: audit target */
-+                      aa_put_label(target);
-+                      goto out;
-+              }
-+      } else
-+              /* already audited error */
-+              error = PTR_ERR(target);
-+out:
-+      put_buffers(old_buffer, new_buffer);
-+
-+      return error;
-+
-+fail:
-+      /* TODO: add back in auditing of new_name and old_name */
-+      error = fn_for_each(label, profile,
-+                      audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */,
-+                                  NULL /* old_name */,
-+                                  NULL, NULL,
-+                                  0, NULL, AA_MAY_PIVOTROOT, &nullperms, info,
-+                                  error));
-+      goto out;
-+}
--- 
-2.11.0
-
diff --git a/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch b/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch
deleted file mode 100644 (file)
index 20892df..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-From 763d17c9a18b0df7dbec2740f10dc40d378e3cc1 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Sun, 6 Aug 2017 05:36:40 -0700
-Subject: [PATCH 08/17] apparmor: cleanup conditional check for label in
- label_print
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit 7e57939b9d67dcfc2c8348fd0e2c76a2f0349c75)
----
- security/apparmor/label.c | 22 ++++++++--------------
- 1 file changed, 8 insertions(+), 14 deletions(-)
-
-diff --git a/security/apparmor/label.c b/security/apparmor/label.c
-index e324f4df3e34..38be7a89cc31 100644
---- a/security/apparmor/label.c
-+++ b/security/apparmor/label.c
-@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp)
-  * cached label name is present and visible
-  * @label->hname only exists if label is namespace hierachical
-  */
--static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label)
-+static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label,
-+                                 int flags)
- {
--      if (label->hname && labels_ns(label) == ns)
-+      if (label->hname && (!ns || labels_ns(label) == ns) &&
-+          !(flags & ~FLAG_SHOW_MODE))
-               return true;
-       return false;
-@@ -1710,10 +1712,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
-       AA_BUG(!ab);
-       AA_BUG(!label);
--      if (!ns)
--              ns = labels_ns(label);
--
--      if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) {
-+      if (!use_label_hname(ns, label, flags) ||
-+          display_mode(ns, label, flags)) {
-               len  = aa_label_asxprint(&name, ns, label, flags, gfp);
-               if (len == -1) {
-                       AA_DEBUG("label print error");
-@@ -1738,10 +1738,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns,
-       AA_BUG(!f);
-       AA_BUG(!label);
--      if (!ns)
--              ns = labels_ns(label);
--
--      if (!use_label_hname(ns, label)) {
-+      if (!use_label_hname(ns, label, flags)) {
-               char *str;
-               int len;
-@@ -1764,10 +1761,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags,
- {
-       AA_BUG(!label);
--      if (!ns)
--              ns = labels_ns(label);
--
--      if (!use_label_hname(ns, label)) {
-+      if (!use_label_hname(ns, label, flags)) {
-               char *str;
-               int len;
--- 
-2.11.0
-
diff --git a/0009-apparmor-add-support-for-absolute-root-view-based-la.patch b/0009-apparmor-add-support-for-absolute-root-view-based-la.patch
deleted file mode 100644 (file)
index 84ee7a1..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-From 6b092bbbf9e17b10f709d11b3bc2d7e493617934 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Sun, 6 Aug 2017 05:39:08 -0700
-Subject: [PATCH 09/17] apparmor: add support for absolute root view based
- labels
-
-With apparmor policy virtualization based on policy namespace View's
-we don't generally want/need absolute root based views, however there
-are cases like debugging and some secid based conversions where
-using a root based view is important.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit eadfbf0898eda94cee0d982626aa24a3146db48b)
----
- security/apparmor/include/label.h |  1 +
- security/apparmor/label.c         | 10 +++++++++-
- 2 files changed, 10 insertions(+), 1 deletion(-)
-
-diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
-index 9a283b722755..af22dcbbcb8a 100644
---- a/security/apparmor/include/label.h
-+++ b/security/apparmor/include/label.h
-@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp);
- #define FLAG_SHOW_MODE 1
- #define FLAG_VIEW_SUBNS 2
- #define FLAG_HIDDEN_UNCONFINED 4
-+#define FLAG_ABS_ROOT 8
- int aa_label_snxprint(char *str, size_t size, struct aa_ns *view,
-                     struct aa_label *label, int flags);
- int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label,
-diff --git a/security/apparmor/label.c b/security/apparmor/label.c
-index 38be7a89cc31..52b4ef14840d 100644
---- a/security/apparmor/label.c
-+++ b/security/apparmor/label.c
-@@ -1607,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns,
-       AA_BUG(!str && size != 0);
-       AA_BUG(!label);
--      if (!ns)
-+      if (flags & FLAG_ABS_ROOT) {
-+              ns = root_ns;
-+              len = snprintf(str, size, "=");
-+              update_for_len(total, len, size, str);
-+      } else if (!ns) {
-               ns = labels_ns(label);
-+      }
-       label_for_each(i, label, profile) {
-               if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) {
-@@ -1868,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str,
-               if (*str == '&')
-                       str++;
-       }
-+      if (*str == '=')
-+              base = &root_ns->unconfined->label;
-+
-       error = vec_setup(profile, vec, len, gfp);
-       if (error)
-               return ERR_PTR(error);
--- 
-2.11.0
-
diff --git a/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch b/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch
deleted file mode 100644 (file)
index 7e481b2..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-From aa4b6bded85552bc5f9f22d2e18ce86c5c17947c Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Tue, 18 Jul 2017 23:37:18 -0700
-Subject: [PATCH 10/17] apparmor: make policy_unpack able to audit different
- info messages
-
-Switch unpack auditing to using the generic name field in the audit
-struct and make it so we can start adding new info messages about
-why an unpack failed.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit 1489d896c5649e9ce1b6000b4857f8baa7a6ab63)
----
- security/apparmor/include/audit.h |  4 +--
- security/apparmor/policy_unpack.c | 52 ++++++++++++++++++++++++++++-----------
- 2 files changed, 40 insertions(+), 16 deletions(-)
-
-diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
-index c3fe1c5ef3bc..620e81169659 100644
---- a/security/apparmor/include/audit.h
-+++ b/security/apparmor/include/audit.h
-@@ -127,9 +127,9 @@ struct apparmor_audit_data {
-                       } fs;
-               };
-               struct {
--                      const char *name;
--                      long pos;
-+                      struct aa_profile *profile;
-                       const char *ns;
-+                      long pos;
-               } iface;
-               int signal;
-               struct {
-diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
-index bda0dce3b582..4ede87c30f8b 100644
---- a/security/apparmor/policy_unpack.c
-+++ b/security/apparmor/policy_unpack.c
-@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va)
-               audit_log_format(ab, " ns=");
-               audit_log_untrustedstring(ab, aad(sa)->iface.ns);
-       }
--      if (aad(sa)->iface.name) {
-+      if (aad(sa)->name) {
-               audit_log_format(ab, " name=");
--              audit_log_untrustedstring(ab, aad(sa)->iface.name);
-+              audit_log_untrustedstring(ab, aad(sa)->name);
-       }
-       if (aad(sa)->iface.pos)
-               audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos);
-@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name,
-               aad(&sa)->iface.pos = e->pos - e->start;
-       aad(&sa)->iface.ns = ns_name;
-       if (new)
--              aad(&sa)->iface.name = new->base.hname;
-+              aad(&sa)->name = new->base.hname;
-       else
--              aad(&sa)->iface.name = name;
-+              aad(&sa)->name = name;
-       aad(&sa)->info = info;
-       aad(&sa)->error = error;
-@@ -583,6 +583,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
- {
-       struct aa_profile *profile = NULL;
-       const char *tmpname, *tmpns = NULL, *name = NULL;
-+      const char *info = "failed to unpack profile";
-       size_t ns_len;
-       struct rhashtable_params params = { 0 };
-       char *key = NULL;
-@@ -604,8 +605,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len);
-       if (tmpns) {
-               *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL);
--              if (!*ns_name)
-+              if (!*ns_name) {
-+                      info = "out of memory";
-                       goto fail;
-+              }
-               name = tmpname;
-       }
-@@ -624,12 +627,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       if (IS_ERR(profile->xmatch)) {
-               error = PTR_ERR(profile->xmatch);
-               profile->xmatch = NULL;
-+              info = "bad xmatch";
-               goto fail;
-       }
-       /* xmatch_len is not optional if xmatch is set */
-       if (profile->xmatch) {
--              if (!unpack_u32(e, &tmp, NULL))
-+              if (!unpack_u32(e, &tmp, NULL)) {
-+                      info = "missing xmatch len";
-                       goto fail;
-+              }
-               profile->xmatch_len = tmp;
-       }
-@@ -637,8 +643,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       (void) unpack_str(e, &profile->disconnected, "disconnected");
-       /* per profile debug flags (complain, audit) */
--      if (!unpack_nameX(e, AA_STRUCT, "flags"))
-+      if (!unpack_nameX(e, AA_STRUCT, "flags")) {
-+              info = "profile missing flags";
-               goto fail;
-+      }
-+      info = "failed to unpack profile flags";
-       if (!unpack_u32(e, &tmp, NULL))
-               goto fail;
-       if (tmp & PACKED_FLAG_HAT)
-@@ -667,6 +676,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-               /* set a default value if path_flags field is not present */
-               profile->path_flags = PATH_MEDIATE_DELETED;
-+      info = "failed to unpack profile capabilities";
-       if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL))
-               goto fail;
-       if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL))
-@@ -676,6 +686,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       if (!unpack_u32(e, &tmpcap.cap[0], NULL))
-               goto fail;
-+      info = "failed to unpack upper profile capabilities";
-       if (unpack_nameX(e, AA_STRUCT, "caps64")) {
-               /* optional upper half of 64 bit caps */
-               if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL))
-@@ -690,6 +701,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-                       goto fail;
-       }
-+      info = "failed to unpack extended profile capabilities";
-       if (unpack_nameX(e, AA_STRUCT, "capsx")) {
-               /* optional extended caps mediation mask */
-               if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL))
-@@ -700,11 +712,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-                       goto fail;
-       }
--      if (!unpack_rlimits(e, profile))
-+      if (!unpack_rlimits(e, profile)) {
-+              info = "failed to unpack profile rlimits";
-               goto fail;
-+      }
-       if (unpack_nameX(e, AA_STRUCT, "policydb")) {
-               /* generic policy dfa - optional and may be NULL */
-+              info = "failed to unpack policydb";
-               profile->policy.dfa = unpack_dfa(e);
-               if (IS_ERR(profile->policy.dfa)) {
-                       error = PTR_ERR(profile->policy.dfa);
-@@ -734,6 +749,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       if (IS_ERR(profile->file.dfa)) {
-               error = PTR_ERR(profile->file.dfa);
-               profile->file.dfa = NULL;
-+              info = "failed to unpack profile file rules";
-               goto fail;
-       } else if (profile->file.dfa) {
-               if (!unpack_u32(e, &profile->file.start, "dfa_start"))
-@@ -746,10 +762,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-       } else
-               profile->file.dfa = aa_get_dfa(nulldfa);
--      if (!unpack_trans_table(e, profile))
-+      if (!unpack_trans_table(e, profile)) {
-+              info = "failed to unpack profile transition table";
-               goto fail;
-+      }
-       if (unpack_nameX(e, AA_STRUCT, "data")) {
-+              info = "out of memory";
-               profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL);
-               if (!profile->data)
-                       goto fail;
-@@ -761,8 +780,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-               params.hashfn = strhash;
-               params.obj_cmpfn = datacmp;
--              if (rhashtable_init(profile->data, &params))
-+              if (rhashtable_init(profile->data, &params)) {
-+                      info = "failed to init key, value hash table";
-                       goto fail;
-+              }
-               while (unpack_strdup(e, &key, NULL)) {
-                       data = kzalloc(sizeof(*data), GFP_KERNEL);
-@@ -784,12 +805,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-                                              profile->data->p);
-               }
--              if (!unpack_nameX(e, AA_STRUCTEND, NULL))
-+              if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
-+                      info = "failed to unpack end of key, value data table";
-                       goto fail;
-+              }
-       }
--      if (!unpack_nameX(e, AA_STRUCTEND, NULL))
-+      if (!unpack_nameX(e, AA_STRUCTEND, NULL)) {
-+              info = "failed to unpack end of profile";
-               goto fail;
-+      }
-       return profile;
-@@ -798,8 +823,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name)
-               name = NULL;
-       else if (!name)
-               name = "unknown";
--      audit_iface(profile, NULL, name, "failed to unpack profile", e,
--                  error);
-+      audit_iface(profile, NULL, name, info, e, error);
-       aa_free_profile(profile);
-       return ERR_PTR(error);
--- 
-2.11.0
-
diff --git a/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch b/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch
deleted file mode 100644 (file)
index 85eafc8..0000000
+++ /dev/null
@@ -1,78 +0,0 @@
-From ba3f778a2ef31454032c2ca9c99d9212feb4dcf1 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Tue, 18 Jul 2017 23:41:13 -0700
-Subject: [PATCH 11/17] apparmor: add more debug asserts to apparmorfs
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-Acked-by: Seth Arnold <seth.arnold@canonical.com>
-(cherry picked from commit 52c9542126fb04df1f12c605b6c22719c9096794)
----
- security/apparmor/apparmorfs.c | 17 +++++++++++++++++
- 1 file changed, 17 insertions(+)
-
-diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
-index 8fa6c898c44b..7acea14c850b 100644
---- a/security/apparmor/apparmorfs.c
-+++ b/security/apparmor/apparmorfs.c
-@@ -1446,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old,
- {
-       int i;
-+      AA_BUG(!old);
-+      AA_BUG(!new);
-+      AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock));
-+
-       for (i = 0; i < AAFS_PROF_SIZEOF; i++) {
-               new->dents[i] = old->dents[i];
-               if (new->dents[i])
-@@ -1509,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent)
-       struct dentry *dent = NULL, *dir;
-       int error;
-+      AA_BUG(!profile);
-+      AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock));
-+
-       if (!parent) {
-               struct aa_profile *p;
-               p = aa_deref_parent(profile);
-@@ -1734,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns)
-       if (!ns)
-               return;
-+      AA_BUG(!mutex_is_locked(&ns->lock));
-       list_for_each_entry(child, &ns->base.profiles, base.list)
-               __aafs_profile_rmdir(child);
-@@ -1906,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
- {
-       struct aa_ns *parent, *next;
-+      AA_BUG(!root);
-+      AA_BUG(!ns);
-+      AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock));
-+
-       /* is next namespace a child */
-       if (!list_empty(&ns->sub_ns)) {
-               next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list);
-@@ -1940,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns)
- static struct aa_profile *__first_profile(struct aa_ns *root,
-                                         struct aa_ns *ns)
- {
-+      AA_BUG(!root);
-+      AA_BUG(ns && !mutex_is_locked(&ns->lock));
-+
-       for (; ns; ns = __next_ns(root, ns)) {
-               if (!list_empty(&ns->base.profiles))
-                       return list_first_entry(&ns->base.profiles,
-@@ -1962,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p)
-       struct aa_profile *parent;
-       struct aa_ns *ns = p->ns;
-+      AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock));
-+
-       /* is next profile a child */
-       if (!list_empty(&p->base.profiles))
-               return list_first_entry(&p->base.profiles, typeof(*p),
--- 
-2.11.0
-
diff --git a/0013-apparmor-move-new_null_profile-to-after-profile-look.patch b/0013-apparmor-move-new_null_profile-to-after-profile-look.patch
deleted file mode 100644 (file)
index f1337f3..0000000
+++ /dev/null
@@ -1,194 +0,0 @@
-From 50d30adbef98a0b6cc531a9413d05f564eb633ee Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Wed, 16 Aug 2017 08:59:57 -0700
-Subject: [PATCH 13/17] apparmor: move new_null_profile to after profile lookup
- fns()
-
-new_null_profile will need to use some of the profile lookup fns()
-so move instead of doing forward fn declarations.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit cf1e50dfc6f627bc2989b57076b129c330fb3f0a)
----
- security/apparmor/policy.c | 158 ++++++++++++++++++++++-----------------------
- 1 file changed, 79 insertions(+), 79 deletions(-)
-
-diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
-index 244ea4a4a8f0..a81a384a63b1 100644
---- a/security/apparmor/policy.c
-+++ b/security/apparmor/policy.c
-@@ -289,85 +289,6 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy,
-       return NULL;
- }
--/**
-- * aa_new_null_profile - create or find a null-X learning profile
-- * @parent: profile that caused this profile to be created (NOT NULL)
-- * @hat: true if the null- learning profile is a hat
-- * @base: name to base the null profile off of
-- * @gfp: type of allocation
-- *
-- * Find/Create a null- complain mode profile used in learning mode.  The
-- * name of the profile is unique and follows the format of parent//null-XXX.
-- * where XXX is based on the @name or if that fails or is not supplied
-- * a unique number
-- *
-- * null profiles are added to the profile list but the list does not
-- * hold a count on them so that they are automatically released when
-- * not in use.
-- *
-- * Returns: new refcounted profile else NULL on failure
-- */
--struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
--                                     const char *base, gfp_t gfp)
--{
--      struct aa_profile *profile;
--      char *name;
--
--      AA_BUG(!parent);
--
--      if (base) {
--              name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
--                             gfp);
--              if (name) {
--                      sprintf(name, "%s//null-%s", parent->base.hname, base);
--                      goto name;
--              }
--              /* fall through to try shorter uniq */
--      }
--
--      name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
--      if (!name)
--              return NULL;
--      sprintf(name, "%s//null-%x", parent->base.hname,
--              atomic_inc_return(&parent->ns->uniq_null));
--
--name:
--      /* lookup to see if this is a dup creation */
--      profile = aa_find_child(parent, basename(name));
--      if (profile)
--              goto out;
--
--      profile = aa_alloc_profile(name, NULL, gfp);
--      if (!profile)
--              goto fail;
--
--      profile->mode = APPARMOR_COMPLAIN;
--      profile->label.flags |= FLAG_NULL;
--      if (hat)
--              profile->label.flags |= FLAG_HAT;
--      profile->path_flags = parent->path_flags;
--
--      /* released on free_profile */
--      rcu_assign_pointer(profile->parent, aa_get_profile(parent));
--      profile->ns = aa_get_ns(parent->ns);
--      profile->file.dfa = aa_get_dfa(nulldfa);
--      profile->policy.dfa = aa_get_dfa(nulldfa);
--
--      mutex_lock(&profile->ns->lock);
--      __add_profile(&parent->base.profiles, profile);
--      mutex_unlock(&profile->ns->lock);
--
--      /* refcount released by caller */
--out:
--      kfree(name);
--
--      return profile;
--
--fail:
--      aa_free_profile(profile);
--      return NULL;
--}
--
- /* TODO: profile accounting - setup in remove */
- /**
-@@ -559,6 +480,85 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
- }
- /**
-+ * aa_new_null_profile - create or find a null-X learning profile
-+ * @parent: profile that caused this profile to be created (NOT NULL)
-+ * @hat: true if the null- learning profile is a hat
-+ * @base: name to base the null profile off of
-+ * @gfp: type of allocation
-+ *
-+ * Find/Create a null- complain mode profile used in learning mode.  The
-+ * name of the profile is unique and follows the format of parent//null-XXX.
-+ * where XXX is based on the @name or if that fails or is not supplied
-+ * a unique number
-+ *
-+ * null profiles are added to the profile list but the list does not
-+ * hold a count on them so that they are automatically released when
-+ * not in use.
-+ *
-+ * Returns: new refcounted profile else NULL on failure
-+ */
-+struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-+                                     const char *base, gfp_t gfp)
-+{
-+      struct aa_profile *profile;
-+      char *name;
-+
-+      AA_BUG(!parent);
-+
-+      if (base) {
-+              name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base),
-+                             gfp);
-+              if (name) {
-+                      sprintf(name, "%s//null-%s", parent->base.hname, base);
-+                      goto name;
-+              }
-+              /* fall through to try shorter uniq */
-+      }
-+
-+      name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp);
-+      if (!name)
-+              return NULL;
-+      sprintf(name, "%s//null-%x", parent->base.hname,
-+              atomic_inc_return(&parent->ns->uniq_null));
-+
-+name:
-+      /* lookup to see if this is a dup creation */
-+      profile = aa_find_child(parent, basename(name));
-+      if (profile)
-+              goto out;
-+
-+      profile = aa_alloc_profile(name, NULL, gfp);
-+      if (!profile)
-+              goto fail;
-+
-+      profile->mode = APPARMOR_COMPLAIN;
-+      profile->label.flags |= FLAG_NULL;
-+      if (hat)
-+              profile->label.flags |= FLAG_HAT;
-+      profile->path_flags = parent->path_flags;
-+
-+      /* released on free_profile */
-+      rcu_assign_pointer(profile->parent, aa_get_profile(parent));
-+      profile->ns = aa_get_ns(parent->ns);
-+      profile->file.dfa = aa_get_dfa(nulldfa);
-+      profile->policy.dfa = aa_get_dfa(nulldfa);
-+
-+      mutex_lock(&profile->ns->lock);
-+      __add_profile(&parent->base.profiles, profile);
-+      mutex_unlock(&profile->ns->lock);
-+
-+      /* refcount released by caller */
-+out:
-+      kfree(name);
-+
-+      return profile;
-+
-+fail:
-+      aa_free_profile(profile);
-+      return NULL;
-+}
-+
-+/**
-  * replacement_allowed - test to see if replacement is allowed
-  * @profile: profile to test if it can be replaced  (MAYBE NULL)
-  * @noreplace: true if replacement shouldn't be allowed but addition is okay
--- 
-2.11.0
-
diff --git a/0014-apparmor-fix-race-condition-in-null-profile-creation.patch b/0014-apparmor-fix-race-condition-in-null-profile-creation.patch
deleted file mode 100644 (file)
index ae57b4b..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-From ab3b869791b6122c7be7e68ca4c08e2c2e8815ac Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Wed, 16 Aug 2017 05:40:49 -0700
-Subject: [PATCH 14/17] apparmor: fix race condition in null profile creation
-
-There is a race when null- profile is being created between the
-initial lookup/creation of the profile and lock/addition of the
-profile. This could result in multiple version of a profile being
-added to the list which need to be removed/replaced.
-
-Since these are learning profile their is no affect on mediation.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit 3aa3de2a4fb8f33ec62b00998bc6b6c6850d41b1)
----
- security/apparmor/policy.c | 14 +++++++++++---
- 1 file changed, 11 insertions(+), 3 deletions(-)
-
-diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
-index a81a384a63b1..4243b0c3f0e4 100644
---- a/security/apparmor/policy.c
-+++ b/security/apparmor/policy.c
-@@ -500,7 +500,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
- struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-                                      const char *base, gfp_t gfp)
- {
--      struct aa_profile *profile;
-+      struct aa_profile *p, *profile;
-+      const char *bname;
-       char *name;
-       AA_BUG(!parent);
-@@ -523,7 +524,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
- name:
-       /* lookup to see if this is a dup creation */
--      profile = aa_find_child(parent, basename(name));
-+      bname = basename(name);
-+      profile = aa_find_child(parent, bname);
-       if (profile)
-               goto out;
-@@ -544,7 +546,13 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat,
-       profile->policy.dfa = aa_get_dfa(nulldfa);
-       mutex_lock(&profile->ns->lock);
--      __add_profile(&parent->base.profiles, profile);
-+      p = __find_child(&parent->base.profiles, bname);
-+      if (p) {
-+              aa_free_profile(profile);
-+              profile = aa_get_profile(p);
-+      } else {
-+              __add_profile(&parent->base.profiles, profile);
-+      }
-       mutex_unlock(&profile->ns->lock);
-       /* refcount released by caller */
--- 
-2.11.0
-
diff --git a/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch b/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch
deleted file mode 100644 (file)
index 1ceb990..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-From 7f2cdd6453518ff76c3855255c91306a2b928c9a Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Wed, 16 Aug 2017 05:48:06 -0700
-Subject: [PATCH 15/17] apparmor: ensure unconfined profiles have dfas
- initialized
-
-Generally unconfined has early bailout tests and does not need the
-dfas initialized, however if an early bailout test is ever missed
-it will result in an oops.
-
-Be defensive and initialize the unconfined profile to have null dfas
-(no permission) so if an early bailout test is missed we fail
-closed (no perms granted) instead of oopsing.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit 034ad2d248927722bdcd1aedb62634cdc2049113)
----
- security/apparmor/policy_ns.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
-index 351d3bab3a3d..62a3589c62ab 100644
---- a/security/apparmor/policy_ns.c
-+++ b/security/apparmor/policy_ns.c
-@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name)
-       ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR |
-               FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED;
-       ns->unconfined->mode = APPARMOR_UNCONFINED;
-+      ns->unconfined->file.dfa = aa_get_dfa(nulldfa);
-+      ns->unconfined->policy.dfa = aa_get_dfa(nulldfa);
-       /* ns and ns->unconfined share ns->unconfined refcount */
-       ns->unconfined->ns = ns;
--- 
-2.11.0
-
diff --git a/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch b/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch
deleted file mode 100644 (file)
index 9ff09f8..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-From 8daf877473653c06a28c86bf72d63ce7e5c1d542 Mon Sep 17 00:00:00 2001
-From: John Johansen <john.johansen@canonical.com>
-Date: Wed, 16 Aug 2017 09:33:48 -0700
-Subject: [PATCH 16/17] apparmor: fix incorrect type assignment when freeing
- proxies
-
-sparse reports
-
-poisoning the proxy->label before freeing the struct is resulting in
-a sparse build warning.
-../security/apparmor/label.c:52:30: warning: incorrect type in assignment (different address spaces)
-../security/apparmor/label.c:52:30:    expected struct aa_label [noderef] <asn:4>*label
-../security/apparmor/label.c:52:30:    got struct aa_label *<noident>
-
-fix with RCU_INIT_POINTER as this is one of those cases where
-rcu_assign_pointer() is not needed.
-
-Signed-off-by: John Johansen <john.johansen@canonical.com>
-(cherry picked from commit 76e22e212a850bbd16cf49f9c586d4635507e0b5)
----
- security/apparmor/label.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/security/apparmor/label.c b/security/apparmor/label.c
-index 52b4ef14840d..c5b99b954580 100644
---- a/security/apparmor/label.c
-+++ b/security/apparmor/label.c
-@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy)
-               /* p->label will not updated any more as p is dead */
-               aa_put_label(rcu_dereference_protected(proxy->label, true));
-               memset(proxy, 0, sizeof(*proxy));
--              proxy->label = (struct aa_label *) PROXY_POISON;
-+              RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON);
-               kfree(proxy);
-       }
- }
--- 
-2.11.0
-
index e0eb9ca1a33fdd2dac5b33f71bf2159b36bb4dca..d11a0aa1ef2b36440edbfb55a3f8a7bc1eaf4f1f 100644 (file)
@@ -24,10 +24,10 @@ index 7bbaca9..a026491 100644
 aufs4.x-rcN base patch
 
 diff --git a/MAINTAINERS b/MAINTAINERS
-index 1c3feff..1a12137 100644
+index af0cb69..d360d2e 100644
 --- a/MAINTAINERS
 +++ b/MAINTAINERS
-@@ -2392,6 +2392,19 @@ F:      include/linux/audit.h
+@@ -2465,6 +2465,19 @@ F:      include/linux/audit.h
  F:    include/uapi/linux/audit.h
  F:    kernel/audit*
  
@@ -48,10 +48,10 @@ index 1c3feff..1a12137 100644
  M:    Miguel Ojeda Sandonis <miguel.ojeda.sandonis@gmail.com>
  W:    http://miguelojeda.es/auxdisplay.htm
 diff --git a/drivers/block/loop.c b/drivers/block/loop.c
-index f321b96..10707c3 100644
+index 85de673..d44de9d 100644
 --- a/drivers/block/loop.c
 +++ b/drivers/block/loop.c
-@@ -700,6 +700,24 @@ static inline int is_loop_device(struct file *file)
+@@ -686,6 +686,24 @@ static inline int is_loop_device(struct file *file)
        return i && S_ISBLK(i->i_mode) && MAJOR(i->i_rdev) == LOOP_MAJOR;
  }
  
@@ -90,7 +90,7 @@ index f901413..e3719a5 100644
                   void (*finish)(void *))
  {
 diff --git a/fs/fcntl.c b/fs/fcntl.c
-index 3b01b64..659760e 100644
+index 448a111..f51c2cf 100644
 --- a/fs/fcntl.c
 +++ b/fs/fcntl.c
 @@ -31,7 +31,7 @@
@@ -112,10 +112,10 @@ index 3b01b64..659760e 100644
                return error;
  
 diff --git a/fs/inode.c b/fs/inode.c
-index 5037059..73820bf 100644
+index d1e35b5..f7800d6 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
-@@ -1641,7 +1641,7 @@ EXPORT_SYMBOL(generic_update_time);
+@@ -1655,7 +1655,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.
   */
@@ -124,13 +124,30 @@ index 5037059..73820bf 100644
  {
        int (*update_time)(struct inode *, struct timespec *, int);
  
+diff --git a/fs/namespace.c b/fs/namespace.c
+index d18deb4..e5a4a7f 100644
+--- a/fs/namespace.c
++++ b/fs/namespace.c
+@@ -846,6 +846,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 0cc7033..6e542f0 100644
+index f0d4b16..6aa8c7a 100644
 --- a/fs/read_write.c
 +++ b/fs/read_write.c
-@@ -473,6 +473,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
+@@ -483,6 +483,28 @@ ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
+               return -EINVAL;
  }
- EXPORT_SYMBOL(__vfs_write);
  
 +vfs_readf_t vfs_readf(struct file *file)
 +{
@@ -154,14 +171,14 @@ index 0cc7033..6e542f0 100644
 +      return ERR_PTR(-ENOSYS);
 +}
 +
- ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
+ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
  {
        mm_segment_t old_fs;
 diff --git a/fs/splice.c b/fs/splice.c
-index ae41201..9753304 100644
+index f3084cc..eb888c6 100644
 --- a/fs/splice.c
 +++ b/fs/splice.c
-@@ -853,8 +853,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
+@@ -837,8 +837,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
  /*
   * Attempt to initiate a splice from pipe to file.
   */
@@ -172,7 +189,7 @@ index ae41201..9753304 100644
  {
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *,
                                loff_t *, size_t, unsigned int);
-@@ -870,9 +870,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+@@ -854,9 +854,9 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
  /*
   * Attempt to initiate a splice from a file to a pipe.
   */
@@ -186,7 +203,7 @@ index ae41201..9753304 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
+index a576aa2..eb61780 100644
 --- a/fs/sync.c
 +++ b/fs/sync.c
 @@ -27,7 +27,7 @@
@@ -211,10 +228,10 @@ index 61eb82c..e700888 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 cbfe127..9b21bb5 100644
+index 13dab19..8ab6566 100644
 --- a/include/linux/fs.h
 +++ b/include/linux/fs.h
-@@ -1262,6 +1262,7 @@ extern void fasync_free(struct fasync_struct *);
+@@ -1264,6 +1264,7 @@ extern void fasync_free(struct fasync_struct *);
  /* can be called from interrupts */
  extern void kill_fasync(struct fasync_struct **, int, int);
  
@@ -222,7 +239,7 @@ index cbfe127..9b21bb5 100644
  extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force);
  extern int f_setown(struct file *filp, unsigned long arg, int force);
  extern void f_delown(struct file *filp);
-@@ -1683,6 +1684,7 @@ struct file_operations {
+@@ -1710,6 +1711,7 @@ struct file_operations {
        ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
        unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
        int (*check_flags)(int);
@@ -230,7 +247,7 @@ index cbfe127..9b21bb5 100644
        int (*flock) (struct file *, int, struct file_lock *);
        ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
        ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
-@@ -1753,6 +1755,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
+@@ -1780,6 +1782,12 @@ ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
                              struct iovec *fast_pointer,
                              struct iovec **ret_pointer);
  
@@ -241,9 +258,9 @@ index cbfe127..9b21bb5 100644
 +vfs_writef_t vfs_writef(struct file *file);
 +
  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 *);
-@@ -2157,6 +2165,7 @@ extern int current_umask(void);
+ extern ssize_t vfs_write(struct file *, const char __user *, size_t, loff_t *);
+@@ -2182,6 +2190,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);
@@ -251,7 +268,7 @@ index cbfe127..9b21bb5 100644
  
  /* /sys/fs */
  extern struct kobject *fs_kobj;
-@@ -2437,6 +2446,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
+@@ -2462,6 +2471,7 @@ static inline bool sb_is_blkdev_sb(struct super_block *sb)
        return false;
  }
  #endif
@@ -259,6 +276,46 @@ index cbfe127..9b21bb5 100644
  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/lockdep.h b/include/linux/lockdep.h
+index bfa8e0b..728d810 100644
+--- a/include/linux/lockdep.h
++++ b/include/linux/lockdep.h
+@@ -405,6 +405,8 @@ static inline int lockdep_match_key(struct lockdep_map *lock,
+       return lock->key == key;
+ }
++struct lock_class *lockdep_hlock_class(struct held_lock *hlock);
++
+ /*
+  * Acquire a lock.
+  *
+@@ -529,6 +531,7 @@ struct lock_class_key { };
+ #define lockdep_depth(tsk)    (0)
++#define lockdep_is_held(lock)                 (1)
+ #define lockdep_is_held_type(l, r)            (1)
+ #define lockdep_assert_held(l)                        do { (void)(l); } while (0)
+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 db42746..12f3a5a 100644
 --- a/include/linux/splice.h
@@ -274,13 +331,34 @@ index db42746..12f3a5a 100644
 +                       struct pipe_inode_info *pipe, size_t len,
 +                       unsigned int flags);
  #endif
+diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
+index e36e652..bc97a97 100644
+--- a/kernel/locking/lockdep.c
++++ b/kernel/locking/lockdep.c
+@@ -144,7 +144,7 @@ static struct lock_list list_entries[MAX_LOCKDEP_ENTRIES];
+ unsigned long nr_lock_classes;
+ static struct lock_class lock_classes[MAX_LOCKDEP_KEYS];
+-static inline struct lock_class *hlock_class(struct held_lock *hlock)
++inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock)
+ {
+       if (!hlock->class_idx) {
+               /*
+@@ -155,6 +155,7 @@ static inline struct lock_class *hlock_class(struct held_lock *hlock)
+       }
+       return lock_classes + hlock->class_idx - 1;
+ }
++#define hlock_class(hlock) lockdep_hlock_class(hlock)
+ #ifdef CONFIG_LOCK_STAT
+ static DEFINE_PER_CPU(struct lock_class_stats[MAX_LOCKDEP_KEYS], cpu_lock_stats);
 aufs4.x-rcN mmap patch
 
 diff --git a/fs/proc/base.c b/fs/proc/base.c
-index 719c2e9..a1b7968 100644
+index ad3b076..ad4a50d 100644
 --- a/fs/proc/base.c
 +++ b/fs/proc/base.c
-@@ -1986,7 +1986,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path)
+@@ -1987,7 +1987,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path)
        down_read(&mm->mmap_sem);
        vma = find_exact_vma(mm, vm_start, vm_end);
        if (vma && vma->vm_file) {
@@ -306,10 +384,10 @@ index 7563437..7c0dc0f 100644
                ino = inode->i_ino;
        }
 diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
-index fe8f326..b2f7f1a 100644
+index 5589b4b..f60aea2 100644
 --- a/fs/proc/task_mmu.c
 +++ b/fs/proc/task_mmu.c
-@@ -293,7 +293,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
+@@ -309,7 +309,10 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid)
        const char *name = NULL;
  
        if (file) {
@@ -321,7 +399,7 @@ index fe8f326..b2f7f1a 100644
                dev = inode->i_sb->s_dev;
                ino = inode->i_ino;
                pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
-@@ -1640,7 +1643,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
+@@ -1734,7 +1737,7 @@ static int show_numa_map(struct seq_file *m, void *v, int is_pid)
        struct proc_maps_private *proc_priv = &numa_priv->proc_maps;
        struct vm_area_struct *vma = v;
        struct numa_maps *md = &numa_priv->md;
@@ -331,10 +409,10 @@ index fe8f326..b2f7f1a 100644
        struct mm_walk walk = {
                .hugetlb_entry = gather_hugetlb_stats,
 diff --git a/fs/proc/task_nommu.c b/fs/proc/task_nommu.c
-index 23266694..58e59b6 100644
+index b00b7660..93e8a86 100644
 --- a/fs/proc/task_nommu.c
 +++ b/fs/proc/task_nommu.c
-@@ -157,7 +157,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
+@@ -155,7 +155,10 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma,
        file = vma->vm_file;
  
        if (file) {
@@ -347,10 +425,10 @@ index 23266694..58e59b6 100644
                ino = inode->i_ino;
                pgoff = (loff_t)vma->vm_pgoff << PAGE_SHIFT;
 diff --git a/include/linux/mm.h b/include/linux/mm.h
-index 46b9ac5..62ba1c3 100644
+index 065d99d..04486c3 100644
 --- a/include/linux/mm.h
 +++ b/include/linux/mm.h
-@@ -1306,6 +1306,28 @@ static inline int fixup_user_fault(struct task_struct *tsk,
+@@ -1348,6 +1348,28 @@ static inline int fixup_user_fault(struct task_struct *tsk,
  }
  #endif
  
@@ -380,10 +458,10 @@ index 46b9ac5..62ba1c3 100644
                unsigned int gup_flags);
  extern int access_remote_vm(struct mm_struct *mm, unsigned long addr,
 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
-index 3cadee0..d0142c1 100644
+index 1861ea8..d85a914 100644
 --- a/include/linux/mm_types.h
 +++ b/include/linux/mm_types.h
-@@ -259,6 +259,7 @@ struct vm_region {
+@@ -260,6 +260,7 @@ struct vm_region {
        unsigned long   vm_top;         /* region allocated to here */
        unsigned long   vm_pgoff;       /* the offset in vm_file corresponding to vm_start */
        struct file     *vm_file;       /* the backing file or NULL */
@@ -391,19 +469,19 @@ index 3cadee0..d0142c1 100644
  
        int             vm_usage;       /* region usage count (access under nommu_region_sem) */
        bool            vm_icache_flushed : 1; /* true if the icache has been flushed for
-@@ -333,6 +334,7 @@ struct vm_area_struct {
+@@ -334,6 +335,7 @@ struct vm_area_struct {
        unsigned long vm_pgoff;         /* Offset (within vm_file) in PAGE_SIZE
                                           units */
        struct file * vm_file;          /* File we map to (can be NULL). */
 +      struct file *vm_prfile;         /* shadow of vm_file */
        void * vm_private_data;         /* was vm_pte (shared mem) */
  
- #ifndef CONFIG_MMU
+       atomic_long_t swap_readahead_info;
 diff --git a/kernel/fork.c b/kernel/fork.c
-index cbbea27..f0fd8a1 100644
+index 07cc743..b1d2b43 100644
 --- a/kernel/fork.c
 +++ b/kernel/fork.c
-@@ -663,7 +663,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
+@@ -676,7 +676,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
                        struct inode *inode = file_inode(file);
                        struct address_space *mapping = file->f_mapping;
  
@@ -413,7 +491,7 @@ index cbbea27..f0fd8a1 100644
                                atomic_dec(&inode->i_writecount);
                        i_mmap_lock_write(mapping);
 diff --git a/mm/Makefile b/mm/Makefile
-index 411bd24..e7de927 100644
+index e3ac3ae..745b26c 100644
 --- a/mm/Makefile
 +++ b/mm/Makefile
 @@ -39,7 +39,7 @@ obj-y                        := filemap.o mempool.o oom_kill.o \
@@ -426,10 +504,10 @@ index 411bd24..e7de927 100644
  obj-y += init-mm.o
  
 diff --git a/mm/filemap.c b/mm/filemap.c
-index 0b41c8c..c5262ff 100644
+index 594d73f..7183aef 100644
 --- a/mm/filemap.c
 +++ b/mm/filemap.c
-@@ -2543,7 +2543,7 @@ int filemap_page_mkwrite(struct vm_fault *vmf)
+@@ -2590,7 +2590,7 @@ int filemap_page_mkwrite(struct vm_fault *vmf)
        int ret = VM_FAULT_LOCKED;
  
        sb_start_pagefault(inode->i_sb);
@@ -439,10 +517,10 @@ index 0b41c8c..c5262ff 100644
        if (page->mapping != inode->i_mapping) {
                unlock_page(page);
 diff --git a/mm/mmap.c b/mm/mmap.c
-index f19efcf..7fdd59e 100644
+index 680506f..081406a 100644
 --- a/mm/mmap.c
 +++ b/mm/mmap.c
-@@ -170,7 +170,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
+@@ -171,7 +171,7 @@ static struct vm_area_struct *remove_vma(struct vm_area_struct *vma)
        if (vma->vm_ops && vma->vm_ops->close)
                vma->vm_ops->close(vma);
        if (vma->vm_file)
@@ -451,7 +529,7 @@ index f19efcf..7fdd59e 100644
        mpol_put(vma_policy(vma));
        kmem_cache_free(vm_area_cachep, vma);
        return next;
-@@ -895,7 +895,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
+@@ -896,7 +896,7 @@ int __vma_adjust(struct vm_area_struct *vma, unsigned long start,
        if (remove_next) {
                if (file) {
                        uprobe_munmap(next, next->vm_start, next->vm_end);
@@ -460,7 +538,7 @@ index f19efcf..7fdd59e 100644
                }
                if (next->anon_vma)
                        anon_vma_merge(vma, next);
-@@ -1745,8 +1745,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
+@@ -1746,8 +1746,8 @@ unsigned long mmap_region(struct file *file, unsigned long addr,
        return addr;
  
  unmap_and_free_vma:
@@ -470,7 +548,7 @@ index f19efcf..7fdd59e 100644
  
        /* Undo any partial mapping done by a device driver. */
        unmap_region(mm, vma, prev, vma->vm_start, vma->vm_end);
-@@ -2568,7 +2568,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
+@@ -2569,7 +2569,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
                goto out_free_mpol;
  
        if (new->vm_file)
@@ -479,7 +557,7 @@ index f19efcf..7fdd59e 100644
  
        if (new->vm_ops && new->vm_ops->open)
                new->vm_ops->open(new);
-@@ -2587,7 +2587,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
+@@ -2588,7 +2588,7 @@ int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
        if (new->vm_ops && new->vm_ops->close)
                new->vm_ops->close(new);
        if (new->vm_file)
@@ -488,7 +566,7 @@ index f19efcf..7fdd59e 100644
        unlink_anon_vmas(new);
   out_free_mpol:
        mpol_put(vma_policy(new));
-@@ -2741,7 +2741,7 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
+@@ -2750,7 +2750,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;
@@ -497,7 +575,7 @@ index f19efcf..7fdd59e 100644
  
        pr_warn_once("%s (%d) uses deprecated remap_file_pages() syscall. See Documentation/vm/remap_file_pages.txt.\n",
                     current->comm, current->pid);
-@@ -2816,10 +2816,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
+@@ -2825,10 +2825,27 @@ SYSCALL_DEFINE5(remap_file_pages, unsigned long, start, unsigned long, size,
                }
        }
  
@@ -526,7 +604,7 @@ index f19efcf..7fdd59e 100644
  out:
        up_write(&mm->mmap_sem);
        if (populate)
-@@ -3110,7 +3127,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
+@@ -3136,7 +3153,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
                if (anon_vma_clone(new_vma, vma))
                        goto out_free_mempol;
                if (new_vma->vm_file)
@@ -536,7 +614,7 @@ index f19efcf..7fdd59e 100644
                        new_vma->vm_ops->open(new_vma);
                vma_link(mm, new_vma, prev, rb_link, rb_parent);
 diff --git a/mm/nommu.c b/mm/nommu.c
-index fc184f5..637ea81 100644
+index 17c00d9..4bcdf94 100644
 --- a/mm/nommu.c
 +++ b/mm/nommu.c
 @@ -641,7 +641,7 @@ static void __put_nommu_region(struct vm_region *region)
@@ -557,7 +635,7 @@ index fc184f5..637ea81 100644
        put_nommu_region(vma->vm_region);
        kmem_cache_free(vm_area_cachep, vma);
  }
-@@ -1326,7 +1326,7 @@ unsigned long do_mmap(struct file *file,
+@@ -1321,7 +1321,7 @@ unsigned long do_mmap(struct file *file,
                                        goto error_just_free;
                                }
                        }
@@ -566,7 +644,7 @@ index fc184f5..637ea81 100644
                        kmem_cache_free(vm_region_jar, region);
                        region = pregion;
                        result = start;
-@@ -1401,10 +1401,10 @@ unsigned long do_mmap(struct file *file,
+@@ -1396,10 +1396,10 @@ unsigned long do_mmap(struct file *file,
        up_write(&nommu_region_sem);
  error:
        if (region->vm_file)
@@ -693,7 +771,7 @@ index e3719a5..3203470 100644
  /**
   * d_ancestor - search for an ancestor
 diff --git a/fs/exec.c b/fs/exec.c
-index 62175cb..f0b6fdd 100644
+index 3e14ba2..6818b01 100644
 --- a/fs/exec.c
 +++ b/fs/exec.c
 @@ -109,6 +109,7 @@ bool path_noexec(const struct path *path)
@@ -705,7 +783,7 @@ index 62175cb..f0b6fdd 100644
  #ifdef CONFIG_USELIB
  /*
 diff --git a/fs/fcntl.c b/fs/fcntl.c
-index 659760e..5c37087 100644
+index f51c2cf..58bf222 100644
 --- a/fs/fcntl.c
 +++ b/fs/fcntl.c
 @@ -84,6 +84,7 @@ int setfl(int fd, struct file * filp, unsigned long arg)
@@ -717,7 +795,7 @@ index 659760e..5c37087 100644
  static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
                       int force)
 diff --git a/fs/file_table.c b/fs/file_table.c
-index 72e861a..01ae52f 100644
+index 61517f5..c6bab39c 100644
 --- a/fs/file_table.c
 +++ b/fs/file_table.c
 @@ -148,6 +148,7 @@ struct file *get_empty_filp(void)
@@ -728,7 +806,7 @@ index 72e861a..01ae52f 100644
  
  /**
   * alloc_file - allocate and initialize a 'struct file'
-@@ -260,6 +261,7 @@ void flush_delayed_fput(void)
+@@ -258,6 +259,7 @@ void flush_delayed_fput(void)
  {
        delayed_fput(NULL);
  }
@@ -736,7 +814,7 @@ index 72e861a..01ae52f 100644
  
  static DECLARE_DELAYED_WORK(delayed_fput_work, delayed_fput);
  
-@@ -302,6 +304,7 @@ void __fput_sync(struct file *file)
+@@ -300,6 +302,7 @@ void __fput_sync(struct file *file)
  }
  
  EXPORT_SYMBOL(fput);
@@ -744,19 +822,19 @@ index 72e861a..01ae52f 100644
  
  void put_filp(struct file *file)
  {
-@@ -310,6 +313,7 @@ void put_filp(struct file *file)
+@@ -308,6 +311,7 @@ void put_filp(struct file *file)
                file_free(file);
        }
  }
 +EXPORT_SYMBOL_GPL(put_filp);
  
  void __init files_init(void)
- { 
+ {
 diff --git a/fs/inode.c b/fs/inode.c
-index 73820bf..7db829e 100644
+index f7800d6..f31a6c7 100644
 --- a/fs/inode.c
 +++ b/fs/inode.c
-@@ -1650,6 +1650,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags)
+@@ -1664,6 +1664,7 @@ int update_time(struct inode *inode, struct timespec *time, int flags)
  
        return update_time(inode, time, flags);
  }
@@ -765,10 +843,10 @@ index 73820bf..7db829e 100644
  /**
   *    touch_atime     -       update the access time
 diff --git a/fs/namespace.c b/fs/namespace.c
-index f8893dc..c55d949 100644
+index e5a4a7f..6d0c376 100644
 --- a/fs/namespace.c
 +++ b/fs/namespace.c
-@@ -463,6 +463,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
+@@ -517,6 +517,7 @@ void __mnt_drop_write(struct vfsmount *mnt)
        mnt_dec_writers(real_mount(mnt));
        preempt_enable();
  }
@@ -776,7 +854,15 @@ index f8893dc..c55d949 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,
+@@ -851,6 +852,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
+@@ -1887,6 +1889,7 @@ int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
        }
        return 0;
  }
@@ -857,7 +943,7 @@ index 9991f88..117042c 100644
  /*
   * Destroy all marks in destroy_list, waits for SRCU period to finish before
 diff --git a/fs/open.c b/fs/open.c
-index 35bb784..92e08c5 100644
+index 7ea1184..6e2e241 100644
 --- a/fs/open.c
 +++ b/fs/open.c
 @@ -64,6 +64,7 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
@@ -877,10 +963,18 @@ index 35bb784..92e08c5 100644
  static int do_dentry_open(struct file *f,
                          struct inode *inode,
 diff --git a/fs/read_write.c b/fs/read_write.c
-index 6e542f0..c6fa090 100644
+index 6aa8c7a..b5d392e 100644
 --- a/fs/read_write.c
 +++ b/fs/read_write.c
-@@ -483,6 +483,7 @@ vfs_readf_t vfs_readf(struct file *file)
+@@ -453,6 +453,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
+       return ret;
+ }
++EXPORT_SYMBOL_GPL(vfs_read);
+ static ssize_t new_sync_write(struct file *filp, const char __user *buf, size_t len, loff_t *ppos)
+ {
+@@ -493,6 +494,7 @@ vfs_readf_t vfs_readf(struct file *file)
                return new_sync_read;
        return ERR_PTR(-ENOSYS);
  }
@@ -888,19 +982,27 @@ index 6e542f0..c6fa090 100644
  
  vfs_writef_t vfs_writef(struct file *file)
  {
-@@ -494,6 +495,7 @@ vfs_writef_t vfs_writef(struct file *file)
+@@ -504,6 +506,7 @@ vfs_writef_t vfs_writef(struct file *file)
                return new_sync_write;
        return ERR_PTR(-ENOSYS);
  }
 +EXPORT_SYMBOL_GPL(vfs_writef);
  
- ssize_t __kernel_write(struct file *file, const char *buf, size_t count, loff_t *pos)
+ ssize_t __kernel_write(struct file *file, const void *buf, size_t count, loff_t *pos)
+ {
+@@ -573,6 +576,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
+       return ret;
+ }
++EXPORT_SYMBOL_GPL(vfs_write);
+ static inline loff_t file_pos_read(struct file *file)
  {
 diff --git a/fs/splice.c b/fs/splice.c
-index 9753304..b38e036 100644
+index eb888c6..7ab89d2 100644
 --- a/fs/splice.c
 +++ b/fs/splice.c
-@@ -866,6 +866,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
+@@ -850,6 +850,7 @@ long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
  
        return splice_write(pipe, out, ppos, len, flags);
  }
@@ -908,7 +1010,7 @@ index 9753304..b38e036 100644
  
  /*
   * Attempt to initiate a splice from a file to a pipe.
-@@ -895,6 +896,7 @@ long do_splice_to(struct file *in, loff_t *ppos,
+@@ -879,6 +880,7 @@ long do_splice_to(struct file *in, loff_t *ppos,
  
        return splice_read(in, ppos, pipe, len, flags);
  }
@@ -917,7 +1019,7 @@ index 9753304..b38e036 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
+index eb61780..32c5a05 100644
 --- a/fs/sync.c
 +++ b/fs/sync.c
 @@ -38,6 +38,7 @@ int __sync_filesystem(struct super_block *sb, int wait)
@@ -929,10 +1031,10 @@ index 7a5fa3f..c9b9d46 100644
  /*
   * Write out and wait upon all dirty data associated with this
 diff --git a/fs/xattr.c b/fs/xattr.c
-index 464c94b..0234d49 100644
+index 61cd28b..35570cd 100644
 --- a/fs/xattr.c
 +++ b/fs/xattr.c
-@@ -296,6 +296,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
+@@ -297,6 +297,7 @@ vfs_getxattr_alloc(struct dentry *dentry, const char *name, char **xattr_value,
        *xattr_value = value;
        return error;
  }
@@ -940,20 +1042,32 @@ index 464c94b..0234d49 100644
  
  ssize_t
  __vfs_getxattr(struct dentry *dentry, struct inode *inode, const char *name,
+diff --git a/kernel/locking/lockdep.c b/kernel/locking/lockdep.c
+index bc97a97..895a1ba 100644
+--- a/kernel/locking/lockdep.c
++++ b/kernel/locking/lockdep.c
+@@ -155,6 +155,7 @@ inline struct lock_class *lockdep_hlock_class(struct held_lock *hlock)
+       }
+       return lock_classes + hlock->class_idx - 1;
+ }
++EXPORT_SYMBOL_GPL(lockdep_hlock_class);
+ #define hlock_class(hlock) lockdep_hlock_class(hlock)
+ #ifdef CONFIG_LOCK_STAT
 diff --git a/kernel/task_work.c b/kernel/task_work.c
-index d513051..e056d54 100644
+index 836a72a..aa00d49 100644
 --- a/kernel/task_work.c
 +++ b/kernel/task_work.c
-@@ -119,3 +119,4 @@ void task_work_run(void)
+@@ -115,3 +115,4 @@ void task_work_run(void)
                } while (work);
        }
  }
 +EXPORT_SYMBOL_GPL(task_work_run);
 diff --git a/security/commoncap.c b/security/commoncap.c
-index 7abebd7..c079ce4 100644
+index fc46f5b..90543ef 100644
 --- a/security/commoncap.c
 +++ b/security/commoncap.c
-@@ -1062,12 +1062,14 @@ int cap_mmap_addr(unsigned long addr)
+@@ -1270,12 +1270,14 @@ int cap_mmap_addr(unsigned long addr)
        }
        return ret;
  }
@@ -989,10 +1103,10 @@ index 03c1652..f88c84b 100644
  int devcgroup_inode_mknod(int mode, dev_t dev)
  {
 diff --git a/security/security.c b/security/security.c
-index 3013237..342ce8b 100644
+index 4bf0f57..b30d1e1 100644
 --- a/security/security.c
 +++ b/security/security.c
-@@ -535,6 +535,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
+@@ -530,6 +530,7 @@ int security_path_rmdir(const struct path *dir, struct dentry *dentry)
                return 0;
        return call_int_hook(path_rmdir, 0, dir, dentry);
  }
@@ -1000,7 +1114,7 @@ index 3013237..342ce8b 100644
  
  int security_path_unlink(const struct path *dir, struct dentry *dentry)
  {
-@@ -551,6 +552,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
+@@ -546,6 +547,7 @@ int security_path_symlink(const struct path *dir, struct dentry *dentry,
                return 0;
        return call_int_hook(path_symlink, 0, dir, dentry, old_name);
  }
@@ -1008,7 +1122,7 @@ index 3013237..342ce8b 100644
  
  int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
                       struct dentry *new_dentry)
-@@ -559,6 +561,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
+@@ -554,6 +556,7 @@ int security_path_link(struct dentry *old_dentry, const struct path *new_dir,
                return 0;
        return call_int_hook(path_link, 0, old_dentry, new_dir, new_dentry);
  }
@@ -1016,7 +1130,7 @@ index 3013237..342ce8b 100644
  
  int security_path_rename(const struct path *old_dir, struct dentry *old_dentry,
                         const struct path *new_dir, struct dentry *new_dentry,
-@@ -586,6 +589,7 @@ int security_path_truncate(const struct path *path)
+@@ -581,6 +584,7 @@ int security_path_truncate(const struct path *path)
                return 0;
        return call_int_hook(path_truncate, 0, path);
  }
@@ -1024,7 +1138,7 @@ index 3013237..342ce8b 100644
  
  int security_path_chmod(const struct path *path, umode_t mode)
  {
-@@ -593,6 +597,7 @@ int security_path_chmod(const struct path *path, umode_t mode)
+@@ -588,6 +592,7 @@ int security_path_chmod(const struct path *path, umode_t mode)
                return 0;
        return call_int_hook(path_chmod, 0, path, mode);
  }
@@ -1032,7 +1146,7 @@ index 3013237..342ce8b 100644
  
  int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
  {
-@@ -600,6 +605,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
+@@ -595,6 +600,7 @@ int security_path_chown(const struct path *path, kuid_t uid, kgid_t gid)
                return 0;
        return call_int_hook(path_chown, 0, path, uid, gid);
  }
@@ -1040,7 +1154,7 @@ index 3013237..342ce8b 100644
  
  int security_path_chroot(const struct path *path)
  {
-@@ -685,6 +691,7 @@ int security_inode_readlink(struct dentry *dentry)
+@@ -680,6 +686,7 @@ int security_inode_readlink(struct dentry *dentry)
                return 0;
        return call_int_hook(inode_readlink, 0, dentry);
  }
@@ -1048,7 +1162,7 @@ index 3013237..342ce8b 100644
  
  int security_inode_follow_link(struct dentry *dentry, struct inode *inode,
                               bool rcu)
-@@ -700,6 +707,7 @@ int security_inode_permission(struct inode *inode, int mask)
+@@ -695,6 +702,7 @@ int security_inode_permission(struct inode *inode, int mask)
                return 0;
        return call_int_hook(inode_permission, 0, inode, mask);
  }
@@ -1056,7 +1170,7 @@ index 3013237..342ce8b 100644
  
  int security_inode_setattr(struct dentry *dentry, struct iattr *attr)
  {
-@@ -871,6 +879,7 @@ int security_file_permission(struct file *file, int mask)
+@@ -866,6 +874,7 @@ int security_file_permission(struct file *file, int mask)
  
        return fsnotify_perm(file, mask);
  }
@@ -1064,7 +1178,7 @@ index 3013237..342ce8b 100644
  
  int security_file_alloc(struct file *file)
  {
-@@ -930,6 +939,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
+@@ -925,6 +934,7 @@ int security_mmap_file(struct file *file, unsigned long prot,
                return ret;
        return ima_file_mmap(file, prot);
  }
@@ -1163,7 +1277,7 @@ 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    2017-09-05 10:42:11.038754821 +0200
++++ linux/Documentation/filesystems/aufs/design/01intro.txt    2017-11-12 22:24:42.257509799 +0100
 @@ -0,0 +1,171 @@
 +
 +# Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -1950,6 +2064,147 @@ 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   2017-11-12 22:24:44.694244127 +0100
+@@ -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   2017-11-12 22:24:44.694244127 +0100
+@@ -0,0 +1,102 @@
++
++# Copyright (C) 2017 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     2017-07-29 12:14:25.896375188 +0200
@@ -2818,8 +3073,8 @@ 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       2017-07-29 12:14:25.896375188 +0200
-@@ -0,0 +1,59 @@
++++ linux/fs/aufs/aufs.h       2017-11-12 22:24:44.697577553 +0100
+@@ -0,0 +1,60 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -2862,15 +3117,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"
@@ -2881,8 +3137,8 @@ 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     2017-07-29 12:14:25.896375188 +0200
-@@ -0,0 +1,1422 @@
++++ linux/fs/aufs/branch.c     2017-11-12 22:24:44.697577553 +0100
+@@ -0,0 +1,1432 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -2918,6 +3174,8 @@ 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);
@@ -3022,7 +3280,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +              goto out;
 +      add_branch->br_xino.xi_nondir.total = 8; /* initial size */
 +      add_branch->br_xino.xi_nondir.array
-+              = kzalloc(sizeof(ino_t) * add_branch->br_xino.xi_nondir.total,
++              = 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;
@@ -3286,6 +3544,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))
@@ -3452,14 +3715,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)) {
@@ -3469,7 +3733,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;
 +}
@@ -3872,6 +4136,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);
@@ -4281,8 +4548,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.c linux/fs/aufs/branch.c
 +      goto out; /* success */
 +
 +out_bf:
-+      if (bf)
-+              kfree(bf);
++      kfree(bf);
 +out:
 +      AuTraceErr(err);
 +      return err;
@@ -4307,8 +4573,8 @@ 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     2017-07-29 12:14:25.896375188 +0200
-@@ -0,0 +1,321 @@
++++ linux/fs/aufs/branch.h     2017-11-12 22:24:44.697577553 +0100
+@@ -0,0 +1,333 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -4336,6 +4602,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"
@@ -4433,6 +4700,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;
 +};
 +
 +/* ---------------------------------------------------------------------- */
@@ -4479,7 +4748,7 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +
 +static inline int au_br_rdonly(struct au_branch *br)
 +{
-+      return ((au_br_sb(br)->s_flags & MS_RDONLY)
++      return (sb_rdonly(au_br_sb(br))
 +              || !au_br_writable(br->br_perm))
 +              ? -EROFS : 0;
 +}
@@ -4599,15 +4868,24 @@ diff -urN /usr/share/empty/fs/aufs/branch.h linux/fs/aufs/branch.h
 +
 +/* ---------------------------------------------------------------------- */
 +
++#define wbr_wh_read_lock(wbr) au_rw_read_lock(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_write_lock(wbr)        au_rw_write_lock(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_read_trylock(wbr)      au_rw_read_trylock(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_write_trylock(wbr) au_rw_write_trylock(&(wbr)->wbr_wh_rwsem)
 +/*
-+ * wbr_wh_read_lock, wbr_wh_write_lock
-+ * wbr_wh_read_unlock, wbr_wh_write_unlock, wbr_wh_downgrade_lock
-+ */
-+AuSimpleRwsemFuncs(wbr_wh, struct au_wbr *wbr, &wbr->wbr_wh_rwsem);
++#define wbr_wh_read_trylock_nested(wbr) \
++      au_rw_read_trylock_nested(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_write_trylock_nested(wbr) \
++      au_rw_write_trylock_nested(&(wbr)->wbr_wh_rwsem)
++*/
 +
-+#define WbrWhMustNoWaiters(wbr)       AuRwMustNoWaiters(&wbr->wbr_wh_rwsem)
-+#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&wbr->wbr_wh_rwsem)
-+#define WbrWhMustWriteLock(wbr)       AuRwMustWriteLock(&wbr->wbr_wh_rwsem)
++#define wbr_wh_read_unlock(wbr)       au_rw_read_unlock(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_write_unlock(wbr)      au_rw_write_unlock(&(wbr)->wbr_wh_rwsem)
++#define wbr_wh_downgrade_lock(wbr)    au_rw_dgrade_lock(&(wbr)->wbr_wh_rwsem)
++
++#define WbrWhMustNoWaiters(wbr)       AuRwMustNoWaiters(&(wbr)->wbr_wh_rwsem)
++#define WbrWhMustAnyLock(wbr) AuRwMustAnyLock(&(wbr)->wbr_wh_rwsem)
++#define WbrWhMustWriteLock(wbr)       AuRwMustWriteLock(&(wbr)->wbr_wh_rwsem)
 +
 +/* ---------------------------------------------------------------------- */
 +
@@ -4632,8 +4910,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      2017-07-29 12:14:25.899708630 +0200
-@@ -0,0 +1,38 @@
++++ linux/fs/aufs/conf.mk      2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,39 @@
 +
 +AuConfStr = CONFIG_AUFS_FS=${CONFIG_AUFS_FS}
 +
@@ -4650,6 +4928,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 \
@@ -4674,8 +4953,8 @@ 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       2017-09-05 10:42:11.055421928 +0200
-@@ -0,0 +1,1442 @@
++++ linux/fs/aufs/cpup.c       2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,1443 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -5197,7 +5476,8 @@ diff -urN /usr/share/empty/fs/aufs/cpup.c linux/fs/aufs/cpup.c
 +              else {
 +                      inode_unlock_shared(h_src_inode);
 +                      err = vfsub_getattr(&h_path, &h_src_attr->st);
-+                      vfsub_inode_lock_shared_nested(h_src_inode, AuLsc_I_CHILD);
++                      vfsub_inode_lock_shared_nested(h_src_inode,
++                                                     AuLsc_I_CHILD);
 +              }
 +              if (unlikely(err)) {
 +                      inode_unlock_shared(h_src_inode);
@@ -6120,8 +6400,8 @@ 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       2017-07-29 12:14:25.899708630 +0200
-@@ -0,0 +1,94 @@
++++ linux/fs/aufs/cpup.h       2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,99 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -6183,6 +6463,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)
@@ -6218,8 +6503,8 @@ 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    2017-07-29 12:14:25.899708630 +0200
-@@ -0,0 +1,438 @@
++++ linux/fs/aufs/dbgaufs.c    2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,437 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -6336,7 +6621,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);
@@ -6356,10 +6641,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);
@@ -6712,7 +6996,7 @@ 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      2017-07-29 12:14:25.899708630 +0200
++++ linux/fs/aufs/dcsub.c      2017-11-12 22:24:42.267510077 +0100
 @@ -0,0 +1,225 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -7081,7 +7365,7 @@ 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      2017-07-29 12:14:25.899708630 +0200
++++ linux/fs/aufs/debug.c      2017-11-12 22:24:42.267510077 +0100
 @@ -0,0 +1,440 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -7754,8 +8038,8 @@ 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     2017-09-05 10:42:11.055421928 +0200
-@@ -0,0 +1,1130 @@
++++ linux/fs/aufs/dentry.c     2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,1152 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -7780,19 +8064,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;
@@ -7807,7 +8085,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;
@@ -7822,9 +8100,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)
@@ -7839,6 +8117,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);
@@ -7887,26 +8172,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);
@@ -7914,6 +8201,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) {
@@ -7925,10 +8213,16 @@ 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);
 +              vfsub_inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
-+              h_dentry = au_do_lookup(h_parent, dentry, bindex, &whname,
-+                                      &args);
++              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))
@@ -7956,6 +8250,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) {
@@ -7972,7 +8275,9 @@ diff -urN /usr/share/empty/fs/aufs/dentry.c linux/fs/aufs/dentry.c
 +
 +out_parent:
 +      dput(parent);
-+      kfree(whname.name);
++      kfree(args.whname.name);
++      if (dirren)
++              au_dr_lkup_fin(&args);
 +out:
 +      return err;
 +}
@@ -8581,7 +8886,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;
@@ -8632,7 +8937,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
@@ -8773,7 +9078,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;
 +
@@ -8846,7 +9151,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",
@@ -8888,8 +9194,8 @@ 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     2017-07-29 12:14:25.899708630 +0200
-@@ -0,0 +1,252 @@
++++ linux/fs/aufs/dentry.h     2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,266 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -8917,6 +9223,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 {
@@ -8938,12 +9245,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 */
@@ -9144,7 +9464,7 @@ 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      2017-07-29 12:14:25.899708630 +0200
++++ linux/fs/aufs/dinfo.c      2017-11-12 22:24:42.267510077 +0100
 @@ -0,0 +1,553 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -9701,7 +10021,7 @@ 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        2017-09-05 10:42:11.058755349 +0200
++++ linux/fs/aufs/dir.c        2017-11-12 22:24:44.704244405 +0100
 @@ -0,0 +1,759 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -10034,8 +10354,8 @@ diff -urN /usr/share/empty/fs/aufs/dir.c linux/fs/aufs/dir.c
 +      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);
@@ -10464,7 +10784,7 @@ 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        2017-07-29 12:14:25.899708630 +0200
++++ linux/fs/aufs/dir.h        2017-11-12 22:24:42.267510077 +0100
 @@ -0,0 +1,131 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -10597,10 +10917,1472 @@ 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     2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,1315 @@
++/*
++ * Copyright (C) 2017 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;
++      __be64 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((__force __be64)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.b);
++              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 = (__force uint64_t)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((__force __be64)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((__force __be32)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((__force __be64)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 = (__force uint32_t)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((__force __be64)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     2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,139 @@
++/*
++ * Copyright (C) 2017 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 <linux/uuid.h>
++#include "hbl.h"
++
++#define AuDirren_NHASH 100
++
++#ifdef CONFIG_AUFS_DIRREN
++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 + UUID_STRING_LEN + 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      2017-07-29 12:14:25.899708630 +0200
-@@ -0,0 +1,371 @@
++++ linux/fs/aufs/dynop.c      2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,369 @@
 +/*
 + * Copyright (C) 2010-2017 Junjiro R. Okajima
 + *
@@ -10630,23 +12412,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;
 +}
@@ -10687,24 +12469,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);
@@ -10723,11 +12504,11 @@ diff -urN /usr/share/empty/fs/aufs/dynop.c linux/fs/aufs/dynop.c
 +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);
 +}
 +
@@ -10814,7 +12595,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,
@@ -10828,8 +12609,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 */
 +
@@ -10843,7 +12624,7 @@ 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) {
 +              kfree(key);
 +              key = old;
@@ -10940,16 +12721,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);
 +}
 +
 +/* ---------------------------------------------------------------------- */
@@ -10962,7 +12742,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)
@@ -10970,11 +12750,11 @@ 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      2017-07-29 12:14:25.899708630 +0200
++++ linux/fs/aufs/dynop.h      2017-11-12 22:24:44.704244405 +0100
 @@ -0,0 +1,74 @@
 +/*
 + * Copyright (C) 2010-2017 Junjiro R. Okajima
@@ -11017,7 +12797,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;
@@ -11052,7 +12832,7 @@ 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     2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/export.c     2017-11-12 22:24:42.267510077 +0100
 @@ -0,0 +1,836 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -12322,8 +14102,8 @@ 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       2017-07-29 12:14:25.903042072 +0200
-@@ -0,0 +1,858 @@
++++ linux/fs/aufs/file.c       2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,856 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -12434,7 +14214,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;
@@ -12469,9 +14249,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);
@@ -12548,11 +14331,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();
@@ -12564,33 +14347,27 @@ 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)
++      di_write_lock_child(dentry);
++      err = au_cmoo(dentry);
++      di_downgrade_lock(dentry, AuLock_IR);
++      if (!err) {
++              if (!aopen)
 +                      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);
++              else {
++                      lockdep_off();
++                      err = args->open(file, vfsub_file_flags(file), NULL);
++                      lockdep_on();
++              }
 +      }
++      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();
@@ -12603,6 +14380,7 @@ diff -urN /usr/share/empty/fs/aufs/file.c linux/fs/aufs/file.c
 +      }
 +
 +out:
++      AuTraceErr(err);
 +      return err;
 +}
 +
@@ -13184,8 +14962,8 @@ 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       2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,331 @@
++++ linux/fs/aufs/file.h       2017-11-12 22:24:44.704244405 +0100
+@@ -0,0 +1,340 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -13251,7 +15029,7 @@ 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;
++      struct hlist_bl_node    fi_hlist;
 +      struct file             *fi_file;       /* very ugly */
 +} ____cacheline_aligned_in_smp;
 +
@@ -13263,7 +15041,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;
@@ -13333,11 +15111,20 @@ diff -urN /usr/share/empty/fs/aufs/file.h linux/fs/aufs/file.h
 +
 +/* ---------------------------------------------------------------------- */
 +
++#define fi_read_lock(f)       au_rw_read_lock(&au_fi(f)->fi_rwsem)
++#define fi_write_lock(f)      au_rw_write_lock(&au_fi(f)->fi_rwsem)
++#define fi_read_trylock(f)    au_rw_read_trylock(&au_fi(f)->fi_rwsem)
++#define fi_write_trylock(f)   au_rw_write_trylock(&au_fi(f)->fi_rwsem)
 +/*
-+ * fi_read_lock, fi_write_lock,
-+ * fi_read_unlock, fi_write_unlock, fi_downgrade_lock
-+ */
-+AuSimpleRwsemFuncs(fi, struct file *f, &au_fi(f)->fi_rwsem);
++#define fi_read_trylock_nested(f) \
++      au_rw_read_trylock_nested(&au_fi(f)->fi_rwsem)
++#define fi_write_trylock_nested(f) \
++      au_rw_write_trylock_nested(&au_fi(f)->fi_rwsem)
++*/
++
++#define fi_read_unlock(f)     au_rw_read_unlock(&au_fi(f)->fi_rwsem)
++#define fi_write_unlock(f)    au_rw_write_unlock(&au_fi(f)->fi_rwsem)
++#define fi_downgrade_lock(f)  au_rw_dgrade_lock(&au_fi(f)->fi_rwsem)
 +
 +/* lock subclass for finfo */
 +enum {
@@ -13519,7 +15306,7 @@ 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      2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/finfo.c      2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,148 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -13671,7 +15458,7 @@ 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       2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/f_op.c       2017-11-12 22:24:44.704244405 +0100
 @@ -0,0 +1,817 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -13776,8 +15563,8 @@ diff -urN /usr/share/empty/fs/aufs/f_op.c linux/fs/aufs/f_op.c
 +      aufs_bindex_t bindex;
 +
 +      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);
@@ -14894,9 +16681,77 @@ diff -urN /usr/share/empty/fs/aufs/fstype.h linux/fs/aufs/fstype.h
 +
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_FSTYPE_H__ */
+diff -urN /usr/share/empty/fs/aufs/hbl.h linux/fs/aufs/hbl.h
+--- /usr/share/empty/fs/aufs/hbl.h     1970-01-01 01:00:00.000000000 +0100
++++ linux/fs/aufs/hbl.h        2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,64 @@
++/*
++ * Copyright (C) 2017 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/>.
++ */
++
++/*
++ * helpers for hlist_bl.h
++ */
++
++#ifndef __AUFS_HBL_H__
++#define __AUFS_HBL_H__
++
++#ifdef __KERNEL__
++
++#include <linux/list_bl.h>
++
++static inline void au_hbl_add(struct hlist_bl_node *node,
++                            struct hlist_bl_head *hbl)
++{
++      hlist_bl_lock(hbl);
++      hlist_bl_add_head(node, hbl);
++      hlist_bl_unlock(hbl);
++}
++
++static inline void au_hbl_del(struct hlist_bl_node *node,
++                            struct hlist_bl_head *hbl)
++{
++      hlist_bl_lock(hbl);
++      hlist_bl_del(node);
++      hlist_bl_unlock(hbl);
++}
++
++#define au_hbl_for_each(pos, head)                                    \
++      for (pos = hlist_bl_first(head);                                \
++           pos;                                                       \
++           pos = pos->next)
++
++static inline unsigned long au_hbl_count(struct hlist_bl_head *hbl)
++{
++      unsigned long cnt;
++      struct hlist_bl_node *pos;
++
++      cnt = 0;
++      hlist_bl_lock(hbl);
++      au_hbl_for_each(pos, hbl)
++              cnt++;
++      hlist_bl_unlock(hbl);
++      return cnt;
++}
++
++#endif /* __KERNEL__ */
++#endif /* __AUFS_HBL_H__ */
 diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c
 --- /usr/share/empty/fs/aufs/hfsnotify.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/hfsnotify.c  2017-07-31 10:27:18.853311720 +0200
++++ linux/fs/aufs/hfsnotify.c  2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,289 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -14933,7 +16788,7 @@ diff -urN /usr/share/empty/fs/aufs/hfsnotify.c linux/fs/aufs/hfsnotify.c
 +                                           hn_mark);
 +      /* AuDbg("here\n"); */
 +      au_cache_free_hnotify(hn);
-+      smp_mb__before_atomic();
++      smp_mb__before_atomic(); /* for atomic64_dec */
 +      if (atomic64_dec_and_test(&au_hfsn_ifree))
 +              wake_up(&au_hfsn_wq);
 +}
@@ -15249,8 +17104,8 @@ diff -urN /usr/share/empty/fs/aufs/hfsplus.c linux/fs/aufs/hfsplus.c
 +}
 diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c
 --- /usr/share/empty/fs/aufs/hnotify.c 1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/hnotify.c    2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,711 @@
++++ linux/fs/aufs/hnotify.c    2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,719 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -15715,6 +17570,14 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c
 +      AuDebugOn(!sbinfo);
 +      si_write_lock(sb, AuLock_NOPLMW);
 +
++      if (au_opt_test(sbinfo->si_mntflags, DIRREN))
++              switch (a->mask & FS_EVENTS_POSS_ON_CHILD) {
++              case FS_MOVED_FROM:
++              case FS_MOVED_TO:
++                      AuWarn1("DIRREN with UDBA may not work correctly "
++                              "for the direct rename(2)\n");
++              }
++
 +      ii_read_lock_parent(a->dir);
 +      bfound = -1;
 +      bbot = au_ibbot(a->dir);
@@ -15964,7 +17827,7 @@ diff -urN /usr/share/empty/fs/aufs/hnotify.c linux/fs/aufs/hnotify.c
 +}
 diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c
 --- /usr/share/empty/fs/aufs/iinfo.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/iinfo.c      2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/iinfo.c      2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,285 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -16253,7 +18116,7 @@ diff -urN /usr/share/empty/fs/aufs/iinfo.c linux/fs/aufs/iinfo.c
 +}
 diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c
 --- /usr/share/empty/fs/aufs/inode.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/inode.c      2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/inode.c      2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,527 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -16784,8 +18647,8 @@ diff -urN /usr/share/empty/fs/aufs/inode.c linux/fs/aufs/inode.c
 +}
 diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h
 --- /usr/share/empty/fs/aufs/inode.h   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/inode.h      2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,694 @@
++++ linux/fs/aufs/inode.h      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,695 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -16814,7 +18677,6 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h
 +
 +#include <linux/fsnotify.h>
 +#include "rwsem.h"
-+#include "vfsub.h"
 +
 +struct vfsmount;
 +
@@ -16867,7 +18729,7 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h
 +struct au_icntnr {
 +      struct au_iinfo iinfo;
 +      struct inode vfs_inode;
-+      struct hlist_node plink;
++      struct hlist_bl_node plink;
 +} ____cacheline_aligned_in_smp;
 +
 +/* au_pin flags */
@@ -17183,10 +19045,9 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h
 +#undef AuWriteLockFunc
 +#undef AuRWLockFuncs
 +
-+/*
-+ * ii_read_unlock, ii_write_unlock, ii_downgrade_lock
-+ */
-+AuSimpleUnlockRwsemFuncs(ii, struct inode *i, &au_ii(i)->ii_rwsem);
++#define ii_read_unlock(i)     au_rw_read_unlock(&au_ii(i)->ii_rwsem)
++#define ii_write_unlock(i)    au_rw_write_unlock(&au_ii(i)->ii_rwsem)
++#define ii_downgrade_lock(i)  au_rw_dgrade_lock(&au_ii(i)->ii_rwsem)
 +
 +#define IiMustNoWaiters(i)    AuRwMustNoWaiters(&au_ii(i)->ii_rwsem)
 +#define IiMustAnyLock(i)      AuRwMustAnyLock(&au_ii(i)->ii_rwsem)
@@ -17465,12 +19326,15 @@ diff -urN /usr/share/empty/fs/aufs/inode.h linux/fs/aufs/inode.h
 +      au_hn_suspend(hdir);
 +}
 +
++#if 0 /* unused */
++#include "vfsub.h"
 +static inline void au_hn_inode_lock_shared_nested(struct au_hinode *hdir,
 +                                                unsigned int sc)
 +{
 +      vfsub_inode_lock_shared_nested(hdir->hi_inode, sc);
 +      au_hn_suspend(hdir);
 +}
++#endif
 +
 +static inline void au_hn_inode_unlock(struct au_hinode *hdir)
 +{
@@ -17705,7 +19569,7 @@ diff -urN /usr/share/empty/fs/aufs/ioctl.c linux/fs/aufs/ioctl.c
 +#endif
 diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c
 --- /usr/share/empty/fs/aufs/i_op_add.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/i_op_add.c   2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/i_op_add.c   2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,920 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -18629,8 +20493,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_add.c linux/fs/aufs/i_op_add.c
 +}
 diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 --- /usr/share/empty/fs/aufs/i_op.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/i_op.c       2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,1452 @@
++++ linux/fs/aufs/i_op.c       2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,1459 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -18885,27 +20749,28 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +/* ---------------------------------------------------------------------- */
 +
 +struct aopen_node {
-+      struct hlist_node hlist;
++      struct hlist_bl_node hblist;
 +      struct file *file, *h_file;
 +};
 +
 +static int au_do_aopen(struct inode *inode, struct file *file)
 +{
-+      struct au_sphlhead *aopen;
++      struct hlist_bl_head *aopen;
++      struct hlist_bl_node *pos;
 +      struct aopen_node *node;
 +      struct au_do_open_args args = {
-+              .no_lock        = 1,
-+              .open           = au_do_open_nondir
++              .aopen  = 1,
++              .open   = au_do_open_nondir
 +      };
 +
 +      aopen = &au_sbi(inode->i_sb)->si_aopen;
-+      spin_lock(&aopen->spin);
-+      hlist_for_each_entry(node, &aopen->head, hlist)
++      hlist_bl_lock(aopen);
++      hlist_bl_for_each_entry(node, pos, aopen, hblist)
 +              if (node->file == file) {
 +                      args.h_file = node->h_file;
 +                      break;
 +              }
-+      spin_unlock(&aopen->spin);
++      hlist_bl_unlock(aopen);
 +      /* AuDebugOn(!args.h_file); */
 +
 +      return au_do_open(file, &args);
@@ -18915,10 +20780,10 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +                          struct file *file, unsigned int open_flag,
 +                          umode_t create_mode, int *opened)
 +{
-+      int err, h_opened = *opened;
++      int err, unlocked, h_opened = *opened;
 +      unsigned int lkup_flags;
 +      struct dentry *parent, *d;
-+      struct au_sphlhead *aopen;
++      struct hlist_bl_head *aopen;
 +      struct vfsub_aopen_args args = {
 +              .open_flag      = open_flag,
 +              .create_mode    = create_mode,
@@ -18960,6 +20825,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +          || !(open_flag & O_CREAT))
 +              goto out_no_open;
 +
++      unlocked = 0;
 +      err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN);
 +      if (unlikely(err))
 +              goto out;
@@ -18990,6 +20856,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +                      put_filp(args.file);
 +              goto out_unlock;
 +      }
++      di_write_unlock(parent);
++      di_write_unlock(dentry);
++      unlocked = 1;
 +
 +      /* some filesystems don't set FILE_CREATED while succeeded? */
 +      *opened |= FILE_CREATED;
@@ -19000,17 +20869,21 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +              args.file = NULL;
 +      }
 +      aopen = &au_sbi(dir->i_sb)->si_aopen;
-+      au_sphl_add(&aopen_node.hlist, aopen);
++      au_hbl_add(&aopen_node.hblist, aopen);
 +      err = finish_open(file, dentry, au_do_aopen, opened);
-+      au_sphl_del(&aopen_node.hlist, aopen);
++      au_hbl_del(&aopen_node.hblist, aopen);
 +      AuTraceErr(err);
 +      AuDbgFile(file);
 +      if (aopen_node.h_file)
 +              fput(aopen_node.h_file);
 +
 +out_unlock:
-+      di_write_unlock(parent);
-+      aufs_read_unlock(dentry, AuLock_DW);
++      if (unlocked)
++              si_read_unlock(dentry->d_sb);
++      else {
++              di_write_unlock(parent);
++              aufs_read_unlock(dentry, AuLock_DW);
++      }
 +      AuDbgDentry(dentry);
 +      if (unlikely(err < 0))
 +              goto out;
@@ -19974,7 +21847,6 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +      lockdep_off();
 +      si_read_lock(sb, AuLock_FLUSH);
 +      ii_write_lock_child(inode);
-+      lockdep_on();
 +
 +      err = 0;
 +      bindex = au_ibtop(inode);
@@ -20002,7 +21874,6 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +              AuDebugOn(1);
 +      }
 +
-+      lockdep_off();
 +      if (!err)
 +              au_cpup_attr_timesizes(inode);
 +      ii_write_unlock(inode);
@@ -20085,7 +21956,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op.c linux/fs/aufs/i_op.c
 +};
 diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c
 --- /usr/share/empty/fs/aufs/i_op_del.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/i_op_del.c   2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/i_op_del.c   2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,511 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -20600,8 +22471,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_del.c linux/fs/aufs/i_op_del.c
 +}
 diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 --- /usr/share/empty/fs/aufs/i_op_ren.c        1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/i_op_ren.c   2017-07-29 12:14:25.903042072 +0200
-@@ -0,0 +1,1165 @@
++++ linux/fs/aufs/i_op_ren.c   2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,1246 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -20638,12 +22509,20 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +#define AuRen_DT_DSTDIR               (1 << 6)
 +#define AuRen_DIROPQ_SRC      (1 << 7)
 +#define AuRen_DIROPQ_DST      (1 << 8)
++#define AuRen_DIRREN          (1 << 9)
++#define AuRen_DROPPED_SRC     (1 << 10)
++#define AuRen_DROPPED_DST     (1 << 11)
 +#define au_ftest_ren(flags, name)     ((flags) & AuRen_##name)
 +#define au_fset_ren(flags, name) \
 +      do { (flags) |= AuRen_##name; } while (0)
 +#define au_fclr_ren(flags, name) \
 +      do { (flags) &= ~AuRen_##name; } while (0)
 +
++#ifndef CONFIG_AUFS_DIRREN
++#undef AuRen_DIRREN
++#define AuRen_DIRREN          0
++#endif
++
 +struct au_ren_args {
 +      struct {
 +              struct dentry *dentry, *h_dentry, *parent, *h_parent,
@@ -20696,6 +22575,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +
 +      struct au_whtmp_rmdir *thargs;
 +      struct dentry *h_dst;
++      struct au_hinode *h_root;
 +};
 +
 +/* ---------------------------------------------------------------------- */
@@ -20910,6 +22790,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      d = a->dst_dentry; /* already renamed on the branch */
 +      always = !!au_opt_test(au_mntflags(d->d_sb), ALWAYS_DIROPQ);
 +      if (au_ftest_ren(a->auren_flags, ISDIR_SRC)
++          && !au_ftest_ren(a->auren_flags, DIRREN)
 +          && a->btgt != au_dbdiropq(a->src_dentry)
 +          && (a->dst_wh_dentry
 +              || a->btgt <= au_dbdiropq(d)
@@ -20957,6 +22838,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +              /* prepare workqueue args for asynchronous rmdir */
 +              h_d = a->dst_h_dentry;
 +              if (au_ftest_ren(a->auren_flags, ISDIR_DST)
++                  /* && !au_ftest_ren(a->auren_flags, DIRREN) */
 +                  && d_is_positive(h_d)) {
 +                      err = -ENOMEM;
 +                      a->thargs = au_whtmp_rmdir_alloc(a->src_dentry->d_sb,
@@ -21006,6 +22888,11 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      }
 +
 +      BUG_ON(d_is_positive(a->dst_h_dentry) && a->src_btop != a->btgt);
++#if 0
++      BUG_ON(!au_ftest_ren(a->auren_flags, DIRREN)
++             && d_is_positive(a->dst_h_dentry)
++             && a->src_btop != a->btgt);
++#endif
 +
 +      /* rename by vfs_rename or cpup */
 +      err = au_ren_or_cpup(a);
@@ -21088,25 +22975,35 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +}
 +
 +/*
-+ * test if @dentry dir can be rename source or not.
-+ * if it can, return 0 and @children is filled.
++ * test if @a->src_dentry dir can be rename source or not.
++ * if it can, return 0.
 + * success means,
 + * - it is a logically empty dir.
 + * - or, it exists on writable branch and has no children including whiteouts
-+ *       on the lower branch.
++ *   on the lower branch unless DIRREN is on.
 + */
-+static int may_rename_srcdir(struct dentry *dentry, aufs_bindex_t btgt)
++static int may_rename_srcdir(struct au_ren_args *a)
 +{
 +      int err;
 +      unsigned int rdhash;
-+      aufs_bindex_t btop;
++      aufs_bindex_t btop, btgt;
++      struct dentry *dentry;
++      struct super_block *sb;
++      struct au_sbinfo *sbinfo;
 +
++      dentry = a->src_dentry;
++      sb = dentry->d_sb;
++      sbinfo = au_sbi(sb);
++      if (au_opt_test(sbinfo->si_mntflags, DIRREN))
++              au_fset_ren(a->auren_flags, DIRREN);
++
++      btgt = a->btgt;
 +      btop = au_dbtop(dentry);
 +      if (btop != btgt) {
 +              struct au_nhash whlist;
 +
-+              SiMustAnyLock(dentry->d_sb);
-+              rdhash = au_sbi(dentry->d_sb)->si_rdhash;
++              SiMustAnyLock(sb);
++              rdhash = sbinfo->si_rdhash;
 +              if (!rdhash)
 +                      rdhash = au_rdhash_est(au_dir_size(/*file*/NULL,
 +                                                         dentry));
@@ -21125,9 +23022,13 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +
 +out:
 +      if (err == -ENOTEMPTY) {
-+              AuWarn1("renaming dir who has child(ren) on multiple branches,"
-+                      " is not supported\n");
-+              err = -EXDEV;
++              if (au_ftest_ren(a->auren_flags, DIRREN)) {
++                      err = 0;
++              } else {
++                      AuWarn1("renaming dir who has child(ren) on multiple "
++                              "branches, is not supported\n");
++                      err = -EXDEV;
++              }
 +      }
 +      return err;
 +}
@@ -21156,7 +23057,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +                      err = may_rename_dstdir(d, &a->whlist);
 +                      au_set_dbtop(d, a->btgt);
 +              } else
-+                      err = may_rename_srcdir(d, a->btgt);
++                      err = may_rename_srcdir(a);
 +      }
 +      a->dst_h_dentry = au_h_dptr(d, au_dbtop(d));
 +      if (unlikely(err))
@@ -21165,7 +23066,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      d = a->src_dentry;
 +      a->src_h_dentry = au_h_dptr(d, au_dbtop(d));
 +      if (au_ftest_ren(a->auren_flags, ISDIR_SRC)) {
-+              err = may_rename_srcdir(d, a->btgt);
++              err = may_rename_srcdir(a);
 +              if (unlikely(err)) {
 +                      au_nhash_wh_free(&a->whlist);
 +                      a->whlist.nh_num = 0;
@@ -21255,6 +23156,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +{
 +      vfsub_unlock_rename(a->src_h_parent, a->src_hdir,
 +                          a->dst_h_parent, a->dst_hdir);
++      if (au_ftest_ren(a->auren_flags, DIRREN)
++          && a->h_root)
++              au_hn_inode_unlock(a->h_root);
 +      if (au_ftest_ren(a->auren_flags, MNT_WRITE))
 +              vfsub_mnt_drop_write(au_br_mnt(a->br));
 +}
@@ -21274,6 +23178,23 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      if (unlikely(err))
 +              goto out;
 +      au_fset_ren(a->auren_flags, MNT_WRITE);
++      if (au_ftest_ren(a->auren_flags, DIRREN)) {
++              struct dentry *root;
++              struct inode *dir;
++
++              /*
++               * sbinfo is already locked, so this ii_read_lock is
++               * unnecessary. but our debugging feature checks it.
++               */
++              root = a->src_inode->i_sb->s_root;
++              if (root != a->src_parent && root != a->dst_parent) {
++                      dir = d_inode(root);
++                      ii_read_lock_parent3(dir);
++                      a->h_root = au_hi(dir, a->btgt);
++                      ii_read_unlock(dir);
++                      au_hn_inode_lock_nested(a->h_root, AuLsc_I_PARENT3);
++              }
++      }
 +      a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir,
 +                                    a->dst_h_parent, a->dst_hdir);
 +      udba = au_opt_udba(a->src_dentry->d_sb);
@@ -21369,34 +23290,39 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +              au_update_dbrange(d, /*do_put_zero*/0);
 +      }
 +
++      if (a->exchange
++          || au_ftest_ren(a->auren_flags, DIRREN)) {
++              d_drop(a->src_dentry);
++              if (au_ftest_ren(a->auren_flags, DIRREN))
++                      au_set_dbwh(a->src_dentry, -1);
++              return;
++      }
++
 +      d = a->src_dentry;
-+      if (!a->exchange) {
-+              au_set_dbwh(d, -1);
-+              bbot = au_dbbot(d);
-+              for (bindex = a->btgt + 1; bindex <= bbot; bindex++) {
-+                      h_d = au_h_dptr(d, bindex);
-+                      if (h_d)
-+                              au_set_h_dptr(d, bindex, NULL);
-+              }
-+              au_set_dbbot(d, a->btgt);
++      au_set_dbwh(d, -1);
++      bbot = au_dbbot(d);
++      for (bindex = a->btgt + 1; bindex <= bbot; bindex++) {
++              h_d = au_h_dptr(d, bindex);
++              if (h_d)
++                      au_set_h_dptr(d, bindex, NULL);
++      }
++      au_set_dbbot(d, a->btgt);
 +
-+              sb = d->d_sb;
-+              i = a->src_inode;
-+              if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
-+                      return; /* success */
++      sb = d->d_sb;
++      i = a->src_inode;
++      if (au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(i))
++              return; /* success */
 +
-+              bbot = au_ibbot(i);
-+              for (bindex = a->btgt + 1; bindex <= bbot; bindex++) {
-+                      h_i = au_h_iptr(i, bindex);
-+                      if (h_i) {
-+                              au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0);
-+                              /* ignore this error */
-+                              au_set_h_iptr(i, bindex, NULL, 0);
-+                      }
++      bbot = au_ibbot(i);
++      for (bindex = a->btgt + 1; bindex <= bbot; bindex++) {
++              h_i = au_h_iptr(i, bindex);
++              if (h_i) {
++                      au_xino_write(sb, bindex, h_i->i_ino, /*ino*/0);
++                      /* ignore this error */
++                      au_set_h_iptr(i, bindex, NULL, 0);
 +              }
-+              au_set_ibbot(i, a->btgt);
 +      }
-+              d_drop(a->src_dentry);
++      au_set_ibbot(i, a->btgt);
 +}
 +
 +/* ---------------------------------------------------------------------- */
@@ -21505,6 +23431,7 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +              unsigned int _flags)
 +{
 +      int err, lock_flags;
++      void *rev;
 +      /* reduce stack space */
 +      struct au_ren_args *a;
 +      struct au_pin pin;
@@ -21564,7 +23491,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +                      goto out_free;
 +              lock_flags |= AuLock_DIRS;
 +      }
-+      err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, lock_flags);
++      err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
++                                      lock_flags);
 +      if (unlikely(err))
 +              goto out_free;
 +
@@ -21717,10 +23645,22 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      /* store timestamps to be revertible */
 +      au_ren_dt(a);
 +
++      /* store dirren info */
++      if (au_ftest_ren(a->auren_flags, DIRREN)) {
++              err = au_dr_rename(a->src_dentry, a->btgt,
++                                 &a->dst_dentry->d_name, &rev);
++              AuTraceErr(err);
++              if (unlikely(err))
++                      goto out_dt;
++      }
++
 +      /* here we go */
 +      err = do_rename(a);
 +      if (unlikely(err))
-+              goto out_dt;
++              goto out_dirren;
++
++      if (au_ftest_ren(a->auren_flags, DIRREN))
++              au_dr_rename_fin(a->src_dentry, a->btgt, rev);
 +
 +      /* update dir attributes */
 +      au_ren_refresh_dir(a);
@@ -21730,6 +23670,9 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +
 +      goto out_hdir; /* success */
 +
++out_dirren:
++      if (au_ftest_ren(a->auren_flags, DIRREN))
++              au_dr_rename_rev(a->src_dentry, a->btgt, rev);
 +out_dt:
 +      au_ren_rev_dt(err, a);
 +out_hdir:
@@ -21743,10 +23686,19 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +      }
 +out_parent:
 +      if (!err) {
++              if (d_unhashed(a->src_dentry))
++                      au_fset_ren(a->auren_flags, DROPPED_SRC);
++              if (d_unhashed(a->dst_dentry))
++                      au_fset_ren(a->auren_flags, DROPPED_DST);
 +              if (!a->exchange)
 +                      d_move(a->src_dentry, a->dst_dentry);
-+              else
++              else {
 +                      d_exchange(a->src_dentry, a->dst_dentry);
++                      if (au_ftest_ren(a->auren_flags, DROPPED_DST))
++                              d_drop(a->dst_dentry);
++              }
++              if (au_ftest_ren(a->auren_flags, DROPPED_SRC))
++                      d_drop(a->src_dentry);
 +      } else {
 +              au_update_dbtop(a->dst_dentry);
 +              if (!a->dst_inode)
@@ -21769,8 +23721,8 @@ diff -urN /usr/share/empty/fs/aufs/i_op_ren.c linux/fs/aufs/i_op_ren.c
 +}
 diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig
 --- /usr/share/empty/fs/aufs/Kconfig   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/Kconfig      2017-07-29 12:14:25.896375188 +0200
-@@ -0,0 +1,185 @@
++++ linux/fs/aufs/Kconfig      2017-11-12 22:24:44.697577553 +0100
+@@ -0,0 +1,198 @@
 +config AUFS_FS
 +      tristate "Aufs (Advanced multi layered unification filesystem) support"
 +      help
@@ -21889,6 +23841,19 @@ diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig
 +      shows better performance in most cases.
 +      See detail in aufs.5.
 +
++config AUFS_DIRREN
++      bool "Workaround for rename(2)-ing a directory"
++      help
++      By default, aufs returns EXDEV error in renameing a dir who has
++      his child on the lower branch, since it is a bad idea to issue
++      rename(2) internally for every lower branch. But user may not
++      accept this behaviour. So here is a workaround to allow such
++      rename(2) and store some extra infromation on the writable
++      branch. Obviously this costs high (and I don't like it).
++      To use this feature, you need to enable this configuration AND
++      to specify the mount option `dirren.'
++      See details in aufs.5 and the design documents.
++
 +config AUFS_SHWH
 +      bool "Show whiteouts"
 +      help
@@ -21958,7 +23923,7 @@ diff -urN /usr/share/empty/fs/aufs/Kconfig linux/fs/aufs/Kconfig
 +endif
 diff -urN /usr/share/empty/fs/aufs/loop.c linux/fs/aufs/loop.c
 --- /usr/share/empty/fs/aufs/loop.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/loop.c       2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/loop.c       2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,147 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -22199,8 +24164,8 @@ diff -urN /usr/share/empty/fs/aufs/magic.mk linux/fs/aufs/magic.mk
 +endif
 diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile
 --- /usr/share/empty/fs/aufs/Makefile  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/Makefile     2017-07-29 12:14:25.896375188 +0200
-@@ -0,0 +1,44 @@
++++ linux/fs/aufs/Makefile     2017-11-12 22:24:44.697577553 +0100
+@@ -0,0 +1,45 @@
 +
 +include ${src}/magic.mk
 +ifeq (${CONFIG_AUFS_FS},m)
@@ -22239,6 +24204,7 @@ diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile
 +aufs-$(CONFIG_AUFS_EXPORT) += export.o
 +aufs-$(CONFIG_AUFS_XATTR) += xattr.o
 +aufs-$(CONFIG_FS_POSIX_ACL) += posix_acl.o
++aufs-$(CONFIG_AUFS_DIRREN) += dirren.o
 +aufs-$(CONFIG_AUFS_FHSM) += fhsm.o
 +aufs-$(CONFIG_AUFS_POLL) += poll.o
 +aufs-$(CONFIG_AUFS_RDU) += rdu.o
@@ -22247,7 +24213,7 @@ diff -urN /usr/share/empty/fs/aufs/Makefile linux/fs/aufs/Makefile
 +aufs-$(CONFIG_AUFS_MAGIC_SYSRQ) += sysrq.o
 diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c
 --- /usr/share/empty/fs/aufs/module.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/module.c     2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/module.c     2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,266 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -22383,7 +24349,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c
 + * iterate_supers_type() doesn't protect us from
 + * remounting (branch management)
 + */
-+struct au_sphlhead au_sbilist;
++struct hlist_bl_head au_sbilist;
 +#endif
 +
 +/*
@@ -22517,7 +24483,7 @@ diff -urN /usr/share/empty/fs/aufs/module.c linux/fs/aufs/module.c
 +module_exit(aufs_exit);
 diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h
 --- /usr/share/empty/fs/aufs/module.h  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/module.h     2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/module.h     2017-11-12 22:24:42.270843503 +0100
 @@ -0,0 +1,101 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -22622,7 +24588,7 @@ diff -urN /usr/share/empty/fs/aufs/module.h linux/fs/aufs/module.h
 +#endif /* __AUFS_MODULE_H__ */
 diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c
 --- /usr/share/empty/fs/aufs/mvdown.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/mvdown.c     2017-07-29 12:14:25.903042072 +0200
++++ linux/fs/aufs/mvdown.c     2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,704 @@
 +/*
 + * Copyright (C) 2011-2017 Junjiro R. Okajima
@@ -22702,7 +24668,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c
 +              for (bindex++; bindex <= bbot; bindex++) {
 +                      br = au_sbr(sb, bindex);
 +                      if (au_br_fhsm(br->br_perm)
-+                          && (!(au_br_sb(br)->s_flags & MS_RDONLY)))
++                          && !sb_rdonly(au_br_sb(br)))
 +                              return bindex;
 +              }
 +      else if (!(a->mvdown.flags & AUFS_MVDOWN_ROLOWER))
@@ -22714,7 +24680,7 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c
 +      else
 +              for (bindex++; bindex <= bbot; bindex++) {
 +                      br = au_sbr(sb, bindex);
-+                      if (!(au_br_sb(br)->s_flags & MS_RDONLY)) {
++                      if (!sb_rdonly(au_br_sb(br))) {
 +                              if (au_br_rdonly(br))
 +                                      a->mvdown.flags
 +                                              |= AUFS_MVDOWN_ROLOWER_R;
@@ -23330,8 +25296,8 @@ diff -urN /usr/share/empty/fs/aufs/mvdown.c linux/fs/aufs/mvdown.c
 +}
 diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 --- /usr/share/empty/fs/aufs/opts.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/opts.c       2017-07-29 12:14:25.903042072 +0200
-@@ -0,0 +1,1846 @@
++++ linux/fs/aufs/opts.c       2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,1891 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -23381,6 +25347,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +      Opt_verbose, Opt_noverbose,
 +      Opt_sum, Opt_nosum, Opt_wsum,
 +      Opt_dirperm1, Opt_nodirperm1,
++      Opt_dirren, Opt_nodirren,
 +      Opt_acl, Opt_noacl,
 +      Opt_tail, Opt_ignore, Opt_ignore_silent, Opt_err
 +};
@@ -23435,10 +25402,18 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +      {Opt_dio, "dio"},
 +      {Opt_nodio, "nodio"},
 +
++#ifdef CONFIG_AUFS_DIRREN
++      {Opt_dirren, "dirren"},
++      {Opt_nodirren, "nodirren"},
++#else
++      {Opt_ignore, "dirren"},
++      {Opt_ignore_silent, "nodirren"},
++#endif
++
 +#ifdef CONFIG_AUFS_FHSM
 +      {Opt_fhsm_sec, "fhsm_sec=%d"},
 +#else
-+      {Opt_ignore_silent, "fhsm_sec=%d"},
++      {Opt_ignore, "fhsm_sec=%d"},
 +#endif
 +
 +      {Opt_diropq_a, "diropq=always"},
@@ -23451,7 +25426,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +
 +      /* keep them temporary */
 +      {Opt_ignore_silent, "nodlgt"},
-+      {Opt_ignore_silent, "clean_plink"},
++      {Opt_ignore, "clean_plink"},
 +
 +#ifdef CONFIG_AUFS_SHWH
 +      {Opt_shwh, "shwh"},
@@ -23489,7 +25464,7 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +      {Opt_acl, "acl"},
 +      {Opt_noacl, "noacl"},
 +#else
-+      {Opt_ignore_silent, "acl"},
++      {Opt_ignore, "acl"},
 +      {Opt_ignore_silent, "noacl"},
 +#endif
 +
@@ -24055,6 +26030,12 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +              case Opt_fhsm_sec:
 +                      AuDbg("fhsm_sec %u\n", opt->fhsm_second);
 +                      break;
++              case Opt_dirren:
++                      AuLabel(dirren);
++                      break;
++              case Opt_nodirren:
++                      AuLabel(nodirren);
++                      break;
 +              case Opt_acl:
 +                      AuLabel(acl);
 +                      break;
@@ -24505,6 +26486,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +              case Opt_wsum:
 +              case Opt_rdblk_def:
 +              case Opt_rdhash_def:
++              case Opt_dirren:
++              case Opt_nodirren:
 +              case Opt_acl:
 +              case Opt_noacl:
 +                      err = 0;
@@ -24771,6 +26754,28 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +              au_fclr_opts(opts->flags, TRUNC_XIB);
 +              break;
 +
++      case Opt_dirren:
++              err = 1;
++              if (!au_opt_test(sbinfo->si_mntflags, DIRREN)) {
++                      err = au_dr_opt_set(sb);
++                      if (!err)
++                              err = 1;
++              }
++              if (err == 1)
++                      au_opt_set(sbinfo->si_mntflags, DIRREN);
++              break;
++      case Opt_nodirren:
++              err = 1;
++              if (au_opt_test(sbinfo->si_mntflags, DIRREN)) {
++                      err = au_dr_opt_clr(sb, au_ftest_opts(opts->flags,
++                                                            DR_FLUSHED));
++                      if (!err)
++                              err = 1;
++              }
++              if (err == 1)
++                      au_opt_clr(sbinfo->si_mntflags, DIRREN);
++              break;
++
 +      case Opt_acl:
 +              sb->s_flags |= MS_POSIXACL;
 +              break;
@@ -25128,7 +27133,11 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +
 +      SiMustWriteLock(sb);
 +
-+      err = 0;
++      err = au_dr_opt_flush(sb);
++      if (unlikely(err))
++              goto out;
++      au_fset_opts(opts->flags, DR_FLUSHED);
++
 +      dir = d_inode(sb->s_root);
 +      sbinfo = au_sbi(sb);
 +      opt_xino = NULL;
@@ -25169,6 +27178,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +              au_fset_opts(opts->flags, REFRESH);
 +
 +      AuDbg("status 0x%x\n", opts->flags);
++
++out:
 +      return err;
 +}
 +
@@ -25180,8 +27191,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.c linux/fs/aufs/opts.c
 +}
 diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h
 --- /usr/share/empty/fs/aufs/opts.h    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/opts.h       2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,213 @@
++++ linux/fs/aufs/opts.h       2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,224 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -25231,11 +27242,16 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h
 +#define AuOpt_WARN_PERM               (1 << 12)       /* warn when add-branch */
 +#define AuOpt_VERBOSE         (1 << 13)       /* busy inode when del-branch */
 +#define AuOpt_DIO             (1 << 14)       /* direct io */
++#define AuOpt_DIRREN          (1 << 15)       /* directory rename */
 +
 +#ifndef CONFIG_AUFS_HNOTIFY
 +#undef AuOpt_UDBA_HNOTIFY
 +#define AuOpt_UDBA_HNOTIFY    0
 +#endif
++#ifndef CONFIG_AUFS_DIRREN
++#undef AuOpt_DIRREN
++#define AuOpt_DIRREN          0
++#endif
 +#ifndef CONFIG_AUFS_SHWH
 +#undef AuOpt_SHWH
 +#define AuOpt_SHWH            0
@@ -25360,12 +27376,18 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h
 +#define AuOpts_TRUNC_XIB      (1 << 2)
 +#define AuOpts_REFRESH_DYAOP  (1 << 3)
 +#define AuOpts_REFRESH_IDOP   (1 << 4)
++#define AuOpts_DR_FLUSHED     (1 << 5)
 +#define au_ftest_opts(flags, name)    ((flags) & AuOpts_##name)
 +#define au_fset_opts(flags, name) \
 +      do { (flags) |= AuOpts_##name; } while (0)
 +#define au_fclr_opts(flags, name) \
 +      do { (flags) &= ~AuOpts_##name; } while (0)
 +
++#ifndef CONFIG_AUFS_DIRREN
++#undef AuOpts_DR_FLUSHED
++#define AuOpts_DR_FLUSHED     0
++#endif
++
 +struct au_opts {
 +      struct au_opt   *opt;
 +      int             max_opt;
@@ -25397,8 +27419,8 @@ diff -urN /usr/share/empty/fs/aufs/opts.h linux/fs/aufs/opts.h
 +#endif /* __AUFS_OPTS_H__ */
 diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 --- /usr/share/empty/fs/aufs/plink.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/plink.c      2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,514 @@
++++ linux/fs/aufs/plink.c      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,515 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -25536,7 +27558,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +{
 +      int i;
 +      struct au_sbinfo *sbinfo;
-+      struct hlist_head *plink_hlist;
++      struct hlist_bl_head *hbl;
++      struct hlist_bl_node *pos;
 +      struct au_icntnr *icntnr;
 +
 +      SiMustAnyLock(sb);
@@ -25546,11 +27569,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +      AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
 +
 +      for (i = 0; i < AuPlink_NHASH; i++) {
-+              plink_hlist = &sbinfo->si_plink[i].head;
-+              rcu_read_lock();
-+              hlist_for_each_entry_rcu(icntnr, plink_hlist, plink)
++              hbl = sbinfo->si_plink + i;
++              hlist_bl_lock(hbl);
++              hlist_bl_for_each_entry(icntnr, pos, hbl, plink)
 +                      AuDbg("%lu\n", icntnr->vfs_inode.i_ino);
-+              rcu_read_unlock();
++              hlist_bl_unlock(hbl);
 +      }
 +}
 +#endif
@@ -25560,7 +27583,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +{
 +      int found, i;
 +      struct au_sbinfo *sbinfo;
-+      struct hlist_head *plink_hlist;
++      struct hlist_bl_head *hbl;
++      struct hlist_bl_node *pos;
 +      struct au_icntnr *icntnr;
 +
 +      sbinfo = au_sbi(inode->i_sb);
@@ -25570,14 +27594,14 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +
 +      found = 0;
 +      i = au_plink_hash(inode->i_ino);
-+      plink_hlist = &sbinfo->si_plink[i].head;
-+      rcu_read_lock();
-+      hlist_for_each_entry_rcu(icntnr, plink_hlist, plink)
++      hbl =  sbinfo->si_plink + i;
++      hlist_bl_lock(hbl);
++      hlist_bl_for_each_entry(icntnr, pos, hbl, plink)
 +              if (&icntnr->vfs_inode == inode) {
 +                      found = 1;
 +                      break;
 +              }
-+      rcu_read_unlock();
++      hlist_bl_unlock(hbl);
 +      return found;
 +}
 +
@@ -25765,9 +27789,9 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +{
 +      struct super_block *sb;
 +      struct au_sbinfo *sbinfo;
-+      struct hlist_head *plink_hlist;
++      struct hlist_bl_head *hbl;
++      struct hlist_bl_node *pos;
 +      struct au_icntnr *icntnr;
-+      struct au_sphlhead *sphl;
 +      int found, err, cnt, i;
 +
 +      sb = inode->i_sb;
@@ -25780,12 +27804,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +              return;
 +
 +      i = au_plink_hash(inode->i_ino);
-+      sphl = sbinfo->si_plink + i;
-+      plink_hlist = &sphl->head;
++      hbl = sbinfo->si_plink + i;
 +      au_igrab(inode);
 +
-+      spin_lock(&sphl->spin);
-+      hlist_for_each_entry(icntnr, plink_hlist, plink) {
++      hlist_bl_lock(hbl);
++      hlist_bl_for_each_entry(icntnr, pos, hbl, plink) {
 +              if (&icntnr->vfs_inode == inode) {
 +                      found = 1;
 +                      break;
@@ -25793,11 +27816,11 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +      }
 +      if (!found) {
 +              icntnr = container_of(inode, struct au_icntnr, vfs_inode);
-+              hlist_add_head_rcu(&icntnr->plink, plink_hlist);
++              hlist_bl_add_head(&icntnr->plink, hbl);
 +      }
-+      spin_unlock(&sphl->spin);
++      hlist_bl_unlock(hbl);
 +      if (!found) {
-+              cnt = au_sphl_count(sphl);
++              cnt = au_hbl_count(hbl);
 +#define msg "unexpectedly unblanced or too many pseudo-links"
 +              if (cnt > AUFS_PLINK_WARN)
 +                      AuWarn1(msg ", %d\n", cnt);
@@ -25805,7 +27828,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +              err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
 +              if (unlikely(err)) {
 +                      pr_warn("err %d, damaged pseudo link.\n", err);
-+                      au_sphl_del_rcu(&icntnr->plink, sphl);
++                      au_hbl_del(&icntnr->plink, hbl);
 +                      iput(&icntnr->vfs_inode);
 +              }
 +      } else
@@ -25817,8 +27840,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +{
 +      int i, warned;
 +      struct au_sbinfo *sbinfo;
-+      struct hlist_head *plink_hlist;
-+      struct hlist_node *tmp;
++      struct hlist_bl_head *hbl;
++      struct hlist_bl_node *pos, *tmp;
 +      struct au_icntnr *icntnr;
 +
 +      SiMustWriteLock(sb);
@@ -25830,14 +27853,14 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +      /* no spin_lock since sbinfo is write-locked */
 +      warned = 0;
 +      for (i = 0; i < AuPlink_NHASH; i++) {
-+              plink_hlist = &sbinfo->si_plink[i].head;
-+              if (!warned && verbose && !hlist_empty(plink_hlist)) {
++              hbl = sbinfo->si_plink + i;
++              if (!warned && verbose && !hlist_bl_empty(hbl)) {
 +                      pr_warn("pseudo-link is not flushed");
 +                      warned = 1;
 +              }
-+              hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink)
++              hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink)
 +                      iput(&icntnr->vfs_inode);
-+              INIT_HLIST_HEAD(plink_hlist);
++              INIT_HLIST_BL_HEAD(hbl);
 +      }
 +}
 +
@@ -25885,8 +27908,8 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id)
 +{
 +      struct au_sbinfo *sbinfo;
-+      struct hlist_head *plink_hlist;
-+      struct hlist_node *tmp;
++      struct hlist_bl_head *hbl;
++      struct hlist_bl_node *pos, *tmp;
 +      struct au_icntnr *icntnr;
 +      struct inode *inode;
 +      int i, do_put;
@@ -25897,15 +27920,15 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +      AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK));
 +      AuDebugOn(au_plink_maint(sb, AuLock_NOPLM));
 +
-+      /* no spin_lock since sbinfo is write-locked */
++      /* no bit_lock since sbinfo is write-locked */
 +      for (i = 0; i < AuPlink_NHASH; i++) {
-+              plink_hlist = &sbinfo->si_plink[i].head;
-+              hlist_for_each_entry_safe(icntnr, tmp, plink_hlist, plink) {
++              hbl = sbinfo->si_plink + i;
++              hlist_bl_for_each_entry_safe(icntnr, pos, tmp, hbl, plink) {
 +                      inode = au_igrab(&icntnr->vfs_inode);
 +                      ii_write_lock_child(inode);
 +                      do_put = au_plink_do_half_refresh(inode, br_id);
 +                      if (do_put) {
-+                              hlist_del(&icntnr->plink);
++                              hlist_bl_del(&icntnr->plink);
 +                              iput(inode);
 +                      }
 +                      ii_write_unlock(inode);
@@ -25915,7 +27938,7 @@ diff -urN /usr/share/empty/fs/aufs/plink.c linux/fs/aufs/plink.c
 +}
 diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c
 --- /usr/share/empty/fs/aufs/poll.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/poll.c       2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/poll.c       2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,52 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -25971,7 +27994,7 @@ diff -urN /usr/share/empty/fs/aufs/poll.c linux/fs/aufs/poll.c
 +}
 diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c
 --- /usr/share/empty/fs/aufs/posix_acl.c       1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/posix_acl.c  2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/posix_acl.c  2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,102 @@
 +/*
 + * Copyright (C) 2014-2017 Junjiro R. Okajima
@@ -26077,8 +28100,8 @@ diff -urN /usr/share/empty/fs/aufs/posix_acl.c linux/fs/aufs/posix_acl.c
 +}
 diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c
 --- /usr/share/empty/fs/aufs/procfs.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/procfs.c     2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,169 @@
++++ linux/fs/aufs/procfs.c     2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,170 @@
 +/*
 + * Copyright (C) 2010-2017 Junjiro R. Okajima
 + *
@@ -26130,6 +28153,7 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c
 +      int err;
 +      struct super_block *sb;
 +      struct au_sbinfo *sbinfo;
++      struct hlist_bl_node *pos;
 +
 +      err = -EBUSY;
 +      if (unlikely(file->private_data))
@@ -26137,14 +28161,14 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c
 +
 +      sb = NULL;
 +      /* don't use au_sbilist_lock() here */
-+      spin_lock(&au_sbilist.spin);
-+      hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list)
++      hlist_bl_lock(&au_sbilist);
++      hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list)
 +              if (id == sysaufs_si_id(sbinfo)) {
 +                      kobject_get(&sbinfo->si_kobj);
 +                      sb = sbinfo->si_sb;
 +                      break;
 +              }
-+      spin_unlock(&au_sbilist.spin);
++      hlist_bl_unlock(&au_sbilist);
 +
 +      err = -EINVAL;
 +      if (unlikely(!sb))
@@ -26250,7 +28274,7 @@ diff -urN /usr/share/empty/fs/aufs/procfs.c linux/fs/aufs/procfs.c
 +}
 diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c
 --- /usr/share/empty/fs/aufs/rdu.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/rdu.c        2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/rdu.c        2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,381 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -26635,8 +28659,8 @@ diff -urN /usr/share/empty/fs/aufs/rdu.c linux/fs/aufs/rdu.c
 +#endif
 diff -urN /usr/share/empty/fs/aufs/rwsem.h linux/fs/aufs/rwsem.h
 --- /usr/share/empty/fs/aufs/rwsem.h   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/rwsem.h      2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,198 @@
++++ linux/fs/aufs/rwsem.h      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,72 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -26665,179 +28689,53 @@ diff -urN /usr/share/empty/fs/aufs/rwsem.h linux/fs/aufs/rwsem.h
 +
 +#include "debug.h"
 +
-+struct au_rwsem {
-+      struct rw_semaphore     rwsem;
-+#ifdef CONFIG_AUFS_DEBUG
-+      /* just for debugging, not almighty counter */
-+      atomic_t                rcnt, wcnt;
-+#endif
-+};
-+
-+#ifdef CONFIG_LOCKDEP
-+#define au_lockdep_set_name(rw)                                               \
-+      lockdep_set_class_and_name(&(rw)->rwsem,                        \
-+                                 /*original key*/(rw)->rwsem.dep_map.key, \
-+                                 /*name*/#rw)
-+#else
-+#define au_lockdep_set_name(rw) do {} while (0)
-+#endif
-+
-+#ifdef CONFIG_AUFS_DEBUG
-+#define AuDbgCntInit(rw) do { \
-+      atomic_set(&(rw)->rcnt, 0); \
-+      atomic_set(&(rw)->wcnt, 0); \
-+      smp_mb(); /* atomic set */ \
-+} while (0)
-+
-+#define AuDbgCnt(rw, cnt)     atomic_read(&(rw)->cnt)
-+#define AuDbgCntInc(rw, cnt)  atomic_inc(&(rw)->cnt)
-+#define AuDbgCntDec(rw, cnt)  WARN_ON(atomic_dec_return(&(rw)->cnt) < 0)
-+#define AuDbgRcntInc(rw)      AuDbgCntInc(rw, rcnt)
-+#define AuDbgRcntDec(rw)      AuDbgCntDec(rw, rcnt)
-+#define AuDbgWcntInc(rw)      AuDbgCntInc(rw, wcnt)
-+#define AuDbgWcntDec(rw)      AuDbgCntDec(rw, wcnt)
-+#else
-+#define AuDbgCnt(rw, cnt)     0
-+#define AuDbgCntInit(rw)      do {} while (0)
-+#define AuDbgRcntInc(rw)      do {} while (0)
-+#define AuDbgRcntDec(rw)      do {} while (0)
-+#define AuDbgWcntInc(rw)      do {} while (0)
-+#define AuDbgWcntDec(rw)      do {} while (0)
-+#endif /* CONFIG_AUFS_DEBUG */
++/* in the futre, the name 'au_rwsem' will be totally gone */
++#define au_rwsem      rw_semaphore
 +
 +/* to debug easier, do not make them inlined functions */
-+#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(&(rw)->rwsem))
++#define AuRwMustNoWaiters(rw) AuDebugOn(rwsem_is_contended(rw))
 +/* rwsem_is_locked() is unusable */
-+#define AuRwMustReadLock(rw)  AuDebugOn(AuDbgCnt(rw, rcnt) <= 0)
-+#define AuRwMustWriteLock(rw) AuDebugOn(AuDbgCnt(rw, wcnt) <= 0)
-+#define AuRwMustAnyLock(rw)   AuDebugOn(AuDbgCnt(rw, rcnt) <= 0       \
-+                                        && AuDbgCnt(rw, wcnt) <= 0)
-+#define AuRwDestroy(rw)               AuDebugOn(AuDbgCnt(rw, rcnt)            \
-+                                        || AuDbgCnt(rw, wcnt))
-+
-+#define au_rw_init(rw) do {                   \
-+              AuDbgCntInit(rw);               \
-+              init_rwsem(&(rw)->rwsem);       \
-+              au_lockdep_set_name(rw);        \
-+      } while (0)
++#define AuRwMustReadLock(rw)  AuDebugOn(!lockdep_recursing(current) \
++                                        && debug_locks \
++                                        && !lockdep_is_held_type(rw, 1))
++#define AuRwMustWriteLock(rw) AuDebugOn(!lockdep_recursing(current) \
++                                        && debug_locks \
++                                        && !lockdep_is_held_type(rw, 0))
++#define AuRwMustAnyLock(rw)   AuDebugOn(!lockdep_recursing(current) \
++                                        && debug_locks \
++                                        && !lockdep_is_held(rw))
++#define AuRwDestroy(rw)               AuDebugOn(!lockdep_recursing(current) \
++                                        && debug_locks \
++                                        && lockdep_is_held(rw))
++
++#define au_rw_init(rw)        init_rwsem(rw)
 +
 +#define au_rw_init_wlock(rw) do {             \
 +              au_rw_init(rw);                 \
-+              down_write(&(rw)->rwsem);       \
-+              AuDbgWcntInc(rw);               \
++              down_write(rw);                 \
 +      } while (0)
 +
-+#define au_rw_init_wlock_nested(rw, lsc) do { \
-+              au_rw_init(rw);                         \
-+              down_write_nested(&(rw)->rwsem, lsc);   \
-+              AuDbgWcntInc(rw);                       \
++#define au_rw_init_wlock_nested(rw, lsc) do { \
++              au_rw_init(rw);                 \
++              down_write_nested(rw, lsc);     \
 +      } while (0)
 +
-+static inline void au_rw_read_lock(struct au_rwsem *rw)
-+{
-+      down_read(&rw->rwsem);
-+      AuDbgRcntInc(rw);
-+}
-+
-+static inline void au_rw_read_lock_nested(struct au_rwsem *rw, unsigned int lsc)
-+{
-+      down_read_nested(&rw->rwsem, lsc);
-+      AuDbgRcntInc(rw);
-+}
-+
-+static inline void au_rw_read_unlock(struct au_rwsem *rw)
-+{
-+      AuRwMustReadLock(rw);
-+      AuDbgRcntDec(rw);
-+      up_read(&rw->rwsem);
-+}
-+
-+static inline void au_rw_dgrade_lock(struct au_rwsem *rw)
-+{
-+      AuRwMustWriteLock(rw);
-+      AuDbgRcntInc(rw);
-+      AuDbgWcntDec(rw);
-+      downgrade_write(&rw->rwsem);
-+}
-+
-+static inline void au_rw_write_lock(struct au_rwsem *rw)
-+{
-+      down_write(&rw->rwsem);
-+      AuDbgWcntInc(rw);
-+}
-+
-+static inline void au_rw_write_lock_nested(struct au_rwsem *rw,
-+                                         unsigned int lsc)
-+{
-+      down_write_nested(&rw->rwsem, lsc);
-+      AuDbgWcntInc(rw);
-+}
-+
-+static inline void au_rw_write_unlock(struct au_rwsem *rw)
-+{
-+      AuRwMustWriteLock(rw);
-+      AuDbgWcntDec(rw);
-+      up_write(&rw->rwsem);
-+}
-+
-+/* why is not _nested version defined */
-+static inline int au_rw_read_trylock(struct au_rwsem *rw)
-+{
-+      int ret;
-+
-+      ret = down_read_trylock(&rw->rwsem);
-+      if (ret)
-+              AuDbgRcntInc(rw);
-+      return ret;
-+}
-+
-+static inline int au_rw_write_trylock(struct au_rwsem *rw)
-+{
-+      int ret;
-+
-+      ret = down_write_trylock(&rw->rwsem);
-+      if (ret)
-+              AuDbgWcntInc(rw);
-+      return ret;
-+}
-+
-+#undef AuDbgCntDec
-+#undef AuDbgRcntInc
-+#undef AuDbgRcntDec
-+#undef AuDbgWcntDec
-+
-+#define AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_lock(param) \
-+{ au_rw_read_lock(rwsem); } \
-+static inline void prefix##_write_lock(param) \
-+{ au_rw_write_lock(rwsem); } \
-+static inline int prefix##_read_trylock(param) \
-+{ return au_rw_read_trylock(rwsem); } \
-+static inline int prefix##_write_trylock(param) \
-+{ return au_rw_write_trylock(rwsem); }
-+/* why is not _nested version defined */
-+/* static inline void prefix##_read_trylock_nested(param, lsc)
-+{ au_rw_read_trylock_nested(rwsem, lsc)); }
-+static inline void prefix##_write_trylock_nestd(param, lsc)
-+{ au_rw_write_trylock_nested(rwsem, lsc); } */
-+
-+#define AuSimpleUnlockRwsemFuncs(prefix, param, rwsem) \
-+static inline void prefix##_read_unlock(param) \
-+{ au_rw_read_unlock(rwsem); } \
-+static inline void prefix##_write_unlock(param) \
-+{ au_rw_write_unlock(rwsem); } \
-+static inline void prefix##_downgrade_lock(param) \
-+{ au_rw_dgrade_lock(rwsem); }
-+
-+#define AuSimpleRwsemFuncs(prefix, param, rwsem) \
-+      AuSimpleLockRwsemFuncs(prefix, param, rwsem) \
-+      AuSimpleUnlockRwsemFuncs(prefix, param, rwsem)
++#define au_rw_read_lock(rw)           down_read(rw)
++#define au_rw_read_lock_nested(rw, lsc)       down_read_nested(rw, lsc)
++#define au_rw_read_unlock(rw)         up_read(rw)
++#define au_rw_dgrade_lock(rw)         downgrade_write(rw)
++#define au_rw_write_lock(rw)          down_write(rw)
++#define au_rw_write_lock_nested(rw, lsc) down_write_nested(rw, lsc)
++#define au_rw_write_unlock(rw)                up_write(rw)
++/* why is not _nested version defined? */
++#define au_rw_read_trylock(rw)                down_read_trylock(rw)
++#define au_rw_write_trylock(rw)               down_write_trylock(rw)
 +
 +#endif /* __KERNEL__ */
 +#endif /* __AUFS_RWSEM_H__ */
 diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c
 --- /usr/share/empty/fs/aufs/sbinfo.c  1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/sbinfo.c     2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/sbinfo.c     2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,304 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -26873,7 +28771,7 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c
 +
 +      sbinfo = container_of(kobj, struct au_sbinfo, si_kobj);
 +      for (i = 0; i < AuPlink_NHASH; i++)
-+              AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head));
++              AuDebugOn(!hlist_bl_empty(sbinfo->si_plink + i));
 +      AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len));
 +
 +      AuDebugOn(percpu_counter_sum(&sbinfo->si_ninodes));
@@ -26936,7 +28834,7 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c
 +      sbinfo->si_xino_brid = -1;
 +      /* leave si_xib_last_pindex and si_xib_next_bit */
 +
-+      au_sphl_init(&sbinfo->si_aopen);
++      INIT_HLIST_BL_HEAD(&sbinfo->si_aopen);
 +
 +      sbinfo->si_rdcache = msecs_to_jiffies(AUFS_RDCACHE_DEF * MSEC_PER_SEC);
 +      sbinfo->si_rdblk = AUFS_RDBLK_DEF;
@@ -26944,11 +28842,11 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c
 +      sbinfo->si_dirwh = AUFS_DIRWH_DEF;
 +
 +      for (i = 0; i < AuPlink_NHASH; i++)
-+              au_sphl_init(sbinfo->si_plink + i);
++              INIT_HLIST_BL_HEAD(sbinfo->si_plink + i);
 +      init_waitqueue_head(&sbinfo->si_plink_wq);
 +      spin_lock_init(&sbinfo->si_plink_maint_lock);
 +
-+      au_sphl_init(&sbinfo->si_files);
++      INIT_HLIST_BL_HEAD(&sbinfo->si_files);
 +
 +      /* with getattr by default */
 +      sbinfo->si_iop_array = aufs_iop;
@@ -27143,127 +29041,10 @@ diff -urN /usr/share/empty/fs/aufs/sbinfo.c linux/fs/aufs/sbinfo.c
 +      di_write_unlock2(d1, d2);
 +      si_read_unlock(d1->d_sb);
 +}
-diff -urN /usr/share/empty/fs/aufs/spl.h linux/fs/aufs/spl.h
---- /usr/share/empty/fs/aufs/spl.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/spl.h        2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,113 @@
-+/*
-+ * Copyright (C) 2005-2017 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/>.
-+ */
-+
-+/*
-+ * simple list protected by a spinlock
-+ */
-+
-+#ifndef __AUFS_SPL_H__
-+#define __AUFS_SPL_H__
-+
-+#ifdef __KERNEL__
-+
-+#if 0
-+struct au_splhead {
-+      spinlock_t              spin;
-+      struct list_head        head;
-+};
-+
-+static inline void au_spl_init(struct au_splhead *spl)
-+{
-+      spin_lock_init(&spl->spin);
-+      INIT_LIST_HEAD(&spl->head);
-+}
-+
-+static inline void au_spl_add(struct list_head *list, struct au_splhead *spl)
-+{
-+      spin_lock(&spl->spin);
-+      list_add(list, &spl->head);
-+      spin_unlock(&spl->spin);
-+}
-+
-+static inline void au_spl_del(struct list_head *list, struct au_splhead *spl)
-+{
-+      spin_lock(&spl->spin);
-+      list_del(list);
-+      spin_unlock(&spl->spin);
-+}
-+
-+static inline void au_spl_del_rcu(struct list_head *list,
-+                                struct au_splhead *spl)
-+{
-+      spin_lock(&spl->spin);
-+      list_del_rcu(list);
-+      spin_unlock(&spl->spin);
-+}
-+#endif
-+
-+/* ---------------------------------------------------------------------- */
-+
-+struct au_sphlhead {
-+      spinlock_t              spin;
-+      struct hlist_head       head;
-+};
-+
-+static inline void au_sphl_init(struct au_sphlhead *sphl)
-+{
-+      spin_lock_init(&sphl->spin);
-+      INIT_HLIST_HEAD(&sphl->head);
-+}
-+
-+static inline void au_sphl_add(struct hlist_node *hlist,
-+                             struct au_sphlhead *sphl)
-+{
-+      spin_lock(&sphl->spin);
-+      hlist_add_head(hlist, &sphl->head);
-+      spin_unlock(&sphl->spin);
-+}
-+
-+static inline void au_sphl_del(struct hlist_node *hlist,
-+                             struct au_sphlhead *sphl)
-+{
-+      spin_lock(&sphl->spin);
-+      hlist_del(hlist);
-+      spin_unlock(&sphl->spin);
-+}
-+
-+static inline void au_sphl_del_rcu(struct hlist_node *hlist,
-+                                 struct au_sphlhead *sphl)
-+{
-+      spin_lock(&sphl->spin);
-+      hlist_del_rcu(hlist);
-+      spin_unlock(&sphl->spin);
-+}
-+
-+static inline unsigned long au_sphl_count(struct au_sphlhead *sphl)
-+{
-+      unsigned long cnt;
-+      struct hlist_node *pos;
-+
-+      cnt = 0;
-+      spin_lock(&sphl->spin);
-+      hlist_for_each(pos, &sphl->head)
-+              cnt++;
-+      spin_unlock(&sphl->spin);
-+      return cnt;
-+}
-+
-+#endif /* __KERNEL__ */
-+#endif /* __AUFS_SPL_H__ */
 diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c
 --- /usr/share/empty/fs/aufs/super.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/super.c      2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,1044 @@
++++ linux/fs/aufs/super.c      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,1046 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -27554,6 +29335,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c
 +
 +      au_fhsm_show(m, sbinfo);
 +
++      AuBool(DIRREN, dirren);
 +      AuBool(SUM, sum);
 +      /* AuBool(SUM_W, wsum); */
 +      AuBool(WARN_PERM, warn_perm);
@@ -28292,6 +30074,7 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c
 +              if (au_opt_test(sbinfo->si_mntflags, PLINK))
 +                      au_plink_put(sb, /*verbose*/1);
 +              au_xino_clr(sb);
++              au_dr_opt_flush(sb);
 +              sbinfo->si_sb = NULL;
 +              aufs_write_unlock(sb->s_root);
 +              au_nwt_flush(&sbinfo->si_nowait);
@@ -28310,8 +30093,8 @@ diff -urN /usr/share/empty/fs/aufs/super.c linux/fs/aufs/super.c
 +};
 diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 --- /usr/share/empty/fs/aufs/super.h   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/super.h      2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,617 @@
++++ linux/fs/aufs/super.h      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,626 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -28340,8 +30123,8 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +
 +#include <linux/fs.h>
 +#include <linux/kobject.h>
++#include "hbl.h"
 +#include "rwsem.h"
-+#include "spl.h"
 +#include "wkq.h"
 +
 +/* policies to select one among multiple writable branches */
@@ -28463,7 +30246,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +#endif
 +
 +      /* dirty trick to suppoer atomic_open */
-+      struct au_sphlhead      si_aopen;
++      struct hlist_bl_head    si_aopen;
 +
 +      /* vdir parameters */
 +      unsigned long           si_rdcache;     /* max cache time in jiffies */
@@ -28479,13 +30262,13 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +      unsigned int            si_dirwh;
 +
 +      /* pseudo_link list */
-+      struct au_sphlhead      si_plink[AuPlink_NHASH];
++      struct hlist_bl_head    si_plink[AuPlink_NHASH];
 +      wait_queue_head_t       si_plink_wq;
 +      spinlock_t              si_plink_maint_lock;
 +      pid_t                   si_plink_maint_pid;
 +
 +      /* file list */
-+      struct au_sphlhead      si_files;
++      struct hlist_bl_head    si_files;
 +
 +      /* with/without getattr, brother of sb->s_d_op */
 +      struct inode_operations *si_iop_array;
@@ -28507,7 +30290,7 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +#endif
 +
 +#ifdef CONFIG_AUFS_SBILIST
-+      struct hlist_node       si_list;
++      struct hlist_bl_node    si_list;
 +#endif
 +
 +      /* dirty, necessary for unmounting, sysfs and sysrq */
@@ -28682,32 +30465,32 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +
 +#ifdef CONFIG_AUFS_SBILIST
 +/* module.c */
-+extern struct au_sphlhead au_sbilist;
++extern struct hlist_bl_head au_sbilist;
 +
 +static inline void au_sbilist_init(void)
 +{
-+      au_sphl_init(&au_sbilist);
++      INIT_HLIST_BL_HEAD(&au_sbilist);
 +}
 +
 +static inline void au_sbilist_add(struct super_block *sb)
 +{
-+      au_sphl_add(&au_sbi(sb)->si_list, &au_sbilist);
++      au_hbl_add(&au_sbi(sb)->si_list, &au_sbilist);
 +}
 +
 +static inline void au_sbilist_del(struct super_block *sb)
 +{
-+      au_sphl_del(&au_sbi(sb)->si_list, &au_sbilist);
++      au_hbl_del(&au_sbi(sb)->si_list, &au_sbilist);
 +}
 +
 +#ifdef CONFIG_AUFS_MAGIC_SYSRQ
 +static inline void au_sbilist_lock(void)
 +{
-+      spin_lock(&au_sbilist.spin);
++      hlist_bl_lock(&au_sbilist);
 +}
 +
 +static inline void au_sbilist_unlock(void)
 +{
-+      spin_unlock(&au_sbilist.spin);
++      hlist_bl_unlock(&au_sbilist);
 +}
 +#define AuGFP_SBILIST GFP_ATOMIC
 +#else
@@ -28773,11 +30556,20 @@ diff -urN /usr/share/empty/fs/aufs/super.h linux/fs/aufs/super.h
 +/* ---------------------------------------------------------------------- */
 +
 +/* lock superblock. mainly for entry point functions */
-+/*
-+ * __si_read_lock, __si_write_lock,
-+ * __si_read_unlock, __si_write_unlock, __si_downgrade_lock
-+ */
-+AuSimpleRwsemFuncs(__si, struct super_block *sb, &au_sbi(sb)->si_rwsem);
++#define __si_read_lock(sb)    au_rw_read_lock(&au_sbi(sb)->si_rwsem)
++#define __si_write_lock(sb)   au_rw_write_lock(&au_sbi(sb)->si_rwsem)
++#define __si_read_trylock(sb) au_rw_read_trylock(&au_sbi(sb)->si_rwsem)
++#define __si_write_trylock(sb)        au_rw_write_trylock(&au_sbi(sb)->si_rwsem)
++/*
++#define __si_read_trylock_nested(sb) \
++      au_rw_read_trylock_nested(&au_sbi(sb)->si_rwsem)
++#define __si_write_trylock_nested(sb) \
++      au_rw_write_trylock_nested(&au_sbi(sb)->si_rwsem)
++*/
++
++#define __si_read_unlock(sb)  au_rw_read_unlock(&au_sbi(sb)->si_rwsem)
++#define __si_write_unlock(sb) au_rw_write_unlock(&au_sbi(sb)->si_rwsem)
++#define __si_downgrade_lock(sb)       au_rw_dgrade_lock(&au_sbi(sb)->si_rwsem)
 +
 +#define SiMustNoWaiters(sb)   AuRwMustNoWaiters(&au_sbi(sb)->si_rwsem)
 +#define SiMustAnyLock(sb)     AuRwMustAnyLock(&au_sbi(sb)->si_rwsem)
@@ -29144,7 +30936,7 @@ diff -urN /usr/share/empty/fs/aufs/sysaufs.h linux/fs/aufs/sysaufs.h
 +#endif /* __SYSAUFS_H__ */
 diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c
 --- /usr/share/empty/fs/aufs/sysfs.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/sysfs.c      2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/sysfs.c      2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,376 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -29524,8 +31316,8 @@ diff -urN /usr/share/empty/fs/aufs/sysfs.c linux/fs/aufs/sysfs.c
 +}
 diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 --- /usr/share/empty/fs/aufs/sysrq.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/sysrq.c      2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,157 @@
++++ linux/fs/aufs/sysrq.c      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,159 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -29558,7 +31350,8 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 +      char *plevel;
 +      struct au_sbinfo *sbinfo;
 +      struct file *file;
-+      struct au_sphlhead *files;
++      struct hlist_bl_head *files;
++      struct hlist_bl_node *pos;
 +      struct au_finfo *finfo;
 +
 +      plevel = au_plevel;
@@ -29617,8 +31410,8 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 +#endif
 +      pr("files\n");
 +      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) {
 +              umode_t mode;
 +
 +              file = finfo->fi_file;
@@ -29626,7 +31419,7 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 +              if (!special_file(mode))
 +                      au_dpri_file(file);
 +      }
-+      spin_unlock(&files->spin);
++      hlist_bl_unlock(files);
 +      pr("done\n");
 +
 +#undef pr
@@ -29643,10 +31436,11 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 +static void au_sysrq(int key __maybe_unused)
 +{
 +      struct au_sbinfo *sbinfo;
++      struct hlist_bl_node *pos;
 +
 +      lockdep_off();
 +      au_sbilist_lock();
-+      hlist_for_each_entry(sbinfo, &au_sbilist.head, si_list)
++      hlist_bl_for_each_entry(sbinfo, pos, &au_sbilist, si_list)
 +              sysrq_sb(sbinfo->si_sb);
 +      au_sbilist_unlock();
 +      lockdep_on();
@@ -29685,7 +31479,7 @@ diff -urN /usr/share/empty/fs/aufs/sysrq.c linux/fs/aufs/sysrq.c
 +}
 diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c
 --- /usr/share/empty/fs/aufs/vdir.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/vdir.c       2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/vdir.c       2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,892 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -30581,8 +32375,8 @@ diff -urN /usr/share/empty/fs/aufs/vdir.c linux/fs/aufs/vdir.c
 +}
 diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c
 --- /usr/share/empty/fs/aufs/vfsub.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/vfsub.c      2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,900 @@
++++ linux/fs/aufs/vfsub.c      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,894 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -30604,26 +32398,20 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c
 + * sub-routines for VFS
 + */
 +
++#include <linux/mnt_namespace.h>
 +#include <linux/namei.h>
 +#include <linux/nsproxy.h>
 +#include <linux/security.h>
 +#include <linux/splice.h>
-+#ifdef CONFIG_AUFS_BR_FUSE
-+#include "../fs/mount.h"
-+#endif
 +#include "aufs.h"
 +
 +#ifdef CONFIG_AUFS_BR_FUSE
 +int vfsub_test_mntns(struct vfsmount *mnt, struct super_block *h_sb)
 +{
-+      struct nsproxy *ns;
-+
 +      if (!au_test_fuse(h_sb) || !au_userns)
 +              return 0;
 +
-+      ns = current->nsproxy;
-+      /* no {get,put}_nsproxy(ns) */
-+      return real_mount(mnt)->mnt_ns == ns->mnt_ns ? 0 : -EACCES;
++      return is_current_mnt_ns(mnt) ? 0 : -EACCES;
 +}
 +#endif
 +
@@ -31485,7 +33273,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.c linux/fs/aufs/vfsub.c
 +}
 diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h
 --- /usr/share/empty/fs/aufs/vfsub.h   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/vfsub.h      2017-09-05 10:42:11.058755349 +0200
++++ linux/fs/aufs/vfsub.h      2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,360 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -31567,7 +33355,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h
 +
 +static inline int vfsub_native_ro(struct inode *inode)
 +{
-+      return (inode->i_sb->s_flags & MS_RDONLY)
++      return sb_rdonly(inode->i_sb)
 +              || IS_RDONLY(inode)
 +              /* || IS_APPEND(inode) */
 +              || IS_IMMUTABLE(inode);
@@ -31849,7 +33637,7 @@ diff -urN /usr/share/empty/fs/aufs/vfsub.h linux/fs/aufs/vfsub.h
 +#endif /* __AUFS_VFSUB_H__ */
 diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c
 --- /usr/share/empty/fs/aufs/wbr_policy.c      1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/wbr_policy.c 2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/wbr_policy.c 2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,830 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -32683,7 +34471,7 @@ diff -urN /usr/share/empty/fs/aufs/wbr_policy.c linux/fs/aufs/wbr_policy.c
 +};
 diff -urN /usr/share/empty/fs/aufs/whout.c linux/fs/aufs/whout.c
 --- /usr/share/empty/fs/aufs/whout.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/whout.c      2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/whout.c      2017-11-12 22:24:42.274176929 +0100
 @@ -0,0 +1,1061 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -33837,8 +35625,8 @@ diff -urN /usr/share/empty/fs/aufs/whout.h linux/fs/aufs/whout.h
 +#endif /* __AUFS_WHOUT_H__ */
 diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 --- /usr/share/empty/fs/aufs/wkq.c     1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/wkq.c        2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,213 @@
++++ linux/fs/aufs/wkq.c        2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,390 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -33877,10 +35665,177 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 +      au_wkq_func_t func;
 +      void *args;
 +
++#ifdef CONFIG_LOCKDEP
++      int dont_check;
++      struct held_lock **hlock;
++#endif
++
 +      struct completion *comp;
 +};
 +
 +/* ---------------------------------------------------------------------- */
++/*
++ * Aufs passes some operations to the workqueue such as the internal copyup.
++ * This scheme looks rather unnatural for LOCKDEP debugging feature, since the
++ * job run by workqueue depends upon the locks acquired in the other task.
++ * Delegating a small operation to the workqueue, aufs passes its lockdep
++ * information too. And the job in the workqueue restores the info in order to
++ * pretend as if it acquired those locks. This is just to make LOCKDEP work
++ * correctly and expectedly.
++ */
++
++#ifndef CONFIG_LOCKDEP
++AuStubInt0(au_wkq_lockdep_alloc, struct au_wkinfo *wkinfo);
++AuStubVoid(au_wkq_lockdep_free, struct au_wkinfo *wkinfo);
++AuStubVoid(au_wkq_lockdep_pre, struct au_wkinfo *wkinfo);
++AuStubVoid(au_wkq_lockdep_post, struct au_wkinfo *wkinfo);
++AuStubVoid(au_wkq_lockdep_init, struct au_wkinfo *wkinfo);
++#else
++static void au_wkq_lockdep_init(struct au_wkinfo *wkinfo)
++{
++      wkinfo->hlock = NULL;
++      wkinfo->dont_check = 0;
++}
++
++/*
++ * 1: matched
++ * 0: unmatched
++ */
++static int au_wkq_lockdep_test(struct lock_class_key *key, const char *name)
++{
++      static DEFINE_SPINLOCK(spin);
++      static struct {
++              char *name;
++              struct lock_class_key *key;
++      } a[] = {
++              { .name = "&sbinfo->si_rwsem" },
++              { .name = "&finfo->fi_rwsem" },
++              { .name = "&dinfo->di_rwsem" },
++              { .name = "&iinfo->ii_rwsem" }
++      };
++      static int set;
++      int i;
++
++      /* lockless read from 'set.' see below */
++      if (set == ARRAY_SIZE(a)) {
++              for (i = 0; i < ARRAY_SIZE(a); i++)
++                      if (a[i].key == key)
++                              goto match;
++              goto unmatch;
++      }
++
++      spin_lock(&spin);
++      if (set)
++              for (i = 0; i < ARRAY_SIZE(a); i++)
++                      if (a[i].key == key) {
++                              spin_unlock(&spin);
++                              goto match;
++                      }
++      for (i = 0; i < ARRAY_SIZE(a); i++) {
++              if (a[i].key) {
++                      if (unlikely(a[i].key == key)) { /* rare but possible */
++                              spin_unlock(&spin);
++                              goto match;
++                      } else
++                              continue;
++              }
++              if (strstr(a[i].name, name)) {
++                      /*
++                       * the order of these three lines is important for the
++                       * lockless read above.
++                       */
++                      a[i].key = key;
++                      spin_unlock(&spin);
++                      set++;
++                      /* AuDbg("%d, %s\n", set, name); */
++                      goto match;
++              }
++      }
++      spin_unlock(&spin);
++      goto unmatch;
++
++match:
++      return 1;
++unmatch:
++      return 0;
++}
++
++static int au_wkq_lockdep_alloc(struct au_wkinfo *wkinfo)
++{
++      int err, n;
++      struct task_struct *curr;
++      struct held_lock **hl, *held_locks, *p;
++
++      err = 0;
++      curr = current;
++      wkinfo->dont_check = lockdep_recursing(curr);
++      if (wkinfo->dont_check)
++              goto out;
++      n = curr->lockdep_depth;
++      if (!n)
++              goto out;
++
++      err = -ENOMEM;
++      wkinfo->hlock = kmalloc_array(n + 1, sizeof(*wkinfo->hlock), GFP_NOFS);
++      if (unlikely(!wkinfo->hlock))
++              goto out;
++
++      err = 0;
++#if 0
++      if (0 && au_debug_test()) /* left for debugging */
++              lockdep_print_held_locks(curr);
++#endif
++      held_locks = curr->held_locks;
++      hl = wkinfo->hlock;
++      while (n--) {
++              p = held_locks++;
++              if (au_wkq_lockdep_test(p->instance->key, p->instance->name))
++                      *hl++ = p;
++      }
++      *hl = NULL;
++
++out:
++      return err;
++}
++
++static void au_wkq_lockdep_free(struct au_wkinfo *wkinfo)
++{
++      kfree(wkinfo->hlock);
++}
++
++static void au_wkq_lockdep_pre(struct au_wkinfo *wkinfo)
++{
++      struct held_lock *p, **hl = wkinfo->hlock;
++      int subclass;
++
++      if (wkinfo->dont_check)
++              lockdep_off();
++      if (!hl)
++              return;
++      while ((p = *hl++)) { /* assignment */
++              subclass = lockdep_hlock_class(p)->subclass;
++              /* AuDbg("%s, %d\n", p->instance->name, subclass); */
++              if (p->read)
++                      rwsem_acquire_read(p->instance, subclass, 0,
++                                         /*p->acquire_ip*/_RET_IP_);
++              else
++                      rwsem_acquire(p->instance, subclass, 0,
++                                    /*p->acquire_ip*/_RET_IP_);
++      }
++}
++
++static void au_wkq_lockdep_post(struct au_wkinfo *wkinfo)
++{
++      struct held_lock *p, **hl = wkinfo->hlock;
++
++      if (wkinfo->dont_check)
++              lockdep_on();
++      if (!hl)
++              return;
++      while ((p = *hl++)) /* assignment */
++              rwsem_release(p->instance, 0, /*p->acquire_ip*/_RET_IP_);
++}
++#endif
 +
 +static void wkq_func(struct work_struct *wk)
 +{
@@ -33889,7 +35844,9 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 +      AuDebugOn(!uid_eq(current_fsuid(), GLOBAL_ROOT_UID));
 +      AuDebugOn(rlimit(RLIMIT_FSIZE) != RLIM_INFINITY);
 +
++      au_wkq_lockdep_pre(wkinfo);
 +      wkinfo->func(wkinfo->args);
++      au_wkq_lockdep_post(wkinfo);
 +      if (au_ftest_wkq(wkinfo->flags, WAIT))
 +              complete(wkinfo->comp);
 +      else {
@@ -33977,16 +35934,23 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 +      };
 +
 +      err = au_wkq_comp_alloc(&wkinfo, &comp);
++      if (unlikely(err))
++              goto out;
++      err = au_wkq_lockdep_alloc(&wkinfo);
++      if (unlikely(err))
++              goto out_comp;
 +      if (!err) {
 +              au_wkq_run(&wkinfo);
 +              /* no timeout, no interrupt */
 +              wait_for_completion(wkinfo.comp);
-+              au_wkq_comp_free(comp);
-+              destroy_work_on_stack(&wkinfo.wk);
 +      }
++      au_wkq_lockdep_free(&wkinfo);
 +
++out_comp:
++      au_wkq_comp_free(comp);
++out:
++      destroy_work_on_stack(&wkinfo.wk);
 +      return err;
-+
 +}
 +
 +/*
@@ -34013,6 +35977,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 +              wkinfo->func = func;
 +              wkinfo->args = args;
 +              wkinfo->comp = NULL;
++              au_wkq_lockdep_init(wkinfo);
 +              kobject_get(wkinfo->kobj);
 +              __module_get(THIS_MODULE); /* todo: ?? */
 +
@@ -34054,7 +36019,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.c linux/fs/aufs/wkq.c
 +}
 diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h
 --- /usr/share/empty/fs/aufs/wkq.h     1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/wkq.h        2017-07-29 12:14:25.906375514 +0200
++++ linux/fs/aufs/wkq.h        2017-11-12 22:24:44.707577830 +0100
 @@ -0,0 +1,93 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
@@ -34083,7 +36048,7 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h
 +
 +#ifdef __KERNEL__
 +
-+#include <linux/percpu_counter.h>
++#include <linux/wait.h>
 +
 +struct super_block;
 +
@@ -34151,8 +36116,8 @@ diff -urN /usr/share/empty/fs/aufs/wkq.h linux/fs/aufs/wkq.h
 +#endif /* __AUFS_WKQ_H__ */
 diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c
 --- /usr/share/empty/fs/aufs/xattr.c   1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/xattr.c      2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,357 @@
++++ linux/fs/aufs/xattr.c      2017-11-12 22:24:44.707577830 +0100
+@@ -0,0 +1,355 @@
 +/*
 + * Copyright (C) 2014-2017 Junjiro R. Okajima
 + *
@@ -34333,12 +36298,10 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c
 +              AuTraceErr(err);
 +      }
 +
-+      if (value)
-+              kfree(value);
++      kfree(value);
 +
 +out_free:
-+      if (o)
-+              kfree(o);
++      kfree(o);
 +out:
 +      if (!unlocked)
 +              inode_unlock_shared(h_isrc);
@@ -34512,8 +36475,8 @@ diff -urN /usr/share/empty/fs/aufs/xattr.c linux/fs/aufs/xattr.c
 +}
 diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c
 --- /usr/share/empty/fs/aufs/xino.c    1970-01-01 01:00:00.000000000 +0100
-+++ linux/fs/aufs/xino.c       2017-07-29 12:14:25.906375514 +0200
-@@ -0,0 +1,1415 @@
++++ linux/fs/aufs/xino.c       2017-11-12 22:24:44.710911257 +0100
+@@ -0,0 +1,1418 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -34660,8 +36623,11 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c
 +              lockdep_off();
 +              err = do_xino_fwrite(func, file, buf, size, pos);
 +              lockdep_on();
-+      } else
++      } else {
++              lockdep_off();
 +              err = xino_fwrite_wkq(func, file, buf, size, pos);
++              lockdep_on();
++      }
 +
 +      return err;
 +}
@@ -35931,8 +37897,8 @@ diff -urN /usr/share/empty/fs/aufs/xino.c linux/fs/aufs/xino.c
 +}
 diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/linux/aufs_type.h
 --- /usr/share/empty/include/uapi/linux/aufs_type.h    1970-01-01 01:00:00.000000000 +0100
-+++ linux/include/uapi/linux/aufs_type.h       2017-09-05 10:42:11.058755349 +0200
-@@ -0,0 +1,419 @@
++++ linux/include/uapi/linux/aufs_type.h       2017-11-12 22:24:44.710911257 +0100
+@@ -0,0 +1,447 @@
 +/*
 + * Copyright (C) 2005-2017 Junjiro R. Okajima
 + *
@@ -35974,7 +37940,7 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin
 +
 +#include <linux/limits.h>
 +
-+#define AUFS_VERSION  "4.x-rcN-20170904"
++#define AUFS_VERSION  "4.x-rcN-20171106"
 +
 +/* todo? move this to linux-2.6.19/include/magic.h */
 +#define AUFS_SUPER_MAGIC      ('a' << 24 | 'u' << 16 | 'f' << 8 | 's')
@@ -36036,6 +38002,13 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin
 +#define AUFS_PLINK_MAINT_DIR  "fs/" AUFS_NAME
 +#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME
 +
++/* dirren, renamed dir */
++#define AUFS_DR_INFO_PFX      AUFS_WH_PFX ".dr."
++#define AUFS_DR_BRHINO_NAME   AUFS_WH_PFX "hino"
++/* whiteouted doubly */
++#define AUFS_WH_DR_INFO_PFX   AUFS_WH_PFX AUFS_DR_INFO_PFX
++#define AUFS_WH_DR_BRHINO     AUFS_WH_PFX AUFS_DR_BRHINO_NAME
++
 +#define AUFS_DIROPQ_NAME      AUFS_WH_PFX ".opq" /* whiteouted doubly */
 +#define AUFS_WH_DIROPQ                AUFS_WH_PFX AUFS_DIROPQ_NAME
 +
@@ -36254,6 +38227,27 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin
 +
 +/* ---------------------------------------------------------------------- */
 +
++/* dirren. the branch is identified by the filename who contains this */
++struct au_drinfo {
++      uint64_t ino;
++      union {
++              uint8_t oldnamelen;
++              uint64_t _padding;
++      };
++      uint8_t oldname[0];
++} __aligned(8);
++
++struct au_drinfo_fdata {
++      uint32_t magic;
++      struct au_drinfo drinfo;
++} __aligned(8);
++
++#define AUFS_DRINFO_MAGIC_V1  ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x01)
++/* future */
++#define AUFS_DRINFO_MAGIC_V2  ('a' << 24 | 'd' << 16 | 'r' << 8 | 0x02)
++
++/* ---------------------------------------------------------------------- */
++
 +struct aufs_wbr_fd {
 +      uint32_t        oflags;
 +      int16_t         brid;
@@ -36355,49 +38349,13 @@ diff -urN /usr/share/empty/include/uapi/linux/aufs_type.h linux/include/uapi/lin
 aufs4.x-rcN loopback patch
 
 diff --git a/drivers/block/loop.c b/drivers/block/loop.c
-index 10707c3..af32e47 100644
+index d44de9d..095672b 100644
 --- a/drivers/block/loop.c
 +++ b/drivers/block/loop.c
-@@ -547,7 +547,7 @@ static int do_req_filebacked(struct loop_device *lo, struct request *rq)
+@@ -595,6 +595,15 @@ static inline void loop_update_dio(struct loop_device *lo)
+                       lo->use_dio);
  }
  
- struct switch_request {
--      struct file *file;
-+      struct file *file, *virt_file;
-       struct completion wait;
- };
-@@ -573,6 +573,7 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
-       mapping = file->f_mapping;
-       mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
-       lo->lo_backing_file = file;
-+      lo->lo_backing_virt_file = p->virt_file;
-       lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
-               mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
-       lo->old_gfp_mask = mapping_gfp_mask(mapping);
-@@ -585,11 +586,13 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
-  * First it needs to flush existing IO, it does this by sending a magic
-  * BIO down the pipe. The completion of this BIO does the actual switch.
-  */
--static int loop_switch(struct loop_device *lo, struct file *file)
-+static int loop_switch(struct loop_device *lo, struct file *file,
-+                     struct file *virt_file)
- {
-       struct switch_request w;
-       w.file = file;
-+      w.virt_file = virt_file;
-       /* freeze queue and wait for completion of scheduled requests */
-       blk_mq_freeze_queue(lo->lo_queue);
-@@ -611,7 +614,16 @@ static int loop_flush(struct loop_device *lo)
-       /* loop not yet configured, no running thread, nothing to flush */
-       if (lo->lo_state != Lo_bound)
-               return 0;
--      return loop_switch(lo, NULL);
-+      return loop_switch(lo, NULL, NULL);
-+}
-+
 +static struct file *loop_real_file(struct file *file)
 +{
 +      struct file *f = NULL;
@@ -36405,10 +38363,12 @@ index 10707c3..af32e47 100644
 +      if (file->f_path.dentry->d_sb->s_op->real_loop)
 +              f = file->f_path.dentry->d_sb->s_op->real_loop(file);
 +      return f;
- }
++}
++
  static void loop_reread_partitions(struct loop_device *lo,
-@@ -648,6 +660,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
+                                  struct block_device *bdev)
+ {
+@@ -629,6 +638,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
                          unsigned int arg)
  {
        struct file     *file, *old_file;
@@ -36416,7 +38376,7 @@ index 10707c3..af32e47 100644
        struct inode    *inode;
        int             error;
  
-@@ -664,9 +677,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
+@@ -645,9 +655,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
        file = fget(arg);
        if (!file)
                goto out;
@@ -36433,14 +38393,16 @@ index 10707c3..af32e47 100644
  
        error = -EINVAL;
  
-@@ -678,17 +698,21 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
-               goto out_putf;
-       /* and ... switch */
--      error = loop_switch(lo, file);
-+      error = loop_switch(lo, file, virt_file);
-       if (error)
-               goto out_putf;
+@@ -662,6 +679,7 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
+       blk_mq_freeze_queue(lo->lo_queue);
+       mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
+       lo->lo_backing_file = file;
++      lo->lo_backing_virt_file = virt_file;
+       lo->old_gfp_mask = mapping_gfp_mask(file->f_mapping);
+       mapping_set_gfp_mask(file->f_mapping,
+                            lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
+@@ -669,12 +687,16 @@ static int loop_change_fd(struct loop_device *lo, struct block_device *bdev,
+       blk_mq_unfreeze_queue(lo->lo_queue);
  
        fput(old_file);
 +      if (old_virt_file)
@@ -36456,7 +38418,7 @@ index 10707c3..af32e47 100644
   out:
        return error;
  }
-@@ -882,7 +906,7 @@ static int loop_prepare_queue(struct loop_device *lo)
+@@ -868,7 +890,7 @@ static int loop_prepare_queue(struct loop_device *lo)
  static int loop_set_fd(struct loop_device *lo, fmode_t mode,
                       struct block_device *bdev, unsigned int arg)
  {
@@ -36464,8 +38426,8 @@ index 10707c3..af32e47 100644
 +      struct file     *file, *f, *virt_file = NULL;
        struct inode    *inode;
        struct address_space *mapping;
-       unsigned lo_blocksize;
-@@ -897,6 +921,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
+       int             lo_flags = 0;
+@@ -882,6 +904,12 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
        file = fget(arg);
        if (!file)
                goto out;
@@ -36478,7 +38440,7 @@ index 10707c3..af32e47 100644
  
        error = -EBUSY;
        if (lo->lo_state != Lo_unbound)
-@@ -949,6 +979,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
+@@ -930,6 +958,7 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
        lo->lo_device = bdev;
        lo->lo_flags = lo_flags;
        lo->lo_backing_file = file;
@@ -36486,7 +38448,7 @@ index 10707c3..af32e47 100644
        lo->transfer = NULL;
        lo->ioctl = NULL;
        lo->lo_sizelimit = 0;
-@@ -981,6 +1012,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
+@@ -963,6 +992,8 @@ static int loop_set_fd(struct loop_device *lo, fmode_t mode,
  
   out_putf:
        fput(file);
@@ -36495,7 +38457,7 @@ index 10707c3..af32e47 100644
   out:
        /* This is safe: open() is still holding a reference. */
        module_put(THIS_MODULE);
-@@ -1027,6 +1060,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,
+@@ -1009,6 +1040,7 @@ loop_init_xfer(struct loop_device *lo, struct loop_func_table *xfer,
  static int loop_clr_fd(struct loop_device *lo)
  {
        struct file *filp = lo->lo_backing_file;
@@ -36503,7 +38465,7 @@ index 10707c3..af32e47 100644
        gfp_t gfp = lo->old_gfp_mask;
        struct block_device *bdev = lo->lo_device;
  
-@@ -1058,6 +1092,7 @@ static int loop_clr_fd(struct loop_device *lo)
+@@ -1040,6 +1072,7 @@ static int loop_clr_fd(struct loop_device *lo)
        spin_lock_irq(&lo->lo_lock);
        lo->lo_state = Lo_rundown;
        lo->lo_backing_file = NULL;
@@ -36511,7 +38473,7 @@ index 10707c3..af32e47 100644
        spin_unlock_irq(&lo->lo_lock);
  
        loop_release_xfer(lo);
-@@ -1102,6 +1137,8 @@ static int loop_clr_fd(struct loop_device *lo)
+@@ -1087,6 +1120,8 @@ static int loop_clr_fd(struct loop_device *lo)
         * bd_mutex which is usually taken before lo_ctl_mutex.
         */
        fput(filp);
@@ -36521,7 +38483,7 @@ index 10707c3..af32e47 100644
  }
  
 diff --git a/drivers/block/loop.h b/drivers/block/loop.h
-index fecd3f9..6b3a7c9 100644
+index 1f39567..128b137 100644
 --- a/drivers/block/loop.h
 +++ b/drivers/block/loop.h
 @@ -46,7 +46,7 @@ struct loop_device {
@@ -36531,10 +38493,10 @@ index fecd3f9..6b3a7c9 100644
 -      struct file *   lo_backing_file;
 +      struct file *   lo_backing_file, *lo_backing_virt_file;
        struct block_device *lo_device;
-       unsigned        lo_blocksize;
        void            *key_data; 
 diff --git a/fs/aufs/f_op.c b/fs/aufs/f_op.c
-index 870717e..ea7fee0 100644
+index 8aff060..e853272 100644
 --- a/fs/aufs/f_op.c
 +++ b/fs/aufs/f_op.c
 @@ -357,7 +357,7 @@ static ssize_t aufs_read_iter(struct kiocb *kio, struct iov_iter *iov_iter)
@@ -36596,10 +38558,10 @@ index e2df495..36e5052 100644
  
  #endif /* __KERNEL__ */
 diff --git a/fs/aufs/super.c b/fs/aufs/super.c
-index 5455fb1..8b9df60 100644
+index 3c300125..128d790 100644
 --- a/fs/aufs/super.c
 +++ b/fs/aufs/super.c
-@@ -837,7 +837,10 @@ static const struct super_operations aufs_sop = {
+@@ -838,7 +838,10 @@ static const struct super_operations aufs_sop = {
        .statfs         = aufs_statfs,
        .put_super      = aufs_put_super,
        .sync_fs        = aufs_sync_fs,
@@ -36612,10 +38574,10 @@ index 5455fb1..8b9df60 100644
  
  /* ---------------------------------------------------------------------- */
 diff --git a/include/linux/fs.h b/include/linux/fs.h
-index 9b21bb5..f7124fa 100644
+index 8ab6566..8dbaa52 100644
 --- a/include/linux/fs.h
 +++ b/include/linux/fs.h
-@@ -1814,6 +1814,10 @@ struct super_operations {
+@@ -1838,6 +1838,10 @@ struct super_operations {
                                  struct shrink_control *);
        long (*free_cached_objects)(struct super_block *,
                                    struct shrink_control *);
index 41ffc068a43b75af49bc76598627795b265edf65..93ecbd274c124bc3215ee0b535496747ee524686 100644 (file)
@@ -69,9 +69,9 @@
 %define                have_pcmcia     0
 %endif
 
-%define                rel             1
-%define                basever         4.13
-%define                postver         .12
+%define                rel             0.1
+%define                basever         4.14
+%define                postver         .0
 
 # define this to '-%{basever}' for longterm branch
 %define                versuffix       %{nil}
@@ -120,7 +120,7 @@ Epoch:              3
 License:       GPL v2
 Group:         Base/Kernel
 Source0:       https://www.kernel.org/pub/linux/kernel/v4.x/linux-%{basever}.tar.xz
-# Source0-md5: ab1a2abc6f37b752dd2595338bec4e78
+# Source0-md5: bacdb9ffdcd922aa069a5e1520160e24
 %if "%{postver}" != ".0"
 Patch0:                https://www.kernel.org/pub/linux/kernel/v4.x/patch-%{version}.xz
 # Patch0-md5:  665b55e1f24ec56de55f7d0302d7c4f5
@@ -197,10 +197,9 @@ Patch101:  kernel-vserver-fixes.patch
 # Patch creation:
 # git clone git://github.com/sfjro/aufs4-standalone.git
 # cd aufs4-standalone
-# git checkout -b aufs4.12 origin/aufs4.12
+# git checkout -b aufs4.14 origin/aufs4.14
 # cat aufs4-kbuild.patch aufs4-base.patch aufs4-mmap.patch aufs4-standalone.patch > ~/rpm/packages/kernel/kernel-aufs4.patch
-# rm -rf linux && mkdir linux
-# cp -a Documentation fs include linux
+# rm -rf linux && mkdir linux; cp -a Documentation fs include linux
 # diff -urN /usr/share/empty linux | filterdiff -x linux/include/uapi/linux/Kbuild >> ~/rpm/packages/kernel/kernel-aufs4.patch
 # cat aufs4-loopback.patch >> ~/rpm/packages/kernel/kernel-aufs4.patch
 #
@@ -218,23 +217,9 @@ Patch2000: kernel-small_fixes.patch
 Patch2001:     kernel-pwc-uncompress.patch
 Patch2003:     kernel-regressions.patch
 
-# http://bazaar.launchpad.net/~apparmor-dev/apparmor/master/files/head:/kernel-patches/v4.13/
-Patch5001:     0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch
-Patch5002:     0003-apparmor-Fix-logical-error-in-verify_header.patch
-Patch5003:     0004-apparmor-Fix-an-error-code-in-aafs_create.patch
-Patch5004:     0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch
-Patch5005:     0006-apparmor-add-the-ability-to-mediate-signals.patch
-Patch5006:     0007-apparmor-add-mount-mediation.patch
-Patch5007:     0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch
-Patch5008:     0009-apparmor-add-support-for-absolute-root-view-based-la.patch
-Patch5009:     0010-apparmor-make-policy_unpack-able-to-audit-different-.patch
-Patch5010:     0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch
-Patch5011:     0012-apparmor-add-base-infastructure-for-socket-mediation.patch
-Patch5012:     0013-apparmor-move-new_null_profile-to-after-profile-look.patch
-Patch5013:     0014-apparmor-fix-race-condition-in-null-profile-creation.patch
-Patch5014:     0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch
-Patch5015:     0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch
-Patch5016:     0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch
+# https://gitlab.com/apparmor/apparmor/tree/master/kernel-patches/v4.14
+Patch5001:     0012-apparmor-add-base-infastructure-for-socket-mediation.patch
+Patch5002:     0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch
 
 # for rescuecd
 # based on ftp://ftp.leg.uct.ac.za/pub/linux/rip/tmpfs_root-2.6.30.diff.gz
@@ -706,20 +691,6 @@ rm -f localversion-rt
 %if %{with apparmor}
 %patch5001 -p1
 %patch5002 -p1
-%patch5003 -p1
-%patch5004 -p1
-%patch5005 -p1
-%patch5006 -p1
-%patch5007 -p1
-%patch5008 -p1
-%patch5009 -p1
-%patch5010 -p1
-%patch5011 -p1
-%patch5012 -p1
-%patch5013 -p1
-%patch5014 -p1
-%patch5015 -p1
-%patch5016 -p1
 %endif
 
 %patch250 -p1
This page took 0.414023 seconds and 4 git commands to generate.