nfsd: move some of fh_compose into helper functions
[safe/jmp/linux-2.6] / fs / nfsd / nfsfh.c
index 0108d3e..78d8ebf 100644 (file)
 
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/svc.h>
+#include <linux/sunrpc/svcauth_gss.h>
 #include <linux/nfsd/nfsd.h>
+#include "auth.h"
 
 #define NFSDDBG_FACILITY               NFSDDBG_FH
 
 
-static int nfsd_nr_verified;
-static int nfsd_nr_put;
-
-extern struct export_operations export_op_default;
-
-#define        CALL(ops,fun) ((ops->fun)?(ops->fun):export_op_default.fun)
-
 /*
  * our acceptability function.
  * if NOSUBTREECHECK, accept anything
@@ -49,11 +44,11 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
                return 1;
 
        tdentry = dget(dentry);
-       while (tdentry != exp->ex_dentry && ! IS_ROOT(tdentry)) {
+       while (tdentry != exp->ex_path.dentry && !IS_ROOT(tdentry)) {
                /* make sure parents give x permission to user */
                int err;
                parent = dget_parent(tdentry);
-               err = permission(parent->d_inode, MAY_EXEC, NULL);
+               err = inode_permission(parent->d_inode, MAY_EXEC);
                if (err < 0) {
                        dput(parent);
                        break;
@@ -61,9 +56,9 @@ static int nfsd_acceptable(void *expv, struct dentry *dentry)
                dput(tdentry);
                tdentry = parent;
        }
-       if (tdentry != exp->ex_dentry)
+       if (tdentry != exp->ex_path.dentry)
                dprintk("nfsd_acceptable failed at %p %s\n", tdentry, tdentry->d_name.name);
-       rv = (tdentry == exp->ex_dentry);
+       rv = (tdentry == exp->ex_path.dentry);
        dput(tdentry);
        return rv;
 }
@@ -98,168 +93,257 @@ nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type)
        return 0;
 }
 
+static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp,
+                                         struct svc_export *exp)
+{
+       /* Check if the request originated from a secure port. */
+       if (!rqstp->rq_secure && EX_SECURE(exp)) {
+               RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
+               dprintk(KERN_WARNING
+                      "nfsd: request from insecure port %s!\n",
+                      svc_print_addr(rqstp, buf, sizeof(buf)));
+               return nfserr_perm;
+       }
+
+       /* Set user creds for this exportpoint */
+       return nfserrno(nfsd_setuser(rqstp, exp));
+}
+
 /*
- * Perform sanity checks on the dentry in a client's file handle.
- *
- * Note that the file handle dentry may need to be freed even after
- * an error return.
- *
- * This is only called at the start of an nfsproc call, so fhp points to
- * a svc_fh which is all 0 except for the over-the-wire file handle.
+ * Use the given filehandle to look up the corresponding export and
+ * dentry.  On success, the results are used to set fh_export and
+ * fh_dentry.
  */
-__be32
-fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
 {
        struct knfsd_fh *fh = &fhp->fh_handle;
-       struct svc_export *exp = NULL;
-       struct dentry   *dentry;
-       __be32          error = 0;
+       struct fid *fid = NULL, sfid;
+       struct svc_export *exp;
+       struct dentry *dentry;
+       int fileid_type;
+       int data_left = fh->fh_size/4;
+       __be32 error;
 
-       dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
+       error = nfserr_stale;
+       if (rqstp->rq_vers > 2)
+               error = nfserr_badhandle;
+       if (rqstp->rq_vers == 4 && fh->fh_size == 0)
+               return nfserr_nofilehandle;
 
-       if (!fhp->fh_dentry) {
-               __u32 *datap=NULL;
-               __u32 tfh[3];           /* filehandle fragment for oldstyle filehandles */
-               int fileid_type;
-               int data_left = fh->fh_size/4;
+       if (fh->fh_version == 1) {
+               int len;
 
-               error = nfserr_stale;
-               if (rqstp->rq_client == NULL)
-                       goto out;
-               if (rqstp->rq_vers > 2)
-                       error = nfserr_badhandle;
-               if (rqstp->rq_vers == 4 && fh->fh_size == 0)
-                       return nfserr_nofilehandle;
-
-               if (fh->fh_version == 1) {
-                       int len;
-                       datap = fh->fh_auth;
-                       if (--data_left<0) goto out;
-                       switch (fh->fh_auth_type) {
-                       case 0: break;
-                       default: goto out;
-                       }
-                       len = key_len(fh->fh_fsid_type) / 4;
-                       if (len == 0) goto out;
-                       if  (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
-                               /* deprecated, convert to type 3 */
-                               len = key_len(FSID_ENCODE_DEV)/4;
-                               fh->fh_fsid_type = FSID_ENCODE_DEV;
-                               fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1])));
-                               fh->fh_fsid[1] = fh->fh_fsid[2];
-                       }
-                       if ((data_left -= len)<0) goto out;
-                       exp = exp_find(rqstp->rq_client, fh->fh_fsid_type, datap, &rqstp->rq_chandle);
-                       datap += len;
-               } else {
-                       dev_t xdev;
-                       ino_t xino;
-                       if (fh->fh_size != NFS_FHSIZE)
-                               goto out;
-                       /* assume old filehandle format */
-                       xdev = old_decode_dev(fh->ofh_xdev);
-                       xino = u32_to_ino_t(fh->ofh_xino);
-                       mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
-                       exp = exp_find(rqstp->rq_client, FSID_DEV, tfh,
-                                      &rqstp->rq_chandle);
+               if (--data_left < 0)
+                       return error;
+               if (fh->fh_auth_type != 0)
+                       return error;
+               len = key_len(fh->fh_fsid_type) / 4;
+               if (len == 0)
+                       return error;
+               if  (fh->fh_fsid_type == FSID_MAJOR_MINOR) {
+                       /* deprecated, convert to type 3 */
+                       len = key_len(FSID_ENCODE_DEV)/4;
+                       fh->fh_fsid_type = FSID_ENCODE_DEV;
+                       fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl(fh->fh_fsid[0]), ntohl(fh->fh_fsid[1])));
+                       fh->fh_fsid[1] = fh->fh_fsid[2];
                }
+               data_left -= len;
+               if (data_left < 0)
+                       return error;
+               exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_auth);
+               fid = (struct fid *)(fh->fh_auth + len);
+       } else {
+               __u32 tfh[2];
+               dev_t xdev;
+               ino_t xino;
+
+               if (fh->fh_size != NFS_FHSIZE)
+                       return error;
+               /* assume old filehandle format */
+               xdev = old_decode_dev(fh->ofh_xdev);
+               xino = u32_to_ino_t(fh->ofh_xino);
+               mk_fsid(FSID_DEV, tfh, xdev, xino, 0, NULL);
+               exp = rqst_exp_find(rqstp, FSID_DEV, tfh);
+       }
 
-               if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN
-                               || PTR_ERR(exp) == -ETIMEDOUT)) {
-                       error = nfserrno(PTR_ERR(exp));
+       error = nfserr_stale;
+       if (PTR_ERR(exp) == -ENOENT)
+               return error;
+
+       if (IS_ERR(exp))
+               return nfserrno(PTR_ERR(exp));
+
+       if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
+               /* Elevate privileges so that the lack of 'r' or 'x'
+                * permission on some parent directory will
+                * not stop exportfs_decode_fh from being able
+                * to reconnect a directory into the dentry cache.
+                * The same problem can affect "SUBTREECHECK" exports,
+                * but as nfsd_acceptable depends on correct
+                * access control settings being in effect, we cannot
+                * fix that case easily.
+                */
+               struct cred *new = prepare_creds();
+               if (!new)
+                       return nfserrno(-ENOMEM);
+               new->cap_effective =
+                       cap_raise_nfsd_set(new->cap_effective,
+                                          new->cap_permitted);
+               put_cred(override_creds(new));
+               put_cred(new);
+       } else {
+               error = nfsd_setuser_and_check_port(rqstp, exp);
+               if (error)
                        goto out;
-               }
+       }
 
-               error = nfserr_stale; 
-               if (!exp || IS_ERR(exp))
-                       goto out;
+       /*
+        * Look up the dentry using the NFS file handle.
+        */
+       error = nfserr_stale;
+       if (rqstp->rq_vers > 2)
+               error = nfserr_badhandle;
+
+       if (fh->fh_version != 1) {
+               sfid.i32.ino = fh->ofh_ino;
+               sfid.i32.gen = fh->ofh_generation;
+               sfid.i32.parent_ino = fh->ofh_dirino;
+               fid = &sfid;
+               data_left = 3;
+               if (fh->ofh_dirino == 0)
+                       fileid_type = FILEID_INO32_GEN;
+               else
+                       fileid_type = FILEID_INO32_GEN_PARENT;
+       } else
+               fileid_type = fh->fh_fileid_type;
+
+       if (fileid_type == FILEID_ROOT)
+               dentry = dget(exp->ex_path.dentry);
+       else {
+               dentry = exportfs_decode_fh(exp->ex_path.mnt, fid,
+                               data_left, fileid_type,
+                               nfsd_acceptable, exp);
+       }
+       if (dentry == NULL)
+               goto out;
+       if (IS_ERR(dentry)) {
+               if (PTR_ERR(dentry) != -EINVAL)
+                       error = nfserrno(PTR_ERR(dentry));
+               goto out;
+       }
 
-               /* Check if the request originated from a secure port. */
-               error = nfserr_perm;
-               if (!rqstp->rq_secure && EX_SECURE(exp)) {
-                       char buf[RPC_MAX_ADDRBUFLEN];
-                       printk(KERN_WARNING
-                              "nfsd: request from insecure port %s!\n",
-                              svc_print_addr(rqstp, buf, sizeof(buf)));
+       if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
+               error = nfsd_setuser_and_check_port(rqstp, exp);
+               if (error) {
+                       dput(dentry);
                        goto out;
                }
+       }
 
-               /* Set user creds for this exportpoint */
-               error = nfserrno(nfsd_setuser(rqstp, exp));
-               if (error)
-                       goto out;
+       if (S_ISDIR(dentry->d_inode->i_mode) &&
+                       (dentry->d_flags & DCACHE_DISCONNECTED)) {
+               printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
+                               dentry->d_parent->d_name.name, dentry->d_name.name);
+       }
 
-               /*
-                * Look up the dentry using the NFS file handle.
-                */
-               error = nfserr_stale;
-               if (rqstp->rq_vers > 2)
-                       error = nfserr_badhandle;
-
-               if (fh->fh_version != 1) {
-                       tfh[0] = fh->ofh_ino;
-                       tfh[1] = fh->ofh_generation;
-                       tfh[2] = fh->ofh_dirino;
-                       datap = tfh;
-                       data_left = 3;
-                       if (fh->ofh_dirino == 0)
-                               fileid_type = 1;
-                       else
-                               fileid_type = 2;
-               } else
-                       fileid_type = fh->fh_fileid_type;
-
-               if (fileid_type == 0)
-                       dentry = dget(exp->ex_dentry);
-               else {
-                       struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
-                       dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
-                                                    datap, data_left,
-                                                    fileid_type,
-                                                    nfsd_acceptable, exp);
-               }
-               if (dentry == NULL)
-                       goto out;
-               if (IS_ERR(dentry)) {
-                       if (PTR_ERR(dentry) != -EINVAL)
-                               error = nfserrno(PTR_ERR(dentry));
-                       goto out;
-               }
+       fhp->fh_dentry = dentry;
+       fhp->fh_export = exp;
+       return 0;
+out:
+       exp_put(exp);
+       return error;
+}
 
-               if (S_ISDIR(dentry->d_inode->i_mode) &&
-                   (dentry->d_flags & DCACHE_DISCONNECTED)) {
-                       printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",
-                              dentry->d_parent->d_name.name, dentry->d_name.name);
-               }
+/**
+ * fh_verify - filehandle lookup and access checking
+ * @rqstp: pointer to current rpc request
+ * @fhp: filehandle to be verified
+ * @type: expected type of object pointed to by filehandle
+ * @access: type of access needed to object
+ *
+ * Look up a dentry from the on-the-wire filehandle, check the client's
+ * access to the export, and set the current task's credentials.
+ *
+ * Regardless of success or failure of fh_verify(), fh_put() should be
+ * called on @fhp when the caller is finished with the filehandle.
+ *
+ * fh_verify() may be called multiple times on a given filehandle, for
+ * example, when processing an NFSv4 compound.  The first call will look
+ * up a dentry using the on-the-wire filehandle.  Subsequent calls will
+ * skip the lookup and just perform the other checks and possibly change
+ * the current task's credentials.
+ *
+ * @type specifies the type of object expected using one of the S_IF*
+ * constants defined in include/linux/stat.h.  The caller may use zero
+ * to indicate that it doesn't care, or a negative integer to indicate
+ * that it expects something not of the given type.
+ *
+ * @access is formed from the NFSD_MAY_* constants defined in
+ * include/linux/nfsd/nfsd.h.
+ */
+__be32
+fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
+{
+       struct svc_export *exp;
+       struct dentry   *dentry;
+       __be32          error;
+
+       dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
 
-               fhp->fh_dentry = dentry;
-               fhp->fh_export = exp;
-               nfsd_nr_verified++;
+       if (!fhp->fh_dentry) {
+               error = nfsd_set_fh_dentry(rqstp, fhp);
+               if (error)
+                       goto out;
+               dentry = fhp->fh_dentry;
+               exp = fhp->fh_export;
        } else {
-               /* just rechecking permissions
-                * (e.g. nfsproc_create calls fh_verify, then nfsd_create does as well)
+               /*
+                * just rechecking permissions
+                * (e.g. nfsproc_create calls fh_verify, then nfsd_create
+                * does as well)
                 */
                dprintk("nfsd: fh_verify - just checking\n");
                dentry = fhp->fh_dentry;
                exp = fhp->fh_export;
-               /* Set user creds for this exportpoint; necessary even
+               /*
+                * Set user creds for this exportpoint; necessary even
                 * in the "just checking" case because this may be a
                 * filehandle that was created by fh_compose, and that
                 * is about to be used in another nfsv4 compound
-                * operation */
-               error = nfserrno(nfsd_setuser(rqstp, exp));
+                * operation.
+                */
+               error = nfsd_setuser_and_check_port(rqstp, exp);
                if (error)
                        goto out;
        }
-       cache_get(&exp->h);
-
 
        error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
        if (error)
                goto out;
 
+       /*
+        * pseudoflavor restrictions are not enforced on NLM,
+        * which clients virtually always use auth_sys for,
+        * even while using RPCSEC_GSS for NFS.
+        */
+       if (access & NFSD_MAY_LOCK)
+               goto skip_pseudoflavor_check;
+       /*
+        * Clients may expect to be able to use auth_sys during mount,
+        * even if they use gss for everything else; see section 2.3.2
+        * of rfc 2623.
+        */
+       if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT
+                       && exp->ex_path.dentry == dentry)
+               goto skip_pseudoflavor_check;
+
+       error = check_nfsd_access(exp, rqstp);
+       if (error)
+               goto out;
+
+skip_pseudoflavor_check:
        /* Finally, check access permissions. */
-       error = nfsd_permission(exp, dentry, access);
+       error = nfsd_permission(rqstp, exp, dentry, access);
 
        if (error) {
                dprintk("fh_verify: %s/%s permission failure, "
@@ -269,8 +353,6 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                        access, ntohl(error));
        }
 out:
-       if (exp && !IS_ERR(exp))
-               exp_put(exp);
        if (error == nfserr_stale)
                nfsdstats.fh_stale++;
        return error;
@@ -284,18 +366,21 @@ out:
  * an inode.  In this case a call to fh_update should be made
  * before the fh goes out on the wire ...
  */
-static inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
-                            __u32 *datap, int *maxsize)
+static void _fh_update(struct svc_fh *fhp, struct svc_export *exp,
+               struct dentry *dentry)
 {
-       struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
+       if (dentry != exp->ex_path.dentry) {
+               struct fid *fid = (struct fid *)
+                       (fhp->fh_handle.fh_auth + fhp->fh_handle.fh_size/4 - 1);
+               int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
+               int subtreecheck = !(exp->ex_flags & NFSEXP_NOSUBTREECHECK);
 
-       if (dentry == exp->ex_dentry) {
-               *maxsize = 0;
-               return 0;
+               fhp->fh_handle.fh_fileid_type =
+                       exportfs_encode_fh(dentry, fid, &maxsize, subtreecheck);
+               fhp->fh_handle.fh_size += maxsize * 4;
+       } else {
+               fhp->fh_handle.fh_fileid_type = FILEID_ROOT;
        }
-
-       return CALL(nop,encode_fh)(dentry, datap, maxsize,
-                         !(exp->ex_flags&NFSEXP_NOSUBTREECHECK));
 }
 
 /*
@@ -312,6 +397,40 @@ static inline void _fh_update_old(struct dentry *dentry,
                fh->ofh_dirino = 0;
 }
 
+static bool is_root_export(struct svc_export *exp)
+{
+       return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root;
+}
+
+static struct super_block *exp_sb(struct svc_export *exp)
+{
+       return exp->ex_path.dentry->d_inode->i_sb;
+}
+
+static bool fsid_type_ok_for_exp(u8 fsid_type, struct svc_export *exp)
+{
+       switch (fsid_type) {
+       case FSID_DEV:
+               if (!old_valid_dev(exp_sb(exp)->s_dev))
+                       return 0;
+               /* FALL THROUGH */
+       case FSID_MAJOR_MINOR:
+       case FSID_ENCODE_DEV:
+               return exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV;
+       case FSID_NUM:
+               return exp->ex_flags & NFSEXP_FSID;
+       case FSID_UUID8:
+       case FSID_UUID16:
+               if (!is_root_export(exp))
+                       return 0;
+               /* fall through */
+       case FSID_UUID4_INUM:
+       case FSID_UUID16_INUM:
+               return exp->ex_uuid != NULL;
+       }
+       return 1;
+}
+
 __be32
 fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
           struct svc_fh *ref_fh)
@@ -329,12 +448,11 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
        struct inode * inode = dentry->d_inode;
        struct dentry *parent = dentry->d_parent;
        __u32 *datap;
-       dev_t ex_dev = exp->ex_dentry->d_inode->i_sb->s_dev;
-       int root_export = (exp->ex_dentry == exp->ex_dentry->d_sb->s_root);
+       dev_t ex_dev = exp_sb(exp)->s_dev;
 
        dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %s/%s, ino=%ld)\n",
                MAJOR(ex_dev), MINOR(ex_dev),
-               (long) exp->ex_dentry->d_inode->i_ino,
+               (long) exp->ex_path.dentry->d_inode->i_ino,
                parent->d_name.name, dentry->d_name.name,
                (inode ? inode->i_ino : 0));
 
@@ -362,54 +480,29 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
                        goto retry;
                }
 
-               /* Need to check that this type works for this
-                * export point.  As the fsid -> filesystem mapping
-                * was guided by user-space, there is no guarantee
-                * that the filesystem actually supports that fsid
-                * type. If it doesn't we loop around again without
-                * ref_fh set.
+               /*
+                * As the fsid -> filesystem mapping was guided by
+                * user-space, there is no guarantee that the filesystem
+                * actually supports that fsid type. If it doesn't we
+                * loop around again without ref_fh set.
                 */
-               switch(fsid_type) {
-               case FSID_DEV:
-                       if (!old_valid_dev(ex_dev))
-                               goto retry;
-                       /* FALL THROUGH */
-               case FSID_MAJOR_MINOR:
-               case FSID_ENCODE_DEV:
-                       if (!(exp->ex_dentry->d_inode->i_sb->s_type->fs_flags
-                             & FS_REQUIRES_DEV))
-                               goto retry;
-                       break;
-               case FSID_NUM:
-                       if (! (exp->ex_flags & NFSEXP_FSID))
-                               goto retry;
-                       break;
-               case FSID_UUID8:
-               case FSID_UUID16:
-                       if (!root_export)
-                               goto retry;
-                       /* fall through */
-               case FSID_UUID4_INUM:
-               case FSID_UUID16_INUM:
-                       if (exp->ex_uuid == NULL)
-                               goto retry;
-                       break;
-               }
+               if (!fsid_type_ok_for_exp(fsid_type, exp))
+                       goto retry;
+       } else if (exp->ex_flags & NFSEXP_FSID) {
+               fsid_type = FSID_NUM;
        } else if (exp->ex_uuid) {
                if (fhp->fh_maxsize >= 64) {
-                       if (root_export)
+                       if (is_root_export(exp))
                                fsid_type = FSID_UUID16;
                        else
                                fsid_type = FSID_UUID16_INUM;
                } else {
-                       if (root_export)
+                       if (is_root_export(exp))
                                fsid_type = FSID_UUID8;
                        else
                                fsid_type = FSID_UUID4_INUM;
                }
-       } else if (exp->ex_flags & NFSEXP_FSID)
-               fsid_type = FSID_NUM;
-       else if (!old_valid_dev(ex_dev))
+       } else if (!old_valid_dev(ex_dev))
                /* for newer device numbers, we must use a newer fsid format */
                fsid_type = FSID_ENCODE_DEV;
        else
@@ -439,7 +532,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
                fhp->fh_handle.ofh_dev =  old_encode_dev(ex_dev);
                fhp->fh_handle.ofh_xdev = fhp->fh_handle.ofh_dev;
                fhp->fh_handle.ofh_xino =
-                       ino_t_to_u32(exp->ex_dentry->d_inode->i_ino);
+                       ino_t_to_u32(exp->ex_path.dentry->d_inode->i_ino);
                fhp->fh_handle.ofh_dirino = ino_t_to_u32(parent_ino(dentry));
                if (inode)
                        _fh_update_old(dentry, exp, &fhp->fh_handle);
@@ -450,24 +543,19 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
                datap = fhp->fh_handle.fh_auth+0;
                fhp->fh_handle.fh_fsid_type = fsid_type;
                mk_fsid(fsid_type, datap, ex_dev,
-                       exp->ex_dentry->d_inode->i_ino,
+                       exp->ex_path.dentry->d_inode->i_ino,
                        exp->ex_fsid, exp->ex_uuid);
 
                len = key_len(fsid_type);
                datap += len/4;
                fhp->fh_handle.fh_size = 4 + len;
 
-               if (inode) {
-                       int size = (fhp->fh_maxsize-len-4)/4;
-                       fhp->fh_handle.fh_fileid_type =
-                               _fh_update(dentry, exp, datap, &size);
-                       fhp->fh_handle.fh_size += size*4;
-               }
+               if (inode)
+                       _fh_update(fhp, exp, dentry);
                if (fhp->fh_handle.fh_fileid_type == 255)
                        return nfserr_opnotsupp;
        }
 
-       nfsd_nr_verified++;
        return 0;
 }
 
@@ -479,7 +567,6 @@ __be32
 fh_update(struct svc_fh *fhp)
 {
        struct dentry *dentry;
-       __u32 *datap;
 
        if (!fhp->fh_dentry)
                goto out_bad;
@@ -490,15 +577,10 @@ fh_update(struct svc_fh *fhp)
        if (fhp->fh_handle.fh_version != 1) {
                _fh_update_old(dentry, fhp->fh_export, &fhp->fh_handle);
        } else {
-               int size;
-               if (fhp->fh_handle.fh_fileid_type != 0)
+               if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT)
                        goto out;
-               datap = fhp->fh_handle.fh_auth+
-                       fhp->fh_handle.fh_size/4 -1;
-               size = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4;
-               fhp->fh_handle.fh_fileid_type =
-                       _fh_update(dentry, fhp->fh_export, datap, &size);
-               fhp->fh_handle.fh_size += size*4;
+
+               _fh_update(fhp, fhp->fh_export, dentry);
                if (fhp->fh_handle.fh_fileid_type == 255)
                        return nfserr_opnotsupp;
        }
@@ -530,7 +612,6 @@ fh_put(struct svc_fh *fhp)
                fhp->fh_pre_saved = 0;
                fhp->fh_post_saved = 0;
 #endif
-               nfsd_nr_put++;
        }
        if (exp) {
                cache_put(&exp->h, &svc_export_cache);
@@ -566,13 +647,22 @@ enum fsid_source fsid_source(struct svc_fh *fhp)
        case FSID_DEV:
        case FSID_ENCODE_DEV:
        case FSID_MAJOR_MINOR:
-               return FSIDSOURCE_DEV;
+               if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV)
+                       return FSIDSOURCE_DEV;
+               break;
        case FSID_NUM:
-               return FSIDSOURCE_FSID;
-       default:
                if (fhp->fh_export->ex_flags & NFSEXP_FSID)
                        return FSIDSOURCE_FSID;
-               else
-                       return FSIDSOURCE_UUID;
+               break;
+       default:
+               break;
        }
+       /* either a UUID type filehandle, or the filehandle doesn't
+        * match the export.
+        */
+       if (fhp->fh_export->ex_flags & NFSEXP_FSID)
+               return FSIDSOURCE_FSID;
+       if (fhp->fh_export->ex_uuid)
+               return FSIDSOURCE_UUID;
+       return FSIDSOURCE_DEV;
 }