diff mbox series

[B] aufs: bugfix, stop omitting path->mnt

Message ID 20211022154715.563645-3-mfo@canonical.com
State New
Headers show
Series [B] aufs: bugfix, stop omitting path->mnt | expand

Commit Message

Mauricio Faria de Oliveira Oct. 22, 2021, 3:47 p.m. UTC
From: "J. R. Okajima" <hooanon05g@gmail.com>

BugLink: https://bugs.launchpad.net/bugs/1948470

For some cases, aufs calls vfs_getattr() with setting NULL to
path->mnt (via vfsub_update_h_iattr() and vfsub_getattr()).
This commit always sets the correct value to it.

Reported-by: Mauricio Faria de Oliveira <mfo@canonical.com>
Originally-patched-by: Mauricio Faria de Oliveira <mfo@canonical.com>
See-also: https://www.mail-archive.com/aufs-users@lists.sourceforge.net/msg05862.html and its thread
Signed-off-by: J. R. Okajima <hooanon05g@gmail.com>
(backported from commit 71dfddc730395d2bae4e1ae3c9259635a719cf69 aufs5-linux.git)
[backport: trivial changes on function/variable names:
 - dentry.c/hunk 4, dirren.c/hunk 4, plink.c/hunk 1:
   s/inode_lock_shared_nested/vfsub_inode_lock_shared_nested/
 - whout.c/hunk 8:
   s/au_kfree_try_rcu/kfree/
 - xino.c/hunk 1:
   s/base/base_file.f_path/ and refresh context lines]
Signed-off-by: Mauricio Faria de Oliveira <mfo@canonical.com>
---
 fs/aufs/cpup.c     |  6 ++--
 fs/aufs/dentry.c   | 35 ++++++++++++--------
 fs/aufs/dentry.h   |  2 +-
 fs/aufs/dirren.c   |  9 +++--
 fs/aufs/export.c   |  2 +-
 fs/aufs/i_op_del.c | 11 +++++--
 fs/aufs/i_op_ren.c | 14 +++++---
 fs/aufs/plink.c    | 62 +++++++++++++++++------------------
 fs/aufs/vfsub.c    | 33 +++++++++++--------
 fs/aufs/vfsub.h    | 10 +++---
 fs/aufs/whout.c    | 82 ++++++++++++++++++++++++----------------------
 fs/aufs/whout.h    |  4 +--
 fs/aufs/xino.c     | 11 ++++---
 13 files changed, 156 insertions(+), 125 deletions(-)
diff mbox series

Patch

diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
index e096069a4e5a..fffa437d83b1 100644
--- a/fs/aufs/cpup.c
+++ b/fs/aufs/cpup.c
@@ -737,11 +737,13 @@  static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path)
 {
 	int err;
 	struct dentry *dentry, *h_dentry, *h_parent, *parent;
+	struct path h_ppath;
 	struct inode *h_dir;
 	aufs_bindex_t bdst;
 
 	dentry = cpg->dentry;
 	bdst = cpg->bdst;
+	h_ppath.mnt = au_sbr_mnt(dentry->d_sb, bdst);
 	h_dentry = au_h_dptr(dentry, bdst);
 	if (!au_ftest_cpup(cpg->flags, OVERWRITE)) {
 		dget(h_dentry);
@@ -753,9 +755,9 @@  static int au_do_ren_after_cpup(struct au_cp_generic *cpg, struct path *h_path)
 	} else {
 		err = 0;
 		parent = dget_parent(dentry);
-		h_parent = au_h_dptr(parent, bdst);
+		h_ppath.dentry = au_h_dptr(parent, bdst);
 		dput(parent);
-		h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
+		h_path->dentry = vfsub_lkup_one(&dentry->d_name, &h_ppath);
 		if (IS_ERR(h_path->dentry))
 			err = PTR_ERR(h_path->dentry);
 	}
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
index 230db04b1aee..56b5434e7fab 100644
--- a/fs/aufs/dentry.c
+++ b/fs/aufs/dentry.c
@@ -33,6 +33,7 @@  au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	struct dentry *h_dentry;
 	struct inode *h_inode;
 	struct au_branch *br;
+	struct path h_path;
 	int wh_found, opq;
 	unsigned char wh_able;
 	const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
@@ -41,9 +42,11 @@  au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 
 	wh_found = 0;
 	br = au_sbr(dentry->d_sb, bindex);
+	h_path.dentry = h_parent;
+	h_path.mnt = au_br_mnt(br);
 	wh_able = !!au_br_whable(br->br_perm);
 	if (wh_able)
-		wh_found = au_wh_test(h_parent, &args->whname, ignore_perm);
+		wh_found = au_wh_test(&h_path, &args->whname, ignore_perm);
 	h_dentry = ERR_PTR(wh_found);
 	if (!wh_found)
 		goto real_lookup;
@@ -58,9 +61,9 @@  au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 
 real_lookup:
 	if (!ignore_perm)
-		h_dentry = vfsub_lkup_one(args->name, h_parent);
+		h_dentry = vfsub_lkup_one(args->name, &h_path);
 	else
-		h_dentry = au_sio_lkup_one(args->name, h_parent);
+		h_dentry = au_sio_lkup_one(args->name, &h_path);
 	if (IS_ERR(h_dentry)) {
 		if (PTR_ERR(h_dentry) == -ENAMETOOLONG
 		    && !allow_neg)
@@ -94,8 +97,9 @@  au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
 	    || (d_really_is_positive(dentry) && !d_is_dir(dentry)))
 		goto out; /* success */
 
+	h_path.dentry = h_dentry;
 	vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
-	opq = au_diropq_test(h_dentry);
+	opq = au_diropq_test(&h_path);
 	inode_unlock_shared(h_inode);
 	if (opq > 0)
 		au_set_dbdiropq(dentry, bindex);
@@ -240,18 +244,18 @@  int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t btop,
 	return err;
 }
 
-struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent)
+struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath)
 {
 	struct dentry *dentry;
 	int wkq_err;
 
-	if (!au_test_h_perm_sio(d_inode(parent), MAY_EXEC))
-		dentry = vfsub_lkup_one(name, parent);
+	if (!au_test_h_perm_sio(d_inode(ppath->dentry), MAY_EXEC))
+		dentry = vfsub_lkup_one(name, ppath);
 	else {
 		struct vfsub_lkup_one_args args = {
 			.errp	= &dentry,
 			.name	= name,
-			.parent	= parent
+			.ppath	= ppath
 		};
 
 		wkq_err = au_wkq_wait(vfsub_call_lkup_one, &args);
@@ -268,16 +272,18 @@  struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent)
 int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh)
 {
 	int err;
-	struct dentry *parent, *h_parent, *h_dentry;
+	struct dentry *parent, *h_dentry;
 	struct au_branch *br;
+	struct path h_ppath;
 
 	parent = dget_parent(dentry);
-	h_parent = au_h_dptr(parent, bindex);
 	br = au_sbr(dentry->d_sb, bindex);
+	h_ppath.dentry = au_h_dptr(parent, bindex);
+	h_ppath.mnt = au_br_mnt(br);
 	if (wh)
-		h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
+		h_dentry = au_whtmp_lkup(h_ppath.dentry, br, &dentry->d_name);
 	else
-		h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent);
+		h_dentry = au_sio_lkup_one(&dentry->d_name, &h_ppath);
 	err = PTR_ERR(h_dentry);
 	if (IS_ERR(h_dentry))
 		goto out;
@@ -352,6 +358,7 @@  static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,
 	struct inode *h_inode;
 	struct dentry *h_d;
 	struct super_block *h_sb;
+	struct path h_ppath;
 
 	err = 0;
 	memset(&ia, -1, sizeof(ia));
@@ -366,7 +373,9 @@  static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,
 		goto out;
 
 	/* main purpose is namei.c:cached_lookup() and d_revalidate */
-	h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent);
+	h_ppath.dentry = h_parent;
+	h_ppath.mnt = au_br_mnt(br);
+	h_d = vfsub_lkup_one(&h_dentry->d_name, &h_ppath);
 	err = PTR_ERR(h_d);
 	if (IS_ERR(h_d))
 		goto out;
diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
index ea45862d6709..1542e31baa2f 100644
--- a/fs/aufs/dentry.h
+++ b/fs/aufs/dentry.h
@@ -71,7 +71,7 @@  struct au_do_lookup_args {
 /* dentry.c */
 extern const struct dentry_operations aufs_dop, aufs_dop_noreval;
 struct au_branch;
-struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent);
+struct dentry *au_sio_lkup_one(struct qstr *name, struct path *ppath);
 int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
 		struct dentry *h_parent, struct au_branch *br);
 
diff --git a/fs/aufs/dirren.c b/fs/aufs/dirren.c
index 3c53b9d50d55..3ce579e97100 100644
--- a/fs/aufs/dirren.c
+++ b/fs/aufs/dirren.c
@@ -262,7 +262,8 @@  static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex,
 		dir = d_inode(path->dentry);
 		inode_lock_nested(dir, AuLsc_I_CHILD);
 	}
-	hinopath.dentry = vfsub_lkup_one(&hinoname, path->dentry);
+	hinopath.mnt = path->mnt;
+	hinopath.dentry = vfsub_lkup_one(&hinoname, (struct path *)path);
 	err = PTR_ERR(hinopath.dentry);
 	if (IS_ERR(hinopath.dentry))
 		goto out_unlock;
@@ -296,7 +297,6 @@  static int au_dr_hino(struct super_block *sb, aufs_bindex_t bindex,
 		}
 		flags = O_WRONLY;
 	}
-	hinopath.mnt = path->mnt;
 	hinofile = vfsub_dentry_open(&hinopath, flags);
 	if (suspend)
 		au_hn_inode_unlock(hdir);
@@ -618,7 +618,7 @@  static int au_drinfo_do_store(struct au_drinfo_store *w,
 	AuDebugOn(elm
 		  && memcmp(elm, page_address(ZERO_PAGE(0)), sizeof(*elm)));
 
-	infopath.dentry = vfsub_lookup_one_len(w->whname, w->h_ppath.dentry,
+	infopath.dentry = vfsub_lookup_one_len(w->whname, &w->h_ppath,
 					       w->whnamelen);
 	AuTraceErrPtr(infopath.dentry);
 	if (IS_ERR(infopath.dentry)) {
@@ -1002,8 +1002,7 @@  static struct au_drinfo *au_drinfo_do_load(struct path *h_ppath,
 	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);
+	infopath.dentry = vfsub_lookup_one_len(whname, h_ppath, whnamelen);
 	if (IS_ERR(infopath.dentry)) {
 		drinfo = (void *)infopath.dentry;
 		goto out;
diff --git a/fs/aufs/export.c b/fs/aufs/export.c
index 34b391c0dc47..fd8f89d666b9 100644
--- a/fs/aufs/export.c
+++ b/fs/aufs/export.c
@@ -405,7 +405,7 @@  static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino,
 
 	/* do not call vfsub_lkup_one() */
 	dir = d_inode(parent);
-	dentry = vfsub_lookup_one_len_unlocked(arg.name, parent, arg.namelen);
+	dentry = vfsub_lookup_one_len_unlocked(arg.name, path, arg.namelen);
 	AuTraceErrPtr(dentry);
 	if (IS_ERR(dentry))
 		goto out_name;
diff --git a/fs/aufs/i_op_del.c b/fs/aufs/i_op_del.c
index f67b74b2eb3a..796b6e4f9543 100644
--- a/fs/aufs/i_op_del.c
+++ b/fs/aufs/i_op_del.c
@@ -92,6 +92,9 @@  int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
 	umode_t h_mode;
 	struct dentry *h_dentry, *h_latest;
 	struct inode *h_inode;
+	struct path h_ppath;
+	struct super_block *sb;
+	struct au_branch *br;
 
 	h_dentry = au_h_dptr(dentry, bindex);
 	if (d_really_is_positive(dentry)) {
@@ -129,12 +132,16 @@  int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
 	 * let's try heavy test.
 	 */
 	err = -EACCES;
-	if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1)
+	sb = dentry->d_sb;
+	br = au_sbr(sb, bindex);
+	if (unlikely(!au_opt_test(au_mntflags(sb), DIRPERM1)
 		     && au_test_h_perm(d_inode(h_parent),
 				       MAY_EXEC | MAY_WRITE)))
 		goto out;
 
-	h_latest = au_sio_lkup_one(&dentry->d_name, h_parent);
+	h_ppath.dentry = h_parent;
+	h_ppath.mnt = au_br_mnt(br);
+	h_latest = au_sio_lkup_one(&dentry->d_name, &h_ppath);
 	err = -EIO;
 	if (IS_ERR(h_latest))
 		goto out;
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
index 22124b1fa9e6..ab08e1d0d151 100644
--- a/fs/aufs/i_op_ren.c
+++ b/fs/aufs/i_op_ren.c
@@ -148,9 +148,12 @@  static void au_ren_rev_rename(int err, struct au_ren_args *a)
 {
 	int rerr;
 	struct inode *delegated;
+	struct path h_ppath = {
+		.dentry	= a->src_h_parent,
+		.mnt	= a->h_path.mnt
+	};
 
-	a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name,
-					  a->src_h_parent);
+	a->h_path.dentry = vfsub_lkup_one(&a->src_dentry->d_name, &h_ppath);
 	rerr = PTR_ERR(a->h_path.dentry);
 	if (IS_ERR(a->h_path.dentry)) {
 		RevertFailure("lkup one %pd", a->src_dentry);
@@ -177,9 +180,12 @@  static void au_ren_rev_whtmp(int err, struct au_ren_args *a)
 {
 	int rerr;
 	struct inode *delegated;
+	struct path h_ppath = {
+		.dentry	= a->dst_h_parent,
+		.mnt	= a->h_path.mnt
+	};
 
-	a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name,
-					  a->dst_h_parent);
+	a->h_path.dentry = vfsub_lkup_one(&a->dst_dentry->d_name, &h_ppath);
 	rerr = PTR_ERR(a->h_path.dentry);
 	if (IS_ERR(a->h_path.dentry)) {
 		RevertFailure("lkup one %pd", a->dst_dentry);
diff --git a/fs/aufs/plink.c b/fs/aufs/plink.c
index f16a2d9cb12e..db41c6989e2e 100644
--- a/fs/aufs/plink.c
+++ b/fs/aufs/plink.c
@@ -205,35 +205,35 @@  static int plink_name(char *name, int len, struct inode *inode,
 struct au_do_plink_lkup_args {
 	struct dentry **errp;
 	struct qstr *tgtname;
-	struct dentry *h_parent;
-	struct au_branch *br;
+	struct path *h_ppath;
 };
 
 static struct dentry *au_do_plink_lkup(struct qstr *tgtname,
-				       struct dentry *h_parent,
-				       struct au_branch *br)
+				       struct path *h_ppath)
 {
 	struct dentry *h_dentry;
 	struct inode *h_inode;
 
-	h_inode = d_inode(h_parent);
+	h_inode = d_inode(h_ppath->dentry);
 	vfsub_inode_lock_shared_nested(h_inode, AuLsc_I_CHILD2);
-	h_dentry = vfsub_lkup_one(tgtname, h_parent);
+	h_dentry = vfsub_lkup_one(tgtname, h_ppath);
 	inode_unlock_shared(h_inode);
+
 	return h_dentry;
 }
 
 static void au_call_do_plink_lkup(void *args)
 {
 	struct au_do_plink_lkup_args *a = args;
-	*a->errp = au_do_plink_lkup(a->tgtname, a->h_parent, a->br);
+	*a->errp = au_do_plink_lkup(a->tgtname, a->h_ppath);
 }
 
 /* lookup the plink-ed @inode under the branch at @bindex */
 struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
 {
-	struct dentry *h_dentry, *h_parent;
+	struct dentry *h_dentry;
 	struct au_branch *br;
+	struct path h_ppath;
 	int wkq_err;
 	char a[PLINK_NAME_LEN];
 	struct qstr tgtname = QSTR_INIT(a, 0);
@@ -241,40 +241,39 @@  struct dentry *au_plink_lkup(struct inode *inode, aufs_bindex_t bindex)
 	AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM));
 
 	br = au_sbr(inode->i_sb, bindex);
-	h_parent = br->br_wbr->wbr_plink;
+	h_ppath.dentry = br->br_wbr->wbr_plink;
+	h_ppath.mnt = au_br_mnt(br);
 	tgtname.len = plink_name(a, sizeof(a), inode, bindex);
 
 	if (!uid_eq(current_fsuid(), GLOBAL_ROOT_UID)) {
 		struct au_do_plink_lkup_args args = {
 			.errp		= &h_dentry,
 			.tgtname	= &tgtname,
-			.h_parent	= h_parent,
-			.br		= br
+			.h_ppath	= &h_ppath
 		};
 
 		wkq_err = au_wkq_wait(au_call_do_plink_lkup, &args);
 		if (unlikely(wkq_err))
 			h_dentry = ERR_PTR(wkq_err);
 	} else
-		h_dentry = au_do_plink_lkup(&tgtname, h_parent, br);
+		h_dentry = au_do_plink_lkup(&tgtname, &h_ppath);
 
 	return h_dentry;
 }
 
 /* create a pseudo-link */
-static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
-		      struct dentry *h_dentry, struct au_branch *br)
+static int do_whplink(struct qstr *tgt, struct path *h_ppath,
+		      struct dentry *h_dentry)
 {
 	int err;
-	struct path h_path = {
-		.mnt = au_br_mnt(br)
-	};
+	struct path h_path;
 	struct inode *h_dir, *delegated;
 
-	h_dir = d_inode(h_parent);
+	h_dir = d_inode(h_ppath->dentry);
 	inode_lock_nested(h_dir, AuLsc_I_CHILD2);
+	h_path.mnt = h_ppath->mnt;
 again:
-	h_path.dentry = vfsub_lkup_one(tgt, h_parent);
+	h_path.dentry = vfsub_lkup_one(tgt, h_ppath);
 	err = PTR_ERR(h_path.dentry);
 	if (IS_ERR(h_path.dentry))
 		goto out;
@@ -315,28 +314,30 @@  static int do_whplink(struct qstr *tgt, struct dentry *h_parent,
 struct do_whplink_args {
 	int *errp;
 	struct qstr *tgt;
-	struct dentry *h_parent;
+	struct path *h_ppath;
 	struct dentry *h_dentry;
-	struct au_branch *br;
 };
 
 static void call_do_whplink(void *args)
 {
 	struct do_whplink_args *a = args;
-	*a->errp = do_whplink(a->tgt, a->h_parent, a->h_dentry, a->br);
+	*a->errp = do_whplink(a->tgt, a->h_ppath, a->h_dentry);
 }
 
 static int whplink(struct dentry *h_dentry, struct inode *inode,
-		   aufs_bindex_t bindex, struct au_branch *br)
+		   aufs_bindex_t bindex)
 {
 	int err, wkq_err;
+	struct au_branch *br;
 	struct au_wbr *wbr;
-	struct dentry *h_parent;
+	struct path h_ppath;
 	char a[PLINK_NAME_LEN];
 	struct qstr tgtname = QSTR_INIT(a, 0);
 
-	wbr = au_sbr(inode->i_sb, bindex)->br_wbr;
-	h_parent = wbr->wbr_plink;
+	br = au_sbr(inode->i_sb, bindex);
+	wbr = br->br_wbr;
+	h_ppath.dentry = wbr->wbr_plink;
+	h_ppath.mnt = au_br_mnt(br);
 	tgtname.len = plink_name(a, sizeof(a), inode, bindex);
 
 	/* always superio. */
@@ -344,15 +345,14 @@  static int whplink(struct dentry *h_dentry, struct inode *inode,
 		struct do_whplink_args args = {
 			.errp		= &err,
 			.tgt		= &tgtname,
-			.h_parent	= h_parent,
-			.h_dentry	= h_dentry,
-			.br		= br
+			.h_ppath	= &h_ppath,
+			.h_dentry	= h_dentry
 		};
 		wkq_err = au_wkq_wait(call_do_whplink, &args);
 		if (unlikely(wkq_err))
 			err = wkq_err;
 	} else
-		err = do_whplink(&tgtname, h_parent, h_dentry, br);
+		err = do_whplink(&tgtname, &h_ppath, h_dentry);
 
 	return err;
 }
@@ -402,7 +402,7 @@  void au_plink_append(struct inode *inode, aufs_bindex_t bindex,
 		if (cnt > AUFS_PLINK_WARN)
 			AuWarn1(msg ", %d\n", cnt);
 #undef msg
-		err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex));
+		err = whplink(h_dentry, inode, bindex);
 		if (unlikely(err)) {
 			pr_warn("err %d, damaged pseudo link.\n", err);
 			au_hbl_del(&icntnr->plink, hbl);
diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
index 8b2304db032a..7fd2327dee82 100644
--- a/fs/aufs/vfsub.c
+++ b/fs/aufs/vfsub.c
@@ -57,6 +57,11 @@  int vfsub_update_h_iattr(struct path *h_path, int *did)
 	struct kstat st;
 	struct super_block *h_sb;
 
+	/*
+	 * Always needs h_path->mnt for LSM or FUSE branch.
+	 */
+	AuDebugOn(!h_path->mnt);
+
 	/* for remote fs, leave work for its getattr or d_revalidate */
 	/* for bad i_attr fs, handle them in aufs_getattr() */
 	/* still some fs may acquire i_mutex. we need to skip them */
@@ -158,38 +163,38 @@  int vfsub_kern_path(const char *name, unsigned int flags, struct path *path)
 }
 
 struct dentry *vfsub_lookup_one_len_unlocked(const char *name,
-					     struct dentry *parent, int len)
+					     struct path *ppath, int len)
 {
-	struct path path = {
-		.mnt = NULL
-	};
+	struct path path;
 
-	path.dentry = lookup_one_len_unlocked(name, parent, len);
+	path.dentry = lookup_one_len_unlocked(name, ppath->dentry, len);
 	if (IS_ERR(path.dentry))
 		goto out;
-	if (d_is_positive(path.dentry))
+	if (d_is_positive(path.dentry)) {
+		path.mnt = ppath->mnt;
 		vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/
+	}
 
 out:
 	AuTraceErrPtr(path.dentry);
 	return path.dentry;
 }
 
-struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
+struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath,
 				    int len)
 {
-	struct path path = {
-		.mnt = NULL
-	};
+	struct path path;
 
 	/* VFS checks it too, but by WARN_ON_ONCE() */
-	IMustLock(d_inode(parent));
+	IMustLock(d_inode(ppath->dentry));
 
-	path.dentry = lookup_one_len(name, parent, len);
+	path.dentry = lookup_one_len(name, ppath->dentry, len);
 	if (IS_ERR(path.dentry))
 		goto out;
-	if (d_is_positive(path.dentry))
+	if (d_is_positive(path.dentry)) {
+		path.mnt = ppath->mnt;
 		vfsub_update_h_iattr(&path, /*did*/NULL); /*ignore*/
+	}
 
 out:
 	AuTraceErrPtr(path.dentry);
@@ -199,7 +204,7 @@  struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
 void vfsub_call_lkup_one(void *args)
 {
 	struct vfsub_lkup_one_args *a = args;
-	*a->errp = vfsub_lkup_one(a->name, a->parent);
+	*a->errp = vfsub_lkup_one(a->name, a->ppath);
 }
 
 /* ---------------------------------------------------------------------- */
diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
index a64b8f02754a..dcd90fddd678 100644
--- a/fs/aufs/vfsub.h
+++ b/fs/aufs/vfsub.h
@@ -109,20 +109,20 @@  int vfsub_atomic_open(struct inode *dir, struct dentry *dentry,
 int vfsub_kern_path(const char *name, unsigned int flags, struct path *path);
 
 struct dentry *vfsub_lookup_one_len_unlocked(const char *name,
-					     struct dentry *parent, int len);
-struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
+					     struct path *ppath, int len);
+struct dentry *vfsub_lookup_one_len(const char *name, struct path *ppath,
 				    int len);
 
 struct vfsub_lkup_one_args {
 	struct dentry **errp;
 	struct qstr *name;
-	struct dentry *parent;
+	struct path *ppath;
 };
 
 static inline struct dentry *vfsub_lkup_one(struct qstr *name,
-					    struct dentry *parent)
+					    struct path *ppath)
 {
-	return vfsub_lookup_one_len(name->name, parent, name->len);
+	return vfsub_lookup_one_len(name->name, ppath, name->len);
 }
 
 void vfsub_call_lkup_one(void *args);
diff --git a/fs/aufs/whout.c b/fs/aufs/whout.c
index 05c069ebb7c4..608f86e55b6b 100644
--- a/fs/aufs/whout.c
+++ b/fs/aufs/whout.c
@@ -60,18 +60,18 @@  int au_wh_name_alloc(struct qstr *wh, const struct qstr *name)
 /* ---------------------------------------------------------------------- */
 
 /*
- * test if the @wh_name exists under @h_parent.
+ * test if the @wh_name exists under @h_ppath.
  * @try_sio specifies the necessary of super-io.
  */
-int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio)
+int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio)
 {
 	int err;
 	struct dentry *wh_dentry;
 
 	if (!try_sio)
-		wh_dentry = vfsub_lkup_one(wh_name, h_parent);
+		wh_dentry = vfsub_lkup_one(wh_name, h_ppath);
 	else
-		wh_dentry = au_sio_lkup_one(wh_name, h_parent);
+		wh_dentry = au_sio_lkup_one(wh_name, h_ppath);
 	err = PTR_ERR(wh_dentry);
 	if (IS_ERR(wh_dentry)) {
 		if (err == -ENAMETOOLONG)
@@ -98,15 +98,15 @@  int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio)
 }
 
 /*
- * test if the @h_dentry sets opaque or not.
+ * test if the @h_path->dentry sets opaque or not.
  */
-int au_diropq_test(struct dentry *h_dentry)
+int au_diropq_test(struct path *h_path)
 {
 	int err;
 	struct inode *h_dir;
 
-	h_dir = d_inode(h_dentry);
-	err = au_wh_test(h_dentry, &diropq_name,
+	h_dir = d_inode(h_path->dentry);
+	err = au_wh_test(h_path, &diropq_name,
 			 au_test_h_perm_sio(h_dir, MAY_EXEC));
 	return err;
 }
@@ -124,6 +124,7 @@  struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
 	/* strict atomic_t is unnecessary here */
 	static unsigned short cnt;
 	struct qstr qs;
+	struct path h_ppath;
 
 	BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN);
 
@@ -147,10 +148,12 @@  struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
 	*p++ = '.';
 	AuDebugOn(name + qs.len + 1 - p <= AUFS_WH_TMP_LEN);
 
+	h_ppath.dentry = h_parent;
+	h_ppath.mnt = au_br_mnt(br);
 	qs.name = name;
 	for (i = 0; i < 3; i++) {
 		sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++);
-		dentry = au_sio_lkup_one(&qs, h_parent);
+		dentry = au_sio_lkup_one(&qs, &h_ppath);
 		if (IS_ERR(dentry) || d_is_negative(dentry))
 			goto out_name;
 		dput(dentry);
@@ -244,21 +247,20 @@  int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path,
 	return err;
 }
 
-static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh,
-			  struct au_branch *br)
+static int unlink_wh_name(struct path *h_ppath, struct qstr *wh)
 {
 	int err;
-	struct path h_path = {
-		.mnt = au_br_mnt(br)
-	};
+	struct path h_path;
 
 	err = 0;
-	h_path.dentry = vfsub_lkup_one(wh, h_parent);
+	h_path.dentry = vfsub_lkup_one(wh, h_ppath);
 	if (IS_ERR(h_path.dentry))
 		err = PTR_ERR(h_path.dentry);
 	else {
-		if (d_is_reg(h_path.dentry))
-			err = do_unlink_wh(d_inode(h_parent), &h_path);
+		if (d_is_reg(h_path.dentry)) {
+			h_path.mnt = h_ppath->mnt;
+			err = do_unlink_wh(d_inode(h_ppath->dentry), &h_path);
+		}
 		dput(h_path.dentry);
 	}
 
@@ -698,15 +700,17 @@  static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex,
 static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
 				unsigned int flags)
 {
-	struct dentry *opq_dentry, *h_dentry;
+	struct dentry *opq_dentry;
 	struct super_block *sb;
 	struct au_branch *br;
+	struct path h_path;
 	int err;
 
 	sb = dentry->d_sb;
 	br = au_sbr(sb, bindex);
-	h_dentry = au_h_dptr(dentry, bindex);
-	opq_dentry = vfsub_lkup_one(&diropq_name, h_dentry);
+	h_path.dentry = au_h_dptr(dentry, bindex);
+	h_path.mnt = au_br_mnt(br);
+	opq_dentry = vfsub_lkup_one(&diropq_name, &h_path);
 	if (IS_ERR(opq_dentry))
 		goto out;
 
@@ -717,11 +721,8 @@  static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex,
 			goto out; /* success */
 		}
 	} else {
-		struct path tmp = {
-			.dentry = opq_dentry,
-			.mnt	= au_br_mnt(br)
-		};
-		err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &tmp);
+		h_path.dentry = opq_dentry;
+		err = do_unlink_wh(au_h_iptr(d_inode(dentry), bindex), &h_path);
 		if (!err)
 			au_set_dbdiropq(dentry, -1);
 	}
@@ -784,11 +785,14 @@  struct dentry *au_wh_lkup(struct dentry *h_parent, struct qstr *base_name,
 	int err;
 	struct qstr wh_name;
 	struct dentry *wh_dentry;
+	struct path h_path;
 
 	err = au_wh_name_alloc(&wh_name, base_name);
 	wh_dentry = ERR_PTR(err);
 	if (!err) {
-		wh_dentry = vfsub_lkup_one(&wh_name, h_parent);
+		h_path.dentry = h_parent;
+		h_path.mnt = au_br_mnt(br);
+		wh_dentry = vfsub_lkup_one(&wh_name, &h_path);
 		kfree(wh_name.name);
 	}
 	return wh_dentry;
@@ -823,8 +827,8 @@  struct dentry *au_wh_create(struct dentry *dentry, aufs_bindex_t bindex,
 /* ---------------------------------------------------------------------- */
 
 /* Delete all whiteouts in this directory on branch bindex. */
-static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,
-			   aufs_bindex_t bindex, struct au_branch *br)
+static int del_wh_children(struct path *h_path, struct au_nhash *whlist,
+			   aufs_bindex_t bindex)
 {
 	int err;
 	unsigned long ul, n;
@@ -854,7 +858,7 @@  static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,
 			if (str->len + AUFS_WH_PFX_LEN <= PATH_MAX) {
 				memcpy(p, str->name, str->len);
 				wh_name.len = AUFS_WH_PFX_LEN + str->len;
-				err = unlink_wh_name(h_dentry, &wh_name, br);
+				err = unlink_wh_name(h_path, &wh_name);
 				if (!err)
 					continue;
 				break;
@@ -873,16 +877,15 @@  static int del_wh_children(struct dentry *h_dentry, struct au_nhash *whlist,
 
 struct del_wh_children_args {
 	int *errp;
-	struct dentry *h_dentry;
+	struct path *h_path;
 	struct au_nhash *whlist;
 	aufs_bindex_t bindex;
-	struct au_branch *br;
 };
 
 static void call_del_wh_children(void *args)
 {
 	struct del_wh_children_args *a = args;
-	*a->errp = del_wh_children(a->h_dentry, a->whlist, a->bindex, a->br);
+	*a->errp = del_wh_children(a->h_path, a->whlist, a->bindex);
 }
 
 /* ---------------------------------------------------------------------- */
@@ -934,7 +937,7 @@  int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
 {
 	int err;
 	unsigned int h_nlink;
-	struct path h_tmp;
+	struct path wh_path;
 	struct inode *wh_inode, *h_dir;
 	struct au_branch *br;
 
@@ -942,6 +945,8 @@  int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
 	IMustLock(h_dir);
 
 	br = au_sbr(dir->i_sb, bindex);
+	wh_path.dentry = wh_dentry;
+	wh_path.mnt = au_br_mnt(br);
 	wh_inode = d_inode(wh_dentry);
 	inode_lock_nested(wh_inode, AuLsc_I_CHILD);
 
@@ -950,15 +955,14 @@  int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
 	 * it means this whlist may have an obsoleted entry.
 	 */
 	if (!au_test_h_perm_sio(wh_inode, MAY_EXEC | MAY_WRITE))
-		err = del_wh_children(wh_dentry, whlist, bindex, br);
+		err = del_wh_children(&wh_path, whlist, bindex);
 	else {
 		int wkq_err;
 		struct del_wh_children_args args = {
 			.errp		= &err,
-			.h_dentry	= wh_dentry,
+			.h_path		= &wh_path,
 			.whlist		= whlist,
-			.bindex		= bindex,
-			.br		= br
+			.bindex		= bindex
 		};
 
 		wkq_err = au_wkq_wait(call_del_wh_children, &args);
@@ -968,10 +972,8 @@  int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex,
 	inode_unlock(wh_inode);
 
 	if (!err) {
-		h_tmp.dentry = wh_dentry;
-		h_tmp.mnt = au_br_mnt(br);
 		h_nlink = h_dir->i_nlink;
-		err = vfsub_rmdir(h_dir, &h_tmp);
+		err = vfsub_rmdir(h_dir, &wh_path);
 		/* some fs doesn't change the parent nlink in some cases */
 		h_nlink -= h_dir->i_nlink;
 	}
diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
index eb4b1823bd49..e13a6dc12483 100644
--- a/fs/aufs/whout.h
+++ b/fs/aufs/whout.h
@@ -28,8 +28,8 @@ 
 
 /* whout.c */
 int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);
-int au_wh_test(struct dentry *h_parent, struct qstr *wh_name, int try_sio);
-int au_diropq_test(struct dentry *h_dentry);
+int au_wh_test(struct path *h_ppath, struct qstr *wh_name, int try_sio);
+int au_diropq_test(struct path *h_path);
 struct au_branch;
 struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br,
 			     struct qstr *prefix);
diff --git a/fs/aufs/xino.c b/fs/aufs/xino.c
index 2f9167a71758..b6c8e451a6b1 100644
--- a/fs/aufs/xino.c
+++ b/fs/aufs/xino.c
@@ -212,20 +212,21 @@  ssize_t xino_fwrite(vfs_writef_t func, struct file *file, void *buf,
 struct file *au_xino_create2(struct file *base_file, struct file *copy_src)
 {
 	struct file *file;
-	struct dentry *base, *parent;
+	struct dentry *base;
 	struct inode *dir, *delegated;
 	struct qstr *name;
-	struct path path;
+	struct path ppath, path;
 	int err;
 
 	base = base_file->f_path.dentry;
-	parent = base->d_parent; /* dir inode is locked */
-	dir = d_inode(parent);
+	ppath.dentry = base->d_parent; /* dir inode is locked */
+	ppath.mnt = base_file->f_path.mnt;
+	dir = d_inode(ppath.dentry);
 	IMustLock(dir);
 
 	file = ERR_PTR(-EINVAL);
 	name = &base->d_name;
-	path.dentry = vfsub_lookup_one_len(name->name, parent, name->len);
+	path.dentry = vfsub_lookup_one_len(name->name, &ppath, name->len);
 	if (IS_ERR(path.dentry)) {
 		file = (void *)path.dentry;
 		pr_err("%pd lookup err %ld\n",