nfsd: fix b0rken error value for setattr on read-only mount
[pandora-kernel.git] / fs / nfsd / nfs4proc.c
index 896da74..694d526 100644 (file)
@@ -39,6 +39,7 @@
 #include "cache.h"
 #include "xdr4.h"
 #include "vfs.h"
+#include "current_stateid.h"
 
 #define NFSDDBG_FACILITY               NFSDDBG_PROC
 
@@ -192,10 +193,13 @@ static __be32 nfsd_check_obj_isreg(struct svc_fh *fh)
 static __be32
 do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
 {
-       struct svc_fh resfh;
+       struct svc_fh *resfh;
        __be32 status;
 
-       fh_init(&resfh, NFS4_FHSIZE);
+       resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL);
+       if (!resfh)
+               return nfserr_jukebox;
+       fh_init(resfh, NFS4_FHSIZE);
        open->op_truncate = 0;
 
        if (open->op_create) {
@@ -220,7 +224,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
                 */
                status = do_nfsd_create(rqstp, current_fh, open->op_fname.data,
                                        open->op_fname.len, &open->op_iattr,
-                                       &resfh, open->op_createmode,
+                                       resfh, open->op_createmode,
                                        (u32 *)open->op_verf.data,
                                        &open->op_truncate, &open->op_created);
 
@@ -234,30 +238,29 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
                                                FATTR4_WORD1_TIME_MODIFY);
        } else {
                status = nfsd_lookup(rqstp, current_fh,
-                                    open->op_fname.data, open->op_fname.len, &resfh);
+                                    open->op_fname.data, open->op_fname.len, resfh);
                fh_unlock(current_fh);
                if (status)
                        goto out;
-               status = nfsd_check_obj_isreg(&resfh);
+               status = nfsd_check_obj_isreg(resfh);
        }
        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);
+               do_set_nfs4_acl(rqstp, resfh, open->op_acl, open->op_bmval);
 
        /* set reply cache */
        fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh,
-                       &resfh.fh_handle);
+                       &resfh->fh_handle);
        if (!open->op_created)
-               status = do_open_permission(rqstp, current_fh, open,
+               status = do_open_permission(rqstp, resfh, open,
                                            NFSD_MAY_NOP);
-
+       set_change_info(&open->op_cinfo, current_fh);
+       fh_dup2(current_fh, resfh);
 out:
-       fh_put(&resfh);
+       fh_put(resfh);
+       kfree(resfh);
        return status;
 }
 
@@ -310,16 +313,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;
 
-       /* We don't yet support WANT bits: */
-       open->op_share_access &= NFS4_SHARE_ACCESS_MASK;
-
        open->op_created = 0;
        /*
         * RFC5661 18.51.3
         * Before RECLAIM_COMPLETE done, server should deny new lock
         */
        if (nfsd4_has_session(cstate) &&
-           !cstate->session->se_client->cl_firststate &&
+           !test_bit(NFSD4_CLIENT_RECLAIM_COMPLETE,
+                     &cstate->session->se_client->cl_flags) &&
            open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
                return nfserr_grace;
 
@@ -452,6 +453,10 @@ nfsd4_restorefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                return nfserr_restorefh;
 
        fh_dup2(&cstate->current_fh, &cstate->save_fh);
+       if (HAS_STATE_ID(cstate, SAVED_STATE_ID_FLAG)) {
+               memcpy(&cstate->current_stateid, &cstate->save_stateid, sizeof(stateid_t));
+               SET_STATE_ID(cstate, CURRENT_STATE_ID_FLAG);
+       }
        return nfs_ok;
 }
 
@@ -463,6 +468,10 @@ nfsd4_savefh(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                return nfserr_nofilehandle;
 
        fh_dup2(&cstate->save_fh, &cstate->current_fh);
+       if (HAS_STATE_ID(cstate, CURRENT_STATE_ID_FLAG)) {
+               memcpy(&cstate->save_stateid, &cstate->current_stateid, sizeof(stateid_t));
+               SET_STATE_ID(cstate, SAVED_STATE_ID_FLAG);
+       }
        return nfs_ok;
 }
 
@@ -481,14 +490,20 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                           &access->ac_supported);
 }
 
+static void gen_boot_verifier(nfs4_verifier *verifier)
+{
+       __be32 verf[2];
+
+       verf[0] = (__be32)nfssvc_boot.tv_sec;
+       verf[1] = (__be32)nfssvc_boot.tv_usec;
+       memcpy(verifier->data, verf, sizeof(verifier->data));
+}
+
 static __be32
 nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
             struct nfsd4_commit *commit)
 {
-       u32 *p = (u32 *)commit->co_verf.data;
-       *p++ = nfssvc_boot.tv_sec;
-       *p++ = nfssvc_boot.tv_usec;
-
+       gen_boot_verifier(&commit->co_verf);
        return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset,
                             commit->co_count);
 }
@@ -826,6 +841,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
              struct nfsd4_setattr *setattr)
 {
        __be32 status = nfs_ok;
+       int err;
 
        if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
                nfs4_lock_state();
@@ -837,9 +853,9 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                        return status;
                }
        }
-       status = fh_want_write(&cstate->current_fh);
-       if (status)
-               return status;
+       err = fh_want_write(&cstate->current_fh);
+       if (err)
+               return nfserrno(err);
        status = nfs_ok;
 
        status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
@@ -865,7 +881,6 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 {
        stateid_t *stateid = &write->wr_stateid;
        struct file *filp = NULL;
-       u32 *p;
        __be32 status = nfs_ok;
        unsigned long cnt;
 
@@ -887,9 +902,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
 
        cnt = write->wr_buflen;
        write->wr_how_written = write->wr_stable_how;
-       p = (u32 *)write->wr_verifier.data;
-       *p++ = nfssvc_boot.tv_sec;
-       *p++ = nfssvc_boot.tv_usec;
+       gen_boot_verifier(&write->wr_verifier);
 
        status =  nfsd_write(rqstp, &cstate->current_fh, filp,
                             write->wr_offset, rqstp->rq_vec, write->wr_vlen,
@@ -1000,6 +1013,8 @@ static inline void nfsd4_increment_op_stats(u32 opnum)
 typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *,
                              void *);
 typedef u32(*nfsd4op_rsize)(struct svc_rqst *, struct nfsd4_op *op);
+typedef void(*stateid_setter)(struct nfsd4_compound_state *, void *);
+typedef void(*stateid_getter)(struct nfsd4_compound_state *, void *);
 
 enum nfsd4_op_flags {
        ALLOWED_WITHOUT_FH = 1 << 0,    /* No current filehandle required */
@@ -1025,6 +1040,10 @@ enum nfsd4_op_flags {
         * the v4.0 case).
         */
        OP_CACHEME = 1 << 6,
+       /*
+        * These are ops which clear current state id.
+        */
+       OP_CLEAR_STATEID = 1 << 7,
 };
 
 struct nfsd4_operation {
@@ -1033,11 +1052,15 @@ struct nfsd4_operation {
        char *op_name;
        /* Try to get response size before operation */
        nfsd4op_rsize op_rsize_bop;
+       stateid_setter op_get_currentstateid;
+       stateid_getter op_set_currentstateid;
 };
 
 static struct nfsd4_operation nfsd4_ops[];
 
+#ifdef NFSD_DEBUG
 static const char *nfsd4_op_name(unsigned opnum);
+#endif
 
 /*
  * Enforce NFSv4.1 COMPOUND ordering rules:
@@ -1215,13 +1238,23 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                if (op->status)
                        goto encode_op;
 
-               if (opdesc->op_func)
+               if (opdesc->op_func) {
+                       if (opdesc->op_get_currentstateid)
+                               opdesc->op_get_currentstateid(cstate, &op->u);
                        op->status = opdesc->op_func(rqstp, cstate, &op->u);
-               else
+               else
                        BUG_ON(op->status == nfs_ok);
 
-               if (!op->status && need_wrongsec_check(rqstp))
-                       op->status = check_nfsd_access(cstate->current_fh.fh_export, rqstp);
+               if (!op->status) {
+                       if (opdesc->op_set_currentstateid)
+                               opdesc->op_set_currentstateid(cstate, &op->u);
+
+                       if (opdesc->op_flags & OP_CLEAR_STATEID)
+                               clear_current_stateid(cstate);
+
+                       if (need_wrongsec_check(rqstp))
+                               op->status = check_nfsd_access(cstate->current_fh.fh_export, rqstp);
+               }
 
 encode_op:
                /* Only from SEQUENCE */
@@ -1413,6 +1446,8 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_CLOSE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_closestateid,
+               .op_set_currentstateid = (stateid_setter)nfsd4_set_closestateid,
        },
        [OP_COMMIT] = {
                .op_func = (nfsd4op_func)nfsd4_commit,
@@ -1422,7 +1457,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
        },
        [OP_CREATE] = {
                .op_func = (nfsd4op_func)nfsd4_create,
-               .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
+               .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME | OP_CLEAR_STATEID,
                .op_name = "OP_CREATE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_create_rsize,
        },
@@ -1431,6 +1466,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_DELEGRETURN",
                .op_rsize_bop = nfsd4_only_status_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_delegreturnstateid,
        },
        [OP_GETATTR] = {
                .op_func = (nfsd4op_func)nfsd4_getattr,
@@ -1453,6 +1489,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_LOCK",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_lock_rsize,
+               .op_set_currentstateid = (stateid_setter)nfsd4_set_lockstateid,
        },
        [OP_LOCKT] = {
                .op_func = (nfsd4op_func)nfsd4_lockt,
@@ -1463,15 +1500,16 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_LOCKU",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_lockustateid,
        },
        [OP_LOOKUP] = {
                .op_func = (nfsd4op_func)nfsd4_lookup,
-               .op_flags = OP_HANDLES_WRONGSEC,
+               .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID,
                .op_name = "OP_LOOKUP",
        },
        [OP_LOOKUPP] = {
                .op_func = (nfsd4op_func)nfsd4_lookupp,
-               .op_flags = OP_HANDLES_WRONGSEC,
+               .op_flags = OP_HANDLES_WRONGSEC | OP_CLEAR_STATEID,
                .op_name = "OP_LOOKUPP",
        },
        [OP_NVERIFY] = {
@@ -1483,6 +1521,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_HANDLES_WRONGSEC | OP_MODIFIES_SOMETHING,
                .op_name = "OP_OPEN",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_open_rsize,
+               .op_set_currentstateid = (stateid_setter)nfsd4_set_openstateid,
        },
        [OP_OPEN_CONFIRM] = {
                .op_func = (nfsd4op_func)nfsd4_open_confirm,
@@ -1495,25 +1534,30 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_OPEN_DOWNGRADE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_status_stateid_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_opendowngradestateid,
+               .op_set_currentstateid = (stateid_setter)nfsd4_set_opendowngradestateid,
        },
        [OP_PUTFH] = {
                .op_func = (nfsd4op_func)nfsd4_putfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
+                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
+                               | OP_CLEAR_STATEID,
                .op_name = "OP_PUTFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
        [OP_PUTPUBFH] = {
                .op_func = (nfsd4op_func)nfsd4_putrootfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
+                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
+                               | OP_CLEAR_STATEID,
                .op_name = "OP_PUTPUBFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
        [OP_PUTROOTFH] = {
                .op_func = (nfsd4op_func)nfsd4_putrootfh,
                .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS
-                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING,
+                               | OP_IS_PUTFH_LIKE | OP_MODIFIES_SOMETHING
+                               | OP_CLEAR_STATEID,
                .op_name = "OP_PUTROOTFH",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize,
        },
@@ -1522,6 +1566,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING,
                .op_name = "OP_READ",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_read_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_readstateid,
        },
        [OP_READDIR] = {
                .op_func = (nfsd4op_func)nfsd4_readdir,
@@ -1576,6 +1621,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_name = "OP_SETATTR",
                .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_setattr_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_setattrstateid,
        },
        [OP_SETCLIENTID] = {
                .op_func = (nfsd4op_func)nfsd4_setclientid,
@@ -1600,6 +1646,7 @@ static struct nfsd4_operation nfsd4_ops[] = {
                .op_flags = OP_MODIFIES_SOMETHING | OP_CACHEME,
                .op_name = "OP_WRITE",
                .op_rsize_bop = (nfsd4op_rsize)nfsd4_write_rsize,
+               .op_get_currentstateid = (stateid_getter)nfsd4_get_writestateid,
        },
        [OP_RELEASE_LOCKOWNER] = {
                .op_func = (nfsd4op_func)nfsd4_release_lockowner,
@@ -1674,12 +1721,14 @@ static struct nfsd4_operation nfsd4_ops[] = {
        },
 };
 
+#ifdef NFSD_DEBUG
 static const char *nfsd4_op_name(unsigned opnum)
 {
        if (opnum < ARRAY_SIZE(nfsd4_ops))
                return nfsd4_ops[opnum].op_name;
        return "unknown_operation";
 }
+#endif
 
 #define nfsd4_voidres                  nfsd4_voidargs
 struct nfsd4_voidargs { int dummy; };