mirror of
https://github.com/torvalds/linux
synced 2024-11-02 18:48:59 +00:00
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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
6a9f40d610
commit
f5beed755b
1 changed files with 67 additions and 57 deletions
124
fs/namei.c
124
fs/namei.c
|
@ -2211,9 +2211,13 @@ EXPORT_SYMBOL(user_path_at);
|
|||
* path-walking is complete.
|
||||
*/
|
||||
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)
|
||||
{
|
||||
struct nameidata nd;
|
||||
struct filename *s = getname(path);
|
||||
int error;
|
||||
|
||||
|
@ -2223,11 +2227,14 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
|
|||
if (IS_ERR(s))
|
||||
return s;
|
||||
|
||||
error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, nd);
|
||||
error = filename_lookup(dfd, s, flags | LOOKUP_PARENT, &nd);
|
||||
if (error) {
|
||||
putname(s);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
*parent = nd.path;
|
||||
*last = nd.last;
|
||||
*type = nd.last_type;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
@ -3630,14 +3637,17 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|||
int error = 0;
|
||||
struct filename *name;
|
||||
struct dentry *dentry;
|
||||
struct nameidata nd;
|
||||
struct path path;
|
||||
struct qstr last;
|
||||
int type;
|
||||
unsigned int lookup_flags = 0;
|
||||
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))
|
||||
return PTR_ERR(name);
|
||||
|
||||
switch(nd.last_type) {
|
||||
switch (type) {
|
||||
case LAST_DOTDOT:
|
||||
error = -ENOTEMPTY;
|
||||
goto exit1;
|
||||
|
@ -3649,13 +3659,12 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|||
goto exit1;
|
||||
}
|
||||
|
||||
nd.flags &= ~LOOKUP_PARENT;
|
||||
error = mnt_want_write(nd.path.mnt);
|
||||
error = mnt_want_write(path.mnt);
|
||||
if (error)
|
||||
goto exit1;
|
||||
|
||||
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags);
|
||||
mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&last, path.dentry, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (IS_ERR(dentry))
|
||||
goto exit2;
|
||||
|
@ -3663,17 +3672,17 @@ static long do_rmdir(int dfd, const char __user *pathname)
|
|||
error = -ENOENT;
|
||||
goto exit3;
|
||||
}
|
||||
error = security_path_rmdir(&nd.path, dentry);
|
||||
error = security_path_rmdir(&path, dentry);
|
||||
if (error)
|
||||
goto exit3;
|
||||
error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
|
||||
error = vfs_rmdir(path.dentry->d_inode, dentry);
|
||||
exit3:
|
||||
dput(dentry);
|
||||
exit2:
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
mnt_drop_write(nd.path.mnt);
|
||||
mutex_unlock(&path.dentry->d_inode->i_mutex);
|
||||
mnt_drop_write(path.mnt);
|
||||
exit1:
|
||||
path_put(&nd.path);
|
||||
path_put(&path);
|
||||
putname(name);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
|
@ -3756,43 +3765,45 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|||
int error;
|
||||
struct filename *name;
|
||||
struct dentry *dentry;
|
||||
struct nameidata nd;
|
||||
struct path path;
|
||||
struct qstr last;
|
||||
int type;
|
||||
struct inode *inode = NULL;
|
||||
struct inode *delegated_inode = NULL;
|
||||
unsigned int lookup_flags = 0;
|
||||
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))
|
||||
return PTR_ERR(name);
|
||||
|
||||
error = -EISDIR;
|
||||
if (nd.last_type != LAST_NORM)
|
||||
if (type != LAST_NORM)
|
||||
goto exit1;
|
||||
|
||||
nd.flags &= ~LOOKUP_PARENT;
|
||||
error = mnt_want_write(nd.path.mnt);
|
||||
error = mnt_want_write(path.mnt);
|
||||
if (error)
|
||||
goto exit1;
|
||||
retry_deleg:
|
||||
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&nd.last, nd.path.dentry, nd.flags);
|
||||
mutex_lock_nested(&path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
|
||||
dentry = __lookup_hash(&last, path.dentry, lookup_flags);
|
||||
error = PTR_ERR(dentry);
|
||||
if (!IS_ERR(dentry)) {
|
||||
/* Why not before? Because we want correct error value */
|
||||
if (nd.last.name[nd.last.len])
|
||||
if (last.name[last.len])
|
||||
goto slashes;
|
||||
inode = dentry->d_inode;
|
||||
if (d_is_negative(dentry))
|
||||
goto slashes;
|
||||
ihold(inode);
|
||||
error = security_path_unlink(&nd.path, dentry);
|
||||
error = security_path_unlink(&path, dentry);
|
||||
if (error)
|
||||
goto exit2;
|
||||
error = vfs_unlink(nd.path.dentry->d_inode, dentry, &delegated_inode);
|
||||
error = vfs_unlink(path.dentry->d_inode, dentry, &delegated_inode);
|
||||
exit2:
|
||||
dput(dentry);
|
||||
}
|
||||
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
|
||||
mutex_unlock(&path.dentry->d_inode->i_mutex);
|
||||
if (inode)
|
||||
iput(inode); /* truncate the inode here */
|
||||
inode = NULL;
|
||||
|
@ -3801,9 +3812,9 @@ static long do_unlinkat(int dfd, const char __user *pathname)
|
|||
if (!error)
|
||||
goto retry_deleg;
|
||||
}
|
||||
mnt_drop_write(nd.path.mnt);
|
||||
mnt_drop_write(path.mnt);
|
||||
exit1:
|
||||
path_put(&nd.path);
|
||||
path_put(&path);
|
||||
putname(name);
|
||||
if (retry_estale(error, lookup_flags)) {
|
||||
lookup_flags |= LOOKUP_REVAL;
|
||||
|
@ -4233,14 +4244,15 @@ EXPORT_SYMBOL(vfs_rename);
|
|||
SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
||||
int, newdfd, const char __user *, newname, unsigned int, flags)
|
||||
{
|
||||
struct dentry *old_dir, *new_dir;
|
||||
struct dentry *old_dentry, *new_dentry;
|
||||
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 filename *from;
|
||||
struct filename *to;
|
||||
unsigned int lookup_flags = 0;
|
||||
unsigned int lookup_flags = 0, target_flags = LOOKUP_RENAME_TARGET;
|
||||
bool should_retry = false;
|
||||
int error;
|
||||
|
||||
|
@ -4254,47 +4266,45 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
|||
if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
|
||||
return -EPERM;
|
||||
|
||||
if (flags & RENAME_EXCHANGE)
|
||||
target_flags = 0;
|
||||
|
||||
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)) {
|
||||
error = PTR_ERR(from);
|
||||
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)) {
|
||||
error = PTR_ERR(to);
|
||||
goto exit1;
|
||||
}
|
||||
|
||||
error = -EXDEV;
|
||||
if (oldnd.path.mnt != newnd.path.mnt)
|
||||
if (old_path.mnt != new_path.mnt)
|
||||
goto exit2;
|
||||
|
||||
old_dir = oldnd.path.dentry;
|
||||
error = -EBUSY;
|
||||
if (oldnd.last_type != LAST_NORM)
|
||||
if (old_type != LAST_NORM)
|
||||
goto exit2;
|
||||
|
||||
new_dir = newnd.path.dentry;
|
||||
if (flags & RENAME_NOREPLACE)
|
||||
error = -EEXIST;
|
||||
if (newnd.last_type != LAST_NORM)
|
||||
if (new_type != LAST_NORM)
|
||||
goto exit2;
|
||||
|
||||
error = mnt_want_write(oldnd.path.mnt);
|
||||
error = mnt_want_write(old_path.mnt);
|
||||
if (error)
|
||||
goto exit2;
|
||||
|
||||
oldnd.flags &= ~LOOKUP_PARENT;
|
||||
newnd.flags &= ~LOOKUP_PARENT;
|
||||
if (!(flags & RENAME_EXCHANGE))
|
||||
newnd.flags |= LOOKUP_RENAME_TARGET;
|
||||
|
||||
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);
|
||||
if (IS_ERR(old_dentry))
|
||||
goto exit3;
|
||||
|
@ -4302,7 +4312,7 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
|||
error = -ENOENT;
|
||||
if (d_is_negative(old_dentry))
|
||||
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);
|
||||
if (IS_ERR(new_dentry))
|
||||
goto exit4;
|
||||
|
@ -4316,16 +4326,16 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
|||
|
||||
if (!d_is_dir(new_dentry)) {
|
||||
error = -ENOTDIR;
|
||||
if (newnd.last.name[newnd.last.len])
|
||||
if (new_last.name[new_last.len])
|
||||
goto exit5;
|
||||
}
|
||||
}
|
||||
/* unless the source is a directory trailing slashes give -ENOTDIR */
|
||||
if (!d_is_dir(old_dentry)) {
|
||||
error = -ENOTDIR;
|
||||
if (oldnd.last.name[oldnd.last.len])
|
||||
if (old_last.name[old_last.len])
|
||||
goto exit5;
|
||||
if (!(flags & RENAME_EXCHANGE) && newnd.last.name[newnd.last.len])
|
||||
if (!(flags & RENAME_EXCHANGE) && new_last.name[new_last.len])
|
||||
goto exit5;
|
||||
}
|
||||
/* source should not be ancestor of target */
|
||||
|
@ -4338,32 +4348,32 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
|
|||
if (new_dentry == trap)
|
||||
goto exit5;
|
||||
|
||||
error = security_path_rename(&oldnd.path, old_dentry,
|
||||
&newnd.path, new_dentry, flags);
|
||||
error = security_path_rename(&old_path, old_dentry,
|
||||
&new_path, new_dentry, flags);
|
||||
if (error)
|
||||
goto exit5;
|
||||
error = vfs_rename(old_dir->d_inode, old_dentry,
|
||||
new_dir->d_inode, new_dentry,
|
||||
error = vfs_rename(old_path.dentry->d_inode, old_dentry,
|
||||
new_path.dentry->d_inode, new_dentry,
|
||||
&delegated_inode, flags);
|
||||
exit5:
|
||||
dput(new_dentry);
|
||||
exit4:
|
||||
dput(old_dentry);
|
||||
exit3:
|
||||
unlock_rename(new_dir, old_dir);
|
||||
unlock_rename(new_path.dentry, old_path.dentry);
|
||||
if (delegated_inode) {
|
||||
error = break_deleg_wait(&delegated_inode);
|
||||
if (!error)
|
||||
goto retry_deleg;
|
||||
}
|
||||
mnt_drop_write(oldnd.path.mnt);
|
||||
mnt_drop_write(old_path.mnt);
|
||||
exit2:
|
||||
if (retry_estale(error, lookup_flags))
|
||||
should_retry = true;
|
||||
path_put(&newnd.path);
|
||||
path_put(&new_path);
|
||||
putname(to);
|
||||
exit1:
|
||||
path_put(&oldnd.path);
|
||||
path_put(&old_path);
|
||||
putname(from);
|
||||
if (should_retry) {
|
||||
should_retry = false;
|
||||
|
|
Loading…
Reference in a new issue