]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/exportfs/expfs.c
sdio: store vendor strings
[linux-2.6-omap-h63xx.git] / fs / exportfs / expfs.c
index 93e77c3d24906af5156254b29ee1cd3e39ba1ac3..8adb32a9387a3e6cb76ba0cf80b9873d51dad3bd 100644 (file)
@@ -1,16 +1,45 @@
 
+#include <linux/exportfs.h>
 #include <linux/fs.h>
 #include <linux/file.h>
 #include <linux/module.h>
-#include <linux/smp_lock.h>
+#include <linux/mount.h>
 #include <linux/namei.h>
 
-struct export_operations export_op_default;
+#define dprintk(fmt, args...) do{}while(0)
 
-#define        CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
 
-#define dprintk(fmt, args...) do{}while(0)
+static int get_name(struct dentry *dentry, char *name,
+               struct dentry *child);
+
+
+static struct dentry *exportfs_get_dentry(struct super_block *sb, void *obj)
+{
+       struct dentry *result = ERR_PTR(-ESTALE);
+
+       if (sb->s_export_op->get_dentry) {
+               result = sb->s_export_op->get_dentry(sb, obj);
+               if (!result)
+                       result = ERR_PTR(-ESTALE);
+       }
+
+       return result;
+}
+
+static int exportfs_get_name(struct dentry *dir, char *name,
+               struct dentry *child)
+{
+       struct export_operations *nop = dir->d_sb->s_export_op;
 
+       if (nop->get_name)
+               return nop->get_name(dir, name, child);
+       else
+               return get_name(dir, name, child);
+}
+
+/*
+ * Check if the dentry or any of it's aliases is acceptable.
+ */
 static struct dentry *
 find_acceptable_alias(struct dentry *result,
                int (*acceptable)(void *context, struct dentry *dentry),
@@ -18,6 +47,9 @@ find_acceptable_alias(struct dentry *result,
 {
        struct dentry *dentry, *toput = NULL;
 
+       if (acceptable(context, result))
+               return result;
+
        spin_lock(&dcache_lock);
        list_for_each_entry(dentry, &result->d_inode->i_dentry, d_alias) {
                dget_locked(dentry);
@@ -38,130 +70,50 @@ find_acceptable_alias(struct dentry *result,
        return NULL;
 }
 
-/**
- * find_exported_dentry - helper routine to implement export_operations->decode_fh
- * @sb:                The &super_block identifying the filesystem
- * @obj:       An opaque identifier of the object to be found - passed to
- *             get_inode
- * @parent:    An optional opqaue identifier of the parent of the object.
- * @acceptable:        A function used to test possible &dentries to see if they are
- *             acceptable
- * @context:   A parameter to @acceptable so that it knows on what basis to
- *             judge.
- *
- * find_exported_dentry is the central helper routine to enable file systems
- * to provide the decode_fh() export_operation.  It's main task is to take
- * an &inode, find or create an appropriate &dentry structure, and possibly
- * splice this into the dcache in the correct place.
- *
- * The decode_fh() operation provided by the filesystem should call
- * find_exported_dentry() with the same parameters that it received except
- * that instead of the file handle fragment, pointers to opaque identifiers
- * for the object and optionally its parent are passed.  The default decode_fh
- * routine passes one pointer to the start of the filehandle fragment, and
- * one 8 bytes into the fragment.  It is expected that most filesystems will
- * take this approach, though the offset to the parent identifier may well be
- * different.
- *
- * find_exported_dentry() will call get_dentry to get an dentry pointer from
- * the file system.  If any &dentry in the d_alias list is acceptable, it will
- * be returned.  Otherwise find_exported_dentry() will attempt to splice a new
- * &dentry into the dcache using get_name() and get_parent() to find the
- * appropriate place.
+/*
+ * Find root of a disconnected subtree and return a reference to it.
  */
-
-struct dentry *
-find_exported_dentry(struct super_block *sb, void *obj, void *parent,
-                    int (*acceptable)(void *context, struct dentry *de),
-                    void *context)
+static struct dentry *
+find_disconnected_root(struct dentry *dentry)
 {
-       struct dentry *result = NULL;
-       struct dentry *target_dir;
-       int err;
-       struct export_operations *nops = sb->s_export_op;
-       struct dentry *alias;
-       int noprogress;
-       char nbuf[NAME_MAX+1];
-
-       /*
-        * Attempt to find the inode.
-        */
-       result = CALL(sb->s_export_op,get_dentry)(sb,obj);
-       err = -ESTALE;
-       if (result == NULL)
-               goto err_out;
-       if (IS_ERR(result)) {
-               err = PTR_ERR(result);
-               goto err_out;
+       dget(dentry);
+       spin_lock(&dentry->d_lock);
+       while (!IS_ROOT(dentry) &&
+              (dentry->d_parent->d_flags & DCACHE_DISCONNECTED)) {
+               struct dentry *parent = dentry->d_parent;
+               dget(parent);
+               spin_unlock(&dentry->d_lock);
+               dput(dentry);
+               dentry = parent;
+               spin_lock(&dentry->d_lock);
        }
-       if (S_ISDIR(result->d_inode->i_mode) &&
-           (result->d_flags & DCACHE_DISCONNECTED)) {
-               /* it is an unconnected directory, we must connect it */
-               ;
-       } else {
-               if (acceptable(context, result))
-                       return result;
-               if (S_ISDIR(result->d_inode->i_mode)) {
-                       err = -EACCES;
-                       goto err_result;
-               }
+       spin_unlock(&dentry->d_lock);
+       return dentry;
+}
 
-               alias = find_acceptable_alias(result, acceptable, context);
-               if (alias)
-                       return alias;
-       }                       
-
-       /* It's a directory, or we are required to confirm the file's
-        * location in the tree based on the parent information
-        */
-       dprintk("find_exported_dentry: need to look harder for %s/%d\n",sb->s_id,*(int*)obj);
-       if (S_ISDIR(result->d_inode->i_mode))
-               target_dir = dget(result);
-       else {
-               if (parent == NULL)
-                       goto err_result;
 
-               target_dir = CALL(sb->s_export_op,get_dentry)(sb,parent);
-               if (IS_ERR(target_dir))
-                       err = PTR_ERR(target_dir);
-               if (target_dir == NULL || IS_ERR(target_dir))
-                       goto err_result;
-       }
-       /*
-        * Now we need to make sure that target_dir is properly connected.
-        * It may already be, as the flag isn't always updated when connection
-        * happens.
-        * So, we walk up parent links until we find a connected directory,
-        * or we run out of directories.  Then we find the parent, find
-        * the name of the child in that parent, and do a lookup.
-        * This should connect the child into the parent
-        * We then repeat.
-        */
+/*
+ * Make sure target_dir is fully connected to the dentry tree.
+ *
+ * It may already be, as the flag isn't always updated when connection happens.
+ */
+static int
+reconnect_path(struct super_block *sb, struct dentry *target_dir)
+{
+       char nbuf[NAME_MAX+1];
+       int noprogress = 0;
+       int err = -ESTALE;
 
-       /* it is possible that a confused file system might not let us complete 
+       /*
+        * It is possible that a confused file system might not let us complete
         * the path to the root.  For example, if get_parent returns a directory
         * in which we cannot find a name for the child.  While this implies a
         * very sick filesystem we don't want it to cause knfsd to spin.  Hence
         * the noprogress counter.  If we go through the loop 10 times (2 is
         * probably enough) without getting anywhere, we just give up
         */
-       noprogress= 0;
        while (target_dir->d_flags & DCACHE_DISCONNECTED && noprogress++ < 10) {
-               struct dentry *pd = target_dir;
-
-               dget(pd);
-               spin_lock(&pd->d_lock);
-               while (!IS_ROOT(pd) &&
-                               (pd->d_parent->d_flags&DCACHE_DISCONNECTED)) {
-                       struct dentry *parent = pd->d_parent;
-
-                       dget(parent);
-                       spin_unlock(&pd->d_lock);
-                       dput(pd);
-                       pd = parent;
-                       spin_lock(&pd->d_lock);
-               }
-               spin_unlock(&pd->d_lock);
+               struct dentry *pd = find_disconnected_root(target_dir);
 
                if (!IS_ROOT(pd)) {
                        /* must have found a connected parent - great */
@@ -176,29 +128,40 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
                        spin_unlock(&pd->d_lock);
                        noprogress = 0;
                } else {
-                       /* we have hit the top of a disconnected path.  Try
-                        * to find parent and connect
-                        * note: racing with some other process renaming a
-                        * directory isn't much of a problem here.  If someone
-                        * renames the directory, it will end up properly
-                        * connected, which is what we want
+                       /*
+                        * We have hit the top of a disconnected path, try to
+                        * find parent and connect.
+                        *
+                        * Racing with some other process renaming a directory
+                        * isn't much of a problem here.  If someone renames
+                        * the directory, it will end up properly connected,
+                        * which is what we want
+                        *
+                        * Getting the parent can't be supported generically,
+                        * the locking is too icky.
+                        *
+                        * Instead we just return EACCES.  If server reboots
+                        * or inodes get flushed, you lose
                         */
-                       struct dentry *ppd;
+                       struct dentry *ppd = ERR_PTR(-EACCES);
                        struct dentry *npd;
 
                        mutex_lock(&pd->d_inode->i_mutex);
-                       ppd = CALL(nops,get_parent)(pd);
+                       if (sb->s_export_op->get_parent)
+                               ppd = sb->s_export_op->get_parent(pd);
                        mutex_unlock(&pd->d_inode->i_mutex);
 
                        if (IS_ERR(ppd)) {
                                err = PTR_ERR(ppd);
-                               dprintk("find_exported_dentry: get_parent of %ld failed, err %d\n",
-                                       pd->d_inode->i_ino, err);
+                               dprintk("%s: get_parent of %ld failed, err %d\n",
+                                       __FUNCTION__, pd->d_inode->i_ino, err);
                                dput(pd);
                                break;
                        }
-                       dprintk("find_exported_dentry: find name of %lu in %lu\n", pd->d_inode->i_ino, ppd->d_inode->i_ino);
-                       err = CALL(nops,get_name)(ppd, nbuf, pd);
+
+                       dprintk("%s: find name of %lu in %lu\n", __FUNCTION__,
+                               pd->d_inode->i_ino, ppd->d_inode->i_ino);
+                       err = exportfs_get_name(ppd, nbuf, pd);
                        if (err) {
                                dput(ppd);
                                dput(pd);
@@ -209,13 +172,14 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
                                        continue;
                                break;
                        }
-                       dprintk("find_exported_dentry: found name: %s\n", nbuf);
+                       dprintk("%s: found name: %s\n", __FUNCTION__, nbuf);
                        mutex_lock(&ppd->d_inode->i_mutex);
                        npd = lookup_one_len(nbuf, ppd, strlen(nbuf));
                        mutex_unlock(&ppd->d_inode->i_mutex);
                        if (IS_ERR(npd)) {
                                err = PTR_ERR(npd);
-                               dprintk("find_exported_dentry: lookup failed: %d\n", err);
+                               dprintk("%s: lookup failed: %d\n",
+                                       __FUNCTION__, err);
                                dput(ppd);
                                dput(pd);
                                break;
@@ -228,7 +192,7 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
                        if (npd == pd)
                                noprogress = 0;
                        else
-                               printk("find_exported_dentry: npd != pd\n");
+                               printk("%s: npd != pd\n", __FUNCTION__);
                        dput(npd);
                        dput(ppd);
                        if (IS_ROOT(pd)) {
@@ -244,15 +208,101 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
                /* something went wrong - oh-well */
                if (!err)
                        err = -ESTALE;
-               goto err_target;
+               return err;
        }
-       /* if we weren't after a directory, have one more step to go */
-       if (result != target_dir) {
-               struct dentry *nresult;
-               err = CALL(nops,get_name)(target_dir, nbuf, result);
+
+       return 0;
+}
+
+/**
+ * find_exported_dentry - helper routine to implement export_operations->decode_fh
+ * @sb:                The &super_block identifying the filesystem
+ * @obj:       An opaque identifier of the object to be found - passed to
+ *             get_inode
+ * @parent:    An optional opqaue identifier of the parent of the object.
+ * @acceptable:        A function used to test possible &dentries to see if they are
+ *             acceptable
+ * @context:   A parameter to @acceptable so that it knows on what basis to
+ *             judge.
+ *
+ * find_exported_dentry is the central helper routine to enable file systems
+ * to provide the decode_fh() export_operation.  It's main task is to take
+ * an &inode, find or create an appropriate &dentry structure, and possibly
+ * splice this into the dcache in the correct place.
+ *
+ * The decode_fh() operation provided by the filesystem should call
+ * find_exported_dentry() with the same parameters that it received except
+ * that instead of the file handle fragment, pointers to opaque identifiers
+ * for the object and optionally its parent are passed.  The default decode_fh
+ * routine passes one pointer to the start of the filehandle fragment, and
+ * one 8 bytes into the fragment.  It is expected that most filesystems will
+ * take this approach, though the offset to the parent identifier may well be
+ * different.
+ *
+ * find_exported_dentry() will call get_dentry to get an dentry pointer from
+ * the file system.  If any &dentry in the d_alias list is acceptable, it will
+ * be returned.  Otherwise find_exported_dentry() will attempt to splice a new
+ * &dentry into the dcache using get_name() and get_parent() to find the
+ * appropriate place.
+ */
+
+struct dentry *
+find_exported_dentry(struct super_block *sb, void *obj, void *parent,
+                    int (*acceptable)(void *context, struct dentry *de),
+                    void *context)
+{
+       struct dentry *result, *alias;
+       int err = -ESTALE;
+
+       /*
+        * Attempt to find the inode.
+        */
+       result = exportfs_get_dentry(sb, obj);
+       if (IS_ERR(result))
+               return result;
+
+       if (S_ISDIR(result->d_inode->i_mode)) {
+               if (!(result->d_flags & DCACHE_DISCONNECTED)) {
+                       if (acceptable(context, result))
+                               return result;
+                       err = -EACCES;
+                       goto err_result;
+               }
+
+               err = reconnect_path(sb, result);
+               if (err)
+                       goto err_result;
+       } else {
+               struct dentry *target_dir, *nresult;
+               char nbuf[NAME_MAX+1];
+
+               alias = find_acceptable_alias(result, acceptable, context);
+               if (alias)
+                       return alias;
+
+               if (parent == NULL)
+                       goto err_result;
+
+               target_dir = exportfs_get_dentry(sb,parent);
+               if (IS_ERR(target_dir)) {
+                       err = PTR_ERR(target_dir);
+                       goto err_result;
+               }
+
+               err = reconnect_path(sb, target_dir);
+               if (err) {
+                       dput(target_dir);
+                       goto err_result;
+               }
+
+               /*
+                * As we weren't after a directory, have one more step to go.
+                */
+               err = exportfs_get_name(target_dir, nbuf, result);
                if (!err) {
                        mutex_lock(&target_dir->d_inode->i_mutex);
-                       nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf));
+                       nresult = lookup_one_len(nbuf, target_dir,
+                                                strlen(nbuf));
                        mutex_unlock(&target_dir->d_inode->i_mutex);
                        if (!IS_ERR(nresult)) {
                                if (nresult->d_inode) {
@@ -262,11 +312,8 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
                                        dput(nresult);
                        }
                }
+               dput(target_dir);
        }
-       dput(target_dir);
-       /* now result is properly connected, it is our best bet */
-       if (acceptable(context, result))
-               return result;
 
        alias = find_acceptable_alias(result, acceptable, context);
        if (alias)
@@ -276,32 +323,16 @@ find_exported_dentry(struct super_block *sb, void *obj, void *parent,
        dput(result);
        /* It might be justifiable to return ESTALE here,
         * but the filehandle at-least looks reasonable good
-        * and it just be a permission problem, so returning
+        * and it may just be a permission problem, so returning
         * -EACCESS is safer
         */
        return ERR_PTR(-EACCES);
 
- err_target:
-       dput(target_dir);
  err_result:
        dput(result);
- err_out:
        return ERR_PTR(err);
 }
 
-
-
-static struct dentry *get_parent(struct dentry *child)
-{
-       /* get_parent cannot be supported generically, the locking
-        * is too icky.
-        * instead, we just return EACCES.  If server reboots or inodes
-        * get flushed, you lose
-        */
-       return ERR_PTR(-EACCES);
-}
-
-
 struct getdents_callback {
        char *name;             /* name that was found. It already points to a
                                   buffer NAME_MAX+1 is size */
@@ -391,61 +422,6 @@ out:
        return error;
 }
 
-
-static struct dentry *export_iget(struct super_block *sb, unsigned long ino, __u32 generation)
-{
-
-       /* iget isn't really right if the inode is currently unallocated!!
-        * This should really all be done inside each filesystem
-        *
-        * ext2fs' read_inode has been strengthed to return a bad_inode if
-        * the inode had been deleted.
-        *
-        * Currently we don't know the generation for parent directory, so
-        * a generation of 0 means "accept any"
-        */
-       struct inode *inode;
-       struct dentry *result;
-       if (ino == 0)
-               return ERR_PTR(-ESTALE);
-       inode = iget(sb, ino);
-       if (inode == NULL)
-               return ERR_PTR(-ENOMEM);
-       if (is_bad_inode(inode)
-           || (generation && inode->i_generation != generation)
-               ) {
-               /* we didn't find the right inode.. */
-               dprintk("fh_verify: Inode %lu, Bad count: %d %d or version  %u %u\n",
-                       inode->i_ino,
-                       inode->i_nlink, atomic_read(&inode->i_count),
-                       inode->i_generation,
-                       generation);
-
-               iput(inode);
-               return ERR_PTR(-ESTALE);
-       }
-       /* now to find a dentry.
-        * If possible, get a well-connected one
-        */
-       result = d_alloc_anon(inode);
-       if (!result) {
-               iput(inode);
-               return ERR_PTR(-ENOMEM);
-       }
-       return result;
-}
-
-
-static struct dentry *get_object(struct super_block *sb, void *vobjp)
-{
-       __u32 *objp = vobjp;
-       unsigned long ino = objp[0];
-       __u32 generation = objp[1];
-
-       return export_iget(sb, ino, generation);
-}
-
-
 /**
  * export_encode_fh - default export_operations->encode_fh function
  * @dentry:  the dentry to encode
@@ -518,16 +494,40 @@ static struct dentry *export_decode_fh(struct super_block *sb, __u32 *fh, int fh
                                   acceptable, context);
 }
 
-struct export_operations export_op_default = {
-       .decode_fh      = export_decode_fh,
-       .encode_fh      = export_encode_fh,
+int exportfs_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len,
+               int connectable)
+{
+       struct export_operations *nop = dentry->d_sb->s_export_op;
+       int error;
+
+       if (nop->encode_fh)
+               error = nop->encode_fh(dentry, fh, max_len, connectable);
+       else
+               error = export_encode_fh(dentry, fh, max_len, connectable);
 
-       .get_name       = get_name,
-       .get_parent     = get_parent,
-       .get_dentry     = get_object,
-};
+       return error;
+}
+EXPORT_SYMBOL_GPL(exportfs_encode_fh);
+
+struct dentry *exportfs_decode_fh(struct vfsmount *mnt, __u32 *fh, int fh_len,
+               int fileid_type, int (*acceptable)(void *, struct dentry *),
+               void *context)
+{
+       struct export_operations *nop = mnt->mnt_sb->s_export_op;
+       struct dentry *result;
+
+       if (nop->decode_fh) {
+               result = nop->decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type,
+                       acceptable, context);
+       } else {
+               result = export_decode_fh(mnt->mnt_sb, fh, fh_len, fileid_type,
+                       acceptable, context);
+       }
+
+       return result;
+}
+EXPORT_SYMBOL_GPL(exportfs_decode_fh);
 
-EXPORT_SYMBOL(export_op_default);
 EXPORT_SYMBOL(find_exported_dentry);
 
 MODULE_LICENSE("GPL");