From: Arkadiusz Miśkiewicz Date: Sun, 12 Nov 2017 22:03:17 +0000 (+0100) Subject: - initial 4.14.0 X-Git-Tag: auto/th/kernel-4.14.0-1~4 X-Git-Url: http://git.pld-linux.org/gitweb.cgi?a=commitdiff_plain;h=8b6a494719f4150ce54b9e4d30810ece069c1c73;p=packages%2Fkernel.git - initial 4.14.0 --- diff --git a/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch b/0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch similarity index 100% rename from 0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch rename to 0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch 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 index 4d9836d9..00000000 --- a/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch +++ /dev/null @@ -1,51 +0,0 @@ -From c6cad5e65a23dcafa1821ca381901297664d9c64 Mon Sep 17 00:00:00 2001 -From: Geert Uytterhoeven -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 -Reviewed-by: Serge Hallyn -Signed-off-by: John Johansen -(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 index be4f854c..00000000 --- a/0003-apparmor-Fix-logical-error-in-verify_header.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 9934296cba701d429a0fc0cf071a40c8c3a1587e Mon Sep 17 00:00:00 2001 -From: Christos Gkekas -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 -Signed-off-by: John Johansen -(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 index 4e937e00..00000000 --- a/0004-apparmor-Fix-an-error-code-in-aafs_create.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 8b3851c7b83f32f2be9d4b48371ddf033afedf62 Mon Sep 17 00:00:00 2001 -From: Dan Carpenter -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 -Acked-by: Serge Hallyn -Signed-off-by: John Johansen -(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 index beea3de9..00000000 --- a/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 4b56e146905bbad2c79ea92e3f49e210ca527572 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Signed-off-by: John Johansen -(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 index ee3672db..00000000 --- a/0006-apparmor-add-the-ability-to-mediate-signals.patch +++ /dev/null @@ -1,397 +0,0 @@ -From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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 -+ -+#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 */ -+ 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 index 99dac5c1..00000000 --- a/0007-apparmor-add-mount-mediation.patch +++ /dev/null @@ -1,1051 +0,0 @@ -From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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 - #include - -+#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 -+#include -+ -+#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 -+#include -+#include -+ -+#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 index 20892df4..00000000 --- a/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch +++ /dev/null @@ -1,71 +0,0 @@ -From 763d17c9a18b0df7dbec2740f10dc40d378e3cc1 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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 index 84ee7a1d..00000000 --- a/0009-apparmor-add-support-for-absolute-root-view-based-la.patch +++ /dev/null @@ -1,63 +0,0 @@ -From 6b092bbbf9e17b10f709d11b3bc2d7e493617934 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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 index 7e481b28..00000000 --- a/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch +++ /dev/null @@ -1,219 +0,0 @@ -From aa4b6bded85552bc5f9f22d2e18ce86c5c17947c Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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, ¶ms)) -+ if (rhashtable_init(profile->data, ¶ms)) { -+ 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 index 85eafc85..00000000 --- a/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch +++ /dev/null @@ -1,78 +0,0 @@ -From ba3f778a2ef31454032c2ca9c99d9212feb4dcf1 Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -Acked-by: Seth Arnold -(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 index f1337f3c..00000000 --- a/0013-apparmor-move-new_null_profile-to-after-profile-look.patch +++ /dev/null @@ -1,194 +0,0 @@ -From 50d30adbef98a0b6cc531a9413d05f564eb633ee Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -(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 index ae57b4bd..00000000 --- a/0014-apparmor-fix-race-condition-in-null-profile-creation.patch +++ /dev/null @@ -1,60 +0,0 @@ -From ab3b869791b6122c7be7e68ca4c08e2c2e8815ac Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -(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 index 1ceb9900..00000000 --- a/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 7f2cdd6453518ff76c3855255c91306a2b928c9a Mon Sep 17 00:00:00 2001 -From: John Johansen -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 -(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 index 9ff09f80..00000000 --- a/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 8daf877473653c06a28c86bf72d63ce7e5c1d542 Mon Sep 17 00:00:00 2001 -From: John Johansen -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] *label -../security/apparmor/label.c:52:30: got struct aa_label * - -fix with RCU_INIT_POINTER as this is one of those cases where -rcu_assign_pointer() is not needed. - -Signed-off-by: John Johansen -(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 - diff --git a/kernel-aufs4.patch b/kernel-aufs4.patch index e0eb9ca1..d11a0aa1 100644 --- a/kernel-aufs4.patch +++ b/kernel-aufs4.patch @@ -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 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 . ++ ++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. ++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 ++#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 +#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 ++#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 . ++ */ ++ ++/* ++ * special handling in renaming a directoy ++ * in order to support looking-up the before-renamed name on the lower readonly ++ * branches ++ */ ++ ++#include ++#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 . ++ */ ++ ++/* ++ * renamed dir info ++ */ ++ ++#ifndef __AUFS_DIRREN_H__ ++#define __AUFS_DIRREN_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++#include ++#include ++#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 . ++ */ ++ ++/* ++ * helpers for hlist_bl.h ++ */ ++ ++#ifndef __AUFS_HBL_H__ ++#define __AUFS_HBL_H__ ++ ++#ifdef __KERNEL__ ++ ++#include ++ ++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 +#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 . -+ */ -+ -+/* -+ * 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 +#include ++#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 +#include +#include +#include +#include -+#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 ++#include + +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 + -+#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 *); diff --git a/kernel.spec b/kernel.spec index 41ffc068..93ecbd27 100644 --- a/kernel.spec +++ b/kernel.spec @@ -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