Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fac7d191 authored by Al Viro's avatar Al Viro
Browse files

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: default avatarOleg Drokin <green@linuxhacker.ru>
Reviewed-and-Tested-by: default avatarJeff Layton <jlayton@poochiereds.net>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1a695a90
Loading
Loading
Loading
Loading
+4 −39
Original line number Diff line number Diff line
@@ -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 @@ static int do_last(struct nameidata *nd,
	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;

	}
	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 @@ static int do_last(struct nameidata *nd,
		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 @@ static int do_last(struct nameidata *nd,
	}
	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,