summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/namei.c144
1 files changed, 55 insertions, 89 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 8d562a7a7e01..4359d22f43f4 100644
--- a/fs/namei.c
+++ b/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,
struct path *path, struct file *file,
const struct open_flags *op,
- bool got_write, bool need_lookup,
+ int open_flag, umode_t mode,
int *opened)
{
struct inode *dir = nd->path.dentry->d_inode;
- unsigned open_flag = op->open_flag;
- umode_t mode;
int error;
int acc_mode;
- int create_error = 0;
struct dentry *const DENTRY_NOT_SET = (void *) -1UL;
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);
if (excl)
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)
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,
open_to_namei_flags(open_flag),
mode, opened);
- if (error < 0) {
- if (create_error && error == -ENOENT)
- error = create_error;
+ if (error < 0)
goto out;
- }
if (error) { /* returned 1, that is */
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)
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:
dput(dentry);
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 inode *dir_inode = dir->d_inode;
+ int open_flag = op->open_flag;
struct dentry *dentry;
- int error;
+ int error, create_error = 0;
bool need_lookup = false;
+ umode_t mode = op->mode;
if (unlikely(IS_DEADDIR(dir_inode)))
return -ENOENT;
@@ -2989,50 +2931,74 @@ static int lookup_open(struct nameidata *nd, struct path *path,
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) {
- return atomic_open(nd, dentry, path, file, op, got_write,
- need_lookup, opened);
+ 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) {
- BUG_ON(dentry->d_inode);
-
dentry = lookup_real(dir_inode, dentry, nd->flags);
if (IS_ERR(dentry))
return PTR_ERR(dentry);
}
/* Negative dentry, just create the file */
- if (!dentry->d_inode && (op->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;
- }
+ if (!dentry->d_inode && (open_flag & O_CREAT)) {
*opened |= FILE_CREATED;
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) {
error = -EACCES;
goto out_dput;
}
error = dir_inode->i_op->create(dir_inode, dentry, mode,
- op->open_flag & O_EXCL);
+ open_flag & O_EXCL);
if (error)
goto out_dput;
fsnotify_create(dir_inode, dentry);
}
+ if (unlikely(create_error) && !dentry->d_inode) {
+ error = create_error;
+ goto out_dput;
+ }
out_no_open:
path->dentry = dentry;
path->mnt = nd->path.mnt;