fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / nfsd / nfsfh.c
index 8e8c234..c59d6fb 100644 (file)
@@ -24,8 +24,6 @@
 #include <linux/nfsd/nfsd.h>
 
 #define NFSDDBG_FACILITY               NFSDDBG_FH
-#define NFSD_PARANOIA 1
-/* #define NFSD_DEBUG_VERBOSE 1 */
 
 
 static int nfsd_nr_verified;
@@ -41,7 +39,7 @@ extern struct export_operations export_op_default;
  * 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)
+static int nfsd_acceptable(void *expv, struct dentry *dentry)
 {
        struct svc_export *exp = expv;
        int rv;
@@ -71,6 +69,35 @@ int nfsd_acceptable(void *expv, struct dentry *dentry)
        return rv;
 }
 
+/* Type check. The correct error return for type mismatches does not seem to be
+ * generally agreed upon. SunOS seems to use EISDIR if file isn't S_IFREG; a
+ * comment in the NFSv3 spec says this is incorrect (implementation notes for
+ * the write call).
+ */
+static inline __be32
+nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type)
+{
+       /* Type can be negative when creating hardlinks - not to a dir */
+       if (type > 0 && (mode & S_IFMT) != type) {
+               if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
+                       return nfserr_symlink;
+               else if (type == S_IFDIR)
+                       return nfserr_notdir;
+               else if ((mode & S_IFMT) == S_IFDIR)
+                       return nfserr_isdir;
+               else
+                       return nfserr_inval;
+       }
+       if (type < 0 && (mode & S_IFMT) == -type) {
+               if (rqstp->rq_vers == 4 && (mode & S_IFMT) == S_IFLNK)
+                       return nfserr_symlink;
+               else if (type == -S_IFDIR)
+                       return nfserr_isdir;
+               else
+                       return nfserr_notdir;
+       }
+       return 0;
+}
 
 /*
  * Perform sanity checks on the dentry in a client's file handle.
@@ -81,14 +108,13 @@ int nfsd_acceptable(void *expv, struct dentry *dentry)
  * 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.
  */
-u32
+__be32
 fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
 {
        struct knfsd_fh *fh = &fhp->fh_handle;
        struct svc_export *exp = NULL;
        struct dentry   *dentry;
-       struct inode    *inode;
-       u32             error = 0;
+       __be32          error = 0;
 
        dprintk("nfsd: fh_verify(%s)\n", SVCFH_fmt(fhp));
 
@@ -141,9 +167,11 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                        exp = exp_find(rqstp->rq_client, 0, tfh, &rqstp->rq_chandle);
                }
 
-               error = nfserr_dropit;
-               if (IS_ERR(exp) && PTR_ERR(exp) == -EAGAIN)
+               if (IS_ERR(exp) && (PTR_ERR(exp) == -EAGAIN
+                               || PTR_ERR(exp) == -ETIMEDOUT)) {
+                       error = nfserrno(PTR_ERR(exp));
                        goto out;
+               }
 
                error = nfserr_stale; 
                if (!exp || IS_ERR(exp))
@@ -153,18 +181,16 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                error = nfserr_perm;
                if (!rqstp->rq_secure && EX_SECURE(exp)) {
                        printk(KERN_WARNING
-                              "nfsd: request from insecure port (%08x:%d)!\n",
-                              ntohl(rqstp->rq_addr.sin_addr.s_addr),
+                              "nfsd: request from insecure port (%u.%u.%u.%u:%d)!\n",
+                              NIPQUAD(rqstp->rq_addr.sin_addr.s_addr),
                               ntohs(rqstp->rq_addr.sin_port));
                        goto out;
                }
 
                /* Set user creds for this exportpoint */
-               error = nfsd_setuser(rqstp, exp);
-               if (error) {
-                       error = nfserrno(error);
+               error = nfserrno(nfsd_setuser(rqstp, exp));
+               if (error)
                        goto out;
-               }
 
                /*
                 * Look up the dentry using the NFS file handle.
@@ -190,10 +216,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                        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);
+                       dentry = CALL(nop,decode_fh)(exp->ex_mnt->mnt_sb,
+                                                    datap, data_left,
+                                                    fileid_type,
+                                                    nfsd_acceptable, exp);
                }
                if (dentry == NULL)
                        goto out;
@@ -202,13 +228,12 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                                error = nfserrno(PTR_ERR(dentry));
                        goto out;
                }
-#ifdef NFSD_PARANOIA
+
                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);
                }
-#endif
 
                fhp->fh_dentry = dentry;
                fhp->fh_export = exp;
@@ -220,50 +245,32 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access)
                dprintk("nfsd: fh_verify - just checking\n");
                dentry = fhp->fh_dentry;
                exp = fhp->fh_export;
+               /* 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));
+               if (error)
+                       goto out;
        }
        cache_get(&exp->h);
 
-       inode = dentry->d_inode;
-
-
-       /* Type check. The correct error return for type mismatches
-        * does not seem to be generally agreed upon. SunOS seems to
-        * use EISDIR if file isn't S_IFREG; a comment in the NFSv3
-        * spec says this is incorrect (implementation notes for the
-        * write call).
-        */
 
-       /* Type can be negative when creating hardlinks - not to a dir */
-       if (type > 0 && (inode->i_mode & S_IFMT) != type) {
-               if (rqstp->rq_vers == 4 && (inode->i_mode & S_IFMT) == S_IFLNK)
-                       error = nfserr_symlink;
-               else if (type == S_IFDIR)
-                       error = nfserr_notdir;
-               else if ((inode->i_mode & S_IFMT) == S_IFDIR)
-                       error = nfserr_isdir;
-               else
-                       error = nfserr_inval;
-               goto out;
-       }
-       if (type < 0 && (inode->i_mode & S_IFMT) == -type) {
-               if (rqstp->rq_vers == 4 && (inode->i_mode & S_IFMT) == S_IFLNK)
-                       error = nfserr_symlink;
-               else if (type == -S_IFDIR)
-                       error = nfserr_isdir;
-               else
-                       error = nfserr_notdir;
+       error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
+       if (error)
                goto out;
-       }
 
        /* Finally, check access permissions. */
        error = nfsd_permission(exp, dentry, access);
 
-#ifdef NFSD_PARANOIA_EXTREME
        if (error) {
-               printk("fh_verify: %s/%s permission failure, acc=%x, error=%d\n",
-                      dentry->d_parent->d_name.name, dentry->d_name.name, access, (error >> 24));
+               dprintk("fh_verify: %s/%s permission failure, "
+                       "acc=%x, error=%d\n",
+                       dentry->d_parent->d_name.name,
+                       dentry->d_name.name,
+                       access, ntohl(error));
        }
-#endif
 out:
        if (exp && !IS_ERR(exp))
                exp_put(exp);
@@ -280,8 +287,8 @@ out:
  * an inode.  In this case a call to fh_update should be made
  * before the fh goes out on the wire ...
  */
-inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
-                     __u32 *datap, int *maxsize)
+static inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
+                            __u32 *datap, int *maxsize)
 {
        struct export_operations *nop = exp->ex_mnt->mnt_sb->s_export_op;
        
@@ -297,8 +304,9 @@ inline int _fh_update(struct dentry *dentry, struct svc_export *exp,
 /*
  * for composing old style file handles
  */
-inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp,
-                          struct knfsd_fh *fh)
+static inline void _fh_update_old(struct dentry *dentry,
+                                 struct svc_export *exp,
+                                 struct knfsd_fh *fh)
 {
        fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
        fh->ofh_generation = dentry->d_inode->i_generation;
@@ -307,12 +315,12 @@ inline void _fh_update_old(struct dentry *dentry, struct svc_export *exp,
                fh->ofh_dirino = 0;
 }
 
-int
+__be32
 fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, struct svc_fh *ref_fh)
 {
        /* ref_fh is a reference file handle.
-        * if it is non-null, then we should compose a filehandle which is
-        * of the same version, where possible.
+        * if it is non-null and for the same filesystem, then we should compose
+        * a filehandle which is of the same version, where possible.
         * Currently, that means that if ref_fh->fh_handle.fh_version == 0xca
         * Then create a 32byte filehandle using nfs_fhbase_old
         *
@@ -331,7 +339,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
                parent->d_name.name, dentry->d_name.name,
                (inode ? inode->i_ino : 0));
 
-       if (ref_fh) {
+       if (ref_fh && ref_fh->fh_export == exp) {
                ref_fh_version = ref_fh->fh_handle.fh_version;
                if (ref_fh_version == 0xca)
                        ref_fh_fsid_type = 0;
@@ -339,13 +347,16 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
                        ref_fh_fsid_type = ref_fh->fh_handle.fh_fsid_type;
                if (ref_fh_fsid_type > 3)
                        ref_fh_fsid_type = 0;
-       }
-       /* make sure ref_fh type works for given export */
-       if (ref_fh_fsid_type == 1 &&
-           !(exp->ex_flags & NFSEXP_FSID)) {
-               /* if we don't have an fsid, we cannot provide one... */
-               ref_fh_fsid_type = 0;
-       }
+
+               /* make sure ref_fh type works for given export */
+               if (ref_fh_fsid_type == 1 &&
+                   !(exp->ex_flags & NFSEXP_FSID)) {
+                       /* if we don't have an fsid, we cannot provide one... */
+                       ref_fh_fsid_type = 0;
+               }
+       } else if (exp->ex_flags & NFSEXP_FSID)
+               ref_fh_fsid_type = 1;
+
        if (!old_valid_dev(ex_dev) && ref_fh_fsid_type == 0) {
                /* for newer device numbers, we must use a newer fsid format */
                ref_fh_version = 1;
@@ -440,7 +451,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, st
  * Update file handle information after changing a dentry.
  * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create
  */
-int
+__be32
 fh_update(struct svc_fh *fhp)
 {
        struct dentry *dentry;
@@ -457,7 +468,7 @@ fh_update(struct svc_fh *fhp)
        } else {
                int size;
                if (fhp->fh_handle.fh_fileid_type != 0)
-                       goto out_uptodate;
+                       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;
@@ -477,10 +488,6 @@ out_negative:
        printk(KERN_ERR "fh_update: %s/%s still negative!\n",
                dentry->d_parent->d_name.name, dentry->d_name.name);
        goto out;
-out_uptodate:
-       printk(KERN_ERR "fh_update: %s/%s already up-to-date!\n",
-               dentry->d_parent->d_name.name, dentry->d_name.name);
-       goto out;
 }
 
 /*
@@ -502,7 +509,7 @@ fh_put(struct svc_fh *fhp)
                nfsd_nr_put++;
        }
        if (exp) {
-               svc_export_put(&exp->h, &svc_export_cache);
+               cache_put(&exp->h, &svc_export_cache);
                fhp->fh_export = NULL;
        }
        return;