Greetings,
  I finally have a concrete (read: code attached) proposal for a
  "filehandle-to-dentry" interface between knfsd and filesystems.

  I would appreciate comments from any interested parties, especially
  developers of filesystems which don't have straight-forward 32bit
  inode numbers (e.g. reiserfs).

  I would really like to see this, or something like it, get in to
  2.4.0-final, so I hope to submit it to Linus in a week or so.

  It has some similarities to previous proposals, but is different
  too.  There is nothing like writing an implementation (and
  accompanying documentation) to blow holes in a design....

  Interesting aspects:

    1/ There is not necessarily a unique dentry for a given
      filehandle.  I had previously suggested returning an array of
      them.  However this may required the fs to do more work than
      needed, and nfsd does not know how big to make the array.
      This implementation passes a function "acceptable" which is used
      to test each possible dentry.  The fs tests each possible dentry
      and returns the first one that is acceptable.

    2/ I wanted to keep all the nfsd code in fs/nfsd and particularly
      in nfsd.o when compiled as a module.  This makes the provision
      of helper functions that the filesystem calls a bit awkward.
      As you will see, if nfsd.o is compiled as a module, it now
      registers an nfsd_linkage structure instead of just a sys_call
      pointer, and this linkage structure contains the address of the
      main helper function.
      The helper function uses three other functions which can be
      provided by the filesystem, or can default.  The filesystem says
      that it wants the defaults to be used by leaving the pointer
      NULL. Hence the "CALL" macro in nfsfh.h.  I'm not very proud of
      this bit, but I haven't come up with anything better.

   3/ With this patch, only ext2fs can be exported.  Naturally when I
      submit to Linus, all other filesystems which it makes sense to
      export will provide an nfsd_operations structure.

   4/ I have tried to be gentle to the code so as to keep the patch
      small and so that it will be clear that there is no major
      functional change.  Nevertheless, I have had to make some real
      changes to provide clean interface.

   5/ I have tried to document most of the important interfaces, but
      have not incorporated any of it into kernel-api.tmpl yet.

   6/ I hope to add another helper function that does the same as
      nfsd_find_fh_dentry, but doesn't need the nfsd_free_path_sem
      semphore.  This can be achieved (I believe) if the filesystem
      co-operates.  The filesystem's lookup() routine would need to be
      changed so that after calling iget (or equivalent) it checks the
      i_dentry list of an IS_ROOT (or DCACHE_NFSD_DISCONNECTED) entry
      and, if it finds one, it d_move's it inplace of the passed
      dentry, and returns that.
      This is all still theory.  Implementation may show otherwise:-)

    7/ I'm not actually 100% confident that this works as-is.
       It compiles and has worked in an earlier incarnation, but I am
       not in a good position to test it today.

Comments and testing most welcome (please).

NeilBrown
--- ./fs/ext2/super.c   2000/08/02 03:34:01     1.1
+++ ./fs/ext2/super.c   2000/08/03 01:56:31
@@ -23,6 +23,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/locks.h>
+#include <linux/nfsd/ops.h>
 #include <asm/uaccess.h>
 
 
@@ -130,6 +131,13 @@
        remount_fs:     ext2_remount,
 };
 
+/* Yes, All of these are left as NULL!!
+ * A NULL value implies the default, which works with ext2-like file
+ * systems, but can be improved upon.  One day they will be.
+ */
+static struct nfsd_operations ext2_nfsd_ops = {
+};
+
 /*
  * This function has been shamelessly adapted from the msdos fs
  */
@@ -591,6 +599,7 @@
         * set up enough so that it can read an inode
         */
        sb->s_op = &ext2_sops;
+       sb->s_nfsd_op = &ext2_nfsd_ops;
        sb->s_root = d_alloc_root(iget(sb, EXT2_ROOT_INO));
        if (!sb->s_root) {
                for (i = 0; i < db_count; i++)
--- ./fs/nfsd/nfsfh.c   2000/07/27 04:43:11     1.1
+++ ./fs/nfsd/nfsfh.c   2000/08/03 02:50:36
@@ -15,6 +15,7 @@
 #include <linux/string.h>
 #include <linux/stat.h>
 #include <linux/dcache.h>
+#include <linux/module.h>
 #include <asm/pgtable.h>
 
 #include <linux/sunrpc/svc.h>
@@ -28,24 +29,22 @@
 static int nfsd_nr_verified = 0;
 static int nfsd_nr_put = 0;
 
+extern struct nfsd_operations nfsd_op_default;
+
+#define        CALL(ops,fun) ((ops->fun)?(ops->fun):nfsd_op_default.fun)
+
 
 struct nfsd_getdents_callback {
-       struct qstr *name;      /* name that was found. name->name already points to a 
buffer */
+       char *name;             /* name that was found. It already points to a buffer 
+NAME_MAX+1 is size */
        unsigned long ino;      /* the inum we are looking for */
        int found;              /* inode matched? */
        int sequence;           /* sequence counter */
 };
 
-/*
- * A rather strange filldir function to capture
- * the name matching the specified inode number.
- */
 static int filldir_one(void * __buf, const char * name, int len,
                        off_t pos, ino_t ino)
 {
        struct nfsd_getdents_callback *buf = __buf;
-       struct qstr *qs = buf->name;
-       char *nbuf = (char*)qs->name; /* cast is to get rid of "const" */
        int result = 0;
 
        buf->sequence++;
@@ -53,22 +52,26 @@
 dprintk("filldir_one: seq=%d, ino=%ld, name=%s\n", buf->sequence, ino, name);
 #endif
        if (buf->ino == ino) {
-               qs->len = len;
-               memcpy(nbuf, name, len);
-               nbuf[len] = '\0';
+               memcpy(buf->name, name, len);
+               buf->name[len] = '\0';
                buf->found = 1;
                result = -1;
        }
        return result;
 }
 
-/*
- * Read a directory and return the name of the specified entry.
- * i_sem is already down().
- * The whole thing is a total BS. It should not be done via readdir(), damnit!
- * Oh, well, as soon as it will be in filesystems...
+/**
+ * nfsd_get_name - default nfsd_operations->get_name function
+ * @dentry: the directory in which to find a name
+ * @name:   a pointer to a %NAME_MAX+1 char buffer to store the name
+ * @child:  the dentry for the child directory.
+ *
+ * calls readdir on the parent until it finds an entry with
+ * the same inode number as the child, and returns that.
  */
-static int get_ino_name(struct dentry *dentry, struct qstr *name, unsigned long ino)
+
+static int nfsd_get_name(struct dentry *dentry, char *name,
+                 struct dentry *child)
 {
        struct inode *dir = dentry->d_inode;
        int error;
@@ -92,7 +95,7 @@
                goto out_close;
 
        buffer.name = name;
-       buffer.ino = ino;
+       buffer.ino = child->d_inode->i_ino;
        buffer.found = 0;
        buffer.sequence = 0;
        while (1) {
@@ -116,60 +119,100 @@
        return error;
 }
 
-/* this should be provided by each filesystem in an nfsd_operations interface as
- * iget isn't really the right interface
+/**
+ * nfsd_get_inode - default nfsd_operations->get_inode function
+ * sb:         super_block of target file system.
+ * inump: pointer to 32bit inode number followed by 32bit generation number
+ *
+ * This function abuses iget() to find the inode with a given
+ * inode number, and checks that the generation number is correct.
+ * It assumes that the filesystems read_inode function will return
+ * a "bad_inode" if the inode number is invalid.
+ *
+ * No self-respecting filesystem should use this function, but
+ * it is here for the transition.
  */
-static struct dentry *nfsd_iget(struct super_block *sb, unsigned long ino, __u32 
generation)
+static struct inode *nfsd_get_inode(struct super_block *sb, void *inump)
 {
-
-       /* 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 list_head *lp;
-       struct dentry *result;
+       __u32 *u32p;
+       unsigned long ino;
+       __u32 generation;
+
+       u32p = (__u32*)inump;
+
+       ino = *u32p++;
+       generation = *u32p;
+
+       if (ino == 0)
+               return NULL;
+       
        inode = iget(sb, ino);
        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",
+               dprintk("nfsd_get_inode: 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);
+               return NULL;
        }
+       return inode;
+}
+/*
+ * return an error, or 1 if confirmed acceptable, or 0 if not confirmed
+ */
+static int nfsd_iget(struct super_block *sb, void *inump, struct dentry **dep,
+                    int (*acceptable)(void *context, struct dentry *de),
+                    void *context)
+{
+
+       /* 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 list_head *lp;
+       struct dentry *result;
+       
+       inode = CALL(sb->s_nfsd_op,get_inode)(sb, inump);
+       if (inode == NULL)
+               return -ESTALE;
+       if (IS_ERR(inode))
+               return PTR_ERR(inode);
        /* now to find a dentry.
         * If possible, get a well-connected one
         */
        spin_lock(&dcache_lock);
        for (lp = inode->i_dentry.next; lp != &inode->i_dentry ; lp=lp->next) {
                result = list_entry(lp,struct dentry, d_alias);
-               if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
+               if (acceptable(context, result)) {
                        dget_locked(result);
                        spin_unlock(&dcache_lock);
                        iput(inode);
-                       return result;
+                       *dep = result;
+                       return 1;
                }
        }
        spin_unlock(&dcache_lock);
        result = d_alloc_root(inode);
        if (result == NULL) {
                iput(inode);
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
        result->d_flags |= DCACHE_NFSD_DISCONNECTED;
        d_rehash(result); /* so a dput won't loose it */
-       return result;
+       *dep = result;
+       return 0; /* not confirmed */
 }
 
 /* this routine links an IS_ROOT dentry into the dcache tree.  It gains "parent"
@@ -185,7 +228,6 @@
        if (!(target->d_flags & DCACHE_NFSD_DISCONNECTED))
                printk("nfsd: d_splice with non-DISCONNECTED target: %s/%s\n", 
parent->d_name.name, name->name);
 #endif
-       name->hash = full_name_hash(name->name, name->len);
        tdentry = d_alloc(parent, name);
        if (tdentry == NULL)
                return -ENOMEM;
@@ -228,11 +270,16 @@
        return 0;
 }
 
-/* this routine finds the dentry of the parent of a given directory
- * it should be in the filesystem accessed by nfsd_operations
- * it assumes lookup("..") works.
+
+
+/**
+ * nfsd_get_parent - default nfsd_operations->get_parent function
+ * child:   directory to find parent of
+ *
+ * This does a fairly simple lookup of ".." from the child to
+ * find the parent.
  */
-struct dentry *nfsd_findparent(struct dentry *child)
+static struct dentry *nfsd_get_parent(struct dentry *child)
 {
        struct dentry *tdentry, *pdentry;
        tdentry = d_alloc(child, &(const struct qstr) {"..", 2, 0});
@@ -277,11 +324,13 @@
        return pdentry;
 }
 
-static struct dentry *splice(struct dentry *child, struct dentry *parent)
+
+
+static struct dentry *splice(struct nfsd_operations *nops, struct dentry *child, 
+struct dentry *parent)
 {
        int err = 0;
        struct qstr qs;
-       char namebuf[256];
+       char namebuf[NAME_MAX+1];
        struct list_head *lp;
        struct dentry *tmp;
        /* child is an IS_ROOT (anonymous) dentry, but it is hypothesised that
@@ -312,14 +361,17 @@
        }
        spin_unlock(&dcache_lock);
        /* well, if we can find a name for child in parent, it should be safe to 
splice it in */
-       err = get_ino_name(parent, &qs, child->d_inode->i_ino);
+       err = CALL(nops,get_name)(parent, namebuf, child);
        if (err)
                goto out;
+       qs.name = namebuf;
+       qs.len = strlen(namebuf);
+       qs.hash = full_name_hash(qs.name, qs.len);
        tmp = d_lookup(parent, &qs);
        if (tmp) {
                /* Now that IS odd.  I wonder what it means... */
                err = -EEXIST;
-               printk("nfsd-fh: found a name that I didn't expect: %s/%s\n", 
parent->d_name.name, qs.name);
+               printk("nfsd_fh: found a name that I didn't expect: %s/%s\n", 
+parent->d_name.name, qs.name);
                dput(tmp);
                goto out;
        }
@@ -333,6 +385,12 @@
                return child;
 }
 
+static int accept_ok(void *context, struct dentry *de)
+{
+       return 1;
+}
+
+
 /*
  * This is the basic lookup mechanism for turning an NFS file handle
  * into a dentry.
@@ -340,13 +398,44 @@
  * we try to find the parent, and the parent of that and so-on until a
  * connection if made.
  */
-static struct dentry *
-find_fh_dentry(struct super_block *sb, ino_t ino, int generation, ino_t dirino, int 
needpath)
+
+/**
+ * nfsd_find_fh_dentry - helper routine to implement nfsd_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 of they are 
+acceptable
+ * @context:   A parameter to @acceptable so that it knows on what basis to judge.
+ *
+ * nfsd_find_fh_dentry is the central helper routine to enable file systems to provide
+ * the decode_fh() nfsd_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 
+nfsd_find_fh_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 a pointer to the start of the filehandle 
+fragment, and
+ * one 8 bytes in to the fragment.  It is expected that most filesystems will take 
+this
+ * approach, though the offset to the parent identifier may well be different.
+ *
+ * nfsd_find_fh_dentry() will call get_inode to get an inode pointer from the file 
+system.  If
+ * any &dentry in the i_dentry list is acceptable, it will be returned.  Otherwise
+ * nfsd_find_fh_dentry() will attempt to splace a new &dentry into the dcache using 
+get_name and
+ * get_parent to find the appropriate place.
+ *
+ */
+#undef nfsd_find_fh_dentry
+struct dentry *nfsd_find_fh_dentry(struct super_block *sb, void *obj, void *parent,
+                                  int (*acceptable)(void *context, struct dentry *de),
+                                  void *context)
 {
        struct dentry *dentry, *result = NULL;
        struct dentry *tmp;
        int  found =0;
        int err;
+       struct nfsd_operations *nops = sb->s_nfsd_op;
+       
        /* the sb->s_nfsd_free_path_sem semaphore is needed to make sure that only one 
unconnected (free)
         * dcache path ever exists, as otherwise two partial paths might get
         * joined together, which would be very confusing.
@@ -355,23 +444,14 @@
         */
 
 
-       nfsdstats.fh_lookup++;
        /*
         * Attempt to find the inode.
         */
  retry:
-       result = nfsd_iget(sb, ino, generation);
-       err = PTR_ERR(result);
-       if (IS_ERR(result))
+       err = nfsd_iget(sb, obj, &result, acceptable, context);
+       if (err < 0)
                goto err_out;
-       err = -ESTALE;
-       if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED))
-               return result;
-
-       /* result is now an anonymous dentry, which may be adequate as it stands, or 
else
-        * will get spliced into the dcache tree */
-
-       if (!S_ISDIR(result->d_inode->i_mode) && ! needpath) {
+       if (err == 1 || acceptable(context, result)) {
                nfsdstats.fh_anon++;
                return result;
        }
@@ -379,12 +459,16 @@
        /* It's a directory, or we are required to confirm the file's
         * location in the tree.
         */
-       dprintk("nfs_fh: need to look harder for %d/%ld\n",sb->s_dev,ino);
+       dprintk("nfs_fh: need to look harder for %d/%d\n",sb->s_dev,*(int*)obj);
        down(&sb->s_nfsd_free_path_sem);
 
        /* claiming the semaphore might have allowed things to get fixed up */
        if (! (result->d_flags & DCACHE_NFSD_DISCONNECTED)) {
                up(&sb->s_nfsd_free_path_sem);
+               if (!acceptable(context, result)) {
+                       dput(result);
+                       result = NULL;
+               }
                return result;
        }
 
@@ -392,13 +476,12 @@
        found = 0;
        if (!S_ISDIR(result->d_inode->i_mode)) {
                nfsdstats.fh_nocache_nondir++;
-               if (dirino == 0)
+               if (parent == NULL)
                        goto err_result; /* don't know how to find parent */
                else {
                        /* need to iget dirino and make sure this inode is in that 
directory */
-                       dentry = nfsd_iget(sb, dirino, 0);
-                       err = PTR_ERR(dentry);
-                       if (IS_ERR(dentry))
+                       err = nfsd_iget(sb, parent, &dentry, accept_ok, NULL);
+                       if (err < 0)
                                goto err_result;
                        err = -ESTALE;
                        if (!dentry->d_inode
@@ -407,7 +490,7 @@
                        }
                        if ((!dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
                                found = 1;
-                       tmp = splice(result, dentry);
+                       tmp = splice(nops, result, dentry);
                        err = PTR_ERR(tmp);
                        if (IS_ERR(tmp))
                                goto err_dentry;
@@ -432,7 +515,7 @@
                struct dentry *pdentry;
                struct inode *parent;
 
-               pdentry = nfsd_findparent(dentry);
+               pdentry = CALL(nops,get_parent)(dentry);
                err = PTR_ERR(pdentry);
                if (IS_ERR(pdentry))
                        goto err_dentry;
@@ -446,7 +529,7 @@
                if (!(dentry->d_flags & DCACHE_NFSD_DISCONNECTED))
                        found = 1;
 
-               tmp = splice(dentry, pdentry);
+               tmp = splice(nops, dentry, pdentry);
                if (tmp != dentry) {
                        /* Something wrong.  We need to drop thw whole dentry->result 
path
                         * whatever it was
@@ -474,6 +557,10 @@
        }
        dput(dentry);
        up(&sb->s_nfsd_free_path_sem);
+       if (!acceptable(context, result)) {
+               dput(result);
+               result = NULL;
+       }
        return result;
 
 err_dentry:
@@ -487,6 +574,55 @@
        return ERR_PTR(err);
 }
 
+
+
+/**
+ * nfsd_decode_fh - default nfsd_operations->decode_fh function
+ * sb:  The superblock
+ * fh:  pointer to the file handle fragment
+ * fh_len: length of file handle fragment
+ * acceptable: function for testing acceptability of dentrys
+ * context:   context for @acceptable
+ *
+ * This default decode_fh() function assumes that the object identifier
+ * is at the start of the fragment, and that the parent identifier, if
+ * present, is 8 bytes in.
+ */
+struct dentry *nfsd_decode_fh(struct super_block *sb, char *fh, int fh_len,
+                        int (*acceptable)(void *context, struct dentry *de),
+                        void *context)
+{
+       char *parent = fh+8;
+       if (fh_len <=8)
+               parent = NULL;
+       return nfsd_find_fh_dentry(sb, fh, parent,
+                                  acceptable, context);
+}
+
+/*
+ * our acceptability function.
+ * if NOSUBTREECHECK, accept anything
+ * if not, require that we can walk up to exp->ex_dentry
+ * doing some checks on the 'x' bits
+ */
+int nfsd_acceptable(void *expv, struct dentry *dentry)
+{
+       struct svc_export *exp = expv;
+       struct dentry *tdentry;
+       if (exp->ex_flags & NFSEXP_NOSUBTREECHECK)
+               return 1;
+
+       for (tdentry = dentry;
+            tdentry != exp->ex_dentry && ! IS_ROOT(tdentry);
+            tdentry = tdentry->d_parent) {
+               /* make sure parents give x permission to user */
+               if (permission(tdentry->d_parent->d_inode, S_IXOTH)<0)
+                       break;
+       }
+       return tdentry == exp->ex_dentry;
+}
+
+
 /*
  * Perform sanity checks on the dentry in a client's file handle.
  *
@@ -551,7 +687,6 @@
 
                if (!exp) {
                        /* export entry revoked */
-                       nfsdstats.fh_stale++;
                        goto out;
                }
 
@@ -571,44 +706,37 @@
                /*
                 * Look up the dentry using the NFS file handle.
                 */
-               error = nfserr_stale;
-               if (rqstp->rq_vers == 3)
-                       error = nfserr_badhandle;
 
                if (fh->fh_version == 1) {
-                       /* if fileid_type != 0, and super_operations provide 
fh_to_dentry lookup,
-                        *  then should use that */
-                       switch (fh->fh_fileid_type) {
-                       case 0:
+                       if (fh->fh_fileid_type == 0) {
                                dentry = dget(exp->ex_dentry);
-                               break;
-                       case 1:
-                               if ((data_left-=2)<0) goto out;
-                               dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-                                                       datap[0], datap[1],
-                                                       0,
-                                                       !(exp->ex_flags & 
NFSEXP_NOSUBTREECHECK));
-                               break;
-                       case 2:
-                               if ((data_left-=3)<0) goto out;
-                               dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-                                                       datap[0], datap[1],
-                                                       datap[2],
-                                                       !(exp->ex_flags & 
NFSEXP_NOSUBTREECHECK));
-                               break;
-                       default: goto out;
+                       } else {
+                               struct nfsd_operations *nop = 
+exp->ex_mnt->mnt_sb->s_nfsd_op;
+                               dentry  = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
+                                                       (char*)datap, 
+fh->fh_fileid_type,
+                                                       nfsd_acceptable, exp);
+                               
+                               nfsdstats.fh_lookup++;
                        }
                } else {
+                       struct nfsd_operations *nop = exp->ex_mnt->mnt_sb->s_nfsd_op;
+                       __u32 handle[4];
+                       handle[0] = fh->ofh_ino;
+                       handle[1] = fh->ofh_generation;
+                       handle[2] = fh->ofh_dirino;
+                       handle[3] = 0;
+                       dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
+                                               (char*)handle, fh->ofh_dirino?12:8,
+                                               nfsd_acceptable, exp);
 
-                       dentry = find_fh_dentry(exp->ex_dentry->d_inode->i_sb,
-                                               fh->ofh_ino, fh->ofh_generation,
-                                               fh->ofh_dirino,
-                                               !(exp->ex_flags & 
NFSEXP_NOSUBTREECHECK));
+                       nfsdstats.fh_lookup++;
                }
-               if (IS_ERR(dentry)) {
-                       error = nfserrno(PTR_ERR(dentry));
+               error = nfserr_stale;
+               if (rqstp->rq_vers == 3 && dentry == NULL)
+                       error = nfserr_badhandle;
+               if (dentry == NULL || IS_ERR(dentry))
                        goto out;
-               }
+               /* now choose the fhandle that we want */
 #ifdef NFSD_PARANOIA
                if (S_ISDIR(dentry->d_inode->i_mode) &&
                    (dentry->d_flags & DCACHE_NFSD_DISCONNECTED)) {
@@ -648,48 +776,8 @@
                goto out;
        }
 
-       /*
-        * Security: Check that the export is valid for dentry <[EMAIL PROTECTED]>
-        */
-       error = 0;
-
-       if (!(exp->ex_flags & NFSEXP_NOSUBTREECHECK)) {
-               if (exp->ex_dentry != dentry) {
-                       struct dentry *tdentry = dentry;
-
-                       do {
-                               tdentry = tdentry->d_parent;
-                               if (exp->ex_dentry == tdentry)
-                                       break;
-                               /* executable only by root and we can't be root */
-                               if (current->fsuid
-                                   && (exp->ex_flags & NFSEXP_ROOTSQUASH)
-                                   && !(tdentry->d_inode->i_uid
-                                        && (tdentry->d_inode->i_mode & S_IXUSR))
-                                   && !(tdentry->d_inode->i_gid
-                                        && (tdentry->d_inode->i_mode & S_IXGRP))
-                                   && !(tdentry->d_inode->i_mode & S_IXOTH)
-                                       ) {
-                                       error = nfserr_stale;
-                                       nfsdstats.fh_stale++;
-                                       dprintk("fh_verify: no root_squashed 
access.\n");
-                               }
-                       } while ((tdentry != tdentry->d_parent));
-                       if (exp->ex_dentry != tdentry) {
-                               error = nfserr_stale;
-                               nfsdstats.fh_stale++;
-                               printk("nfsd Security: %s/%s bad export.\n",
-                                      dentry->d_parent->d_name.name,
-                                      dentry->d_name.name);
-                               goto out;
-                       }
-               }
-       }
-
        /* Finally, check access permissions. */
-       if (!error) {
-               error = nfsd_permission(exp, dentry, access);
-       }
+       error = nfsd_permission(exp, dentry, access);
 #ifdef NFSD_PARANOIA
        if (error) {
                printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
@@ -697,9 +785,46 @@
        }
 #endif
 out:
+       if (error == nfserr_stale)
+               nfsdstats.fh_stale++;
        return error;
 }
 
+
+/**
+ * nfsd_encode_fh - default nfsd_operations->encode_fh function
+ * dentry:  the dentry to encode
+ * fh:      where to stor the file handle fragment
+ * max_len: maximum length to store there
+ * connectable: whether to store parent infomation
+ *
+ * This default encode_fh function assumes that the 32 inode number
+ * is suitable for locating an inode, and that the generation number
+ * can be used to check that it is still valid.  It places them in the
+ * filehandle fragment where nfsd_decode_fh expects to find them.
+ */
+int nfsd_encode_fh(struct dentry *dentry, char *fh, int max_len,
+                  int connectable)
+{
+       struct inode * inode = dentry->d_inode;
+       struct inode *parent = dentry->d_parent->d_inode;
+       __u32 new[4];
+       int cnt = 8;
+       
+       if (max_len < 8 || (connectable && max_len < 16))
+               return -ENOSPC;
+
+       new[0] = inode->i_ino;
+       new[1] = inode->i_generation;
+       if (connectable && !S_ISDIR(inode->i_mode)) {
+               new[2] = parent->i_ino;
+               new[3] = parent->i_generation;
+               cnt= 16;
+       }
+       memcpy(fh, new, cnt);
+       return cnt;
+}
+
 /*
  * Compose a file handle for an NFS reply.
  *
@@ -710,20 +835,24 @@
 inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
                      __u32 **datapp, int maxsize)
 {
-       __u32 *datap= *datapp;
+       struct nfsd_operations *nop = exp->ex_mnt->mnt_sb->s_nfsd_op;
+       int len, len2;
+       char *datap= (char*) *datapp;
+
        if (dentry == exp->ex_dentry)
                return 0;
-       /* if super_operations provides dentry_to_fh lookup, should use that */
-       
-       *datap++ = ino_t_to_u32(dentry->d_inode->i_ino);
-       *datap++ = dentry->d_inode->i_generation;
-       if (S_ISDIR(dentry->d_inode->i_mode) || (exp->ex_flags & 
NFSEXP_NOSUBTREECHECK)){
-               *datapp = datap;
-               return 1;
-       }
-       *datap++ = ino_t_to_u32(dentry->d_parent->d_inode->i_ino);
-       *datapp = datap;
-       return 2;
+
+       len = CALL(nop,encode_fh)(dentry, datap, maxsize,
+                            !(exp->ex_flags&NFSEXP_NOSUBTREECHECK));
+       if (len<0)
+               return len;
+
+       /* round to four-byte boundry */
+       len2=len;
+       while (len2&3)
+               datap[len2++] = 0;
+       *datapp = (__u32*) (datap+len2);
+       return len;
 }
 
 int
@@ -732,6 +861,7 @@
        struct inode * inode = dentry->d_inode;
        struct dentry *parent = dentry->d_parent;
        __u32 *datap;
+       int err;
 
        dprintk("nfsd: fh_compose(exp %x/%ld %s/%s, ino=%ld)\n",
                exp->ex_dev, (long) exp->ex_ino,
@@ -757,16 +887,16 @@
        *datap++ = htonl((MAJOR(exp->ex_dev)<<16)| MINOR(exp->ex_dev));
        *datap++ = ino_t_to_u32(exp->ex_ino);
 
-       if (inode)
-               fhp->fh_handle.fh_fileid_type =
-                       _fh_update(dentry, exp, &datap, fhp->fh_maxsize-3);
+       if (inode) {
+               err = _fh_update(dentry, exp, &datap, fhp->fh_maxsize-3*4);
+               if (err < 0)
+                       return nfserr_opnotsupp;
+               fhp->fh_handle.fh_fileid_type = err;
+       }
 
        fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
 
-
        nfsd_nr_verified++;
-       if (fhp->fh_handle.fh_fileid_type == 255)
-               return nfserr_opnotsupp;
        return 0;
 }
 
@@ -779,6 +909,7 @@
 {
        struct dentry *dentry;
        __u32 *datap;
+       int err;
        
        if (!fhp->fh_dentry)
                goto out_bad;
@@ -790,8 +921,10 @@
                goto out_uptodate;
        datap = fhp->fh_handle.fh_auth+
                          fhp->fh_handle.fh_size/4 -1;
-       fhp->fh_handle.fh_fileid_type =
-               _fh_update(dentry, fhp->fh_export, &datap, 
fhp->fh_maxsize-fhp->fh_handle.fh_size);
+       err = _fh_update(dentry, fhp->fh_export, &datap, 
+fhp->fh_maxsize-fhp->fh_handle.fh_size);
+       if (err<0)
+               return nfserr_opnotsupp;
+       fhp->fh_handle.fh_fileid_type = err;
        fhp->fh_handle.fh_size = (datap-fhp->fh_handle.fh_auth+1)*4;
 out:
        return 0;
@@ -824,3 +957,14 @@
        }
        return;
 }
+
+struct nfsd_operations nfsd_op_default = {
+       decode_fh:      nfsd_decode_fh,
+       encode_fh:      nfsd_encode_fh,
+
+       get_name:       nfsd_get_name,
+       get_parent:     nfsd_get_parent,
+       get_inode:      nfsd_get_inode,
+};
+
+EXPORT_SYMBOL(nfsd_find_fh_dentry);
--- ./fs/nfsd/export.c  2000/08/01 07:00:53     1.1
+++ ./fs/nfsd/export.c  2000/08/02 06:07:22
@@ -211,8 +211,7 @@
                goto finish;
 
        err = -EINVAL;
-       if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) ||
-           inode->i_sb->s_op->read_inode == NULL) {
+       if (inode->i_sb->s_nfsd_op == NULL) {
                dprintk("exp_export: export of invalid fs type.\n");
                goto finish;
        }
--- ./fs/nfsd/Makefile  2000/08/02 23:43:54     1.1
+++ ./fs/nfsd/Makefile  2000/08/02 23:44:31
@@ -8,9 +8,10 @@
 # Note 2! The CFLAGS definitions are now in the main makefile.
 
 O_TARGET := nfsd.o
-O_OBJS   := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \
+O_OBJS   := nfssvc.o nfsctl.o nfsproc.o vfs.o \
            export.o auth.o lockd.o nfscache.o nfsxdr.o \
            stats.o
+OX_OBJS  := nfsfh.o
 ifdef CONFIG_NFSD_V3
   O_OBJS += nfs3proc.o nfs3xdr.o
 endif
--- ./fs/nfsd/nfsctl.c  2000/08/02 23:48:16     1.1
+++ ./fs/nfsd/nfsctl.c  2000/08/03 00:56:22
@@ -312,8 +312,12 @@
 EXPORT_NO_SYMBOLS;
 MODULE_AUTHOR("Olaf Kirch <[EMAIL PROTECTED]>");
 
-extern long (*do_nfsservctl)(int, void *, void *);
+#undef nfsd_find_fh_dentry
 
+struct nfsd_linkage nfsd_linkage_s = {
+       do_nfsservctl:  handle_sys_nfsservctl,
+       find_fh_dentry: nfsd_find_fh_dentry,
+};
 /*
  * Initialize the module
  */
@@ -321,7 +325,7 @@
 init_module(void)
 {
        printk(KERN_INFO "Installing knfsd (copyright (C) 1996 [EMAIL PROTECTED]).\n");
-       do_nfsservctl = handle_sys_nfsservctl;
+       nfsd_linkage = &nfsd_linkage_s;
        return 0;
 }
 
@@ -331,7 +335,7 @@
 void
 cleanup_module(void)
 {
-       do_nfsservctl = NULL;
+       nfsd_linkage = NULL;
        nfsd_export_shutdown();
        nfsd_cache_shutdown();
        remove_proc_entry("fs/nfs/exports", NULL);
@@ -340,3 +344,4 @@
        nfsd_lockd_shutdown();
 }
 #endif
+
--- ./fs/filesystems.c  2000/08/02 23:32:32     1.1
+++ ./fs/filesystems.c  2000/08/03 00:51:59
@@ -16,12 +16,13 @@
 #include <linux/major.h>
 #include <linux/smp.h>
 #include <linux/smp_lock.h>
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 #include <linux/lockd/bind.h>
 #include <linux/lockd/xdr.h>
 #include <linux/init.h>
+#ifdef CONFIG_NFSD_MODULE
+#include <linux/nfsd/ops.h>
+#endif
 
 #ifdef CONFIG_CODA_FS
 extern int init_coda(void);
@@ -48,32 +49,29 @@
 #endif
 }
 
-#ifndef CONFIG_NFSD
 #ifdef CONFIG_NFSD_MODULE
-long (*do_nfsservctl)(int, void *, void *);
-#endif
+struct nfsd_linkage *nfsd_linkage = NULL;
+
 long
 asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
 {
-#ifndef CONFIG_NFSD_MODULE
-       return -ENOSYS;
-#else
        int ret = -ENOSYS;
-       
+
        lock_kernel();
-       if (do_nfsservctl) {
-               ret = do_nfsservctl(cmd, argp, resp);
-               goto out;
-       }
-#ifdef CONFIG_KMOD
-       if (request_module ("nfsd") == 0) {
-               if (do_nfsservctl)
-                       ret = do_nfsservctl(cmd, argp, resp);
-       }
-#endif /* CONFIG_KMOD */
-out:
+       if (nfsd_linkage ||
+           (request_module ("nfsd") == 0 && nfsd_linkage))
+               ret = nfsd_linkage->do_nfsservctl(cmd, argp, resp);
+
        unlock_kernel();
+
        return ret;
-#endif /* CONFIG_NFSD_MODULE */
 }
-#endif /* CONFIG_NFSD */
+#else
+# ifndef CONFIG_NFSD
+long
+asmlinkage sys_nfsservctl(int cmd, void *argp, void *resp)
+{
+       return -ENOSYS;
+}
+# endif
+#endif /* CONFIG_NFSD_MODULE */
--- ./kernel/ksyms.c    2000/08/02 23:49:03     1.1
+++ ./kernel/ksyms.c    2000/08/03 00:12:00
@@ -45,6 +45,9 @@
 #include <linux/highuid.h>
 #include <linux/brlock.h>
 #include <linux/fs.h>
+#ifdef CONFIG_NFSD_MODULE
+#include <linux/nfsd/ops.h>
+#endif
 
 #if defined(CONFIG_PROC_FS)
 #include <linux/proc_fs.h>
@@ -55,9 +58,6 @@
 
 extern int console_loglevel;
 extern void set_device_ro(kdev_t dev,int flag);
-#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
-extern long (*do_nfsservctl)(int, void *, void *);
-#endif
 
 extern void *sys_call_table;
 
@@ -265,8 +265,8 @@
 EXPORT_SYMBOL(filemap_sync);
 EXPORT_SYMBOL(lock_page);
 
-#if !defined(CONFIG_NFSD) && defined(CONFIG_NFSD_MODULE)
-EXPORT_SYMBOL(do_nfsservctl);
+#if defined(CONFIG_NFSD_MODULE)
+EXPORT_SYMBOL(nfsd_linkage);
 #endif
 
 /* device registration */
--- ./include/linux/nfsd/ops.h  2000/07/27 04:32:36     1.1
+++ ./include/linux/nfsd/ops.h  2000/08/03 01:58:08
@@ -0,0 +1,125 @@
+/**
+ * &nfsd_operations - for nfsd to communicate with file systems
+ * decode_fh:      decode a file handle fragment and return a &struct dentry
+ * encode_fh:      encode a file handle fragment from a dentry
+ * get_name:       find the name for a given inode in a given directory
+ * get_parent:     find the parent of a given directory
+ * get_inode:      find the inode given a file handle sub-fragment
+ *
+ * Description:
+ *    The nfsd_operations structure provides a means for nfsd to communicate
+ *    with a particular exported file system  - particularly enabling nfsd and
+ *    the filesystem to co-operate when dealing with file handles.
+ *
+ *    nfsd_operations contains two basic operation for dealing with file handles,
+ *    decode_fh() and encode_fh(), and allows for some other operations to be defined
+ *    which standard helper routines use to get specific information from th
+ *    filesystem.
+ *
+ *    nfsd encodes information use to determine which filesystem a filehandle
+ *    applies to in the initial part of the file handle.  The remained, termed a
+ *    file handle fragment, is controlled completely by the filesystem.
+ *    The standard helper routines assume that this fragment will contain one or two
+ *    sub-fragments, one which identifies the file, and one which may be used to
+ *    identify the (a) directory containing the file.
+ *
+ *    In some situations, nfsd need to get a dentry which is connected into a
+ *    specific part of the file tree.  To allow for this, it passes the function
+ *    acceptable() together with a @context which can be used to see if the dentry
+ *    is acceptable.  As there can be multiple dentry for a given file, the filesystem
+ *    should check each one for acceptability before looking for the next.  As soon
+ *    as an acceptable one is found, it should be returned.
+ *
+ * decode_fh:
+ *    @decode_fh is given a &struct super_block (@sb), a file handle fragment (@fh, 
+@fh_len)
+ *    and an acceptability testing function (@acceptable, @context).  It should return
+ *    a &struct dentry which refers to the same file that the file handle fragment 
+refers
+ *    to,  and which passes the acceptability test.  If it cannot, it should return
+ *    a %NULL pointer if the file was found but no acceptable &dentries were 
+available, or
+ *    a %ERR_PTR error code indicating why it couldn't be found (e.g. %ENOENT or 
+%ENOMEM).
+ *
+ * encode_fh:
+ *    @encode_fh should store in the file handle fragment @fh (using atmost @max_len 
+bytes)
+ *    information that can be used by @decode_fh to recover the file refered to by the
+ *    &struct dentry @de.  If the @connectable flag is set, the encode_fh() should 
+store
+ *    sufficient information so that a good attempt can be made to find not only
+ *    the file but also it's place in the filesystem.   This typically means storing
+ *    a reference to de->d_parent in the filehandle fragment.
+ *    encode_fh() should return the number of bytes stored or a negative error code
+ *    such as %-ENOSPC
+ *
+ * get_name:
+ *    @get_name should find a name for the given @child in the given @parent 
+directory.
+ *    The name should be stored in the @name (with the understanding that it is 
+already
+ *    pointing to a a %NAME_MAX+1 sized buffer.   get_name() should return %0 on 
+success,
+ *    a negative error code.
+ *    @get_name will be called with the parents i_sem claimed.
+ *
+ * get_parent:
+ *    @get_parent should find the parent directory for the given @child which is also
+ *    a directory.  In the event that it cannot be found, or storage space cannot be
+ *    allocated, a %ERR_PTR should be returned.
+ *
+ * get_inode:
+ *    Given a &super_block (@sb) and a pointer to a file-system specific inode 
+identifier,
+ *    possibly an inode number (@inump), get_inode() should find the identified inode.
+ *    If it cannot be found, either a %NULL pointer or an %ERR_PTR code can be 
+returned.
+ *    The %inump will be whatever was passed to nfsd_find_fh_dentry() in either the
+ *    @obj or @parent parameters.
+ */
+
+struct nfsd_operations {
+       struct dentry *(*decode_fh)(struct super_block *sb, char *fh, int fh_len,
+                        int (*acceptable)(void *context, struct dentry *de),
+                        void *context);
+       int (*encode_fh)(struct dentry *de, char *fh, int max_len,
+                        int connectable);
+
+       /* the following are only called from the filesystem itself */
+       int (*get_name)(struct dentry *parent, char *name,
+                       struct dentry *child);
+       struct dentry * (*get_parent)(struct dentry *child);
+       struct inode *(*get_inode)(struct super_block *sb, void *inump);
+
+};
+
+
+
+/**
+ * &nfsd_linkage - structure for nfsd to register it's presence
+ * do_nfsservctl:  handler for sys_nfsservctl syscall
+ * find_fh_dentry: helper for finding dentry from filehandle
+ *
+ * When nfsd is compiled as a module, it registers it's presence
+ * by setting the global variable $nfsd_linkage to be a pointer to
+ * an appropriate &struct nfsd_linkage.  This currently has two fields.
+ *
+ * @do_nfsservctl should contain a pointer to the implementation of
+ * the sy_nfsservctl system call.
+ *
+ * @find_fh_dentry is a helper function that filesystems may use
+ * to help convert a filehandle into a &dentry.  It inturn calls the
+ * private entry points in the &nfsd_operations structure: get_name,
+ * get_parent and get_inode.
+ *
+ * When nfsd is compiled in the the kernel, or not included at all,
+ * this structure is not used and the linkage to these routines is
+ * more direct.
+ **/
+
+struct dentry * nfsd_find_fh_dentry(struct super_block *sb, void *obj, void *parent,
+                                   int (*acceptable)(void *context, struct dentry 
+*de),
+                                   void *context);
+
+#ifdef CONFIG_NFSD_MODULE
+
+extern struct nfsd_linkage {
+       long (*do_nfsservctl)(int cmd, void *argp, void *resp);
+       struct dentry * (*find_fh_dentry)(struct super_block *sb, void *obj, void 
+*parent,
+                                         int (*acceptable)(void *context, struct 
+dentry *de),
+                                         void *context);
+} *nfsd_linkage;
+
+#define nfsd_find_fh_dentry (nfsd_linkage->find_fh_dentry)
+
+#endif
--- ./include/linux/nfsd/nfsd.h 2000/08/02 04:05:10     1.1
+++ ./include/linux/nfsd/nfsd.h 2000/08/03 02:05:57
@@ -21,6 +21,7 @@
 #include <linux/nfsd/export.h>
 #include <linux/nfsd/auth.h>
 #include <linux/nfsd/stats.h>
+#include <linux/nfsd/ops.h>
 
 /*
  * nfsd version
--- ./include/linux/nfsd/syscall.h      2000/08/03 00:48:03     1.1
+++ ./include/linux/nfsd/syscall.h      2000/08/03 00:55:28
@@ -132,11 +132,8 @@
 /*
  * Kernel syscall implementation.
  */
-#if defined(CONFIG_NFSD) || defined(CONFIG_NFSD_MODULE)
 extern asmlinkage long sys_nfsservctl(int, void *, void *);
-#else
-#define sys_nfsservctl         sys_ni_syscall
-#endif
+
 extern int             exp_addclient(struct nfsctl_client *ncp);
 extern int             exp_delclient(struct nfsctl_client *ncp);
 extern int             exp_export(struct nfsctl_export *nxp);
--- ./include/linux/fs.h        2000/07/27 04:30:23     1.1
+++ ./include/linux/fs.h        2000/08/03 00:22:25
@@ -629,6 +629,7 @@
        struct file_system_type *s_type;
        struct super_operations *s_op;
        struct dquot_operations *dq_op;
+       struct nfsd_operations  *s_nfsd_op;
        unsigned long           s_flags;
        unsigned long           s_magic;
        struct dentry           *s_root;
--- ./arch/ia64/ia32/sys_ia32.c 2000/08/02 23:53:19     1.1
+++ ./arch/ia64/ia32/sys_ia32.c 2000/08/02 23:53:32
@@ -4578,6 +4578,8 @@
        case NFSCTL_GETFH:
                err = nfs_getfh32_trans(karg, arg32);
                break;
+       case NFSCTL_GETFS:
+               /* FIXME */
        default:
                err = -EINVAL;
                break;

Reply via email to