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

Commit 1643b43f authored by Al Viro's avatar Al Viro
Browse files

lookup_open(): lift the "fallback to !O_CREAT" logics from atomic_open()



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent b3d58eaf
Loading
Loading
Loading
Loading
+55 −89
Original line number Original line Diff line number Diff line
@@ -2824,63 +2824,19 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode)
static int atomic_open(struct nameidata *nd, struct dentry *dentry,
static int atomic_open(struct nameidata *nd, struct dentry *dentry,
			struct path *path, struct file *file,
			struct path *path, struct file *file,
			const struct open_flags *op,
			const struct open_flags *op,
			bool got_write, bool need_lookup,
			int open_flag, umode_t mode,
			int *opened)
			int *opened)
{
{
	struct inode *dir =  nd->path.dentry->d_inode;
	struct inode *dir =  nd->path.dentry->d_inode;
	unsigned open_flag = op->open_flag;
	umode_t mode;
	int error;
	int error;
	int acc_mode;
	int acc_mode;
	int create_error = 0;
	struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
	struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
	bool excl;
	bool excl;


	BUG_ON(dentry->d_inode);

	mode = op->mode;
	if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
		mode &= ~current_umask();

	excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
	excl = (open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT);
	if (excl)
	if (excl)
		open_flag &= ~O_TRUNC;
		open_flag &= ~O_TRUNC;


	/*
	 * Checking write permission is tricky, bacuse we don't know if we are
	 * going to actually need it: O_CREAT opens should work as long as the
	 * file exists.  But checking existence breaks atomicity.  The trick is
	 * to check access and if not granted clear O_CREAT from the flags.
	 *
	 * Another problem is returing the "right" error value (e.g. for an
	 * O_EXCL open we want to return EEXIST not EROFS).
	 */
	if (open_flag & O_CREAT) {
		if (unlikely(!got_write)) {
			create_error = -EROFS;
			if (open_flag & (O_EXCL | O_TRUNC)) {
				/* Fall back and fail with the right error */
				goto no_open;
			}
			/* No side effects, safe to clear O_CREAT */
			open_flag &= ~O_CREAT;
		} else {
			create_error = may_o_create(&nd->path, dentry, mode);
			if (create_error) {
				if (open_flag & O_EXCL)
					goto no_open;
				open_flag &= ~O_CREAT;
			}
		}
	} else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) &&
		   unlikely(!got_write)) {
		/*
		 * No O_CREATE -> atomicity not a requirement -> fall
		 * back to lookup + open
		 */
		goto no_open;
	}

	if (nd->flags & LOOKUP_DIRECTORY)
	if (nd->flags & LOOKUP_DIRECTORY)
		open_flag |= O_DIRECTORY;
		open_flag |= O_DIRECTORY;


@@ -2889,11 +2845,8 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
	error = dir->i_op->atomic_open(dir, dentry, file,
	error = dir->i_op->atomic_open(dir, dentry, file,
				       open_to_namei_flags(open_flag),
				       open_to_namei_flags(open_flag),
				       mode, opened);
				       mode, opened);
	if (error < 0) {
	if (error < 0)
		if (create_error && error == -ENOENT)
			error = create_error;
		goto out;
		goto out;
	}


	if (error) {	/* returned 1, that is */
	if (error) {	/* returned 1, that is */
		if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
		if (WARN_ON(file->f_path.dentry == DENTRY_NOT_SET)) {
@@ -2906,7 +2859,9 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
		}
		}
		if (*opened & FILE_CREATED)
		if (*opened & FILE_CREATED)
			fsnotify_create(dir, dentry);
			fsnotify_create(dir, dentry);
		goto looked_up;
		path->dentry = dentry;
		path->mnt = nd->path.mnt;
		return 1;
	}
	}


	/*
	/*
@@ -2925,21 +2880,6 @@ static int atomic_open(struct nameidata *nd, struct dentry *dentry,
out:
out:
	dput(dentry);
	dput(dentry);
	return error;
	return error;

no_open:
	if (need_lookup) {
		dentry = lookup_real(dir, dentry, nd->flags);
		if (IS_ERR(dentry))
			return PTR_ERR(dentry);
	}
looked_up:
	if (create_error && !dentry->d_inode) {
		error = create_error;
		goto out;
	}
	path->dentry = dentry;
	path->mnt = nd->path.mnt;
	return 1;
}
}


/*
/*
@@ -2967,9 +2907,11 @@ static int lookup_open(struct nameidata *nd, struct path *path,
{
{
	struct dentry *dir = nd->path.dentry;
	struct dentry *dir = nd->path.dentry;
	struct inode *dir_inode = dir->d_inode;
	struct inode *dir_inode = dir->d_inode;
	int open_flag = op->open_flag;
	struct dentry *dentry;
	struct dentry *dentry;
	int error;
	int error, create_error = 0;
	bool need_lookup = false;
	bool need_lookup = false;
	umode_t mode = op->mode;


	if (unlikely(IS_DEADDIR(dir_inode)))
	if (unlikely(IS_DEADDIR(dir_inode)))
		return -ENOENT;
		return -ENOENT;
@@ -2989,50 +2931,74 @@ static int lookup_open(struct nameidata *nd, struct path *path,
		goto out_no_open;
		goto out_no_open;
	}
	}


	/*
	 * Checking write permission is tricky, bacuse we don't know if we are
	 * going to actually need it: O_CREAT opens should work as long as the
	 * file exists.  But checking existence breaks atomicity.  The trick is
	 * to check access and if not granted clear O_CREAT from the flags.
	 *
	 * Another problem is returing the "right" error value (e.g. for an
	 * O_EXCL open we want to return EEXIST not EROFS).
	 */
	if (open_flag & O_CREAT) {
		if (!IS_POSIXACL(dir->d_inode))
			mode &= ~current_umask();
		if (unlikely(!got_write)) {
			create_error = -EROFS;
			open_flag &= ~O_CREAT;
			if (open_flag & (O_EXCL | O_TRUNC))
				goto no_open;
			/* No side effects, safe to clear O_CREAT */
		} else {
			create_error = may_o_create(&nd->path, dentry, mode);
			if (create_error) {
				open_flag &= ~O_CREAT;
				if (open_flag & O_EXCL)
					goto no_open;
			}
		}
	} else if ((open_flag & (O_TRUNC|O_WRONLY|O_RDWR)) &&
		   unlikely(!got_write)) {
		/*
		 * No O_CREATE -> atomicity not a requirement -> fall
		 * back to lookup + open
		 */
		goto no_open;
	}

	if (dir_inode->i_op->atomic_open) {
	if (dir_inode->i_op->atomic_open) {
		return atomic_open(nd, dentry, path, file, op, got_write,
		error = atomic_open(nd, dentry, path, file, op, open_flag,
				   need_lookup, opened);
				    mode, opened);
		if (unlikely(error == -ENOENT) && create_error)
			error = create_error;
		return error;
	}
	}


no_open:
	if (need_lookup) {
	if (need_lookup) {
		BUG_ON(dentry->d_inode);

		dentry = lookup_real(dir_inode, dentry, nd->flags);
		dentry = lookup_real(dir_inode, dentry, nd->flags);
		if (IS_ERR(dentry))
		if (IS_ERR(dentry))
			return PTR_ERR(dentry);
			return PTR_ERR(dentry);
	}
	}


	/* Negative dentry, just create the file */
	/* Negative dentry, just create the file */
	if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
	if (!dentry->d_inode && (open_flag & O_CREAT)) {
		umode_t mode = op->mode;
		if (!IS_POSIXACL(dir->d_inode))
			mode &= ~current_umask();
		/*
		 * This write is needed to ensure that a
		 * rw->ro 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 finish_open().
		 */
		if (!got_write) {
			error = -EROFS;
			goto out_dput;
		}
		*opened |= FILE_CREATED;
		*opened |= FILE_CREATED;
		audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
		audit_inode_child(dir_inode, dentry, AUDIT_TYPE_CHILD_CREATE);
		error = may_o_create(&nd->path, dentry, mode);
		if (error)
			goto out_dput;
		if (!dir_inode->i_op->create) {
		if (!dir_inode->i_op->create) {
			error = -EACCES;
			error = -EACCES;
			goto out_dput;
			goto out_dput;
		}
		}
		error = dir_inode->i_op->create(dir_inode, dentry, mode,
		error = dir_inode->i_op->create(dir_inode, dentry, mode,
						op->open_flag & O_EXCL);
						open_flag & O_EXCL);
		if (error)
		if (error)
			goto out_dput;
			goto out_dput;
		fsnotify_create(dir_inode, dentry);
		fsnotify_create(dir_inode, dentry);
	}
	}
	if (unlikely(create_error) && !dentry->d_inode) {
		error = create_error;
		goto out_dput;
	}
out_no_open:
out_no_open:
	path->dentry = dentry;
	path->dentry = dentry;
	path->mnt = nd->path.mnt;
	path->mnt = nd->path.mnt;