]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/namei.c
PCI: Add builtin_pci_driver() to avoid registration boilerplate
[karo-tx-linux.git] / fs / namei.c
index ae4e4c18b2ac0b2c366f893f7ffa4d898446c0f7..726d211db4842715f71e1911f6940c93b19fe57f 100644 (file)
@@ -560,6 +560,24 @@ static int __nd_alloc_stack(struct nameidata *nd)
        return 0;
 }
 
+/**
+ * path_connected - Verify that a path->dentry is below path->mnt.mnt_root
+ * @path: nameidate to verify
+ *
+ * Rename can sometimes move a file or directory outside of a bind
+ * mount, path_connected allows those cases to be detected.
+ */
+static bool path_connected(const struct path *path)
+{
+       struct vfsmount *mnt = path->mnt;
+
+       /* Only bind mounts can have disconnected paths */
+       if (mnt->mnt_root == mnt->mnt_sb->s_root)
+               return true;
+
+       return is_subdir(path->dentry, mnt->mnt_root);
+}
+
 static inline int nd_alloc_stack(struct nameidata *nd)
 {
        if (likely(nd->depth != EMBEDDED_LEVELS))
@@ -879,7 +897,7 @@ static inline int may_follow_link(struct nameidata *nd)
                return 0;
 
        /* Allowed if parent directory not sticky and world-writable. */
-       parent = nd->path.dentry->d_inode;
+       parent = nd->inode;
        if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
                return 0;
 
@@ -1296,6 +1314,8 @@ static int follow_dotdot_rcu(struct nameidata *nd)
                                return -ECHILD;
                        nd->path.dentry = parent;
                        nd->seq = seq;
+                       if (unlikely(!path_connected(&nd->path)))
+                               return -ENOENT;
                        break;
                } else {
                        struct mount *mnt = real_mount(nd->path.mnt);
@@ -1396,7 +1416,7 @@ static void follow_mount(struct path *path)
        }
 }
 
-static void follow_dotdot(struct nameidata *nd)
+static int follow_dotdot(struct nameidata *nd)
 {
        if (!nd->root.mnt)
                set_root(nd);
@@ -1412,6 +1432,8 @@ static void follow_dotdot(struct nameidata *nd)
                        /* rare case of legitimate dget_parent()... */
                        nd->path.dentry = dget_parent(nd->path.dentry);
                        dput(old);
+                       if (unlikely(!path_connected(&nd->path)))
+                               return -ENOENT;
                        break;
                }
                if (!follow_up(&nd->path))
@@ -1419,6 +1441,7 @@ static void follow_dotdot(struct nameidata *nd)
        }
        follow_mount(&nd->path);
        nd->inode = nd->path.dentry->d_inode;
+       return 0;
 }
 
 /*
@@ -1634,7 +1657,7 @@ static inline int handle_dots(struct nameidata *nd, int type)
                if (nd->flags & LOOKUP_RCU) {
                        return follow_dotdot_rcu(nd);
                } else
-                       follow_dotdot(nd);
+                       return follow_dotdot(nd);
        }
        return 0;
 }
@@ -1954,8 +1977,13 @@ OK:
                                continue;
                        }
                }
-               if (unlikely(!d_can_lookup(nd->path.dentry)))
+               if (unlikely(!d_can_lookup(nd->path.dentry))) {
+                       if (nd->flags & LOOKUP_RCU) {
+                               if (unlazy_walk(nd, NULL, 0))
+                                       return -ECHILD;
+                       }
                        return -ENOTDIR;
+               }
        }
 }
 
@@ -2410,7 +2438,7 @@ done:
 
 /**
  * path_mountpoint - look up a path to be umounted
- * @nameidata: lookup context
+ * @nd:                lookup context
  * @flags:     lookup flags
  * @path:      pointer to container for result
  *