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

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

sanitize path_walk() mess



New helper: path_lookupat().  Basically, what do_path_lookup() boils to
modulo -ECHILD/-ESTALE handler.  path_walk* family is gone; vfs_path_lookup()
is using link_path_walk() directly, do_path_lookup() and do_filp_open()
are using path_lookupat().

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 52094c8a
Loading
Loading
Loading
Loading
+56 −92
Original line number Diff line number Diff line
@@ -1520,59 +1520,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
	return err;
}

static inline int path_walk_rcu(const char *name, struct nameidata *nd)
{
	current->total_link_count = 0;

	return link_path_walk(name, nd);
}

static inline int path_walk_simple(const char *name, struct nameidata *nd)
{
	current->total_link_count = 0;

	return link_path_walk(name, nd);
}

static int path_walk(const char *name, struct nameidata *nd)
{
	struct path save = nd->path;
	int result;

	current->total_link_count = 0;

	/* make sure the stuff we saved doesn't go away */
	path_get(&save);

	result = link_path_walk(name, nd);
	if (result == -ESTALE) {
		/* nd->path had been dropped */
		current->total_link_count = 0;
		nd->path = save;
		nd->inode = save.dentry->d_inode;
		path_get(&nd->path);
		nd->flags |= LOOKUP_REVAL;
		result = link_path_walk(name, nd);
	}

	path_put(&save);

	return result;
}

static void path_finish_rcu(struct nameidata *nd)
{
	if (nd->flags & LOOKUP_RCU) {
		/* RCU dangling. Cancel it. */
		nd->flags &= ~LOOKUP_RCU;
		nd->root.mnt = NULL;
		rcu_read_unlock();
		br_read_unlock(vfsmount_lock);
	}
	if (nd->file)
		fput(nd->file);
}

static int path_init_rcu(int dfd, const char *name, unsigned int flags, struct nameidata *nd)
{
	int retval = 0;
@@ -1697,7 +1644,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
}

/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
static int do_path_lookup(int dfd, const char *name,
static int path_lookupat(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	int retval;
@@ -1716,37 +1663,52 @@ static int do_path_lookup(int dfd, const char *name,
	 * be handled by restarting a traditional ref-walk (which will always
	 * be able to complete).
	 */
	if (flags & LOOKUP_RCU)
		retval = path_init_rcu(dfd, name, flags, nd);
	else
		retval = path_init(dfd, name, flags, nd);

	if (unlikely(retval))
		return retval;
	retval = path_walk_rcu(name, nd);
	path_finish_rcu(nd);
	if (nd->root.mnt) {
		path_put(&nd->root);

	current->total_link_count = 0;
	retval = link_path_walk(name, nd);

	if (nd->flags & LOOKUP_RCU) {
		/* RCU dangling. Cancel it. */
		nd->flags &= ~LOOKUP_RCU;
		nd->root.mnt = NULL;
		rcu_read_unlock();
		br_read_unlock(vfsmount_lock);
	}

	if (nd->file) {
		fput(nd->file);
		nd->file = NULL;
	}

	if (unlikely(retval == -ECHILD || retval == -ESTALE)) {
		/* slower, locked walk */
		if (retval == -ESTALE)
			flags |= LOOKUP_REVAL;
		retval = path_init(dfd, name, flags, nd);
		if (unlikely(retval))
			return retval;
		retval = path_walk(name, nd);
	if (nd->root.mnt) {
		path_put(&nd->root);
		nd->root.mnt = NULL;
	}
	return retval;
}

static int do_path_lookup(int dfd, const char *name,
				unsigned int flags, struct nameidata *nd)
{
	int retval = path_lookupat(dfd, name, flags | LOOKUP_RCU, nd);
	if (unlikely(retval == -ECHILD))
		retval = path_lookupat(dfd, name, flags, nd);
	if (unlikely(retval == -ESTALE))
		retval = path_lookupat(dfd, name, flags | LOOKUP_REVAL, nd);

	if (likely(!retval)) {
		if (unlikely(!audit_dummy_context())) {
			if (nd->path.dentry && nd->inode)
				audit_inode(name, nd->path.dentry);
		}
	}

	return retval;
}

@@ -1776,7 +1738,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
		    const char *name, unsigned int flags,
		    struct nameidata *nd)
{
	int retval;
	int result;

	/* same as do_path_lookup */
	nd->last_type = LAST_ROOT;
@@ -1790,15 +1752,27 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
	path_get(&nd->root);
	nd->inode = nd->path.dentry->d_inode;

	retval = path_walk(name, nd);
	if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
	current->total_link_count = 0;

	result = link_path_walk(name, nd);
	if (result == -ESTALE) {
		/* nd->path had been dropped */
		current->total_link_count = 0;
		nd->path.dentry = dentry;
		nd->path.mnt = mnt;
		nd->inode = dentry->d_inode;
		path_get(&nd->path);
		nd->flags |= LOOKUP_REVAL;
		result = link_path_walk(name, nd);
	}
	if (unlikely(!result && !audit_dummy_context() && nd->path.dentry &&
				nd->inode))
		audit_inode(name, nd->path.dentry);

	path_put(&nd->root);
	nd->root.mnt = NULL;

	return retval;
	return result;
}

static struct dentry *__lookup_hash(struct qstr *name,
@@ -2483,24 +2457,14 @@ struct file *do_filp_open(int dfd, const char *pathname,

creat:
	/* OK, have to create the file. Find the parent. */
	error = path_init_rcu(dfd, pathname,
			LOOKUP_PARENT | (flags & LOOKUP_REVAL), &nd);
	if (error)
		goto out_filp;
	error = path_walk_rcu(pathname, &nd);
	path_finish_rcu(&nd);
	if (unlikely(error == -ECHILD || error == -ESTALE)) {
		/* slower, locked walk */
		if (error == -ESTALE) {
	error = path_lookupat(dfd, pathname, LOOKUP_PARENT | LOOKUP_RCU, &nd);
	if (unlikely(error == -ECHILD))
		error = path_lookupat(dfd, pathname, LOOKUP_PARENT, &nd);
	if (unlikely(error == -ESTALE)) {
reval:
		flags |= LOOKUP_REVAL;
		}
		error = path_init(dfd, pathname,
				LOOKUP_PARENT | (flags & LOOKUP_REVAL), &nd);
		if (error)
			goto out_filp;

		error = path_walk_simple(pathname, &nd);
		error = path_lookupat(dfd, pathname,
				LOOKUP_PARENT | LOOKUP_REVAL, &nd);
	}
	if (unlikely(error))
		goto out_filp;