tools lib traceevent: Add missing break in make_bprint_args
[pandora-kernel.git] / fs / namei.c
index 90210b4..7d69419 100644 (file)
@@ -2202,6 +2202,8 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        struct file *filp;
        struct inode *inode;
        int symlink_ok = 0;
+       struct path save_parent = { .dentry = NULL, .mnt = NULL };
+       bool retried = false;
        int error;
 
        nd->flags &= ~LOOKUP_PARENT;
@@ -2249,37 +2251,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
 
                        inode = path->dentry->d_inode;
                }
-               error = -ENOENT;
-               if (!inode) {
-                       path_to_nameidata(path, nd);
-                       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 */
-               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;
+               goto finish_lookup;
        }
 
        /* create side of things */
@@ -2297,6 +2269,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        if (nd->last.name[nd->last.len])
                goto exit;
 
+retry_lookup:
        mutex_lock(&dir->d_inode->i_mutex);
 
        dentry = lookup_hash(nd);
@@ -2360,6 +2333,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 +2352,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 +2389,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,6 +2422,7 @@ common:
 out:
        if (want_write)
                mnt_drop_write(nd->path.mnt);
+       path_put(&save_parent);
        terminate_walk(nd);
        return filp;
 
@@ -2486,6 +2486,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: