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

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
...@@ -2824,63 +2824,19 @@ static int may_o_create(struct path *dir, struct dentry *dentry, umode_t mode) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment