Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux-2.6-block.git] / fs / namei.c
index 2c30c84d4ea1da5e17381caeea6de5ae27e47a0e..f415c6683a837ac3cb1c5bf1d9600b6a3aec9d7e 100644 (file)
@@ -2222,6 +2222,188 @@ user_path_parent(int dfd, const char __user *path, struct nameidata *nd,
        return s;
 }
 
+/**
+ * umount_lookup_last - look up last component for umount
+ * @nd:   pathwalk nameidata - currently pointing at parent directory of "last"
+ * @path: pointer to container for result
+ *
+ * This is a special lookup_last function just for umount. In this case, we
+ * need to resolve the path without doing any revalidation.
+ *
+ * The nameidata should be the result of doing a LOOKUP_PARENT pathwalk. Since
+ * mountpoints are always pinned in the dcache, their ancestors are too. Thus,
+ * in almost all cases, this lookup will be served out of the dcache. The only
+ * cases where it won't are if nd->last refers to a symlink or the path is
+ * bogus and it doesn't exist.
+ *
+ * Returns:
+ * -error: if there was an error during lookup. This includes -ENOENT if the
+ *         lookup found a negative dentry. The nd->path reference will also be
+ *         put in this case.
+ *
+ * 0:      if we successfully resolved nd->path and found it to not to be a
+ *         symlink that needs to be followed. "path" will also be populated.
+ *         The nd->path reference will also be put.
+ *
+ * 1:      if we successfully resolved nd->last and found it to be a symlink
+ *         that needs to be followed. "path" will be populated with the path
+ *         to the link, and nd->path will *not* be put.
+ */
+static int
+umount_lookup_last(struct nameidata *nd, struct path *path)
+{
+       int error = 0;
+       struct dentry *dentry;
+       struct dentry *dir = nd->path.dentry;
+
+       if (unlikely(nd->flags & LOOKUP_RCU)) {
+               WARN_ON_ONCE(1);
+               error = -ECHILD;
+               goto error_check;
+       }
+
+       nd->flags &= ~LOOKUP_PARENT;
+
+       if (unlikely(nd->last_type != LAST_NORM)) {
+               error = handle_dots(nd, nd->last_type);
+               if (!error)
+                       dentry = dget(nd->path.dentry);
+               goto error_check;
+       }
+
+       mutex_lock(&dir->d_inode->i_mutex);
+       dentry = d_lookup(dir, &nd->last);
+       if (!dentry) {
+               /*
+                * No cached dentry. Mounted dentries are pinned in the cache,
+                * so that means that this dentry is probably a symlink or the
+                * path doesn't actually point to a mounted dentry.
+                */
+               dentry = d_alloc(dir, &nd->last);
+               if (!dentry) {
+                       error = -ENOMEM;
+               } else {
+                       dentry = lookup_real(dir->d_inode, dentry, nd->flags);
+                       if (IS_ERR(dentry))
+                               error = PTR_ERR(dentry);
+               }
+       }
+       mutex_unlock(&dir->d_inode->i_mutex);
+
+error_check:
+       if (!error) {
+               if (!dentry->d_inode) {
+                       error = -ENOENT;
+                       dput(dentry);
+               } else {
+                       path->dentry = dentry;
+                       path->mnt = mntget(nd->path.mnt);
+                       if (should_follow_link(dentry->d_inode,
+                                               nd->flags & LOOKUP_FOLLOW))
+                               return 1;
+                       follow_mount(path);
+               }
+       }
+       terminate_walk(nd);
+       return error;
+}
+
+/**
+ * path_umountat - look up a path to be umounted
+ * @dfd:       directory file descriptor to start walk from
+ * @name:      full pathname to walk
+ * @flags:     lookup flags
+ * @nd:                pathwalk nameidata
+ *
+ * Look up the given name, but don't attempt to revalidate the last component.
+ * Returns 0 and "path" will be valid on success; Retuns error otherwise.
+ */
+static int
+path_umountat(int dfd, const char *name, struct path *path, unsigned int flags)
+{
+       struct file *base = NULL;
+       struct nameidata nd;
+       int err;
+
+       err = path_init(dfd, name, flags | LOOKUP_PARENT, &nd, &base);
+       if (unlikely(err))
+               return err;
+
+       current->total_link_count = 0;
+       err = link_path_walk(name, &nd);
+       if (err)
+               goto out;
+
+       /* If we're in rcuwalk, drop out of it to handle last component */
+       if (nd.flags & LOOKUP_RCU) {
+               err = unlazy_walk(&nd, NULL);
+               if (err) {
+                       terminate_walk(&nd);
+                       goto out;
+               }
+       }
+
+       err = umount_lookup_last(&nd, path);
+       while (err > 0) {
+               void *cookie;
+               struct path link = *path;
+               err = may_follow_link(&link, &nd);
+               if (unlikely(err))
+                       break;
+               nd.flags |= LOOKUP_PARENT;
+               err = follow_link(&link, &nd, &cookie);
+               if (err)
+                       break;
+               err = umount_lookup_last(&nd, path);
+               put_link(&nd, &link, cookie);
+       }
+out:
+       if (base)
+               fput(base);
+
+       if (nd.root.mnt && !(nd.flags & LOOKUP_ROOT))
+               path_put(&nd.root);
+
+       return err;
+}
+
+/**
+ * user_path_umountat - lookup a path from userland in order to umount it
+ * @dfd:       directory file descriptor
+ * @name:      pathname from userland
+ * @flags:     lookup flags
+ * @path:      pointer to container to hold result
+ *
+ * A umount is a special case for path walking. We're not actually interested
+ * in the inode in this situation, and ESTALE errors can be a problem. We
+ * simply want track down the dentry and vfsmount attached at the mountpoint
+ * and avoid revalidating the last component.
+ *
+ * Returns 0 and populates "path" on success.
+ */
+int
+user_path_umountat(int dfd, const char __user *name, unsigned int flags,
+                       struct path *path)
+{
+       struct filename *s = getname(name);
+       int error;
+
+       if (IS_ERR(s))
+               return PTR_ERR(s);
+
+       error = path_umountat(dfd, s->name, path, flags | LOOKUP_RCU);
+       if (unlikely(error == -ECHILD))
+               error = path_umountat(dfd, s->name, path, flags);
+       if (unlikely(error == -ESTALE))
+               error = path_umountat(dfd, s->name, path, flags | LOOKUP_REVAL);
+
+       if (likely(!error))
+               audit_inode(s, path->dentry, 0);
+
+       putname(s);
+       return error;
+}
+
 /*
  * It's inline, so penalty for filesystems that don't use sticky bit is
  * minimal.