ASoC: sh: fsi-hdmi: Add FSI port and HDMI selection
[pandora-kernel.git] / fs / namei.c
index 16109da..7d77f24 100644 (file)
@@ -367,18 +367,6 @@ void path_get(struct path *path)
 }
 EXPORT_SYMBOL(path_get);
 
-/**
- * path_get_long - get a long reference to a path
- * @path: path to get the reference to
- *
- * Given a path increment the reference count to the dentry and the vfsmount.
- */
-void path_get_long(struct path *path)
-{
-       mntget_long(path->mnt);
-       dget(path->dentry);
-}
-
 /**
  * path_put - put a reference to a path
  * @path: path to put the reference to
@@ -392,18 +380,6 @@ void path_put(struct path *path)
 }
 EXPORT_SYMBOL(path_put);
 
-/**
- * path_put_long - put a long reference to a path
- * @path: path to put the reference to
- *
- * Given a path decrement the reference count to the dentry and the vfsmount.
- */
-void path_put_long(struct path *path)
-{
-       dput(path->dentry);
-       mntput_long(path->mnt);
-}
-
 /**
  * nameidata_drop_rcu - drop this nameidata out of rcu-walk
  * @nd: nameidata pathwalk data to drop
@@ -800,12 +776,8 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
        touch_atime(link->mnt, dentry);
        nd_set_link(nd, NULL);
 
-       if (link->mnt != nd->path.mnt) {
-               path_to_nameidata(link, nd);
-               nd->inode = nd->path.dentry->d_inode;
-               dget(dentry);
-       }
-       mntget(link->mnt);
+       if (link->mnt == nd->path.mnt)
+               mntget(link->mnt);
 
        nd->last_type = LAST_BIND;
        *p = dentry->d_inode->i_op->follow_link(dentry, nd);
@@ -904,10 +876,17 @@ static int follow_automount(struct path *path, unsigned flags,
                            bool *need_mntput)
 {
        struct vfsmount *mnt;
+       int err;
 
        if (!path->dentry->d_op || !path->dentry->d_op->d_automount)
                return -EREMOTE;
 
+       /* We don't want to mount if someone supplied AT_NO_AUTOMOUNT
+        * and this is the terminal part of the path.
+        */
+       if ((flags & LOOKUP_NO_AUTOMOUNT) && !(flags & LOOKUP_CONTINUE))
+               return -EISDIR; /* we actually want to stop here */
+
        /* We want to mount if someone is trying to open/create a file of any
         * type under the mountpoint, wants to traverse through the mountpoint
         * or wants to open the mounted directory.
@@ -940,26 +919,33 @@ static int follow_automount(struct path *path, unsigned flags,
                        return -EREMOTE;
                return PTR_ERR(mnt);
        }
+
        if (!mnt) /* mount collision */
                return 0;
 
-       if (mnt->mnt_sb == path->mnt->mnt_sb &&
-           mnt->mnt_root == path->dentry) {
-               mntput(mnt);
-               return -ELOOP;
+       err = finish_automount(mnt, path);
+
+       switch (err) {
+       case -EBUSY:
+               /* Someone else made a mount here whilst we were busy */
+               return 0;
+       case 0:
+               dput(path->dentry);
+               if (*need_mntput)
+                       mntput(path->mnt);
+               path->mnt = mnt;
+               path->dentry = dget(mnt->mnt_root);
+               *need_mntput = true;
+               return 0;
+       default:
+               return err;
        }
 
-       dput(path->dentry);
-       if (*need_mntput)
-               mntput(path->mnt);
-       path->mnt = mnt;
-       path->dentry = dget(mnt->mnt_root);
-       *need_mntput = true;
-       return 0;
 }
 
 /*
  * Handle a dentry that is managed in some way.
+ * - Flagged for transit management (autofs)
  * - Flagged as mountpoint
  * - Flagged as automount point
  *
@@ -979,6 +965,17 @@ static int follow_managed(struct path *path, unsigned flags)
        while (managed = ACCESS_ONCE(path->dentry->d_flags),
               managed &= DCACHE_MANAGED_DENTRY,
               unlikely(managed != 0)) {
+               /* Allow the filesystem to manage the transit without i_mutex
+                * being held. */
+               if (managed & DCACHE_MANAGE_TRANSIT) {
+                       BUG_ON(!path->dentry->d_op);
+                       BUG_ON(!path->dentry->d_op->d_manage);
+                       ret = path->dentry->d_op->d_manage(path->dentry,
+                                                          false, false);
+                       if (ret < 0)
+                               return ret == -EISDIR ? 0 : ret;
+               }
+
                /* Transit to a mounted filesystem. */
                if (managed & DCACHE_MOUNTED) {
                        struct vfsmount *mounted = lookup_mnt(path);
@@ -1012,7 +1009,7 @@ static int follow_managed(struct path *path, unsigned flags)
        return 0;
 }
 
-int follow_down(struct path *path)
+int follow_down_one(struct path *path)
 {
        struct vfsmount *mounted;
 
@@ -1029,7 +1026,7 @@ int follow_down(struct path *path)
 
 /*
  * Skip to top of mountpoint pile in rcuwalk mode.  We abort the rcu-walk if we
- * meet an automount point and we're not walking to "..".  True is returned to
+ * meet a managed dentry and we're not walking to "..".  True is returned to
  * continue, false to abort.
  */
 static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
@@ -1037,6 +1034,10 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
 {
        while (d_mountpoint(path->dentry)) {
                struct vfsmount *mounted;
+               if (unlikely(path->dentry->d_flags & DCACHE_MANAGE_TRANSIT) &&
+                   !reverse_transit &&
+                   path->dentry->d_op->d_manage(path->dentry, false, true) < 0)
+                       return false;
                mounted = __lookup_mnt(path->mnt, path->dentry, 1);
                if (!mounted)
                        break;
@@ -1086,6 +1087,58 @@ static int follow_dotdot_rcu(struct nameidata *nd)
        return 0;
 }
 
+/*
+ * Follow down to the covering mount currently visible to userspace.  At each
+ * point, the filesystem owning that dentry may be queried as to whether the
+ * caller is permitted to proceed or not.
+ *
+ * Care must be taken as namespace_sem may be held (indicated by mounting_here
+ * being true).
+ */
+int follow_down(struct path *path, bool mounting_here)
+{
+       unsigned managed;
+       int ret;
+
+       while (managed = ACCESS_ONCE(path->dentry->d_flags),
+              unlikely(managed & DCACHE_MANAGED_DENTRY)) {
+               /* Allow the filesystem to manage the transit without i_mutex
+                * being held.
+                *
+                * We indicate to the filesystem if someone is trying to mount
+                * something here.  This gives autofs the chance to deny anyone
+                * other than its daemon the right to mount on its
+                * superstructure.
+                *
+                * The filesystem may sleep at this point.
+                */
+               if (managed & DCACHE_MANAGE_TRANSIT) {
+                       BUG_ON(!path->dentry->d_op);
+                       BUG_ON(!path->dentry->d_op->d_manage);
+                       ret = path->dentry->d_op->d_manage(
+                               path->dentry, mounting_here, false);
+                       if (ret < 0)
+                               return ret == -EISDIR ? 0 : ret;
+               }
+
+               /* Transit to a mounted filesystem. */
+               if (managed & DCACHE_MOUNTED) {
+                       struct vfsmount *mounted = lookup_mnt(path);
+                       if (!mounted)
+                               break;
+                       dput(path->dentry);
+                       mntput(path->mnt);
+                       path->mnt = mounted;
+                       path->dentry = dget(mounted->mnt_root);
+                       continue;
+               }
+
+               /* Don't handle automount points here */
+               break;
+       }
+       return 0;
+}
+
 /*
  * Skip to top of mountpoint pile in refwalk mode for follow_dotdot()
  */
@@ -1219,8 +1272,10 @@ done:
        path->mnt = mnt;
        path->dentry = dentry;
        err = follow_managed(path, nd->flags);
-       if (unlikely(err < 0))
+       if (unlikely(err < 0)) {
+               path_put_conditional(path, nd);
                return err;
+       }
        *inode = path->dentry->d_inode;
        return 0;
 
@@ -1268,17 +1323,6 @@ fail:
        return PTR_ERR(dentry);
 }
 
-/*
- * This is a temporary kludge to deal with "automount" symlinks; proper
- * solution is to trigger them on follow_mount(), so that do_lookup()
- * would DTRT.  To be killed before 2.6.34-final.
- */
-static inline int follow_on_final(struct inode *inode, unsigned lookup_flags)
-{
-       return inode && unlikely(inode->i_op->follow_link) &&
-               ((lookup_flags & LOOKUP_FOLLOW) || S_ISDIR(inode->i_mode));
-}
-
 /*
  * Name resolution.
  * This is the basic name resolution function, turning a pathname into
@@ -1417,7 +1461,8 @@ last_component:
                err = do_lookup(nd, &this, &next, &inode);
                if (err)
                        break;
-               if (follow_on_final(inode, lookup_flags)) {
+               if (inode && unlikely(inode->i_op->follow_link) &&
+                   (lookup_flags & LOOKUP_FOLLOW)) {
                        if (nameidata_dentry_drop_rcu_maybe(nd, next.dentry))
                                return -ECHILD;
                        BUG_ON(inode != next.dentry->d_inode);
@@ -2470,8 +2515,7 @@ reval:
                struct inode *linki = link.dentry->d_inode;
                void *cookie;
                error = -ELOOP;
-               /* S_ISDIR part is a temporary automount kludge */
-               if (!(nd.flags & LOOKUP_FOLLOW) && !S_ISDIR(linki->i_mode))
+               if (!(nd.flags & LOOKUP_FOLLOW))
                        goto exit_dput;
                if (count++ == 32)
                        goto exit_dput;
@@ -3530,6 +3574,7 @@ const struct inode_operations page_symlink_inode_operations = {
 };
 
 EXPORT_SYMBOL(user_path_at);
+EXPORT_SYMBOL(follow_down_one);
 EXPORT_SYMBOL(follow_down);
 EXPORT_SYMBOL(follow_up);
 EXPORT_SYMBOL(get_write_access); /* binfmt_aout */