Commit 9c577491 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'work.dotdot1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs pathwalk sanitizing from Al Viro:
 "Massive pathwalk rewrite and cleanups.

  Several iterations have been posted; hopefully this thing is getting
  readable and understandable now. Pretty much all parts of pathname
  resolutions are affected...

  The branch is identical to what has sat in -next, except for commit
  message in "lift all calls of step_into() out of follow_dotdot/
  follow_dotdot_rcu", crediting Qian Cai for reporting the bug; only
  commit message changed there."

* 'work.dotdot1' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (69 commits)
  lookup_open(): don't bother with fallbacks to lookup+create
  atomic_open(): no need to pass struct open_flags anymore
  open_last_lookups(): move complete_walk() into do_open()
  open_last_lookups(): lift O_EXCL|O_CREAT handling into do_open()
  open_last_lookups(): don't abuse complete_walk() when all we want is unlazy
  open_last_lookups(): consolidate fsnotify_create() calls
  take post-lookup part of do_last() out of loop
  link_path_walk(): sample parent's i_uid and i_mode for the last component
  __nd_alloc_stack(): make it return bool
  reserve_stack(): switch to __nd_alloc_stack()
  pick_link(): take reserving space on stack into a new helper
  pick_link(): more straightforward handling of allocation failures
  fold path_to_nameidata() into its only remaining caller
  pick_link(): pass it struct path already with normal refcounting rules
  fs/namei.c: kill follow_mount()
  non-RCU analogue of the previous commit
  helper for mount rootwards traversal
  follow_dotdot(): be lazy about changing nd->path
  follow_dotdot_rcu(): be lazy about changing nd->path
  follow_dotdot{,_rcu}(): massage loops
  ...
parents d987ca1c 99a4a90c
...@@ -404,11 +404,8 @@ that is the "next" component in the pathname. ...@@ -404,11 +404,8 @@ that is the "next" component in the pathname.
``int last_type`` ``int last_type``
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT``, ``LAST_DOTDOT``, or This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT`` or ``LAST_DOTDOT``.
``LAST_BIND``. The ``last`` field is only valid if the type is The ``last`` field is only valid if the type is ``LAST_NORM``.
``LAST_NORM``. ``LAST_BIND`` is used when following a symlink and no
components of the symlink have been processed yet. Others should be
fairly self-explanatory.
``struct path root`` ``struct path root``
~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~
......
...@@ -186,7 +186,7 @@ static int find_autofs_mount(const char *pathname, ...@@ -186,7 +186,7 @@ static int find_autofs_mount(const char *pathname,
struct path path; struct path path;
int err; int err;
err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0); err = kern_path(pathname, LOOKUP_MOUNTPOINT, &path);
if (err) if (err)
return err; return err;
err = -ENOENT; err = -ENOENT;
...@@ -519,8 +519,8 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp, ...@@ -519,8 +519,8 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
if (!fp || param->ioctlfd == -1) { if (!fp || param->ioctlfd == -1) {
if (autofs_type_any(type)) if (autofs_type_any(type))
err = kern_path_mountpoint(AT_FDCWD, err = kern_path(name, LOOKUP_FOLLOW | LOOKUP_MOUNTPOINT,
name, &path, LOOKUP_FOLLOW); &path);
else else
err = find_autofs_mount(name, &path, err = find_autofs_mount(name, &path,
test_by_type, &type); test_by_type, &type);
......
...@@ -60,7 +60,6 @@ extern int finish_clean_context(struct fs_context *fc); ...@@ -60,7 +60,6 @@ extern int finish_clean_context(struct fs_context *fc);
*/ */
extern int filename_lookup(int dfd, struct filename *name, unsigned flags, extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
struct path *path, struct path *root); struct path *path, struct path *root);
extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
extern int vfs_path_lookup(struct dentry *, struct vfsmount *, extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *); const char *, unsigned int, struct path *);
long do_mknodat(int dfd, const char __user *filename, umode_t mode, long do_mknodat(int dfd, const char __user *filename, umode_t mode,
......
This diff is collapsed.
...@@ -1669,7 +1669,7 @@ int ksys_umount(char __user *name, int flags) ...@@ -1669,7 +1669,7 @@ int ksys_umount(char __user *name, int flags)
struct path path; struct path path;
struct mount *mnt; struct mount *mnt;
int retval; int retval;
int lookup_flags = 0; int lookup_flags = LOOKUP_MOUNTPOINT;
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
return -EINVAL; return -EINVAL;
...@@ -1680,7 +1680,7 @@ int ksys_umount(char __user *name, int flags) ...@@ -1680,7 +1680,7 @@ int ksys_umount(char __user *name, int flags)
if (!(flags & UMOUNT_NOFOLLOW)) if (!(flags & UMOUNT_NOFOLLOW))
lookup_flags |= LOOKUP_FOLLOW; lookup_flags |= LOOKUP_FOLLOW;
retval = user_path_mountpoint_at(AT_FDCWD, name, lookup_flags, &path); retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
if (retval) if (retval)
goto out; goto out;
mnt = real_mount(path.mnt); mnt = real_mount(path.mnt);
...@@ -2697,45 +2697,32 @@ static int do_move_mount_old(struct path *path, const char *old_name) ...@@ -2697,45 +2697,32 @@ static int do_move_mount_old(struct path *path, const char *old_name)
/* /*
* add a mount into a namespace's mount tree * add a mount into a namespace's mount tree
*/ */
static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags) static int do_add_mount(struct mount *newmnt, struct mountpoint *mp,
struct path *path, int mnt_flags)
{ {
struct mountpoint *mp; struct mount *parent = real_mount(path->mnt);
struct mount *parent;
int err;
mnt_flags &= ~MNT_INTERNAL_FLAGS; mnt_flags &= ~MNT_INTERNAL_FLAGS;
mp = lock_mount(path);
if (IS_ERR(mp))
return PTR_ERR(mp);
parent = real_mount(path->mnt);
err = -EINVAL;
if (unlikely(!check_mnt(parent))) { if (unlikely(!check_mnt(parent))) {
/* that's acceptable only for automounts done in private ns */ /* that's acceptable only for automounts done in private ns */
if (!(mnt_flags & MNT_SHRINKABLE)) if (!(mnt_flags & MNT_SHRINKABLE))
goto unlock; return -EINVAL;
/* ... and for those we'd better have mountpoint still alive */ /* ... and for those we'd better have mountpoint still alive */
if (!parent->mnt_ns) if (!parent->mnt_ns)
goto unlock; return -EINVAL;
} }
/* Refuse the same filesystem on the same mount point */ /* Refuse the same filesystem on the same mount point */
err = -EBUSY;
if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb && if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
path->mnt->mnt_root == path->dentry) path->mnt->mnt_root == path->dentry)
goto unlock; return -EBUSY;
err = -EINVAL;
if (d_is_symlink(newmnt->mnt.mnt_root)) if (d_is_symlink(newmnt->mnt.mnt_root))
goto unlock; return -EINVAL;
newmnt->mnt.mnt_flags = mnt_flags; newmnt->mnt.mnt_flags = mnt_flags;
err = graft_tree(newmnt, parent, mp); return graft_tree(newmnt, parent, mp);
unlock:
unlock_mount(mp);
return err;
} }
static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags); static bool mount_too_revealing(const struct super_block *sb, int *new_mnt_flags);
...@@ -2748,6 +2735,7 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, ...@@ -2748,6 +2735,7 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
unsigned int mnt_flags) unsigned int mnt_flags)
{ {
struct vfsmount *mnt; struct vfsmount *mnt;
struct mountpoint *mp;
struct super_block *sb = fc->root->d_sb; struct super_block *sb = fc->root->d_sb;
int error; int error;
...@@ -2768,7 +2756,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint, ...@@ -2768,7 +2756,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
mnt_warn_timestamp_expiry(mountpoint, mnt); mnt_warn_timestamp_expiry(mountpoint, mnt);
error = do_add_mount(real_mount(mnt), mountpoint, mnt_flags); mp = lock_mount(mountpoint);
if (IS_ERR(mp)) {
mntput(mnt);
return PTR_ERR(mp);
}
error = do_add_mount(real_mount(mnt), mp, mountpoint, mnt_flags);
unlock_mount(mp);
if (error < 0) if (error < 0)
mntput(mnt); mntput(mnt);
return error; return error;
...@@ -2829,23 +2823,63 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, ...@@ -2829,23 +2823,63 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags,
int finish_automount(struct vfsmount *m, struct path *path) int finish_automount(struct vfsmount *m, struct path *path)
{ {
struct mount *mnt = real_mount(m); struct dentry *dentry = path->dentry;
struct mountpoint *mp;
struct mount *mnt;
int err; int err;
if (!m)
return 0;
if (IS_ERR(m))
return PTR_ERR(m);
mnt = real_mount(m);
/* The new mount record should have at least 2 refs to prevent it being /* The new mount record should have at least 2 refs to prevent it being
* expired before we get a chance to add it * expired before we get a chance to add it
*/ */
BUG_ON(mnt_get_count(mnt) < 2); BUG_ON(mnt_get_count(mnt) < 2);
if (m->mnt_sb == path->mnt->mnt_sb && if (m->mnt_sb == path->mnt->mnt_sb &&
m->mnt_root == path->dentry) { m->mnt_root == dentry) {
err = -ELOOP; err = -ELOOP;
goto fail; goto discard;
} }
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE); /*
if (!err) * we don't want to use lock_mount() - in this case finding something
return 0; * that overmounts our mountpoint to be means "quitely drop what we've
fail: * got", not "try to mount it on top".
*/
inode_lock(dentry->d_inode);
namespace_lock();
if (unlikely(cant_mount(dentry))) {
err = -ENOENT;
goto discard_locked;
}
rcu_read_lock();
if (unlikely(__lookup_mnt(path->mnt, dentry))) {
rcu_read_unlock();
err = 0;
goto discard_locked;
}
rcu_read_unlock();
mp = get_mountpoint(dentry);
if (IS_ERR(mp)) {
err = PTR_ERR(mp);
goto discard_locked;
}
err = do_add_mount(mnt, mp, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
unlock_mount(mp);
if (unlikely(err))
goto discard;
mntput(m);
return 0;
discard_locked:
namespace_unlock();
inode_unlock(dentry->d_inode);
discard:
/* remove m from any expiration list it may be on */ /* remove m from any expiration list it may be on */
if (!list_empty(&mnt->mnt_expire)) { if (!list_empty(&mnt->mnt_expire)) {
namespace_lock(); namespace_lock();
......
...@@ -1046,8 +1046,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) ...@@ -1046,8 +1046,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
if (flags & O_CREAT) { if (flags & O_CREAT) {
op->intent |= LOOKUP_CREATE; op->intent |= LOOKUP_CREATE;
if (flags & O_EXCL) if (flags & O_EXCL) {
op->intent |= LOOKUP_EXCL; op->intent |= LOOKUP_EXCL;
flags |= O_NOFOLLOW;
}
} }
if (flags & O_DIRECTORY) if (flags & O_DIRECTORY)
......
...@@ -15,7 +15,7 @@ enum { MAX_NESTED_LINKS = 8 }; ...@@ -15,7 +15,7 @@ enum { MAX_NESTED_LINKS = 8 };
/* /*
* Type of the last component on LOOKUP_PARENT * Type of the last component on LOOKUP_PARENT
*/ */
enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
/* pathwalk mode */ /* pathwalk mode */
#define LOOKUP_FOLLOW 0x0001 /* follow links at the end */ #define LOOKUP_FOLLOW 0x0001 /* follow links at the end */
...@@ -23,6 +23,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -23,6 +23,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
#define LOOKUP_AUTOMOUNT 0x0004 /* force terminal automount */ #define LOOKUP_AUTOMOUNT 0x0004 /* force terminal automount */
#define LOOKUP_EMPTY 0x4000 /* accept empty path [user_... only] */ #define LOOKUP_EMPTY 0x4000 /* accept empty path [user_... only] */
#define LOOKUP_DOWN 0x8000 /* follow mounts in the starting point */ #define LOOKUP_DOWN 0x8000 /* follow mounts in the starting point */
#define LOOKUP_MOUNTPOINT 0x0080 /* follow mounts in the end */
#define LOOKUP_REVAL 0x0020 /* tell ->d_revalidate() to trust no cache */ #define LOOKUP_REVAL 0x0020 /* tell ->d_revalidate() to trust no cache */
#define LOOKUP_RCU 0x0040 /* RCU pathwalk mode; semi-internal */ #define LOOKUP_RCU 0x0040 /* RCU pathwalk mode; semi-internal */
...@@ -64,7 +65,6 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne ...@@ -64,7 +65,6 @@ extern struct dentry *kern_path_create(int, const char *, struct path *, unsigne
extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int); extern struct dentry *user_path_create(int, const char __user *, struct path *, unsigned int);
extern void done_path_create(struct path *, struct dentry *); extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int); extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment