Commit f5beed75 authored by Al Viro's avatar Al Viro

name: shift nameidata down into user_path_walk()

that avoids having nameidata on stack during the calls of
->rmdir()/->unlink() and *two* of those during the calls
of ->rename().
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 6a9f40d6
...@@ -2211,9 +2211,13 @@ EXPORT_SYMBOL(user_path_at); ...@@ -2211,9 +2211,13 @@ EXPORT_SYMBOL(user_path_at);
* path-walking is complete. * path-walking is complete.
*/ */
static struct filename * static struct filename *
user_path_parent(int dfd, const char __user *path, struct nameidata *nd, user_path_parent(int dfd, const char __user *path,
struct path *parent,
struct qstr *last,
int *type,
unsigned int flags) unsigned int flags)
{ {
struct nameidata nd;
struct filename *s = getname(path); struct filename *s = getname(path);
int error; int error;
...@@ -2223,11 +2227,14 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd, ...@@ -2223,11 +2227,14 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
if (IS_ERR(s)) if (IS_ERR(s))
return s; return s;
error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd); error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, &nd);
if (error) { if (error) {
putname(s); putname(s);
return ERR_PTR(error); return ERR_PTR(error);
} }
*parent = nd.path;
*last = nd.last;
*type = nd.last_type;
return s; return s;
} }
...@@ -3630,14 +3637,17 @@ static long do_rmdir(int dfd, const char __user *pathname) ...@@ -3630,14 +3637,17 @@ static long do_rmdir(int dfd, const char __user *pathname)
int error = 0; int error = 0;
struct filename *name; struct filename *name;
struct dentry *dentry; struct dentry *dentry;
struct nameidata nd; struct path path;
struct qstr last;
int type;
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0;
retry: retry:
name = user_path_parent(dfd, pathname, &nd, lookup_flags); name = user_path_parent(dfd, pathname,
&path, &last, &type, lookup_flags);
if (IS_ERR(name)) if (IS_ERR(name))
return PTR_ERR(name); return PTR_ERR(name);
switch(nd.last_type) { switch (type) {
case LAST_DOTDOT: case LAST_DOTDOT:
error = -ENOTEMPTY; error = -ENOTEMPTY;
goto exit1; goto exit1;
...@@ -3649,13 +3659,12 @@ static long do_rmdir(int dfd, const char __user *pathname) ...@@ -3649,13 +3659,12 @@ static long do_rmdir(int dfd, const char __user *pathname)
goto exit1; goto exit1;
} }
nd.flags &= ~LOOKUP_PARENT; error = mnt_want_write(path.mnt);
error = mnt_want_write(nd.path.mnt);
if (error) if (error)
goto exit1; goto exit1;
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); dentry = __lookup_hash(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
goto exit2; goto exit2;
...@@ -3663,17 +3672,17 @@ static long do_rmdir(int dfd, const char __user *pathname) ...@@ -3663,17 +3672,17 @@ static long do_rmdir(int dfd, const char __user *pathname)
error = -ENOENT; error = -ENOENT;
goto exit3; goto exit3;
} }
error = security_path_rmdir(&nd.path, dentry); error = security_path_rmdir(&path, dentry);
if (error) if (error)
goto exit3; goto exit3;
error = vfs_rmdir(nd.path.dentry->d_inode, dentry); error = vfs_rmdir(path.dentry->d_inode, dentry);
exit3: exit3:
dput(dentry); dput(dentry);
exit2: exit2:
mutex_unlock(&nd.path.dentry->d_inode->i_mutex); mutex_unlock(&path.dentry->d_inode->i_mutex);
mnt_drop_write(nd.path.mnt); mnt_drop_write(path.mnt);
exit1: exit1:
path_put(&nd.path); path_put(&path);
putname(name); putname(name);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
...@@ -3756,43 +3765,45 @@ static long do_unlinkat(int dfd, const char __user *pathname) ...@@ -3756,43 +3765,45 @@ static long do_unlinkat(int dfd, const char __user *pathname)
int error; int error;
struct filename *name; struct filename *name;
struct dentry *dentry; struct dentry *dentry;
struct nameidata nd; struct path path;
struct qstr last;
int type;
struct inode *inode = NULL; struct inode *inode = NULL;
struct inode *delegated_inode = NULL; struct inode *delegated_inode = NULL;
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0;
retry: retry:
name = user_path_parent(dfd, pathname, &nd, lookup_flags); name = user_path_parent(dfd, pathname,
&path, &last, &type, lookup_flags);
if (IS_ERR(name)) if (IS_ERR(name))
return PTR_ERR(name); return PTR_ERR(name);
error = -EISDIR; error = -EISDIR;
if (nd.last_type != LAST_NORM) if (type != LAST_NORM)
goto exit1; goto exit1;
nd.flags &= ~LOOKUP_PARENT; error = mnt_want_write(path.mnt);
error = mnt_want_write(nd.path.mnt);
if (error) if (error)
goto exit1; goto exit1;
retry_deleg: retry_deleg:
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags); dentry = __lookup_hash(&last, path.dentry, lookup_flags);
error = PTR_ERR(dentry); error = PTR_ERR(dentry);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
/* Why not before? Because we want correct error value */ /* Why not before? Because we want correct error value */
if (nd.last.name[nd.last.len]) if (last.name[last.len])
goto slashes; goto slashes;
inode = dentry->d_inode; inode = dentry->d_inode;
if (d_is_negative(dentry)) if (d_is_negative(dentry))
goto slashes; goto slashes;
ihold(inode); ihold(inode);
error = security_path_unlink(&nd.path, dentry); error = security_path_unlink(&path, dentry);
if (error) if (error)
goto exit2; goto exit2;
error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode); error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
exit2: exit2:
dput(dentry); dput(dentry);
} }
mutex_unlock(&nd.path.dentry->d_inode->i_mutex); mutex_unlock(&path.dentry->d_inode->i_mutex);
if (inode) if (inode)
iput(inode); /* truncate the inode here */ iput(inode); /* truncate the inode here */
inode = NULL; inode = NULL;
...@@ -3801,9 +3812,9 @@ static long do_unlinkat(int dfd, const char __user *pathname) ...@@ -3801,9 +3812,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
} }
mnt_drop_write(nd.path.mnt); mnt_drop_write(path.mnt);
exit1: exit1:
path_put(&nd.path); path_put(&path);
putname(name); putname(name);
if (retry_estale(error, lookup_flags)) { if (retry_estale(error, lookup_flags)) {
lookup_flags |= LOOKUP_REVAL; lookup_flags |= LOOKUP_REVAL;
...@@ -4233,14 +4244,15 @@ EXPORT_SYMBOL(vfs_rename); ...@@ -4233,14 +4244,15 @@ EXPORT_SYMBOL(vfs_rename);
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
int, newdfd, const char __user *, newname, unsigned int, flags) int, newdfd, const char __user *, newname, unsigned int, flags)
{ {
struct dentry *old_dir, *new_dir;
struct dentry *old_dentry, *new_dentry; struct dentry *old_dentry, *new_dentry;
struct dentry *trap; struct dentry *trap;
struct nameidata oldnd, newnd; struct path old_path, new_path;
struct qstr old_last, new_last;
int old_type, new_type;
struct inode *delegated_inode = NULL; struct inode *delegated_inode = NULL;
struct filename *from; struct filename *from;
struct filename *to; struct filename *to;
unsigned int lookup_flags = 0; unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
bool should_retry = false; bool should_retry = false;
int error; int error;
...@@ -4254,47 +4266,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, ...@@ -4254,47 +4266,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD)) if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
return -EPERM; return -EPERM;
if (flags & RENAME_EXCHANGE)
target_flags = 0;
retry: retry:
from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags); from = user_path_parent(olddfd, oldname,
&old_path, &old_last, &old_type, lookup_flags);
if (IS_ERR(from)) { if (IS_ERR(from)) {
error = PTR_ERR(from); error = PTR_ERR(from);
goto exit; goto exit;
} }
to = user_path_parent(newdfd, newname, &newnd, lookup_flags); to = user_path_parent(newdfd, newname,
&new_path, &new_last, &new_type, lookup_flags);
if (IS_ERR(to)) { if (IS_ERR(to)) {
error = PTR_ERR(to); error = PTR_ERR(to);
goto exit1; goto exit1;
} }
error = -EXDEV; error = -EXDEV;
if (oldnd.path.mnt != newnd.path.mnt) if (old_path.mnt != new_path.mnt)
goto exit2; goto exit2;
old_dir = oldnd.path.dentry;
error = -EBUSY; error = -EBUSY;
if (oldnd.last_type != LAST_NORM) if (old_type != LAST_NORM)
goto exit2; goto exit2;
new_dir = newnd.path.dentry;
if (flags & RENAME_NOREPLACE) if (flags & RENAME_NOREPLACE)
error = -EEXIST; error = -EEXIST;
if (newnd.last_type != LAST_NORM) if (new_type != LAST_NORM)
goto exit2; goto exit2;
error = mnt_want_write(oldnd.path.mnt); error = mnt_want_write(old_path.mnt);
if (error) if (error)
goto exit2; goto exit2;
oldnd.flags &= ~LOOKUP_PARENT;
newnd.flags &= ~LOOKUP_PARENT;
if (!(flags & RENAME_EXCHANGE))
newnd.flags |= LOOKUP_RENAME_TARGET;
retry_deleg: retry_deleg:
trap = lock_rename(new_dir, old_dir); trap = lock_rename(new_path.dentry, old_path.dentry);
old_dentry = __lookup_hash(&oldnd.last, oldnd.path.dentry, oldnd.flags); old_dentry = __lookup_hash(&old_last, old_path.dentry, lookup_flags);
error = PTR_ERR(old_dentry); error = PTR_ERR(old_dentry);
if (IS_ERR(old_dentry)) if (IS_ERR(old_dentry))
goto exit3; goto exit3;
...@@ -4302,7 +4312,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, ...@@ -4302,7 +4312,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
error = -ENOENT; error = -ENOENT;
if (d_is_negative(old_dentry)) if (d_is_negative(old_dentry))
goto exit4; goto exit4;
new_dentry = __lookup_hash(&newnd.last, newnd.path.dentry, newnd.flags); new_dentry = __lookup_hash(&new_last, new_path.dentry, lookup_flags | target_flags);
error = PTR_ERR(new_dentry); error = PTR_ERR(new_dentry);
if (IS_ERR(new_dentry)) if (IS_ERR(new_dentry))
goto exit4; goto exit4;
...@@ -4316,16 +4326,16 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, ...@@ -4316,16 +4326,16 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
if (!d_is_dir(new_dentry)) { if (!d_is_dir(new_dentry)) {
error = -ENOTDIR; error = -ENOTDIR;
if (newnd.last.name[newnd.last.len]) if (new_last.name[new_last.len])
goto exit5; goto exit5;
} }
} }
/* unless the source is a directory trailing slashes give -ENOTDIR */ /* unless the source is a directory trailing slashes give -ENOTDIR */
if (!d_is_dir(old_dentry)) { if (!d_is_dir(old_dentry)) {
error = -ENOTDIR; error = -ENOTDIR;
if (oldnd.last.name[oldnd.last.len]) if (old_last.name[old_last.len])
goto exit5; goto exit5;
if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len]) if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
goto exit5; goto exit5;
} }
/* source should not be ancestor of target */ /* source should not be ancestor of target */
...@@ -4338,32 +4348,32 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname, ...@@ -4338,32 +4348,32 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
if (new_dentry == trap) if (new_dentry == trap)
goto exit5; goto exit5;
error = security_path_rename(&oldnd.path, old_dentry, error = security_path_rename(&old_path, old_dentry,
&newnd.path, new_dentry, flags); &new_path, new_dentry, flags);
if (error) if (error)
goto exit5; goto exit5;
error = vfs_rename(old_dir->d_inode, old_dentry, error = vfs_rename(old_path.dentry->d_inode, old_dentry,
new_dir->d_inode, new_dentry, new_path.dentry->d_inode, new_dentry,
&delegated_inode, flags); &delegated_inode, flags);
exit5: exit5:
dput(new_dentry); dput(new_dentry);
exit4: exit4:
dput(old_dentry); dput(old_dentry);
exit3: exit3:
unlock_rename(new_dir, old_dir); unlock_rename(new_path.dentry, old_path.dentry);
if (delegated_inode) { if (delegated_inode) {
error = break_deleg_wait(&delegated_inode); error = break_deleg_wait(&delegated_inode);
if (!error) if (!error)
goto retry_deleg; goto retry_deleg;
} }
mnt_drop_write(oldnd.path.mnt); mnt_drop_write(old_path.mnt);
exit2: exit2:
if (retry_estale(error, lookup_flags)) if (retry_estale(error, lookup_flags))
should_retry = true; should_retry = true;
path_put(&newnd.path); path_put(&new_path);
putname(to); putname(to);
exit1: exit1:
path_put(&oldnd.path); path_put(&old_path);
putname(from); putname(from);
if (should_retry) { if (should_retry) {
should_retry = false; should_retry = false;
......
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