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

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

gut do_filp_open() a bit more (do_last separation)



Brute-force separation of stuff reachable from do_last: with
the exception of do_link:; just take all that crap to a helper
function as-is and have it tell the caller if it has to go
to do_link.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 648fa861
Loading
Loading
Loading
Loading
+103 −68
Original line number Diff line number Diff line
@@ -1645,6 +1645,104 @@ exit:
	return ERR_PTR(error);
}

static struct file *do_last(struct nameidata *nd, struct path *path,
			    int open_flag, int flag, int acc_mode,
			    int mode, const char *pathname,
			    struct dentry *dir, int *is_link)
{
	struct file *filp;
	int error;

	*is_link = 0;

	error = PTR_ERR(path->dentry);
	if (IS_ERR(path->dentry)) {
		mutex_unlock(&dir->d_inode->i_mutex);
		goto exit;
	}

	if (IS_ERR(nd->intent.open.file)) {
		error = PTR_ERR(nd->intent.open.file);
		goto exit_mutex_unlock;
	}

	/* Negative dentry, just create the file */
	if (!path->dentry->d_inode) {
		/*
		 * This write is needed to ensure that a
		 * ro->rw transition does not occur between
		 * the time when the file is created and when
		 * a permanent write count is taken through
		 * the 'struct file' in nameidata_to_filp().
		 */
		error = mnt_want_write(nd->path.mnt);
		if (error)
			goto exit_mutex_unlock;
		error = __open_namei_create(nd, path, open_flag, mode);
		if (error) {
			mnt_drop_write(nd->path.mnt);
			goto exit;
		}
		filp = nameidata_to_filp(nd);
		mnt_drop_write(nd->path.mnt);
		if (nd->root.mnt)
			path_put(&nd->root);
		if (!IS_ERR(filp)) {
			error = ima_file_check(filp, acc_mode);
			if (error) {
				fput(filp);
				filp = ERR_PTR(error);
			}
		}
		return filp;
	}

	/*
	 * It already exists.
	 */
	mutex_unlock(&dir->d_inode->i_mutex);
	audit_inode(pathname, path->dentry);

	error = -EEXIST;
	if (flag & O_EXCL)
		goto exit_dput;

	if (__follow_mount(path)) {
		error = -ELOOP;
		if (flag & O_NOFOLLOW)
			goto exit_dput;
	}

	error = -ENOENT;
	if (!path->dentry->d_inode)
		goto exit_dput;
	if (path->dentry->d_inode->i_op->follow_link) {
		*is_link = 1;
		return NULL;
	}

	path_to_nameidata(path, nd);
	error = -EISDIR;
	if (S_ISDIR(path->dentry->d_inode->i_mode))
		goto exit;
	filp = finish_open(nd, open_flag, flag, acc_mode);
	if (nd->root.mnt)
		path_put(&nd->root);
	return filp;

exit_mutex_unlock:
	mutex_unlock(&dir->d_inode->i_mutex);
exit_dput:
	path_put_conditional(path, nd);
exit:
	if (!IS_ERR(nd->intent.open.file))
		release_open_intent(nd);
	if (nd->root.mnt)
		path_put(&nd->root);
	path_put(&nd->path);
	return ERR_PTR(error);
}

/*
 * Note that the low bits of the passed in "open_flag"
 * are not the same as in the local variable "flag". See
@@ -1661,6 +1759,7 @@ struct file *do_filp_open(int dfd, const char *pathname,
	int count = 0;
	int flag = open_to_namei_flags(open_flag);
	int force_reval = 0;
	int is_link;

	/*
	 * O_SYNC is implemented as __O_SYNC|O_DSYNC.  As many places only
@@ -1754,82 +1853,18 @@ reval:
	path.mnt = nd.path.mnt;

do_last:
	error = PTR_ERR(path.dentry);
	if (IS_ERR(path.dentry)) {
		mutex_unlock(&dir->d_inode->i_mutex);
		goto exit;
	}

	if (IS_ERR(nd.intent.open.file)) {
		error = PTR_ERR(nd.intent.open.file);
		goto exit_mutex_unlock;
	}

	/* Negative dentry, just create the file */
	if (!path.dentry->d_inode) {
		/*
		 * This write is needed to ensure that a
		 * ro->rw transition does not occur between
		 * the time when the file is created and when
		 * a permanent write count is taken through
		 * the 'struct file' in nameidata_to_filp().
		 */
		error = mnt_want_write(nd.path.mnt);
		if (error)
			goto exit_mutex_unlock;
		error = __open_namei_create(&nd, &path, open_flag, mode);
		if (error) {
			mnt_drop_write(nd.path.mnt);
			goto exit;
		}
		filp = nameidata_to_filp(&nd);
		mnt_drop_write(nd.path.mnt);
		if (nd.root.mnt)
			path_put(&nd.root);
		if (!IS_ERR(filp)) {
			error = ima_file_check(filp, acc_mode);
			if (error) {
				fput(filp);
				filp = ERR_PTR(error);
			}
		}
		return filp;
	}

	/*
	 * It already exists.
	 */
	mutex_unlock(&dir->d_inode->i_mutex);
	audit_inode(pathname, path.dentry);

	error = -EEXIST;
	if (flag & O_EXCL)
		goto exit_dput;

	if (__follow_mount(&path)) {
		error = -ELOOP;
		if (flag & O_NOFOLLOW)
			goto exit_dput;
	}

	error = -ENOENT;
	if (!path.dentry->d_inode)
		goto exit_dput;
	if (path.dentry->d_inode->i_op->follow_link)
	filp = do_last(&nd, &path, open_flag, flag, acc_mode, mode,
		       pathname, dir, &is_link);
	if (is_link)
		goto do_link;
	return filp;

	path_to_nameidata(&path, &nd);
	error = -EISDIR;
	if (S_ISDIR(path.dentry->d_inode->i_mode))
		goto exit;
ok:
	filp = finish_open(&nd, open_flag, flag, acc_mode);
	if (nd.root.mnt)
		path_put(&nd.root);
	return filp;

exit_mutex_unlock:
	mutex_unlock(&dir->d_inode->i_mutex);
exit_dput:
	path_put_conditional(&path, &nd);
exit: