Merge branch 'topic/core-cleanup' into for-linus
[safe/jmp/linux-2.6] / fs / nfsd / nfs4xdr.c
index ab005fc..34ccf81 100644 (file)
  * at the end of nfs4svc_decode_compoundargs.
  */
 
-#include <linux/param.h>
-#include <linux/smp.h>
-#include <linux/fs.h>
+#include <linux/slab.h>
 #include <linux/namei.h>
-#include <linux/vfs.h>
+#include <linux/statfs.h>
 #include <linux/utsname.h>
-#include <linux/sunrpc/xdr.h>
-#include <linux/sunrpc/svc.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/state.h>
-#include <linux/nfsd/xdr4.h>
 #include <linux/nfsd_idmap.h>
-#include <linux/nfs4.h>
 #include <linux/nfs4_acl.h>
-#include <linux/sunrpc/gss_api.h>
 #include <linux/sunrpc/svcauth_gss.h>
 
+#include "xdr4.h"
+#include "vfs.h"
+
 #define NFSDDBG_FACILITY               NFSDDBG_XDR
 
 /*
@@ -168,10 +161,10 @@ static __be32 *read_buf(struct nfsd4_compoundargs *argp, u32 nbytes)
        argp->p = page_address(argp->pagelist[0]);
        argp->pagelist++;
        if (argp->pagelen < PAGE_SIZE) {
-               argp->end = p + (argp->pagelen>>2);
+               argp->end = argp->p + (argp->pagelen>>2);
                argp->pagelen = 0;
        } else {
-               argp->end = p + (PAGE_SIZE>>2);
+               argp->end = argp->p + (PAGE_SIZE>>2);
                argp->pagelen -= PAGE_SIZE;
        }
        memcpy(((char*)p)+avail, argp->p, (nbytes - avail));
@@ -244,20 +237,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
        DECODE_TAIL;
 }
 
-static u32 nfsd_attrmask[] = {
-       NFSD_WRITEABLE_ATTRS_WORD0,
-       NFSD_WRITEABLE_ATTRS_WORD1,
-       NFSD_WRITEABLE_ATTRS_WORD2
-};
-
-static u32 nfsd41_ex_attrmask[] = {
-       NFSD_SUPPATTR_EXCLCREAT_WORD0,
-       NFSD_SUPPATTR_EXCLCREAT_WORD1,
-       NFSD_SUPPATTR_EXCLCREAT_WORD2
-};
-
 static __be32
-nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
+nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                   struct iattr *iattr, struct nfs4_acl **acl)
 {
        int expected_len, len = 0;
@@ -270,18 +251,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
        if ((status = nfsd4_decode_bitmap(argp, bmval)))
                return status;
 
-       /*
-        * According to spec, unsupported attributes return ERR_ATTRNOTSUPP;
-        * read-only attributes return ERR_INVAL.
-        */
-       if ((bmval[0] & ~nfsd_suppattrs0(argp->minorversion)) ||
-           (bmval[1] & ~nfsd_suppattrs1(argp->minorversion)) ||
-           (bmval[2] & ~nfsd_suppattrs2(argp->minorversion)))
-               return nfserr_attrnotsupp;
-       if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
-           (bmval[2] & ~writable[2]))
-               return nfserr_inval;
-
        READ_BUF(4);
        READ32(expected_len);
 
@@ -414,8 +383,11 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
                        goto xdr_error;
                }
        }
-       BUG_ON(bmval[2]);       /* no such writeable attr supported yet */
-       if (len != expected_len)
+       if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
+           || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
+           || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
+               READ_BUF(expected_len - len);
+       else if (len != expected_len)
                goto xdr_error;
 
        DECODE_TAIL;
@@ -508,8 +480,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
        if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
                return status;
 
-       status = nfsd4_decode_fattr(argp, create->cr_bmval, nfsd_attrmask,
-                                   &create->cr_iattr, &create->cr_acl);
+       status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
+                                   &create->cr_acl);
        if (status)
                goto out;
 
@@ -672,7 +644,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                case NFS4_CREATE_UNCHECKED:
                case NFS4_CREATE_GUARDED:
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               nfsd_attrmask, &open->op_iattr, &open->op_acl);
+                               &open->op_iattr, &open->op_acl);
                        if (status)
                                goto out;
                        break;
@@ -686,8 +658,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                        READ_BUF(8);
                        COPYMEM(open->op_verf.data, 8);
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               nfsd41_ex_attrmask, &open->op_iattr,
-                               &open->op_acl);
+                               &open->op_iattr, &open->op_acl);
                        if (status)
                                goto out;
                        break;
@@ -883,8 +854,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
        status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
        if (status)
                return status;
-       return nfsd4_decode_fattr(argp, setattr->sa_bmval, nfsd_attrmask,
-                                 &setattr->sa_iattr, &setattr->sa_acl);
+       return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
+                                 &setattr->sa_acl);
 }
 
 static __be32
@@ -1455,16 +1426,16 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
                        argp->p = page_address(argp->pagelist[0]);
                        argp->pagelist++;
                        if (argp->pagelen < PAGE_SIZE) {
-                               argp->end = p + (argp->pagelen>>2);
+                               argp->end = argp->p + (argp->pagelen>>2);
                                argp->pagelen = 0;
                        } else {
-                               argp->end = p + (PAGE_SIZE>>2);
+                               argp->end = argp->p + (PAGE_SIZE>>2);
                                argp->pagelen -= PAGE_SIZE;
                        }
                }
                op->opnum = ntohl(*argp->p++);
 
-               if (op->opnum >= OP_ACCESS && op->opnum < ops->nops)
+               if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP)
                        op->status = ops->decoders[op->opnum](argp, &op->u);
                else {
                        op->opnum = OP_ILLEGAL;
@@ -1558,7 +1529,7 @@ static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
        } } while (0);
 
 /* Encode as an array of strings the string given with components
- * seperated @sep.
+ * separated @sep.
  */
 static __be32 nfsd4_encode_components(char sep, char *components,
                                   __be32 **pp, int *buflen)
@@ -1621,7 +1592,8 @@ static __be32 nfsd4_encode_fs_location4(struct nfsd4_fs_location *location,
 static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *stat)
 {
        struct svc_fh tmp_fh;
-       char *path, *rootpath;
+       char *path = NULL, *rootpath;
+       size_t rootlen;
 
        fh_init(&tmp_fh, NFS4_FHSIZE);
        *stat = exp_pseudoroot(rqstp, &tmp_fh);
@@ -1631,14 +1603,18 @@ static char *nfsd4_path(struct svc_rqst *rqstp, struct svc_export *exp, __be32 *
 
        path = exp->ex_pathname;
 
-       if (strncmp(path, rootpath, strlen(rootpath))) {
+       rootlen = strlen(rootpath);
+       if (strncmp(path, rootpath, rootlen)) {
                dprintk("nfsd: fs_locations failed;"
                        "%s is not contained in %s\n", path, rootpath);
                *stat = nfserr_notsupp;
-               return NULL;
+               path = NULL;
+               goto out;
        }
-
-       return path + strlen(rootpath);
+       path += rootlen;
+out:
+       fh_put(&tmp_fh);
+       return path;
 }
 
 /*
@@ -1815,11 +1791,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                                goto out_nfserr;
                }
        }
-       if (bmval0 & FATTR4_WORD0_FS_LOCATIONS) {
-               if (exp->ex_fslocs.locations == NULL) {
-                       bmval0 &= ~FATTR4_WORD0_FS_LOCATIONS;
-               }
-       }
        if ((buflen -= 16) < 0)
                goto out_resource;
 
@@ -1847,8 +1818,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        goto out_resource;
                if (!aclsupport)
                        word0 &= ~FATTR4_WORD0_ACL;
-               if (!exp->ex_fslocs.locations)
-                       word0 &= ~FATTR4_WORD0_FS_LOCATIONS;
                if (!word2) {
                        WRITE32(2);
                        WRITE32(word0);
@@ -2153,9 +2122,15 @@ out_acl:
                 * and this is the root of a cross-mounted filesystem.
                 */
                if (ignore_crossmnt == 0 &&
-                   exp->ex_path.mnt->mnt_root->d_inode == dentry->d_inode) {
-                       err = vfs_getattr(exp->ex_path.mnt->mnt_parent,
-                               exp->ex_path.mnt->mnt_mountpoint, &stat);
+                   dentry == exp->ex_path.mnt->mnt_root) {
+                       struct path path = exp->ex_path;
+                       path_get(&path);
+                       while (follow_up(&path)) {
+                               if (path.dentry != path.mnt->mnt_root)
+                                       break;
+                       }
+                       err = vfs_getattr(path.mnt, path.dentry, &stat);
+                       path_put(&path);
                        if (err)
                                goto out_nfserr;
                }
@@ -2210,6 +2185,15 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
        dentry = lookup_one_len(name, cd->rd_fhp->fh_dentry, namlen);
        if (IS_ERR(dentry))
                return nfserrno(PTR_ERR(dentry));
+       if (!dentry->d_inode) {
+               /*
+                * nfsd_buffered_readdir drops the i_mutex between
+                * readdir and calling this callback, leaving a window
+                * where this directory entry could have gone away.
+                */
+               dput(dentry);
+               return nfserr_noent;
+       }
 
        exp_get(exp);
        /*
@@ -2219,11 +2203,14 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
         * we will not follow the cross mount and will fill the attribtutes
         * directly from the mountpoint dentry.
         */
-       if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval))
-               ignore_crossmnt = 1;
-       else if (d_mountpoint(dentry)) {
+       if (nfsd_mountpoint(dentry, exp)) {
                int err;
 
+               if (!(exp->ex_flags & NFSEXP_V4ROOT)
+                               && !attributes_need_mount(cd->rd_bmval)) {
+                       ignore_crossmnt = 1;
+                       goto out_encode;
+               }
                /*
                 * Why the heck aren't we just using nfsd_lookup??
                 * Different "."/".." handling?  Something else?
@@ -2239,6 +2226,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_readdir *cd,
                        goto out_put;
 
        }
+out_encode:
        nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
                                        cd->rd_rqstp, ignore_crossmnt);
 out_put:
@@ -2272,6 +2260,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        struct nfsd4_readdir *cd = container_of(ccd, struct nfsd4_readdir, common);
        int buflen;
        __be32 *p = cd->buffer;
+       __be32 *cookiep;
        __be32 nfserr = nfserr_toosmall;
 
        /* In nfsv4, "." and ".." never make it onto the wire.. */
@@ -2288,7 +2277,7 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
                goto fail;
 
        *p++ = xdr_one;                             /* mark entry present */
-       cd->offset = p;                             /* remember pointer */
+       cookiep = p;
        p = xdr_encode_hyper(p, NFS_OFFSET_MAX);    /* offset of next entry */
        p = xdr_encode_array(p, name, namlen);      /* name length & name */
 
@@ -2302,6 +2291,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
                goto fail;
        case nfserr_dropit:
                goto fail;
+       case nfserr_noent:
+               goto skip_entry;
        default:
                /*
                 * If the client requested the RDATTR_ERROR attribute,
@@ -2320,6 +2311,8 @@ nfsd4_encode_dirent(void *ccdv, const char *name, int namlen,
        }
        cd->buflen -= (p - cd->buffer);
        cd->buffer = p;
+       cd->offset = cookiep;
+skip_entry:
        cd->common.err = nfs_ok;
        return 0;
 fail:
@@ -3072,6 +3065,7 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
        WRITE32(0);
 
        ADJUST_ARGS();
+       resp->cstate.datap = p; /* DRC cache data pointer */
        return 0;
 }
 
@@ -3174,7 +3168,7 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
                return status;
 
        session = resp->cstate.session;
-       if (session == NULL || slot->sl_cache_entry.ce_cachethis == 0)
+       if (session == NULL || slot->sl_cachethis == 0)
                return status;
 
        if (resp->opcnt >= args->opcnt)
@@ -3191,7 +3185,7 @@ static int nfsd4_check_drc_limit(struct nfsd4_compoundres *resp)
        dprintk("%s length %u, xb->page_len %u tlen %u pad %u\n", __func__,
                length, xb->page_len, tlen, pad);
 
-       if (length <= session->se_fmaxresp_cached)
+       if (length <= session->se_fchannel.maxresp_cached)
                return status;
        else
                return nfserr_rep_too_big_to_cache;
@@ -3299,6 +3293,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
        /*
         * All that remains is to write the tag and operation count...
         */
+       struct nfsd4_compound_state *cs = &resp->cstate;
        struct kvec *iov;
        p = resp->tagp;
        *p++ = htonl(resp->taglen);
@@ -3312,17 +3307,11 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
                iov = &rqstp->rq_res.head[0];
        iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base;
        BUG_ON(iov->iov_len > PAGE_SIZE);
-       if (nfsd4_has_session(&resp->cstate)) {
-               if (resp->cstate.status == nfserr_replay_cache &&
-                               !nfsd4_not_cached(resp)) {
-                       iov->iov_len = resp->cstate.iovlen;
-               } else {
-                       nfsd4_store_cache_entry(resp);
-                       dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
-                       resp->cstate.slot->sl_inuse = 0;
-               }
-               if (resp->cstate.session)
-                       nfsd4_put_session(resp->cstate.session);
+       if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) {
+               nfsd4_store_cache_entry(resp);
+               dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__);
+               resp->cstate.slot->sl_inuse = false;
+               nfsd4_put_session(resp->cstate.session);
        }
        return 1;
 }