fix EOPENSTALE bug in do_last()
EOPENSTALE occuring at the last component of a trailing symlink ends up with do_last() retrying its lookup. After the symlink body has been discarded. The thing is, all this retry_lookup logics in there is not needed at all - the upper layers will do the right thing if we simply return that -EOPENSTALE as we would with any other error. Trying to microoptimize in do_last() is a lot of headache for no good reason. Cc: stable@vger.kernel.org # v4.2+ Tested-by: Oleg Drokin <green@linuxhacker.ru> Reviewed-and-Tested-by: Jeff Layton <jlayton@poochiereds.net> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
1a695a905c
commit
fac7d1917d
1 changed files with 4 additions and 39 deletions
43
fs/namei.c
43
fs/namei.c
|
@ -3166,9 +3166,7 @@ static int do_last(struct nameidata *nd,
|
|||
int acc_mode = op->acc_mode;
|
||||
unsigned seq;
|
||||
struct inode *inode;
|
||||
struct path save_parent = { .dentry = NULL, .mnt = NULL };
|
||||
struct path path;
|
||||
bool retried = false;
|
||||
int error;
|
||||
|
||||
nd->flags &= ~LOOKUP_PARENT;
|
||||
|
@ -3211,7 +3209,6 @@ static int do_last(struct nameidata *nd,
|
|||
return -EISDIR;
|
||||
}
|
||||
|
||||
retry_lookup:
|
||||
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
|
||||
error = mnt_want_write(nd->path.mnt);
|
||||
if (!error)
|
||||
|
@ -3292,23 +3289,14 @@ finish_lookup:
|
|||
if (unlikely(error))
|
||||
return error;
|
||||
|
||||
if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) {
|
||||
path_to_nameidata(&path, nd);
|
||||
} else {
|
||||
save_parent.dentry = nd->path.dentry;
|
||||
save_parent.mnt = mntget(path.mnt);
|
||||
nd->path.dentry = path.dentry;
|
||||
|
||||
}
|
||||
path_to_nameidata(&path, nd);
|
||||
nd->inode = inode;
|
||||
nd->seq = seq;
|
||||
/* Why this, you ask? _Now_ we might have grown LOOKUP_JUMPED... */
|
||||
finish_open:
|
||||
error = complete_walk(nd);
|
||||
if (error) {
|
||||
path_put(&save_parent);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
audit_inode(nd->name, nd->path.dentry, 0);
|
||||
error = -EISDIR;
|
||||
if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
|
||||
|
@ -3331,13 +3319,9 @@ finish_open_created:
|
|||
goto out;
|
||||
BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
|
||||
error = vfs_open(&nd->path, file, current_cred());
|
||||
if (!error) {
|
||||
*opened |= FILE_OPENED;
|
||||
} else {
|
||||
if (error == -EOPENSTALE)
|
||||
goto stale_open;
|
||||
if (error)
|
||||
goto out;
|
||||
}
|
||||
*opened |= FILE_OPENED;
|
||||
opened:
|
||||
error = open_check_o_direct(file);
|
||||
if (!error)
|
||||
|
@ -3353,26 +3337,7 @@ out:
|
|||
}
|
||||
if (got_write)
|
||||
mnt_drop_write(nd->path.mnt);
|
||||
path_put(&save_parent);
|
||||
return error;
|
||||
|
||||
stale_open:
|
||||
/* If no saved parent or already retried then can't retry */
|
||||
if (!save_parent.dentry || retried)
|
||||
goto out;
|
||||
|
||||
BUG_ON(save_parent.dentry != dir);
|
||||
path_put(&nd->path);
|
||||
nd->path = save_parent;
|
||||
nd->inode = dir->d_inode;
|
||||
save_parent.mnt = NULL;
|
||||
save_parent.dentry = NULL;
|
||||
if (got_write) {
|
||||
mnt_drop_write(nd->path.mnt);
|
||||
got_write = false;
|
||||
}
|
||||
retried = true;
|
||||
goto retry_lookup;
|
||||
}
|
||||
|
||||
static int do_tmpfile(struct nameidata *nd, unsigned flags,
|
||||
|
|
Loading…
Reference in a new issue