nfsd4: implement reclaim_complete
[safe/jmp/linux-2.6] / fs / nfsd / nfs4proc.c
index ae21a4e..59ec449 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  fs/nfsd/nfs4proc.c
- *
  *  Server-side procedures for NFSv4.
  *
  *  Copyright (c) 2002 The Regents of the University of Michigan.
  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
-
-#include <linux/param.h>
-#include <linux/major.h>
-#include <linux/slab.h>
 #include <linux/file.h>
+#include <linux/slab.h>
 
-#include <linux/sunrpc/svc.h>
-#include <linux/nfsd/nfsd.h>
-#include <linux/nfsd/cache.h>
-#include <linux/nfs4.h>
-#include <linux/nfsd/state.h>
-#include <linux/nfsd/xdr4.h>
-#include <linux/nfs4_acl.h>
-#include <linux/sunrpc/gss_api.h>
+#include "cache.h"
+#include "xdr4.h"
+#include "vfs.h"
 
 #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;
+
+       /*
+        * 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 can be supported
+        * in current environment or not.
+        */
+       if (bmval[0] & FATTR4_WORD0_ACL) {
+               if (!IS_POSIXACL(dentry->d_inode))
+                       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 int
+is_create_with_attrs(struct nfsd4_open *open)
+{
+       return open->op_create == NFS4_OPEN_CREATE
+               && (open->op_createmode == NFS4_CREATE_UNCHECKED
+                   || open->op_createmode == NFS4_CREATE_GUARDED
+                   || open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1);
+}
+
+/*
+ * if error occurs when setting the acl, just clear the acl bit
+ * in the returned attr bitmap.
+ */
+static void
+do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
+               struct nfs4_acl *acl, u32 *bmval)
+{
+       __be32 status;
+
+       status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
+       if (status)
+               /*
+                * We should probably fail the whole open at this point,
+                * but we've already created the file, so it's too late;
+                * So this seems the least of evils:
+                */
+               bmval[0] &= ~FATTR4_WORD0_ACL;
+}
+
 static inline void
 fh_dup2(struct svc_fh *dst, struct svc_fh *src)
 {
@@ -74,7 +160,7 @@ do_open_permission(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
                accmode |= NFSD_MAY_READ;
        if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE)
                accmode |= (NFSD_MAY_WRITE | NFSD_MAY_TRUNC);
-       if (open->op_share_deny & NFS4_SHARE_DENY_WRITE)
+       if (open->op_share_deny & NFS4_SHARE_DENY_READ)
                accmode |= NFSD_MAY_WRITE;
 
        status = fh_verify(rqstp, current_fh, S_IFREG, accmode);
@@ -93,6 +179,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.
@@ -119,6 +220,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
        if (status)
                goto out;
 
+       if (is_create_with_attrs(open) && open->op_acl != NULL)
+               do_set_nfs4_acl(rqstp, &resfh, open->op_acl, open->op_bmval);
+
        set_change_info(&open->op_cinfo, current_fh);
        fh_dup2(current_fh, &resfh);
 
@@ -162,12 +266,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 +291,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 +314,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 +488,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
@@ -425,12 +553,17 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                status = nfserr_badtype;
        }
 
-       if (!status) {
-               fh_unlock(&cstate->current_fh);
-               set_change_info(&create->cr_cinfo, &cstate->current_fh);
-               fh_dup2(&cstate->current_fh, &resfh);
-       }
+       if (status)
+               goto out;
 
+       if (create->cr_acl != NULL)
+               do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
+                               create->cr_bmval);
+
+       fh_unlock(&cstate->current_fh);
+       set_change_info(&create->cr_cinfo, &cstate->current_fh);
+       fh_dup2(&cstate->current_fh, &resfh);
+out:
        fh_put(&resfh);
        return status;
 }
@@ -448,8 +581,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;
@@ -513,9 +647,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        nfs4_lock_state();
        /* check stateid */
-       if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh,
-                               &read->rd_stateid,
-                               RD_STATE, &read->rd_filp))) {
+       if ((status = nfs4_preprocess_stateid_op(cstate, &read->rd_stateid,
+                                                RD_STATE, &read->rd_filp))) {
                dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
                goto out;
        }
@@ -541,8 +674,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)))
@@ -646,7 +780,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
                nfs4_lock_state();
-               status = nfs4_preprocess_stateid_op(&cstate->current_fh,
+               status = nfs4_preprocess_stateid_op(cstate,
                        &setattr->sa_stateid, WR_STATE, NULL);
                nfs4_unlock_state();
                if (status) {
@@ -658,6 +792,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);
@@ -686,8 +826,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                return nfserr_inval;
 
        nfs4_lock_state();
-       status = nfs4_preprocess_stateid_op(&cstate->current_fh, stateid,
-                                       WR_STATE, &filp);
+       status = nfs4_preprocess_stateid_op(cstate, stateid, WR_STATE, &filp);
        if (filp)
                get_file(filp);
        nfs4_unlock_state();
@@ -733,9 +872,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;
@@ -762,7 +902,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;
@@ -828,48 +969,36 @@ static struct nfsd4_operation nfsd4_ops[];
 static const char *nfsd4_op_name(unsigned opnum);
 
 /*
- * This is a replay of a compound for which no cache entry pages
- * were used. Encode the sequence operation, and if cachethis is FALSE
- * encode the uncache rep error on the next operation.
- */
-static __be32
-nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args,
-                        struct nfsd4_compoundres *resp)
-{
-       struct nfsd4_op *op;
-
-       dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__,
-               resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis);
-
-       /* Encode the replayed sequence operation */
-       BUG_ON(resp->opcnt != 1);
-       op = &args->ops[resp->opcnt - 1];
-       nfsd4_encode_operation(resp, op);
-
-       /*return nfserr_retry_uncached_rep in next operation. */
-       if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) {
-               op = &args->ops[resp->opcnt++];
-               op->status = nfserr_retry_uncached_rep;
-               nfsd4_encode_operation(resp, op);
-       }
-       return op->status;
-}
-
-/*
- * Enforce NFSv4.1 COMPOUND ordering rules.
+ * Enforce NFSv4.1 COMPOUND ordering rules:
  *
- * TODO:
- * - enforce NFS4ERR_NOT_ONLY_OP,
- * - DESTROY_SESSION MUST be the final operation in the COMPOUND request.
+ * Also note, enforced elsewhere:
+ *     - SEQUENCE other than as first op results in
+ *       NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().)
+ *     - BIND_CONN_TO_SESSION must be the only op in its compound
+ *       (Will be enforced in nfsd4_bind_conn_to_session().)
+ *     - DESTROY_SESSION must be the final operation in a compound, if
+ *       sessionid's in SEQUENCE and DESTROY_SESSION are the same.
+ *       (Enforced in nfsd4_destroy_session().)
  */
-static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args)
+static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args)
 {
-       if (args->minorversion && args->opcnt > 0) {
-               struct nfsd4_op *op = &args->ops[0];
-               return (op->status == nfserr_op_illegal) ||
-                      (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP);
-       }
-       return true;
+       struct nfsd4_op *op = &args->ops[0];
+
+       /* These ordering requirements don't apply to NFSv4.0: */
+       if (args->minorversion == 0)
+               return nfs_ok;
+       /* This is weird, but OK, not our problem: */
+       if (args->opcnt == 0)
+               return nfs_ok;
+       if (op->status == nfserr_op_illegal)
+               return nfs_ok;
+       if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP))
+               return nfserr_op_not_in_session;
+       if (op->opnum == OP_SEQUENCE)
+               return nfs_ok;
+       if (args->opcnt != 1)
+               return nfserr_not_only_op;
+       return nfs_ok;
 }
 
 /*
@@ -897,7 +1026,9 @@ 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;
+       resp->cstate.session = NULL;
        fh_init(&resp->cstate.current_fh, NFS4_FHSIZE);
        fh_init(&resp->cstate.save_fh, NFS4_FHSIZE);
        /* Use the deferral mechanism only for NFSv4.0 compounds */
@@ -907,16 +1038,16 @@ 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)) {
+       status = nfs41_check_op_ordering(args);
+       if (status) {
                op = &args->ops[0];
-               op->status = nfserr_sequence_pos;
+               op->status = status;
                goto encode_op;
        }
 
-       status = nfs_ok;
        while (!status && resp->opcnt < args->opcnt) {
                op = &args->ops[resp->opcnt++];
 
@@ -963,13 +1094,10 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                        BUG_ON(op->status == nfs_ok);
 
 encode_op:
-               /* Only from SEQUENCE or CREATE_SESSION */
+               /* Only from SEQUENCE */
                if (resp->cstate.status == nfserr_replay_cache) {
                        dprintk("%s NFS4.1 replay from cache\n", __func__);
-                       if (nfsd4_not_cached(resp))
-                               status = nfsd4_enc_uncached_replay(args, resp);
-                       else
-                               status = op->status;
+                       status = op->status;
                        goto out;
                }
                if (op->status == nfserr_replay_me) {
@@ -1184,6 +1312,11 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
                .op_name = "OP_SEQUENCE",
        },
+       [OP_RECLAIM_COMPLETE] = {
+               .op_func = (nfsd4op_func)nfsd4_reclaim_complete,
+               .op_flags = ALLOWED_WITHOUT_FH,
+               .op_name = "OP_RECLAIM_COMPLETE",
+       },
 };
 
 static const char *nfsd4_op_name(unsigned opnum)
@@ -1193,24 +1326,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,
@@ -1222,8 +1340,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 = {