Currently vfsub_lookup_one_len[_unlocked]() don't
set `struct path.mnt` before passing it up to VFS
in vfs_getattr(), which can hit a kernel bug/oops.
Looking at fs/, other usage of `struct path.mnt`
never checks for NULL, so it's assumed to be set,
and thus should always be set by callers.
Many other places in aufs do set/pass `mnt` right,
particularly seen w/ calls to vfsub_dentry_open(),
including another two callers of vfsub_get_attr().
This patch introduces a `struct vfsmount` to the
functions by replacing `struct dentry *parent` w/
`struct path *ppath`, which contains both *dentry
and *mnt, and passes *mnt instead of NULL for VFS.
This fixes a ~10 year old bug (checked) that is
exposed by AppArmor when accessing the provided
`path.mnt.mnt_flags` since commit (torvalds.git):
(with a public report 4 years 10 months ago [1])
commit 02125a826459a6ad142f8d91c5b6357562f96615
Author: Al Viro <[email protected]>
Date: Mon Dec 5 08:43:34 2011 -0500
fix apparmor dereferencing potentially freed dentry,
sanitize__d_path() API
And `path.mnt` is NULL at least since early 2009,
maybe longer (aufs2-standalone.git, aufs2 branch):
commit d8a76a43eff98f77bf47650ff430e80952af5f82
Author: J. R. Okajima <[email protected]>
Date: Wed Mar 25 22:39:29 2009 +0900
aufs2 standalone version for linux-2.6.
More details on checking the ~10 year old bug below.
Nowadays it's hit even without AppArmor in 5.15-rcN.
Traces:
===
AppArmor disabled:
[ 89.908402] BUG: kernel NULL pointer dereference, address:
0000000000000018
...
[ 89.918876] CPU: 0 PID: 669 Comm: openat Not tainted 5.15.0-rc2+ #4
[ 89.921600] Hardware name: QEMU Standard PC ...
[ 89.925159] RIP: 0010:vfs_getattr_nosec+0x81/0x140
...
[ 89.976333] Call Trace:
[ 89.977852] vfsub_update_h_iattr+0x92/0xa0
[ 89.980511] vfsub_lookup_one_len+0x5e/0x70
[ 89.984948] au_wh_test+0x1c/0x80
[ 89.986344] au_lkup_dentry+0x192/0x520
[ 89.988310] aufs_lookup.part.0+0x10e/0x200
[ 89.990470] aufs_atomic_open+0x198/0x400
[ 89.992114] ? may_create+0x133/0x140
[ 89.993037] path_openat+0x645/0x1070
[ 89.994588] do_filp_open+0xad/0x120
[ 89.995507] do_sys_openat2+0x241/0x320
[ 89.996685] do_sys_open+0x3f/0x80
[ 89.997795] do_syscall_64+0x3b/0x90
[ 89.998985] entry_SYSCALL_64_after_hwframe+0x44/0xae
AppArmor enabled:
[ 63.182645] BUG: kernel NULL pointer dereference, address:
0000000000000018
...
[ 63.203176] CPU: 0 PID: 799 Comm: openat Not tainted 5.15.0-rc2+ #4
[ 63.206765] Hardware name: QEMU Standard PC ...
[ 63.210428] RIP: 0010:common_perm_cond+0x17/0x70
...
[ 63.248667] Call Trace:
[ 63.249558] security_inode_getattr+0x2b/0x40
[ 63.251174] vfs_getattr+0x18/0x40
[ 63.252573] vfsub_update_h_iattr+0x92/0xa0
[ 63.253711] ? lookup_dcache+0x38/0x60
[ 63.254613] ? lookup_one_len+0x6b/0x90
[ 63.255545] vfsub_lookup_one_len+0x5e/0x70
[ 63.256631] au_wh_test+0x1c/0x80
[ 63.257459] au_lkup_dentry+0x192/0x520
[ 63.258431] aufs_lookup.part.0+0x10e/0x200
[ 63.259446] aufs_atomic_open+0x198/0x400
[ 63.260371] ? h_permission+0x5a/0xc0
[ 63.261074] path_openat+0x645/0x1070
[ 63.261769] do_filp_open+0xad/0x120
[ 63.262487] do_sys_openat2+0x241/0x320
[ 63.263229] do_sys_open+0x3f/0x80
[ 63.263835] do_syscall_64+0x3b/0x90
[ 63.264437] entry_SYSCALL_64_after_hwframe+0x44/0xae
Changes:
===
Unfortunately the changed functions (and callers)
are referenced in many locations in the aufs code,
so even though the core change is small, a lot of
resulting changes are required.
Thankfully it's a simple modification to replace
`struct dentry/path` in the function call and it's
usually already available (otherwise, get `mnt` w/
`au_br_mnt(au_sbr(dentry->d_sb, bindex))` as done
in other functions in aufs.
vfsub_lookup_one_len
- au_drinfo_do_store
- au_drinfo_do_load
- vfsub_lkup_one
- - au_do_ren_after_cpup
- - au_do_lookup
- - au_sio_lkup_one
- - - au_do_lookup
- - - au_lkup_neg
- - - au_may_del
- - - au_wh_test
- - - au_whtmp_lkup
- - au_h_verify_dentry
- - au_dr_hino
- - au_ren_rev_rename
- - au_ren_rev_whtmp
- - au_do_plink_lkup
- - do_whplink
- - vfsub_call_lkup_one
- - - au_sio_lkup_one
- - au_wh_test
- - - au_do_lookup
- - - au_diropq_test
- - - - au_do_lookup
- - unlink_wh_name
- - do_diropq
- - au_wh_lkup
- au_xino_create2
vfsub_lookup_one_len_unlocked
- au_lkup_by_ino
Reproducer:
===
- openat("test", O_CREAT)
- read-only branch with "test" file
(apparently for vfsub_lookup_one_len() -> vfsub_update_h_iattr())
- read-write branch on fuseblk
(for vfsub_update_h_iattr() -> vfsub_getattr())
without the "test" file
- [optional in v5.15/required in earlier versions:
apparmor-enabled application]
1) test app
# cat openat.c
#include <stdio.h>
#include <fcntl.h>
int main() {
int rc;
rc = openat(AT_FDCWD, "test", O_RDWR | O_CREAT | S_IRWXU);
if (rc < 0) {
perror("openat");
return 1;
}
return 0;
}
# gcc -o openat openat.c
2) ntfs-3g mount (fuseblk)
# truncate -s 1g ntfs.img
# DEV=$(losetup -f --show ntfs.img)
# mkfs.ntfs --fast $DEV
# mkdir ntfs
# mount -t ntfs-3g $DEV ntfs
# mount | grep ntfs
/dev/loop6 on /home/ubuntu/ntfs type fuseblk
(rw,relatime,user_id=0,group_id=0,allow_other,blksize=4096)
3) aufs mount (with 'test' file in the read-only branch)
# mkdir ro aufs
# touch ro/test
# mount -t aufs -o br=ntfs:ro none aufs
4) [optional] enable apparmor for the test app (even in complain mode for
aa-genprof)
# aa-genprof ./openat &
...
Please start the application to be profiled in
another window and exercise its functionality now.
...
<press enter>
[1]+ Stopped aa-genprof ./openat
5) remove 'test' file from read-write branch (still exists in read-only branch)
# cd aufs
# rm test
6) run the test app
# ../openat
Killed
(See kernel error above.)
The 10 year old bug:
===
The commit that introduced AppArmor's access to `path.mnt.mnt_flags`
(dated 2011/12/05) was introduced in v3.2-rc5 (dated 2011/12/09) and
released in v3.2 (dated 2012/01/04).
~/git/linux$ git describe --contains
02125a826459a6ad142f8d91c5b6357562f96615
v3.2-rc5~37^2
The bug reproduces in a Ubuntu 12.04 Precise Pangolin VM, that runs
a 3.2-based kernel by default, with aufs support. But for the sake
of in-depth analysis, let's go with aufs upstream sources.
Build the aufs3.2 branch from aufs3-linux.git (based on linux v3.2;
note that aufs sets `path.mnt = NULL` since 2.6), adding 3 patches
for apparmor userspace tools in apparmor 2.8.0 (kernel-patches/3.2)
from [2].
Enable aufs, fuse, apparmor, devtmpfs, and ext4; this allows 12.04
to boot w/ this kernel. You need the initrd.img from its original
3.2 kernel for the boot process to finish (udev/scripts.)
Now follow the reproducer steps above, and the bug is hit w/ v3.2.
[ 68.628728] BUG: unable to handle kernel NULL pointer dereference at
0000000000000061
...
[ 68.629648] Pid: 1807, comm: openat Not tainted 3.2.0 #4 QEMU Standard
PC (i440FX + PIIX, 1996)
[ 68.629648] RIP: 0010:[<ffffffff81245057>] [<ffffffff81245057>]
aa_get_name+0x77/0x340
...
[ 68.629648] Call Trace:
[ 68.629648] [<ffffffff8124a848>] aa_path_perm+0x78/0x190
[ 68.629648] [<ffffffff811f1f37>] ? fuse_change_attributes+0x57/0xc0
[ 68.629648] [<ffffffff811eb288>] ?
fuse_change_entry_timeout.isra.10+0x38/0x50
[ 68.629648] [<ffffffff8124978b>] common_perm+0x5b/0x70
[ 68.629648] [<ffffffff812497ec>] apparmor_inode_getattr+0x4c/0x50
[ 68.629648] [<ffffffff8122628b>] security_inode_getattr+0x1b/0x30
[ 68.629648] [<ffffffff8111ac7a>] vfs_getattr+0x2a/0x80
[ 68.629648] [<ffffffff811fd6a7>] vfsub_update_h_iattr+0x67/0x70
[ 68.629648] [<ffffffff81226267>] ? security_inode_permission+0x17/0x20
[ 68.629648] [<ffffffff8112176e>] ? lookup_one_len+0xee/0x120
[ 68.629648] [<ffffffff810d66da>] ? get_page_from_freelist+0x30a/0x7b0
[ 68.629648] [<ffffffff811fd92b>] vfsub_lookup_one_len+0x3b/0x50
[ 68.629648] [<ffffffff8120477b>] au_lkup_one+0x1b/0x30
[ 68.629648] [<ffffffff812013ef>] au_wh_test+0x1f/0xd0
[ 68.629648] [<ffffffff8112ab47>] ? dget_parent+0x27/0x60
[ 68.629648] [<ffffffff814f1ec8>] ? mutex_lock+0x18/0x40
[ 68.629648] [<ffffffff81204b30>] au_lkup_dentry+0x370/0x4d0
[ 68.629648] [<ffffffff8110f7f0>] ? __kmalloc+0xf0/0x190
[ 68.629648] [<ffffffff8120388a>] ? au_di_alloc+0x4a/0xb0
[ 68.629648] [<ffffffff8120c44c>] aufs_lookup+0xec/0x240
[ 68.629648] [<ffffffff811208d0>] d_alloc_and_lookup+0x40/0x80
[ 68.629648] [<ffffffff8112d3c0>] ? d_lookup+0x30/0x50
[ 68.629648] [<ffffffff8112133f>] __lookup_hash.part.30+0xbf/0xe0
[ 68.629648] [<ffffffff81226267>] ? security_inode_permission+0x17/0x20
[ 68.629648] [<ffffffff81124568>] lookup_hash+0x48/0x60
[ 68.629648] [<ffffffff81124d8f>] do_last+0x38f/0x910
[ 68.629648] [<ffffffff811253dd>] path_openat+0xcd/0x3c0
[ 68.629648] [<ffffffff810f1979>] ? handle_mm_fault+0x139/0x240
[ 68.629648] [<ffffffff811257bd>] do_filp_open+0x3d/0xa0
[ 68.629648] [<ffffffff81281f12>] ? __strncpy_from_user+0x22/0x60
[ 68.629648] [<ffffffff81131307>] ? alloc_fd+0x47/0x140
[ 68.629648] [<ffffffff81115872>] do_sys_open+0xf2/0x1d0
[ 68.629648] [<ffffffff8111597c>] sys_openat+0xc/0x10
[ 68.629648] [<ffffffff814fa57b>] system_call_fastpath+0x16/0x1b
[1]
https://unix.stackexchange.com/questions/324571/docker-run-causing-kernel-panic
[2] https://launchpad.net/apparmor/2.8/2.8.0/+download/apparmor-2.8.0.tar.gz
Signed-off-by: Mauricio Faria de Oliveira <[email protected]>
---
fs/aufs/cpup.c | 5 ++++-
fs/aufs/dentry.c | 36 +++++++++++++++++++++++++-----------
fs/aufs/dentry.h | 2 +-
fs/aufs/dirren.c | 6 +++---
fs/aufs/export.c | 2 +-
fs/aufs/i_op_del.c | 6 +++++-
fs/aufs/i_op_ren.c | 12 ++++++++++--
fs/aufs/plink.c | 12 ++++++++++--
fs/aufs/vfsub.c | 15 ++++++++-------
fs/aufs/vfsub.h | 10 +++++-----
fs/aufs/whout.c | 36 ++++++++++++++++++++++++++----------
fs/aufs/whout.h | 4 ++--
fs/aufs/xino.c | 6 +++++-
13 files changed, 105 insertions(+), 47 deletions(-)
diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
index dbaa01a55904..2a9a76335e75 100644
--- a/fs/aufs/cpup.c
+++ b/fs/aufs/cpup.c
@@ -759,11 +759,14 @@ static int au_do_ren_after_cpup(struct au_cp_generic
*cpg, struct path *h_path)
h_path->dentry = dget(au_h_dptr(dentry, bdst));
au_set_h_dptr(dentry, bdst, h_dentry);
} else {
+ struct path h_ppath;
err = 0;
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bdst);
dput(parent);
- h_path->dentry = vfsub_lkup_one(&dentry->d_name, h_parent);
+ h_ppath.dentry = h_parent;
+ h_ppath.mnt = au_br_mnt(au_sbr(parent->d_sb, bdst));
+ 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 4b63daab7220..1775ec6ac189 100644
--- a/fs/aufs/dentry.c
+++ b/fs/aufs/dentry.c
@@ -34,20 +34,24 @@ au_do_lookup(struct dentry *h_parent, struct dentry *dentry,
{
struct dentry *h_dentry;
struct inode *h_inode;
- struct au_branch *br;
+ struct au_branch *br = au_sbr(dentry->d_sb, bindex);
struct user_namespace *h_userns;
int wh_found, opq;
unsigned char wh_able;
const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG);
const unsigned char ignore_perm = !!au_ftest_lkup(args->flags,
IGNORE_PERM);
+ struct path h_path;
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
wh_found = 0;
- br = au_sbr(dentry->d_sb, bindex);
h_userns = au_br_userns(br);
wh_able = !!au_br_whable(br->br_perm);
if (wh_able)
- wh_found = au_wh_test(h_userns, h_parent, &args->whname,
+ wh_found = au_wh_test(h_userns, &h_ppath, &args->whname,
ignore_perm);
h_dentry = ERR_PTR(wh_found);
if (!wh_found)
@@ -63,9 +67,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_ppath);
else
- h_dentry = au_sio_lkup_one(h_userns, args->name, h_parent);
+ h_dentry = au_sio_lkup_one(h_userns, args->name, &h_ppath);
if (IS_ERR(h_dentry)) {
if (PTR_ERR(h_dentry) == -ENAMETOOLONG
&& !allow_neg)
@@ -99,8 +103,10 @@ real_lookup:
|| (d_really_is_positive(dentry) && !d_is_dir(dentry)))
goto out; /* success */
+ h_path.dentry = h_dentry;
+ h_path.mnt = au_br_mnt(au_sbr(dentry->d_sb, bindex));
inode_lock_shared_nested(h_inode, AuLsc_I_CHILD);
- opq = au_diropq_test(h_userns, h_dentry);
+ opq = au_diropq_test(h_userns, &h_path);
inode_unlock_shared(h_inode);
if (opq > 0)
au_set_dbdiropq(dentry, bindex);
@@ -246,18 +252,19 @@ out:
}
struct dentry *au_sio_lkup_one(struct user_namespace *userns, struct qstr
*name,
- struct dentry *parent)
+ struct path *ppath)
{
+ struct dentry *parent = ppath->dentry;
struct dentry *dentry;
int wkq_err;
if (!au_test_h_perm_sio(userns, d_inode(parent), MAY_EXEC))
- dentry = vfsub_lkup_one(name, parent);
+ 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);
@@ -277,15 +284,18 @@ int au_lkup_neg(struct dentry *dentry, aufs_bindex_t
bindex, int wh)
struct dentry *parent, *h_parent, *h_dentry;
struct au_branch *br;
struct user_namespace *h_userns;
+ struct path h_ppath;
parent = dget_parent(dentry);
h_parent = au_h_dptr(parent, bindex);
br = au_sbr(dentry->d_sb, bindex);
h_userns = au_br_userns(br);
+ h_ppath.dentry = h_parent;
+ h_ppath.mnt = au_br_mnt(au_sbr(parent->d_sb, bindex));
if (wh)
h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name);
else
- h_dentry = au_sio_lkup_one(h_userns, &dentry->d_name, h_parent);
+ h_dentry = au_sio_lkup_one(h_userns, &dentry->d_name, &h_ppath);
err = PTR_ERR(h_dentry);
if (IS_ERR(h_dentry))
goto out;
@@ -360,6 +370,10 @@ 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 = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
err = 0;
memset(&ia, -1, sizeof(ia));
@@ -374,7 +388,7 @@ 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_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 a1df828fd132..26d1332d269b 100644
--- a/fs/aufs/dentry.h
+++ b/fs/aufs/dentry.h
@@ -74,7 +74,7 @@ struct au_do_lookup_args {
extern const struct dentry_operations aufs_dop, aufs_dop_noreval;
struct au_branch;
struct dentry *au_sio_lkup_one(struct user_namespace *userns, struct qstr
*name,
- struct dentry *parent);
+ 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 42f590603c91..d7026bd5531b 100644
--- a/fs/aufs/dirren.c
+++ b/fs/aufs/dirren.c
@@ -263,7 +263,7 @@ 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.dentry = vfsub_lkup_one(&hinoname, path);
err = PTR_ERR(hinopath.dentry);
if (IS_ERR(hinopath.dentry))
goto out_unlock;
@@ -619,7 +619,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)) {
@@ -1003,7 +1003,7 @@ static struct au_drinfo *au_drinfo_do_load(struct path
*h_ppath,
unlocked = 0;
h_dir = d_inode(h_ppath->dentry);
inode_lock_shared_nested(h_dir, AuLsc_I_PARENT);
- infopath.dentry = vfsub_lookup_one_len(whname, h_ppath->dentry,
+ infopath.dentry = vfsub_lookup_one_len(whname, h_ppath,
whnamelen);
if (IS_ERR(infopath.dentry)) {
drinfo = (void *)infopath.dentry;
diff --git a/fs/aufs/export.c b/fs/aufs/export.c
index cb4bcfc8af40..e28bceb7386a 100644
--- a/fs/aufs/export.c
+++ b/fs/aufs/export.c
@@ -406,7 +406,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 a11987581548..6d80a35c3c10 100644
--- a/fs/aufs/i_op_del.c
+++ b/fs/aufs/i_op_del.c
@@ -95,6 +95,10 @@ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
struct dentry *h_dentry, *h_latest;
struct inode *h_inode;
struct user_namespace *h_userns;
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(au_sbr(dentry->d_sb, bindex))
+ };
h_dentry = au_h_dptr(dentry, bindex);
if (d_really_is_positive(dentry)) {
@@ -138,7 +142,7 @@ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex,
MAY_EXEC | MAY_WRITE)))
goto out;
- h_latest = au_sio_lkup_one(h_userns, &dentry->d_name, h_parent);
+ h_latest = au_sio_lkup_one(h_userns, &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 022b6e8cef11..c107e8d7f288 100644
--- a/fs/aufs/i_op_ren.c
+++ b/fs/aufs/i_op_ren.c
@@ -150,9 +150,13 @@ 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);
+ &h_ppath);
rerr = PTR_ERR(a->h_path.dentry);
if (IS_ERR(a->h_path.dentry)) {
RevertFailure("lkup one %pd", a->src_dentry);
@@ -179,9 +183,13 @@ 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);
+ &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 518350041aa2..cb3a71523fb5 100644
--- a/fs/aufs/plink.c
+++ b/fs/aufs/plink.c
@@ -216,10 +216,14 @@ static struct dentry *au_do_plink_lkup(struct qstr
*tgtname,
{
struct dentry *h_dentry;
struct inode *h_inode;
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
h_inode = d_inode(h_parent);
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;
}
@@ -270,12 +274,16 @@ static int do_whplink(struct qstr *tgt, struct dentry
*h_parent,
struct path h_path = {
.mnt = au_br_mnt(br)
};
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
struct inode *h_dir, *delegated;
h_dir = d_inode(h_parent);
inode_lock_nested(h_dir, AuLsc_I_CHILD2);
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;
diff --git a/fs/aufs/vfsub.c b/fs/aufs/vfsub.c
index 77682fba944f..dd9d3f8900fd 100644
--- a/fs/aufs/vfsub.c
+++ b/fs/aufs/vfsub.c
@@ -165,11 +165,12 @@ int vfsub_kern_path(const char *name, unsigned int flags,
struct path *path)
return err;
}
-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, bool locked)
{
+ struct dentry *parent = ppath->dentry;
struct path path = {
- .mnt = NULL
+ .mnt = ppath->mnt
};
if (locked) {
@@ -191,21 +192,21 @@ out:
}
struct dentry *vfsub_lookup_one_len_unlocked(const char *name,
- struct dentry *parent, int len)
+ struct path *ppath, int len)
{
- return __vfsub_lookup_one_len(name, parent, len, false);
+ return __vfsub_lookup_one_len(name, ppath, len, false);
}
-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)
{
- return __vfsub_lookup_one_len(name, parent, len, true);
+ return __vfsub_lookup_one_len(name, ppath, len, true);
}
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 8e1fca0406e4..cd4390f8f392 100644
--- a/fs/aufs/vfsub.h
+++ b/fs/aufs/vfsub.h
@@ -103,20 +103,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 8849e6239dd1..e54e6b707c2d 100644
--- a/fs/aufs/whout.c
+++ b/fs/aufs/whout.c
@@ -64,16 +64,17 @@ int au_wh_name_alloc(struct qstr *wh, const struct qstr
*name)
* test if the @wh_name exists under @h_parent.
* @try_sio specifies the necessary of super-io.
*/
-int au_wh_test(struct user_namespace *h_userns, struct dentry *h_parent,
+int au_wh_test(struct user_namespace *h_userns, 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(h_userns, wh_name, h_parent);
+ wh_dentry = au_sio_lkup_one(h_userns, wh_name, h_ppath);
err = PTR_ERR(wh_dentry);
if (IS_ERR(wh_dentry)) {
if (err == -ENAMETOOLONG)
@@ -102,13 +103,13 @@ out:
/*
* test if the @h_dentry sets opaque or not.
*/
-int au_diropq_test(struct user_namespace *h_userns, struct dentry *h_dentry)
+int au_diropq_test(struct user_namespace *h_userns, struct path *h_path)
{
int err;
struct inode *h_dir;
- h_dir = d_inode(h_dentry);
- err = au_wh_test(h_userns, h_dentry, &diropq_name,
+ h_dir = d_inode(h_path->dentry);
+ err = au_wh_test(h_userns, h_path, &diropq_name,
au_test_h_perm_sio(h_userns, h_dir, MAY_EXEC));
return err;
}
@@ -127,6 +128,10 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent,
struct au_branch *br,
static unsigned short cnt;
struct qstr qs;
struct user_namespace *h_userns;
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
BUILD_BUG_ON(sizeof(cnt) * 2 > AUFS_WH_TMP_LEN);
@@ -154,7 +159,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent,
struct au_branch *br,
qs.name = name;
for (i = 0; i < 3; i++) {
sprintf(p, "%.*x", AUFS_WH_TMP_LEN, cnt++);
- dentry = au_sio_lkup_one(h_userns, &qs, h_parent);
+ dentry = au_sio_lkup_one(h_userns, &qs, &h_ppath);
if (IS_ERR(dentry) || d_is_negative(dentry))
goto out_name;
dput(dentry);
@@ -255,9 +260,13 @@ static int unlink_wh_name(struct dentry *h_parent, struct
qstr *wh,
struct path h_path = {
.mnt = au_br_mnt(br)
};
+ struct path h_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
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 {
@@ -705,12 +714,15 @@ static struct dentry *do_diropq(struct dentry *dentry,
aufs_bindex_t bindex,
struct dentry *opq_dentry, *h_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 = h_dentry;
+ h_path.mnt = au_br_mnt(br);
+ opq_dentry = vfsub_lkup_one(&diropq_name, &h_path);
if (IS_ERR(opq_dentry))
goto out;
@@ -791,11 +803,15 @@ 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_ppath = {
+ .dentry = h_parent,
+ .mnt = au_br_mnt(br)
+ };
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);
+ wh_dentry = vfsub_lkup_one(&wh_name, &h_ppath);
au_kfree_try_rcu(wh_name.name);
}
return wh_dentry;
diff --git a/fs/aufs/whout.h b/fs/aufs/whout.h
index 08bce298f8a3..d55c4f9a9659 100644
--- a/fs/aufs/whout.h
+++ b/fs/aufs/whout.h
@@ -29,9 +29,9 @@
/* whout.c */
int au_wh_name_alloc(struct qstr *wh, const struct qstr *name);
-int au_wh_test(struct user_namespace *h_userns, struct dentry *h_parent,
+int au_wh_test(struct user_namespace *h_userns, struct path *h_ppath,
struct qstr *wh_name, int try_sio);
-int au_diropq_test(struct user_namespace *h_userns, struct dentry *h_dentry);
+int au_diropq_test(struct user_namespace *h_userns, 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 b126b34206f3..7e7ba771f378 100644
--- a/fs/aufs/xino.c
+++ b/fs/aufs/xino.c
@@ -241,6 +241,10 @@ struct file *au_xino_create2(struct super_block *sb,
struct path *base,
struct path path;
int err, do_unlock;
struct au_xino_lock_dir ldir;
+ struct path ppath = {
+ .dentry = base->dentry->d_parent,
+ .mnt = base->mnt
+ };
do_unlock = 1;
au_xino_lock_dir(sb, base, &ldir);
@@ -250,7 +254,7 @@ struct file *au_xino_create2(struct super_block *sb, struct
path *base,
IMustLock(dir);
name = &dentry->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", dentry, PTR_ERR(path.dentry));
--
2.30.2