nfsd: Pull write-gathering code out of nfsd_vfs_write
[safe/jmp/linux-2.6] / fs / nfsd / nfs4proc.c
index e6a0f31..7c88017 100644 (file)
 
 #define NFSDDBG_FACILITY               NFSDDBG_PROC
 
+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
+check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+                  u32 *bmval, u32 *writable)
+{
+       struct dentry *dentry = cstate->current_fh.fh_dentry;
+       struct svc_export *exp = cstate->current_fh.fh_export;
+
+       /*
+        * Check about attributes are supported by the NFSv4 server or not.
+        * According to spec, unsupported attributes return ERR_ATTRNOTSUPP.
+        */
+       if ((bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) ||
+           (bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) ||
+           (bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
+               return nfserr_attrnotsupp;
+
+       /*
+        * Check FATTR4_WORD0_ACL & FATTR4_WORD0_FS_LOCATIONS can be supported
+        * in current environment or not.
+        */
+       if (bmval[0] & FATTR4_WORD0_ACL) {
+               if (!IS_POSIXACL(dentry->d_inode))
+                       return nfserr_attrnotsupp;
+       }
+       if (bmval[0] & FATTR4_WORD0_FS_LOCATIONS) {
+               if (exp->ex_fslocs.locations == NULL)
+                       return nfserr_attrnotsupp;
+       }
+
+       /*
+        * According to spec, read-only attributes return ERR_INVAL.
+        */
+       if (writable) {
+               if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
+                   (bmval[2] & ~writable[2]))
+                       return nfserr_inval;
+       }
+
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_check_open_attributes(struct svc_rqst *rqstp,
+       struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
+{
+       __be32 status = nfs_ok;
+
+       if (open->op_create == NFS4_OPEN_CREATE) {
+               if (open->op_createmode == NFS4_CREATE_UNCHECKED
+                   || open->op_createmode == NFS4_CREATE_GUARDED)
+                       status = check_attr_support(rqstp, cstate,
+                                       open->op_bmval, nfsd_attrmask);
+               else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1)
+                       status = check_attr_support(rqstp, cstate,
+                                       open->op_bmval, nfsd41_ex_attrmask);
+       }
+
+       return status;
+}
+
 static inline void
 fh_dup2(struct svc_fh *dst, struct svc_fh *src)
 {
@@ -93,6 +165,21 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
        open->op_truncate = 0;
 
        if (open->op_create) {
+               /* FIXME: check session persistence and pnfs flags.
+                * The nfsv4.1 spec requires the following semantics:
+                *
+                * Persistent   | pNFS   | Server REQUIRED | Client Allowed
+                * Reply Cache  | server |                 |
+                * -------------+--------+-----------------+--------------------
+                * no           | no     | EXCLUSIVE4_1    | EXCLUSIVE4_1
+                *              |        |                 | (SHOULD)
+                *              |        | and EXCLUSIVE4  | or EXCLUSIVE4
+                *              |        |                 | (SHOULD NOT)
+                * no           | yes    | EXCLUSIVE4_1    | EXCLUSIVE4_1
+                * yes          | no     | GUARDED4        | GUARDED4
+                * yes          | yes    | GUARDED4        | GUARDED4
+                */
+
                /*
                 * Note: create modes (UNCHECKED,GUARDED...) are the same
                 * in NFSv4 as in v3.
@@ -162,12 +249,23 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
        return status;
 }
 
+static void
+copy_clientid(clientid_t *clid, struct nfsd4_session *session)
+{
+       struct nfsd4_sessionid *sid =
+                       (struct nfsd4_sessionid *)session->se_sessionid.data;
+
+       clid->cl_boot = sid->clientid.cl_boot;
+       clid->cl_id = sid->clientid.cl_id;
+}
 
 static __be32
 nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
           struct nfsd4_open *open)
 {
        __be32 status;
+       struct nfsd4_compoundres *resp;
+
        dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
                (int)open->op_fname.len, open->op_fname.data,
                open->op_stateowner);
@@ -176,10 +274,14 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
                return nfserr_inval;
 
+       if (nfsd4_has_session(cstate))
+               copy_clientid(&open->op_clientid, cstate->session);
+
        nfs4_lock_state();
 
        /* check seqid for replay. set nfs4_owner */
-       status = nfsd4_process_open1(open);
+       resp = rqstp->rq_resp;
+       status = nfsd4_process_open1(&resp->cstate, open);
        if (status == nfserr_replay_me) {
                struct nfs4_replay *rp = &open->op_stateowner->so_replay;
                fh_put(&cstate->current_fh);
@@ -195,6 +297,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                goto out;
 
+       status = nfsd4_check_open_attributes(rqstp, cstate, open);
+       if (status)
+               goto out;
+
        /* Openowner is now set, so sequence id will get bumped.  Now we need
         * these checks before we do any creates: */
        status = nfserr_grace;
@@ -365,6 +471,11 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
 
+       status = check_attr_support(rqstp, cstate, create->cr_bmval,
+                                   nfsd_attrmask);
+       if (status)
+               return status;
+
        switch (create->cr_type) {
        case NF4LNK:
                /* ugh! we have to null-terminate the linktext, or
@@ -448,8 +559,9 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
                return nfserr_inval;
 
-       getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0;
-       getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1;
+       getattr->ga_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
+       getattr->ga_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
+       getattr->ga_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
 
        getattr->ga_fhp = &cstate->current_fh;
        return nfs_ok;
@@ -540,8 +652,9 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)
                return nfserr_inval;
 
-       readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0;
-       readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1;
+       readdir->rd_bmval[0] &= nfsd_suppattrs0(cstate->minorversion);
+       readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion);
+       readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion);
 
        if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) ||
            (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE)))
@@ -657,6 +770,12 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
        status = nfs_ok;
+
+       status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
+                                   nfsd_attrmask);
+       if (status)
+               goto out;
+
        if (setattr->sa_acl != NULL)
                status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
                                            setattr->sa_acl);
@@ -731,9 +850,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
 
-       if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0)
-           || (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1))
-               return nfserr_attrnotsupp;
+       status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
+       if (status)
+               return status;
+
        if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
            || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
                return nfserr_inval;
@@ -760,7 +880,8 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                goto out_kfree;
 
-       p = buf + 3;
+       /* skip bitmap */
+       p = buf + 1 + ntohl(buf[0]);
        status = nfserr_not_same;
        if (ntohl(*p++) != verify->ve_attrlen)
                goto out_kfree;
@@ -895,6 +1016,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        resp->tag = args->tag;
        resp->opcnt = 0;
        resp->rqstp = rqstp;
+       resp->cstate.minorversion = args->minorversion;
        resp->cstate.replay_owner = NULL;
        fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
        fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
@@ -905,7 +1027,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
         * According to RFC3010, this takes precedence over all other errors.
         */
        status = nfserr_minor_vers_mismatch;
-       if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION)
+       if (args->minorversion > nfsd_supported_minorversion)
                goto out;
 
        if (!nfs41_op_ordering_ok(args)) {
@@ -1191,24 +1313,9 @@ static const char *nfsd4_op_name(unsigned opnum)
        return "unknown_operation";
 }
 
-#define nfs4svc_decode_voidargs                NULL
-#define nfs4svc_release_void           NULL
 #define nfsd4_voidres                  nfsd4_voidargs
-#define nfs4svc_release_compound       NULL
 struct nfsd4_voidargs { int dummy; };
 
-#define PROC(name, argt, rest, relt, cache, respsize)  \
- { (svc_procfunc) nfsd4_proc_##name,           \
-   (kxdrproc_t) nfs4svc_decode_##argt##args,   \
-   (kxdrproc_t) nfs4svc_encode_##rest##res,    \
-   (kxdrproc_t) nfs4svc_release_##relt,                \
-   sizeof(struct nfsd4_##argt##args),          \
-   sizeof(struct nfsd4_##rest##res),           \
-   0,                                          \
-   cache,                                      \
-   respsize,                                   \
- }
-
 /*
  * TODO: At the present time, the NFSv4 server does not do XID caching
  * of requests.  Implementing XID caching would not be a serious problem,
@@ -1220,8 +1327,23 @@ struct nfsd4_voidargs { int dummy; };
  * better XID's.
  */
 static struct svc_procedure            nfsd_procedures4[2] = {
-  PROC(null,    void,          void,           void,     RC_NOCACHE, 1),
-  PROC(compound, compound,     compound,       compound, RC_NOCACHE, NFSD_BUFSIZE/4)
+       [NFSPROC4_NULL] = {
+               .pc_func = (svc_procfunc) nfsd4_proc_null,
+               .pc_encode = (kxdrproc_t) nfs4svc_encode_voidres,
+               .pc_argsize = sizeof(struct nfsd4_voidargs),
+               .pc_ressize = sizeof(struct nfsd4_voidres),
+               .pc_cachetype = RC_NOCACHE,
+               .pc_xdrressize = 1,
+       },
+       [NFSPROC4_COMPOUND] = {
+               .pc_func = (svc_procfunc) nfsd4_proc_compound,
+               .pc_decode = (kxdrproc_t) nfs4svc_decode_compoundargs,
+               .pc_encode = (kxdrproc_t) nfs4svc_encode_compoundres,
+               .pc_argsize = sizeof(struct nfsd4_compoundargs),
+               .pc_ressize = sizeof(struct nfsd4_compoundres),
+               .pc_cachetype = RC_NOCACHE,
+               .pc_xdrressize = NFSD_BUFSIZE/4,
+       },
 };
 
 struct svc_version     nfsd_version4 = {