lookup_open(): lift the "fallback to !O_CREAT" logics from atomic_open()
authorAl Viro <viro@zeniv.linux.org.uk>
Wed, 27 Apr 2016 23:14:10 +0000 (19:14 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 2 May 2016 23:51:15 +0000 (19:51 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/namei.c

index 8d562a7a7e019ae107195311d11756990ef93d5b..4359d22f43f458310dd5050bcf59e78e8b18d100 100644 (file)
@@ -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;