[PATCH v2 2/2] aufs: set struct path.mnt in vfsub_lookup_one_len[_unlocked]()
Status: Beta
Brought to you by:
sfjro
From: Mauricio F. de O. <mf...@ca...> - 2021-10-08 19:57:04
|
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 <vi...@ze...> 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 <hoo...@ya...> 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 <mf...@ca...> --- 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 |