|
@@ -1725,30 +1725,35 @@ static int pick_link(struct nameidata *nd, struct path *link,
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
+enum {WALK_FOLLOW = 1, WALK_MORE = 2};
|
|
|
+
|
|
|
/*
|
|
|
* Do we need to follow links? We _really_ want to be able
|
|
|
* to do this check without having to look at inode->i_op,
|
|
|
* so we keep a cache of "no, this doesn't need follow_link"
|
|
|
* for the common case.
|
|
|
*/
|
|
|
-static inline int should_follow_link(struct nameidata *nd, struct path *link,
|
|
|
- int follow,
|
|
|
- struct inode *inode, unsigned seq)
|
|
|
+static inline int step_into(struct nameidata *nd, struct path *path,
|
|
|
+ int flags, struct inode *inode, unsigned seq)
|
|
|
{
|
|
|
- if (likely(!d_is_symlink(link->dentry)))
|
|
|
- return 0;
|
|
|
- if (!follow)
|
|
|
+ if (!(flags & WALK_MORE) && nd->depth)
|
|
|
+ put_link(nd);
|
|
|
+ if (likely(!d_is_symlink(path->dentry)) ||
|
|
|
+ !(flags & WALK_FOLLOW || nd->flags & LOOKUP_FOLLOW)) {
|
|
|
+ /* not a symlink or should not follow */
|
|
|
+ path_to_nameidata(path, nd);
|
|
|
+ nd->inode = inode;
|
|
|
+ nd->seq = seq;
|
|
|
return 0;
|
|
|
+ }
|
|
|
/* make sure that d_is_symlink above matches inode */
|
|
|
if (nd->flags & LOOKUP_RCU) {
|
|
|
- if (read_seqcount_retry(&link->dentry->d_seq, seq))
|
|
|
+ if (read_seqcount_retry(&path->dentry->d_seq, seq))
|
|
|
return -ECHILD;
|
|
|
}
|
|
|
- return pick_link(nd, link, inode, seq);
|
|
|
+ return pick_link(nd, path, inode, seq);
|
|
|
}
|
|
|
|
|
|
-enum {WALK_GET = 1, WALK_PUT = 2};
|
|
|
-
|
|
|
static int walk_component(struct nameidata *nd, int flags)
|
|
|
{
|
|
|
struct path path;
|
|
@@ -1762,7 +1767,7 @@ static int walk_component(struct nameidata *nd, int flags)
|
|
|
*/
|
|
|
if (unlikely(nd->last_type != LAST_NORM)) {
|
|
|
err = handle_dots(nd, nd->last_type);
|
|
|
- if (flags & WALK_PUT)
|
|
|
+ if (!(flags & WALK_MORE) && nd->depth)
|
|
|
put_link(nd);
|
|
|
return err;
|
|
|
}
|
|
@@ -1789,15 +1794,7 @@ static int walk_component(struct nameidata *nd, int flags)
|
|
|
inode = d_backing_inode(path.dentry);
|
|
|
}
|
|
|
|
|
|
- if (flags & WALK_PUT)
|
|
|
- put_link(nd);
|
|
|
- err = should_follow_link(nd, &path, flags & WALK_GET, inode, seq);
|
|
|
- if (unlikely(err))
|
|
|
- return err;
|
|
|
- path_to_nameidata(&path, nd);
|
|
|
- nd->inode = inode;
|
|
|
- nd->seq = seq;
|
|
|
- return 0;
|
|
|
+ return step_into(nd, &path, flags, inode, seq);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -2104,9 +2101,10 @@ OK:
|
|
|
if (!name)
|
|
|
return 0;
|
|
|
/* last component of nested symlink */
|
|
|
- err = walk_component(nd, WALK_GET | WALK_PUT);
|
|
|
+ err = walk_component(nd, WALK_FOLLOW);
|
|
|
} else {
|
|
|
- err = walk_component(nd, WALK_GET);
|
|
|
+ /* not the last component */
|
|
|
+ err = walk_component(nd, WALK_FOLLOW | WALK_MORE);
|
|
|
}
|
|
|
if (err < 0)
|
|
|
return err;
|
|
@@ -2248,12 +2246,7 @@ static inline int lookup_last(struct nameidata *nd)
|
|
|
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
|
|
|
|
|
|
nd->flags &= ~LOOKUP_PARENT;
|
|
|
- return walk_component(nd,
|
|
|
- nd->flags & LOOKUP_FOLLOW
|
|
|
- ? nd->depth
|
|
|
- ? WALK_PUT | WALK_GET
|
|
|
- : WALK_GET
|
|
|
- : 0);
|
|
|
+ return walk_component(nd, 0);
|
|
|
}
|
|
|
|
|
|
/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
|
|
@@ -2558,28 +2551,9 @@ int user_path_at_empty(int dfd, const char __user *name, unsigned flags,
|
|
|
}
|
|
|
EXPORT_SYMBOL(user_path_at_empty);
|
|
|
|
|
|
-/*
|
|
|
- * NB: most callers don't do anything directly with the reference to the
|
|
|
- * to struct filename, but the nd->last pointer points into the name string
|
|
|
- * allocated by getname. So we must hold the reference to it until all
|
|
|
- * path-walking is complete.
|
|
|
- */
|
|
|
-static inline struct filename *
|
|
|
-user_path_parent(int dfd, const char __user *path,
|
|
|
- struct path *parent,
|
|
|
- struct qstr *last,
|
|
|
- int *type,
|
|
|
- unsigned int flags)
|
|
|
-{
|
|
|
- /* only LOOKUP_REVAL is allowed in extra flags */
|
|
|
- return filename_parentat(dfd, getname(path), flags & LOOKUP_REVAL,
|
|
|
- parent, last, type);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* mountpoint_last - look up last component for umount
|
|
|
* @nd: pathwalk nameidata - currently pointing at parent directory of "last"
|
|
|
- * @path: pointer to container for result
|
|
|
*
|
|
|
* This is a special lookup_last function just for umount. In this case, we
|
|
|
* need to resolve the path without doing any revalidation.
|
|
@@ -2592,23 +2566,20 @@ user_path_parent(int dfd, const char __user *path,
|
|
|
*
|
|
|
* Returns:
|
|
|
* -error: if there was an error during lookup. This includes -ENOENT if the
|
|
|
- * lookup found a negative dentry. The nd->path reference will also be
|
|
|
- * put in this case.
|
|
|
+ * lookup found a negative dentry.
|
|
|
*
|
|
|
- * 0: if we successfully resolved nd->path and found it to not to be a
|
|
|
- * symlink that needs to be followed. "path" will also be populated.
|
|
|
- * The nd->path reference will also be put.
|
|
|
+ * 0: if we successfully resolved nd->last and found it to not to be a
|
|
|
+ * symlink that needs to be followed.
|
|
|
*
|
|
|
* 1: if we successfully resolved nd->last and found it to be a symlink
|
|
|
- * that needs to be followed. "path" will be populated with the path
|
|
|
- * to the link, and nd->path will *not* be put.
|
|
|
+ * that needs to be followed.
|
|
|
*/
|
|
|
static int
|
|
|
-mountpoint_last(struct nameidata *nd, struct path *path)
|
|
|
+mountpoint_last(struct nameidata *nd)
|
|
|
{
|
|
|
int error = 0;
|
|
|
- struct dentry *dentry;
|
|
|
struct dentry *dir = nd->path.dentry;
|
|
|
+ struct path path;
|
|
|
|
|
|
/* If we're in rcuwalk, drop out of it to handle last component */
|
|
|
if (nd->flags & LOOKUP_RCU) {
|
|
@@ -2622,37 +2593,28 @@ mountpoint_last(struct nameidata *nd, struct path *path)
|
|
|
error = handle_dots(nd, nd->last_type);
|
|
|
if (error)
|
|
|
return error;
|
|
|
- dentry = dget(nd->path.dentry);
|
|
|
+ path.dentry = dget(nd->path.dentry);
|
|
|
} else {
|
|
|
- dentry = d_lookup(dir, &nd->last);
|
|
|
- if (!dentry) {
|
|
|
+ path.dentry = d_lookup(dir, &nd->last);
|
|
|
+ if (!path.dentry) {
|
|
|
/*
|
|
|
* No cached dentry. Mounted dentries are pinned in the
|
|
|
* cache, so that means that this dentry is probably
|
|
|
* a symlink or the path doesn't actually point
|
|
|
* to a mounted dentry.
|
|
|
*/
|
|
|
- dentry = lookup_slow(&nd->last, dir,
|
|
|
+ path.dentry = lookup_slow(&nd->last, dir,
|
|
|
nd->flags | LOOKUP_NO_REVAL);
|
|
|
- if (IS_ERR(dentry))
|
|
|
- return PTR_ERR(dentry);
|
|
|
+ if (IS_ERR(path.dentry))
|
|
|
+ return PTR_ERR(path.dentry);
|
|
|
}
|
|
|
}
|
|
|
- if (d_is_negative(dentry)) {
|
|
|
- dput(dentry);
|
|
|
+ if (d_is_negative(path.dentry)) {
|
|
|
+ dput(path.dentry);
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
- if (nd->depth)
|
|
|
- put_link(nd);
|
|
|
- path->dentry = dentry;
|
|
|
- path->mnt = nd->path.mnt;
|
|
|
- error = should_follow_link(nd, path, nd->flags & LOOKUP_FOLLOW,
|
|
|
- d_backing_inode(dentry), 0);
|
|
|
- if (unlikely(error))
|
|
|
- return error;
|
|
|
- mntget(path->mnt);
|
|
|
- follow_mount(path);
|
|
|
- return 0;
|
|
|
+ path.mnt = nd->path.mnt;
|
|
|
+ return step_into(nd, &path, 0, d_backing_inode(path.dentry), 0);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -2672,13 +2634,19 @@ path_mountpoint(struct nameidata *nd, unsigned flags, struct path *path)
|
|
|
if (IS_ERR(s))
|
|
|
return PTR_ERR(s);
|
|
|
while (!(err = link_path_walk(s, nd)) &&
|
|
|
- (err = mountpoint_last(nd, path)) > 0) {
|
|
|
+ (err = mountpoint_last(nd)) > 0) {
|
|
|
s = trailing_symlink(nd);
|
|
|
if (IS_ERR(s)) {
|
|
|
err = PTR_ERR(s);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+ if (!err) {
|
|
|
+ *path = nd->path;
|
|
|
+ nd->path.mnt = NULL;
|
|
|
+ nd->path.dentry = NULL;
|
|
|
+ follow_mount(path);
|
|
|
+ }
|
|
|
terminate_walk(nd);
|
|
|
return err;
|
|
|
}
|
|
@@ -3335,18 +3303,11 @@ static int do_last(struct nameidata *nd,
|
|
|
seq = 0; /* out of RCU mode, so the value doesn't matter */
|
|
|
inode = d_backing_inode(path.dentry);
|
|
|
finish_lookup:
|
|
|
- if (nd->depth)
|
|
|
- put_link(nd);
|
|
|
- error = should_follow_link(nd, &path, nd->flags & LOOKUP_FOLLOW,
|
|
|
- inode, seq);
|
|
|
+ error = step_into(nd, &path, 0, inode, seq);
|
|
|
if (unlikely(error))
|
|
|
return error;
|
|
|
-
|
|
|
- path_to_nameidata(&path, nd);
|
|
|
- nd->inode = inode;
|
|
|
- nd->seq = seq;
|
|
|
- /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
|
|
|
finish_open:
|
|
|
+ /* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
|
|
|
error = complete_walk(nd);
|
|
|
if (error)
|
|
|
return error;
|
|
@@ -3861,8 +3822,8 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|
|
int type;
|
|
|
unsigned int lookup_flags = 0;
|
|
|
retry:
|
|
|
- name = user_path_parent(dfd, pathname,
|
|
|
- &path, &last, &type, lookup_flags);
|
|
|
+ name = filename_parentat(dfd, getname(pathname), lookup_flags,
|
|
|
+ &path, &last, &type);
|
|
|
if (IS_ERR(name))
|
|
|
return PTR_ERR(name);
|
|
|
|
|
@@ -3991,8 +3952,8 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|
|
struct inode *delegated_inode = NULL;
|
|
|
unsigned int lookup_flags = 0;
|
|
|
retry:
|
|
|
- name = user_path_parent(dfd, pathname,
|
|
|
- &path, &last, &type, lookup_flags);
|
|
|
+ name = filename_parentat(dfd, getname(pathname), lookup_flags,
|
|
|
+ &path, &last, &type);
|
|
|
if (IS_ERR(name))
|
|
|
return PTR_ERR(name);
|
|
|
|
|
@@ -4491,15 +4452,15 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
|
|
target_flags = 0;
|
|
|
|
|
|
retry:
|
|
|
- from = user_path_parent(olddfd, oldname,
|
|
|
- &old_path, &old_last, &old_type, lookup_flags);
|
|
|
+ from = filename_parentat(olddfd, getname(oldname), lookup_flags,
|
|
|
+ &old_path, &old_last, &old_type);
|
|
|
if (IS_ERR(from)) {
|
|
|
error = PTR_ERR(from);
|
|
|
goto exit;
|
|
|
}
|
|
|
|
|
|
- to = user_path_parent(newdfd, newname,
|
|
|
- &new_path, &new_last, &new_type, lookup_flags);
|
|
|
+ to = filename_parentat(newdfd, getname(newname), lookup_flags,
|
|
|
+ &new_path, &new_last, &new_type);
|
|
|
if (IS_ERR(to)) {
|
|
|
error = PTR_ERR(to);
|
|
|
goto exit1;
|