vfs: add lookup_open()
[platform/adaptation/renesas_rcar/renesas_kernel.git] / fs / namei.c
index 90210b4..fad7117 100644 (file)
@@ -605,7 +605,7 @@ static inline void path_to_nameidata(const struct path *path,
 static inline void put_link(struct nameidata *nd, struct path *link, void *cookie)
 {
        struct inode *inode = link->dentry->d_inode;
-       if (!IS_ERR(cookie) && inode->i_op->put_link)
+       if (inode->i_op->put_link)
                inode->i_op->put_link(link->dentry, nd, cookie);
        path_put(link);
 }
@@ -613,19 +613,19 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki
 static __always_inline int
 follow_link(struct path *link, struct nameidata *nd, void **p)
 {
-       int error;
        struct dentry *dentry = link->dentry;
+       int error;
+       char *s;
 
        BUG_ON(nd->flags & LOOKUP_RCU);
 
        if (link->mnt == nd->path.mnt)
                mntget(link->mnt);
 
-       if (unlikely(current->total_link_count >= 40)) {
-               *p = ERR_PTR(-ELOOP); /* no ->put_link(), please */
-               path_put(&nd->path);
-               return -ELOOP;
-       }
+       error = -ELOOP;
+       if (unlikely(current->total_link_count >= 40))
+               goto out_put_nd_path;
+
        cond_resched();
        current->total_link_count++;
 
@@ -633,30 +633,37 @@ follow_link(struct path *link, struct nameidata *nd, void **p)
        nd_set_link(nd, NULL);
 
        error = security_inode_follow_link(link->dentry, nd);
-       if (error) {
-               *p = ERR_PTR(error); /* no ->put_link(), please */
-               path_put(&nd->path);
-               return error;
-       }
+       if (error)
+               goto out_put_nd_path;
 
        nd->last_type = LAST_BIND;
        *p = dentry->d_inode->i_op->follow_link(dentry, nd);
        error = PTR_ERR(*p);
-       if (!IS_ERR(*p)) {
-               char *s = nd_get_link(nd);
-               error = 0;
-               if (s)
-                       error = __vfs_follow_link(nd, s);
-               else if (nd->last_type == LAST_BIND) {
-                       nd->flags |= LOOKUP_JUMPED;
-                       nd->inode = nd->path.dentry->d_inode;
-                       if (nd->inode->i_op->follow_link) {
-                               /* stepped on a _really_ weird one */
-                               path_put(&nd->path);
-                               error = -ELOOP;
-                       }
+       if (IS_ERR(*p))
+               goto out_put_link;
+
+       error = 0;
+       s = nd_get_link(nd);
+       if (s) {
+               error = __vfs_follow_link(nd, s);
+       } else if (nd->last_type == LAST_BIND) {
+               nd->flags |= LOOKUP_JUMPED;
+               nd->inode = nd->path.dentry->d_inode;
+               if (nd->inode->i_op->follow_link) {
+                       /* stepped on a _really_ weird one */
+                       path_put(&nd->path);
+                       error = -ELOOP;
                }
        }
+       if (unlikely(error))
+               put_link(nd, link, *p);
+
+       return error;
+
+out_put_nd_path:
+       path_put(&nd->path);
+out_put_link:
+       path_put(link);
        return error;
 }
 
@@ -1383,9 +1390,10 @@ static inline int nested_symlink(struct path *path, struct nameidata *nd)
                void *cookie;
 
                res = follow_link(&link, nd, &cookie);
-               if (!res)
-                       res = walk_component(nd, path, &nd->last,
-                                            nd->last_type, LOOKUP_FOLLOW);
+               if (res)
+                       break;
+               res = walk_component(nd, path, &nd->last,
+                                    nd->last_type, LOOKUP_FOLLOW);
                put_link(nd, &link, cookie);
        } while (res > 0);
 
@@ -1777,8 +1785,9 @@ static int path_lookupat(int dfd, const char *name,
                        struct path link = path;
                        nd->flags |= LOOKUP_PARENT;
                        err = follow_link(&link, nd, &cookie);
-                       if (!err)
-                               err = lookup_last(nd, &path);
+                       if (err)
+                               break;
+                       err = lookup_last(nd, &path);
                        put_link(nd, &link, cookie);
                }
        }
@@ -2188,20 +2197,76 @@ static inline int open_to_namei_flags(int flag)
 }
 
 /*
+ * Lookup, maybe create and open the last component
+ *
+ * Must be called with i_mutex held on parent.
+ *
+ * Returns open file or NULL on success, error otherwise.  NULL means no open
+ * was performed, only lookup.
+ */
+static struct file *lookup_open(struct nameidata *nd, struct path *path,
+                               const struct open_flags *op,
+                               int *want_write, bool *created)
+{
+       struct dentry *dir = nd->path.dentry;
+       struct dentry *dentry;
+       int error;
+
+       *created = false;
+       dentry = lookup_hash(nd);
+       if (IS_ERR(dentry))
+               return ERR_CAST(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 nameidata_to_filp().
+                */
+               error = mnt_want_write(nd->path.mnt);
+               if (error)
+                       goto out_dput;
+               *want_write = 1;
+               *created = true;
+               error = security_path_mknod(&nd->path, dentry, mode, 0);
+               if (error)
+                       goto out_dput;
+               error = vfs_create(dir->d_inode, dentry, mode, nd);
+               if (error)
+                       goto out_dput;
+       }
+       path->dentry = dentry;
+       path->mnt = nd->path.mnt;
+       return NULL;
+
+out_dput:
+       dput(dentry);
+       return ERR_PTR(error);
+}
+
+/*
  * Handle the last step of open()
  */
 static struct file *do_last(struct nameidata *nd, struct path *path,
                            const struct open_flags *op, const char *pathname)
 {
        struct dentry *dir = nd->path.dentry;
-       struct dentry *dentry;
        int open_flag = op->open_flag;
        int will_truncate = open_flag & O_TRUNC;
        int want_write = 0;
        int acc_mode = op->acc_mode;
        struct file *filp;
        struct inode *inode;
+       bool created;
        int symlink_ok = 0;
+       struct path save_parent = { .dentry = NULL, .mnt = NULL };
+       bool retried = false;
        int error;
 
        nd->flags &= ~LOOKUP_PARENT;
@@ -2239,112 +2304,51 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                        symlink_ok = 1;
                /* we _can_ be in RCU mode here */
                error = lookup_fast(nd, &nd->last, path, &inode);
-               if (unlikely(error)) {
-                       if (error < 0)
-                               goto exit;
+               if (likely(!error))
+                       goto finish_lookup;
 
-                       error = lookup_slow(nd, &nd->last, path);
-                       if (error < 0)
-                               goto exit;
-
-                       inode = path->dentry->d_inode;
-               }
-               error = -ENOENT;
-               if (!inode) {
-                       path_to_nameidata(path, nd);
+               if (error < 0)
                        goto exit;
-               }
 
-               if (should_follow_link(inode, !symlink_ok)) {
-                       if (nd->flags & LOOKUP_RCU) {
-                               if (unlikely(unlazy_walk(nd, path->dentry))) {
-                                       error = -ECHILD;
-                                       goto exit;
-                               }
-                       }
-                       BUG_ON(inode != path->dentry->d_inode);
-                       return NULL;
-               }
-               path_to_nameidata(path, nd);
-               nd->inode = inode;
-
-               /* sayonara */
+               BUG_ON(nd->inode != dir->d_inode);
+       } else {
+               /* create side of things */
+               /*
+                * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED
+                * has been cleared when we got to the last component we are
+                * about to look up
+                */
                error = complete_walk(nd);
                if (error)
                        return ERR_PTR(error);
 
-               error = -ENOTDIR;
-               if (nd->flags & LOOKUP_DIRECTORY) {
-                       if (!nd->inode->i_op->lookup)
-                               goto exit;
-               }
-               audit_inode(pathname, nd->path.dentry);
-               goto ok;
+               audit_inode(pathname, dir);
+               error = -EISDIR;
+               /* trailing slashes? */
+               if (nd->last.name[nd->last.len])
+                       goto exit;
        }
 
-       /* create side of things */
-       /*
-        * This will *only* deal with leaving RCU mode - LOOKUP_JUMPED has been
-        * cleared when we got to the last component we are about to look up
-        */
-       error = complete_walk(nd);
-       if (error)
-               return ERR_PTR(error);
-
-       audit_inode(pathname, dir);
-       error = -EISDIR;
-       /* trailing slashes? */
-       if (nd->last.name[nd->last.len])
-               goto exit;
-
+retry_lookup:
        mutex_lock(&dir->d_inode->i_mutex);
+       filp = lookup_open(nd, path, op, &want_write, &created);
+       mutex_unlock(&dir->d_inode->i_mutex);
 
-       dentry = lookup_hash(nd);
-       error = PTR_ERR(dentry);
-       if (IS_ERR(dentry)) {
-               mutex_unlock(&dir->d_inode->i_mutex);
-               goto exit;
-       }
-
-       path->dentry = dentry;
-       path->mnt = nd->path.mnt;
+       if (IS_ERR(filp))
+               goto out;
 
-       /* Negative dentry, just create the file */
-       if (!dentry->d_inode) {
-               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 nameidata_to_filp().
-                */
-               error = mnt_want_write(nd->path.mnt);
-               if (error)
-                       goto exit_mutex_unlock;
-               want_write = 1;
+       if (created) {
                /* Don't check for write permission, don't truncate */
                open_flag &= ~O_TRUNC;
                will_truncate = 0;
                acc_mode = MAY_OPEN;
-               error = security_path_mknod(&nd->path, dentry, mode, 0);
-               if (error)
-                       goto exit_mutex_unlock;
-               error = vfs_create(dir->d_inode, dentry, mode, nd);
-               if (error)
-                       goto exit_mutex_unlock;
-               mutex_unlock(&dir->d_inode->i_mutex);
-               dput(nd->path.dentry);
-               nd->path.dentry = dentry;
+               path_to_nameidata(path, nd);
                goto common;
        }
 
        /*
         * It already exists.
         */
-       mutex_unlock(&dir->d_inode->i_mutex);
        audit_inode(pathname, path->dentry);
 
        error = -EEXIST;
@@ -2360,6 +2364,8 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 
        BUG_ON(nd->flags & LOOKUP_RCU);
        inode = path->dentry->d_inode;
+finish_lookup:
+       /* we _can_ be in RCU mode here */
        error = -ENOENT;
        if (!inode) {
                path_to_nameidata(path, nd);
@@ -2377,18 +2383,28 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                return NULL;
        }
 
-       path_to_nameidata(path, nd);
+       if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
+               path_to_nameidata(path, nd);
+       } else {
+               save_parent.dentry = nd->path.dentry;
+               save_parent.mnt = mntget(path->mnt);
+               nd->path.dentry = path->dentry;
+
+       }
        nd->inode = inode;
        /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
        error = complete_walk(nd);
-       if (error)
+       if (error) {
+               path_put(&save_parent);
                return ERR_PTR(error);
+       }
        error = -EISDIR;
        if ((open_flag & O_CREAT) && S_ISDIR(nd->inode->i_mode))
                goto exit;
        error = -ENOTDIR;
        if ((nd->flags & LOOKUP_DIRECTORY) && !nd->inode->i_op->lookup)
                goto exit;
+       audit_inode(pathname, nd->path.dentry);
 ok:
        if (!S_ISREG(nd->inode->i_mode))
                will_truncate = 0;
@@ -2404,6 +2420,20 @@ common:
        if (error)
                goto exit;
        filp = nameidata_to_filp(nd);
+       if (filp == ERR_PTR(-EOPENSTALE) && save_parent.dentry && !retried) {
+               BUG_ON(save_parent.dentry != dir);
+               path_put(&nd->path);
+               nd->path = save_parent;
+               nd->inode = dir->d_inode;
+               save_parent.mnt = NULL;
+               save_parent.dentry = NULL;
+               if (want_write) {
+                       mnt_drop_write(nd->path.mnt);
+                       want_write = 0;
+               }
+               retried = true;
+               goto retry_lookup;
+       }
        if (!IS_ERR(filp)) {
                error = ima_file_check(filp, op->acc_mode);
                if (error) {
@@ -2423,11 +2453,10 @@ common:
 out:
        if (want_write)
                mnt_drop_write(nd->path.mnt);
+       path_put(&save_parent);
        terminate_walk(nd);
        return filp;
 
-exit_mutex_unlock:
-       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
        path_put_conditional(path, nd);
 exit:
@@ -2475,9 +2504,8 @@ static struct file *path_openat(int dfd, const char *pathname,
                nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
                error = follow_link(&link, nd, &cookie);
                if (unlikely(error))
-                       filp = ERR_PTR(error);
-               else
-                       filp = do_last(nd, &path, op, pathname);
+                       goto out_filp;
+               filp = do_last(nd, &path, op, pathname);
                put_link(nd, &link, cookie);
        }
 out:
@@ -2486,6 +2514,12 @@ out:
        if (base)
                fput(base);
        release_open_intent(nd);
+       if (filp == ERR_PTR(-EOPENSTALE)) {
+               if (flags & LOOKUP_RCU)
+                       filp = ERR_PTR(-ECHILD);
+               else
+                       filp = ERR_PTR(-ESTALE);
+       }
        return filp;
 
 out_filp: