lookup_open(): lift the "fallback to !O_CREAT" logics from atomic_open()
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
b3d58eaffb
commit
1643b43fbd
146
fs/namei.c
146
fs/namei.c
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dir_inode->i_op->atomic_open) {
|
/*
|
||||||
return atomic_open(nd, dentry, path, file, op, got_write,
|
* Checking write permission is tricky, bacuse we don't know if we are
|
||||||
need_lookup, opened);
|
* 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 (need_lookup) {
|
if (dir_inode->i_op->atomic_open) {
|
||||||
BUG_ON(dentry->d_inode);
|
error = atomic_open(nd, dentry, path, file, op, open_flag,
|
||||||
|
mode, opened);
|
||||||
|
if (unlikely(error == -ENOENT) && create_error)
|
||||||
|
error = create_error;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
no_open:
|
||||||
|
if (need_lookup) {
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user