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

Commit 9f1fafee authored by Al Viro's avatar Al Viro
Browse files

merge handle_reval_dot and nameidata_drop_rcu_last



new helper: complete_walk().  Done on successful completion
of walk, drops out of RCU mode, does d_revalidate of final
result if that hadn't been done already.

handle_reval_dot() and nameidata_drop_rcu_last() subsumed into
that one; callers converted to use of complete_walk().

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 19660af7
Loading
Loading
Loading
Loading
+40 −81
Original line number Diff line number Diff line
@@ -468,43 +468,6 @@ err_root:
	return -ECHILD;
}

/**
 * nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
 * @nd: nameidata pathwalk data to drop
 * Returns: 0 on success, -ECHILD on failure
 *
 * nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
 * nd->path should be the final element of the lookup, so nd->root is discarded.
 * Must be called from rcu-walk context.
 */
static int nameidata_drop_rcu_last(struct nameidata *nd)
{
	struct dentry *dentry = nd->path.dentry;

	BUG_ON(!(nd->flags & LOOKUP_RCU));
	nd->flags &= ~LOOKUP_RCU;
	if (!(nd->flags & LOOKUP_ROOT))
		nd->root.mnt = NULL;
	spin_lock(&dentry->d_lock);
	if (!__d_rcu_to_refcount(dentry, nd->seq))
		goto err_unlock;
	BUG_ON(nd->inode != dentry->d_inode);
	spin_unlock(&dentry->d_lock);

	mntget(nd->path.mnt);

	rcu_read_unlock();
	br_read_unlock(vfsmount_lock);

	return 0;

err_unlock:
	spin_unlock(&dentry->d_lock);
	rcu_read_unlock();
	br_read_unlock(vfsmount_lock);
	return -ECHILD;
}

/**
 * release_open_intent - free up open intent resources
 * @nd: pointer to nameidata
@@ -548,26 +511,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
	return dentry;
}

/*
 * handle_reval_path - force revalidation of a dentry
 *
 * In some situations the path walking code will trust dentries without
 * revalidating them. This causes problems for filesystems that depend on
 * d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
 * (which indicates that it's possible for the dentry to go stale), force
 * a d_revalidate call before proceeding.
/**
 * complete_walk - successful completion of path walk
 * @nd:  pointer nameidata
 *
 * Returns 0 if the revalidation was successful. If the revalidation fails,
 * either return the error returned by d_revalidate or -ESTALE if the
 * revalidation it just returned 0. If d_revalidate returns 0, we attempt to
 * invalidate the dentry. It's up to the caller to handle putting references
 * to the path if necessary.
 * If we had been in RCU mode, drop out of it and legitimize nd->path.
 * Revalidate the final result, unless we'd already done that during
 * the path walk or the filesystem doesn't ask for it.  Return 0 on
 * success, -error on failure.  In case of failure caller does not
 * need to drop nd->path.
 */
static inline int handle_reval_path(struct nameidata *nd)
static int complete_walk(struct nameidata *nd)
{
	struct dentry *dentry = nd->path.dentry;
	int status;

	if (nd->flags & LOOKUP_RCU) {
		nd->flags &= ~LOOKUP_RCU;
		if (!(nd->flags & LOOKUP_ROOT))
			nd->root.mnt = NULL;
		spin_lock(&dentry->d_lock);
		if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
			spin_unlock(&dentry->d_lock);
			rcu_read_unlock();
			br_read_unlock(vfsmount_lock);
			return -ECHILD;
		}
		BUG_ON(nd->inode != dentry->d_inode);
		spin_unlock(&dentry->d_lock);
		mntget(nd->path.mnt);
		rcu_read_unlock();
		br_read_unlock(vfsmount_lock);
	}

	if (likely(!(nd->flags & LOOKUP_JUMPED)))
		return 0;

@@ -585,6 +561,7 @@ static inline int handle_reval_path(struct nameidata *nd)
	if (!status)
		status = -ESTALE;

	path_put(&nd->path);
	return status;
}

@@ -1598,18 +1575,8 @@ static int path_lookupat(int dfd, const char *name,
		}
	}

	if (nd->flags & LOOKUP_RCU) {
		/* went all way through without dropping RCU */
		BUG_ON(err);
		if (nameidata_drop_rcu_last(nd))
			err = -ECHILD;
	}

	if (!err) {
		err = handle_reval_path(nd);
		if (err)
			path_put(&nd->path);
	}
	if (!err)
		err = complete_walk(nd);

	if (!err && nd->flags & LOOKUP_DIRECTORY) {
		if (!nd->inode->i_op->lookup) {
@@ -2075,13 +2042,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
			return ERR_PTR(error);
		/* fallthrough */
	case LAST_ROOT:
		if (nd->flags & LOOKUP_RCU) {
			if (nameidata_drop_rcu_last(nd))
				return ERR_PTR(-ECHILD);
		}
		error = handle_reval_path(nd);
		error = complete_walk(nd);
		if (error)
			goto exit;
			return ERR_PTR(error);
		audit_inode(pathname, nd->path.dentry);
		if (open_flag & O_CREAT) {
			error = -EISDIR;
@@ -2089,10 +2052,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
		}
		goto ok;
	case LAST_BIND:
		/* can't be RCU mode here */
		error = handle_reval_path(nd);
		error = complete_walk(nd);
		if (error)
			goto exit;
			return ERR_PTR(error);
		audit_inode(pathname, dir);
		goto ok;
	}
@@ -2111,10 +2073,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
		if (error) /* symlink */
			return NULL;
		/* sayonara */
		if (nd->flags & LOOKUP_RCU) {
			if (nameidata_drop_rcu_last(nd))
		error = complete_walk(nd);
		if (error)
			return ERR_PTR(-ECHILD);
		}

		error = -ENOTDIR;
		if (nd->flags & LOOKUP_DIRECTORY) {
@@ -2126,11 +2087,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
	}

	/* create side of things */

	if (nd->flags & LOOKUP_RCU) {
		if (nameidata_drop_rcu_last(nd))
			return ERR_PTR(-ECHILD);
	}
	error = complete_walk(nd);
	if (error)
		return ERR_PTR(error);

	audit_inode(pathname, dir);
	error = -EISDIR;