forked from Minki/linux
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 ...
This commit is contained in:
commit
9c577491b9
@ -404,11 +404,8 @@ that is the "next" component in the pathname.
|
||||
``int last_type``
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT``, ``LAST_DOTDOT``, or
|
||||
``LAST_BIND``. The ``last`` field is only valid if the type is
|
||||
``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.
|
||||
This is one of ``LAST_NORM``, ``LAST_ROOT``, ``LAST_DOT`` or ``LAST_DOTDOT``.
|
||||
The ``last`` field is only valid if the type is ``LAST_NORM``.
|
||||
|
||||
``struct path root``
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -186,7 +186,7 @@ static int find_autofs_mount(const char *pathname,
|
||||
struct path path;
|
||||
int err;
|
||||
|
||||
err = kern_path_mountpoint(AT_FDCWD, pathname, &path, 0);
|
||||
err = kern_path(pathname, LOOKUP_MOUNTPOINT, &path);
|
||||
if (err)
|
||||
return err;
|
||||
err = -ENOENT;
|
||||
@ -519,8 +519,8 @@ static int autofs_dev_ioctl_ismountpoint(struct file *fp,
|
||||
|
||||
if (!fp || param->ioctlfd == -1) {
|
||||
if (autofs_type_any(type))
|
||||
err = kern_path_mountpoint(AT_FDCWD,
|
||||
name, &path, LOOKUP_FOLLOW);
|
||||
err = kern_path(name, LOOKUP_FOLLOW | LOOKUP_MOUNTPOINT,
|
||||
&path);
|
||||
else
|
||||
err = find_autofs_mount(name, &path,
|
||||
test_by_type, &type);
|
||||
|
@ -60,7 +60,6 @@ extern int finish_clean_context(struct fs_context *fc);
|
||||
*/
|
||||
extern int filename_lookup(int dfd, struct filename *name, unsigned flags,
|
||||
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 *,
|
||||
const char *, unsigned int, struct path *);
|
||||
long do_mknodat(int dfd, const char __user *filename, umode_t mode,
|
||||
|
1502
fs/namei.c
1502
fs/namei.c
File diff suppressed because it is too large
Load Diff
@ -1669,7 +1669,7 @@ int ksys_umount(char __user *name, int flags)
|
||||
struct path path;
|
||||
struct mount *mnt;
|
||||
int retval;
|
||||
int lookup_flags = 0;
|
||||
int lookup_flags = LOOKUP_MOUNTPOINT;
|
||||
|
||||
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
|
||||
return -EINVAL;
|
||||
@ -1680,7 +1680,7 @@ int ksys_umount(char __user *name, int flags)
|
||||
if (!(flags & UMOUNT_NOFOLLOW))
|
||||
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)
|
||||
goto out;
|
||||
mnt = real_mount(path.mnt);
|
||||
@ -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
|
||||
*/
|
||||
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;
|
||||
int err;
|
||||
struct mount *parent = real_mount(path->mnt);
|
||||
|
||||
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))) {
|
||||
/* that's acceptable only for automounts done in private ns */
|
||||
if (!(mnt_flags & MNT_SHRINKABLE))
|
||||
goto unlock;
|
||||
return -EINVAL;
|
||||
/* ... and for those we'd better have mountpoint still alive */
|
||||
if (!parent->mnt_ns)
|
||||
goto unlock;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Refuse the same filesystem on the same mount point */
|
||||
err = -EBUSY;
|
||||
if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
|
||||
path->mnt->mnt_root == path->dentry)
|
||||
goto unlock;
|
||||
return -EBUSY;
|
||||
|
||||
err = -EINVAL;
|
||||
if (d_is_symlink(newmnt->mnt.mnt_root))
|
||||
goto unlock;
|
||||
return -EINVAL;
|
||||
|
||||
newmnt->mnt.mnt_flags = mnt_flags;
|
||||
err = graft_tree(newmnt, parent, mp);
|
||||
|
||||
unlock:
|
||||
unlock_mount(mp);
|
||||
return err;
|
||||
return graft_tree(newmnt, parent, mp);
|
||||
}
|
||||
|
||||
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,
|
||||
unsigned int mnt_flags)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
struct mountpoint *mp;
|
||||
struct super_block *sb = fc->root->d_sb;
|
||||
int error;
|
||||
|
||||
@ -2768,7 +2756,13 @@ static int do_new_mount_fc(struct fs_context *fc, struct path *mountpoint,
|
||||
|
||||
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)
|
||||
mntput(mnt);
|
||||
return error;
|
||||
@ -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)
|
||||
{
|
||||
struct mount *mnt = real_mount(m);
|
||||
struct dentry *dentry = path->dentry;
|
||||
struct mountpoint *mp;
|
||||
struct mount *mnt;
|
||||
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
|
||||
* expired before we get a chance to add it
|
||||
*/
|
||||
BUG_ON(mnt_get_count(mnt) < 2);
|
||||
|
||||
if (m->mnt_sb == path->mnt->mnt_sb &&
|
||||
m->mnt_root == path->dentry) {
|
||||
m->mnt_root == dentry) {
|
||||
err = -ELOOP;
|
||||
goto fail;
|
||||
goto discard;
|
||||
}
|
||||
|
||||
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
|
||||
if (!err)
|
||||
return 0;
|
||||
fail:
|
||||
/*
|
||||
* we don't want to use lock_mount() - in this case finding something
|
||||
* that overmounts our mountpoint to be means "quitely drop what we've
|
||||
* 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 */
|
||||
if (!list_empty(&mnt->mnt_expire)) {
|
||||
namespace_lock();
|
||||
|
@ -1046,8 +1046,10 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
|
||||
|
||||
if (flags & O_CREAT) {
|
||||
op->intent |= LOOKUP_CREATE;
|
||||
if (flags & O_EXCL)
|
||||
if (flags & O_EXCL) {
|
||||
op->intent |= LOOKUP_EXCL;
|
||||
flags |= O_NOFOLLOW;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & O_DIRECTORY)
|
||||
|
@ -15,7 +15,7 @@ enum { MAX_NESTED_LINKS = 8 };
|
||||
/*
|
||||
* 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 */
|
||||
#define LOOKUP_FOLLOW 0x0001 /* follow links at the end */
|
||||
@ -23,6 +23,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
|
||||
#define LOOKUP_AUTOMOUNT 0x0004 /* force terminal automount */
|
||||
#define LOOKUP_EMPTY 0x4000 /* accept empty path [user_... only] */
|
||||
#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_RCU 0x0040 /* RCU pathwalk mode; semi-internal */
|
||||
@ -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 void done_path_create(struct path *, struct dentry *);
|
||||
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 *lookup_one_len(const char *, struct dentry *, int);
|
||||
|
Loading…
Reference in New Issue
Block a user