diff options
Diffstat (limited to 'fs/nfsd/nfs4proc.c')
-rw-r--r-- | fs/nfsd/nfs4proc.c | 246 |
1 files changed, 172 insertions, 74 deletions
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 9fa60a3ad48c..b2883e9c6381 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -93,6 +93,21 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
93 | open->op_truncate = 0; | 93 | open->op_truncate = 0; |
94 | 94 | ||
95 | if (open->op_create) { | 95 | if (open->op_create) { |
96 | /* FIXME: check session persistence and pnfs flags. | ||
97 | * The nfsv4.1 spec requires the following semantics: | ||
98 | * | ||
99 | * Persistent | pNFS | Server REQUIRED | Client Allowed | ||
100 | * Reply Cache | server | | | ||
101 | * -------------+--------+-----------------+-------------------- | ||
102 | * no | no | EXCLUSIVE4_1 | EXCLUSIVE4_1 | ||
103 | * | | | (SHOULD) | ||
104 | * | | and EXCLUSIVE4 | or EXCLUSIVE4 | ||
105 | * | | | (SHOULD NOT) | ||
106 | * no | yes | EXCLUSIVE4_1 | EXCLUSIVE4_1 | ||
107 | * yes | no | GUARDED4 | GUARDED4 | ||
108 | * yes | yes | GUARDED4 | GUARDED4 | ||
109 | */ | ||
110 | |||
96 | /* | 111 | /* |
97 | * Note: create modes (UNCHECKED,GUARDED...) are the same | 112 | * Note: create modes (UNCHECKED,GUARDED...) are the same |
98 | * in NFSv4 as in v3. | 113 | * in NFSv4 as in v3. |
@@ -103,11 +118,13 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
103 | (u32 *)open->op_verf.data, | 118 | (u32 *)open->op_verf.data, |
104 | &open->op_truncate, &created); | 119 | &open->op_truncate, &created); |
105 | 120 | ||
106 | /* If we ever decide to use different attrs to store the | 121 | /* |
107 | * verifier in nfsd_create_v3, then we'll need to change this | 122 | * Following rfc 3530 14.2.16, use the returned bitmask |
123 | * to indicate which attributes we used to store the | ||
124 | * verifier: | ||
108 | */ | 125 | */ |
109 | if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0) | 126 | if (open->op_createmode == NFS4_CREATE_EXCLUSIVE && status == 0) |
110 | open->op_bmval[1] |= (FATTR4_WORD1_TIME_ACCESS | | 127 | open->op_bmval[1] = (FATTR4_WORD1_TIME_ACCESS | |
111 | FATTR4_WORD1_TIME_MODIFY); | 128 | FATTR4_WORD1_TIME_MODIFY); |
112 | } else { | 129 | } else { |
113 | status = nfsd_lookup(rqstp, current_fh, | 130 | status = nfsd_lookup(rqstp, current_fh, |
@@ -118,13 +135,11 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
118 | goto out; | 135 | goto out; |
119 | 136 | ||
120 | set_change_info(&open->op_cinfo, current_fh); | 137 | set_change_info(&open->op_cinfo, current_fh); |
121 | |||
122 | /* set reply cache */ | ||
123 | fh_dup2(current_fh, &resfh); | 138 | fh_dup2(current_fh, &resfh); |
124 | open->op_stateowner->so_replay.rp_openfh_len = resfh.fh_handle.fh_size; | ||
125 | memcpy(open->op_stateowner->so_replay.rp_openfh, | ||
126 | &resfh.fh_handle.fh_base, resfh.fh_handle.fh_size); | ||
127 | 139 | ||
140 | /* set reply cache */ | ||
141 | fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh, | ||
142 | &resfh.fh_handle); | ||
128 | if (!created) | 143 | if (!created) |
129 | status = do_open_permission(rqstp, current_fh, open, | 144 | status = do_open_permission(rqstp, current_fh, open, |
130 | NFSD_MAY_NOP); | 145 | NFSD_MAY_NOP); |
@@ -150,10 +165,8 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ | |||
150 | memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); | 165 | memset(&open->op_cinfo, 0, sizeof(struct nfsd4_change_info)); |
151 | 166 | ||
152 | /* set replay cache */ | 167 | /* set replay cache */ |
153 | open->op_stateowner->so_replay.rp_openfh_len = current_fh->fh_handle.fh_size; | 168 | fh_copy_shallow(&open->op_stateowner->so_replay.rp_openfh, |
154 | memcpy(open->op_stateowner->so_replay.rp_openfh, | 169 | ¤t_fh->fh_handle); |
155 | ¤t_fh->fh_handle.fh_base, | ||
156 | current_fh->fh_handle.fh_size); | ||
157 | 170 | ||
158 | open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && | 171 | open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) && |
159 | (open->op_iattr.ia_size == 0); | 172 | (open->op_iattr.ia_size == 0); |
@@ -164,12 +177,23 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ | |||
164 | return status; | 177 | return status; |
165 | } | 178 | } |
166 | 179 | ||
180 | static void | ||
181 | copy_clientid(clientid_t *clid, struct nfsd4_session *session) | ||
182 | { | ||
183 | struct nfsd4_sessionid *sid = | ||
184 | (struct nfsd4_sessionid *)session->se_sessionid.data; | ||
185 | |||
186 | clid->cl_boot = sid->clientid.cl_boot; | ||
187 | clid->cl_id = sid->clientid.cl_id; | ||
188 | } | ||
167 | 189 | ||
168 | static __be32 | 190 | static __be32 |
169 | nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 191 | nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
170 | struct nfsd4_open *open) | 192 | struct nfsd4_open *open) |
171 | { | 193 | { |
172 | __be32 status; | 194 | __be32 status; |
195 | struct nfsd4_compoundres *resp; | ||
196 | |||
173 | dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", | 197 | dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n", |
174 | (int)open->op_fname.len, open->op_fname.data, | 198 | (int)open->op_fname.len, open->op_fname.data, |
175 | open->op_stateowner); | 199 | open->op_stateowner); |
@@ -178,16 +202,19 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
178 | if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) | 202 | if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL) |
179 | return nfserr_inval; | 203 | return nfserr_inval; |
180 | 204 | ||
205 | if (nfsd4_has_session(cstate)) | ||
206 | copy_clientid(&open->op_clientid, cstate->session); | ||
207 | |||
181 | nfs4_lock_state(); | 208 | nfs4_lock_state(); |
182 | 209 | ||
183 | /* check seqid for replay. set nfs4_owner */ | 210 | /* check seqid for replay. set nfs4_owner */ |
184 | status = nfsd4_process_open1(open); | 211 | resp = rqstp->rq_resp; |
212 | status = nfsd4_process_open1(&resp->cstate, open); | ||
185 | if (status == nfserr_replay_me) { | 213 | if (status == nfserr_replay_me) { |
186 | struct nfs4_replay *rp = &open->op_stateowner->so_replay; | 214 | struct nfs4_replay *rp = &open->op_stateowner->so_replay; |
187 | fh_put(&cstate->current_fh); | 215 | fh_put(&cstate->current_fh); |
188 | cstate->current_fh.fh_handle.fh_size = rp->rp_openfh_len; | 216 | fh_copy_shallow(&cstate->current_fh.fh_handle, |
189 | memcpy(&cstate->current_fh.fh_handle.fh_base, rp->rp_openfh, | 217 | &rp->rp_openfh); |
190 | rp->rp_openfh_len); | ||
191 | status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); | 218 | status = fh_verify(rqstp, &cstate->current_fh, 0, NFSD_MAY_NOP); |
192 | if (status) | 219 | if (status) |
193 | dprintk("nfsd4_open: replay failed" | 220 | dprintk("nfsd4_open: replay failed" |
@@ -209,10 +236,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
209 | 236 | ||
210 | switch (open->op_claim_type) { | 237 | switch (open->op_claim_type) { |
211 | case NFS4_OPEN_CLAIM_DELEGATE_CUR: | 238 | case NFS4_OPEN_CLAIM_DELEGATE_CUR: |
212 | status = nfserr_inval; | ||
213 | if (open->op_create) | ||
214 | goto out; | ||
215 | /* fall through */ | ||
216 | case NFS4_OPEN_CLAIM_NULL: | 239 | case NFS4_OPEN_CLAIM_NULL: |
217 | /* | 240 | /* |
218 | * (1) set CURRENT_FH to the file being opened, | 241 | * (1) set CURRENT_FH to the file being opened, |
@@ -455,8 +478,9 @@ nfsd4_getattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
455 | if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) | 478 | if (getattr->ga_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) |
456 | return nfserr_inval; | 479 | return nfserr_inval; |
457 | 480 | ||
458 | getattr->ga_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; | 481 | getattr->ga_bmval[0] &= nfsd_suppattrs0(cstate->minorversion); |
459 | getattr->ga_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; | 482 | getattr->ga_bmval[1] &= nfsd_suppattrs1(cstate->minorversion); |
483 | getattr->ga_bmval[2] &= nfsd_suppattrs2(cstate->minorversion); | ||
460 | 484 | ||
461 | getattr->ga_fhp = &cstate->current_fh; | 485 | getattr->ga_fhp = &cstate->current_fh; |
462 | return nfs_ok; | 486 | return nfs_ok; |
@@ -520,9 +544,8 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
520 | 544 | ||
521 | nfs4_lock_state(); | 545 | nfs4_lock_state(); |
522 | /* check stateid */ | 546 | /* check stateid */ |
523 | if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh, | 547 | if ((status = nfs4_preprocess_stateid_op(cstate, &read->rd_stateid, |
524 | &read->rd_stateid, | 548 | RD_STATE, &read->rd_filp))) { |
525 | CHECK_FH | RD_STATE, &read->rd_filp))) { | ||
526 | dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); | 549 | dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); |
527 | goto out; | 550 | goto out; |
528 | } | 551 | } |
@@ -548,8 +571,9 @@ nfsd4_readdir(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
548 | if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) | 571 | if (readdir->rd_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1) |
549 | return nfserr_inval; | 572 | return nfserr_inval; |
550 | 573 | ||
551 | readdir->rd_bmval[0] &= NFSD_SUPPORTED_ATTRS_WORD0; | 574 | readdir->rd_bmval[0] &= nfsd_suppattrs0(cstate->minorversion); |
552 | readdir->rd_bmval[1] &= NFSD_SUPPORTED_ATTRS_WORD1; | 575 | readdir->rd_bmval[1] &= nfsd_suppattrs1(cstate->minorversion); |
576 | readdir->rd_bmval[2] &= nfsd_suppattrs2(cstate->minorversion); | ||
553 | 577 | ||
554 | if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || | 578 | if ((cookie > ~(u32)0) || (cookie == 1) || (cookie == 2) || |
555 | (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) | 579 | (cookie == 0 && memcmp(readdir->rd_verf.data, zeroverf.data, NFS4_VERIFIER_SIZE))) |
@@ -653,8 +677,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
653 | 677 | ||
654 | if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { | 678 | if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { |
655 | nfs4_lock_state(); | 679 | nfs4_lock_state(); |
656 | status = nfs4_preprocess_stateid_op(&cstate->current_fh, | 680 | status = nfs4_preprocess_stateid_op(cstate, |
657 | &setattr->sa_stateid, CHECK_FH | WR_STATE, NULL); | 681 | &setattr->sa_stateid, WR_STATE, NULL); |
658 | nfs4_unlock_state(); | 682 | nfs4_unlock_state(); |
659 | if (status) { | 683 | if (status) { |
660 | dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); | 684 | dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); |
@@ -685,6 +709,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
685 | struct file *filp = NULL; | 709 | struct file *filp = NULL; |
686 | u32 *p; | 710 | u32 *p; |
687 | __be32 status = nfs_ok; | 711 | __be32 status = nfs_ok; |
712 | unsigned long cnt; | ||
688 | 713 | ||
689 | /* no need to check permission - this will be done in nfsd_write() */ | 714 | /* no need to check permission - this will be done in nfsd_write() */ |
690 | 715 | ||
@@ -692,8 +717,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
692 | return nfserr_inval; | 717 | return nfserr_inval; |
693 | 718 | ||
694 | nfs4_lock_state(); | 719 | nfs4_lock_state(); |
695 | status = nfs4_preprocess_stateid_op(&cstate->current_fh, stateid, | 720 | status = nfs4_preprocess_stateid_op(cstate, stateid, WR_STATE, &filp); |
696 | CHECK_FH | WR_STATE, &filp); | ||
697 | if (filp) | 721 | if (filp) |
698 | get_file(filp); | 722 | get_file(filp); |
699 | nfs4_unlock_state(); | 723 | nfs4_unlock_state(); |
@@ -703,7 +727,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
703 | return status; | 727 | return status; |
704 | } | 728 | } |
705 | 729 | ||
706 | write->wr_bytes_written = write->wr_buflen; | 730 | cnt = write->wr_buflen; |
707 | write->wr_how_written = write->wr_stable_how; | 731 | write->wr_how_written = write->wr_stable_how; |
708 | p = (u32 *)write->wr_verifier.data; | 732 | p = (u32 *)write->wr_verifier.data; |
709 | *p++ = nfssvc_boot.tv_sec; | 733 | *p++ = nfssvc_boot.tv_sec; |
@@ -711,10 +735,12 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
711 | 735 | ||
712 | status = nfsd_write(rqstp, &cstate->current_fh, filp, | 736 | status = nfsd_write(rqstp, &cstate->current_fh, filp, |
713 | write->wr_offset, rqstp->rq_vec, write->wr_vlen, | 737 | write->wr_offset, rqstp->rq_vec, write->wr_vlen, |
714 | write->wr_buflen, &write->wr_how_written); | 738 | &cnt, &write->wr_how_written); |
715 | if (filp) | 739 | if (filp) |
716 | fput(filp); | 740 | fput(filp); |
717 | 741 | ||
742 | write->wr_bytes_written = cnt; | ||
743 | |||
718 | if (status == nfserr_symlink) | 744 | if (status == nfserr_symlink) |
719 | status = nfserr_inval; | 745 | status = nfserr_inval; |
720 | return status; | 746 | return status; |
@@ -737,8 +763,9 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
737 | if (status) | 763 | if (status) |
738 | return status; | 764 | return status; |
739 | 765 | ||
740 | if ((verify->ve_bmval[0] & ~NFSD_SUPPORTED_ATTRS_WORD0) | 766 | if ((verify->ve_bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) |
741 | || (verify->ve_bmval[1] & ~NFSD_SUPPORTED_ATTRS_WORD1)) | 767 | || (verify->ve_bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) |
768 | || (verify->ve_bmval[2] & ~nfsd_suppattrs2(cstate->minorversion))) | ||
742 | return nfserr_attrnotsupp; | 769 | return nfserr_attrnotsupp; |
743 | if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) | 770 | if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR) |
744 | || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) | 771 | || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1)) |
@@ -766,7 +793,8 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
766 | if (status) | 793 | if (status) |
767 | goto out_kfree; | 794 | goto out_kfree; |
768 | 795 | ||
769 | p = buf + 3; | 796 | /* skip bitmap */ |
797 | p = buf + 1 + ntohl(buf[0]); | ||
770 | status = nfserr_not_same; | 798 | status = nfserr_not_same; |
771 | if (ntohl(*p++) != verify->ve_attrlen) | 799 | if (ntohl(*p++) != verify->ve_attrlen) |
772 | goto out_kfree; | 800 | goto out_kfree; |
@@ -813,39 +841,17 @@ static inline void nfsd4_increment_op_stats(u32 opnum) | |||
813 | nfsdstats.nfs4_opcount[opnum]++; | 841 | nfsdstats.nfs4_opcount[opnum]++; |
814 | } | 842 | } |
815 | 843 | ||
816 | static void cstate_free(struct nfsd4_compound_state *cstate) | ||
817 | { | ||
818 | if (cstate == NULL) | ||
819 | return; | ||
820 | fh_put(&cstate->current_fh); | ||
821 | fh_put(&cstate->save_fh); | ||
822 | BUG_ON(cstate->replay_owner); | ||
823 | kfree(cstate); | ||
824 | } | ||
825 | |||
826 | static struct nfsd4_compound_state *cstate_alloc(void) | ||
827 | { | ||
828 | struct nfsd4_compound_state *cstate; | ||
829 | |||
830 | cstate = kmalloc(sizeof(struct nfsd4_compound_state), GFP_KERNEL); | ||
831 | if (cstate == NULL) | ||
832 | return NULL; | ||
833 | fh_init(&cstate->current_fh, NFS4_FHSIZE); | ||
834 | fh_init(&cstate->save_fh, NFS4_FHSIZE); | ||
835 | cstate->replay_owner = NULL; | ||
836 | return cstate; | ||
837 | } | ||
838 | |||
839 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, | 844 | typedef __be32(*nfsd4op_func)(struct svc_rqst *, struct nfsd4_compound_state *, |
840 | void *); | 845 | void *); |
846 | enum nfsd4_op_flags { | ||
847 | ALLOWED_WITHOUT_FH = 1 << 0, /* No current filehandle required */ | ||
848 | ALLOWED_ON_ABSENT_FS = 2 << 0, /* ops processed on absent fs */ | ||
849 | ALLOWED_AS_FIRST_OP = 3 << 0, /* ops reqired first in compound */ | ||
850 | }; | ||
841 | 851 | ||
842 | struct nfsd4_operation { | 852 | struct nfsd4_operation { |
843 | nfsd4op_func op_func; | 853 | nfsd4op_func op_func; |
844 | u32 op_flags; | 854 | u32 op_flags; |
845 | /* Most ops require a valid current filehandle; a few don't: */ | ||
846 | #define ALLOWED_WITHOUT_FH 1 | ||
847 | /* GETATTR and ops not listed as returning NFS4ERR_MOVED: */ | ||
848 | #define ALLOWED_ON_ABSENT_FS 2 | ||
849 | char *op_name; | 855 | char *op_name; |
850 | }; | 856 | }; |
851 | 857 | ||
@@ -854,6 +860,51 @@ static struct nfsd4_operation nfsd4_ops[]; | |||
854 | static const char *nfsd4_op_name(unsigned opnum); | 860 | static const char *nfsd4_op_name(unsigned opnum); |
855 | 861 | ||
856 | /* | 862 | /* |
863 | * This is a replay of a compound for which no cache entry pages | ||
864 | * were used. Encode the sequence operation, and if cachethis is FALSE | ||
865 | * encode the uncache rep error on the next operation. | ||
866 | */ | ||
867 | static __be32 | ||
868 | nfsd4_enc_uncached_replay(struct nfsd4_compoundargs *args, | ||
869 | struct nfsd4_compoundres *resp) | ||
870 | { | ||
871 | struct nfsd4_op *op; | ||
872 | |||
873 | dprintk("--> %s resp->opcnt %d ce_cachethis %u \n", __func__, | ||
874 | resp->opcnt, resp->cstate.slot->sl_cache_entry.ce_cachethis); | ||
875 | |||
876 | /* Encode the replayed sequence operation */ | ||
877 | BUG_ON(resp->opcnt != 1); | ||
878 | op = &args->ops[resp->opcnt - 1]; | ||
879 | nfsd4_encode_operation(resp, op); | ||
880 | |||
881 | /*return nfserr_retry_uncached_rep in next operation. */ | ||
882 | if (resp->cstate.slot->sl_cache_entry.ce_cachethis == 0) { | ||
883 | op = &args->ops[resp->opcnt++]; | ||
884 | op->status = nfserr_retry_uncached_rep; | ||
885 | nfsd4_encode_operation(resp, op); | ||
886 | } | ||
887 | return op->status; | ||
888 | } | ||
889 | |||
890 | /* | ||
891 | * Enforce NFSv4.1 COMPOUND ordering rules. | ||
892 | * | ||
893 | * TODO: | ||
894 | * - enforce NFS4ERR_NOT_ONLY_OP, | ||
895 | * - DESTROY_SESSION MUST be the final operation in the COMPOUND request. | ||
896 | */ | ||
897 | static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args) | ||
898 | { | ||
899 | if (args->minorversion && args->opcnt > 0) { | ||
900 | struct nfsd4_op *op = &args->ops[0]; | ||
901 | return (op->status == nfserr_op_illegal) || | ||
902 | (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP); | ||
903 | } | ||
904 | return true; | ||
905 | } | ||
906 | |||
907 | /* | ||
857 | * COMPOUND call. | 908 | * COMPOUND call. |
858 | */ | 909 | */ |
859 | static __be32 | 910 | static __be32 |
@@ -863,12 +914,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
863 | { | 914 | { |
864 | struct nfsd4_op *op; | 915 | struct nfsd4_op *op; |
865 | struct nfsd4_operation *opdesc; | 916 | struct nfsd4_operation *opdesc; |
866 | struct nfsd4_compound_state *cstate = NULL; | 917 | struct nfsd4_compound_state *cstate = &resp->cstate; |
867 | int slack_bytes; | 918 | int slack_bytes; |
868 | __be32 status; | 919 | __be32 status; |
869 | 920 | ||
870 | resp->xbuf = &rqstp->rq_res; | 921 | resp->xbuf = &rqstp->rq_res; |
871 | resp->p = rqstp->rq_res.head[0].iov_base + rqstp->rq_res.head[0].iov_len; | 922 | resp->p = rqstp->rq_res.head[0].iov_base + |
923 | rqstp->rq_res.head[0].iov_len; | ||
872 | resp->tagp = resp->p; | 924 | resp->tagp = resp->p; |
873 | /* reserve space for: taglen, tag, and opcnt */ | 925 | /* reserve space for: taglen, tag, and opcnt */ |
874 | resp->p += 2 + XDR_QUADLEN(args->taglen); | 926 | resp->p += 2 + XDR_QUADLEN(args->taglen); |
@@ -877,18 +929,25 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
877 | resp->tag = args->tag; | 929 | resp->tag = args->tag; |
878 | resp->opcnt = 0; | 930 | resp->opcnt = 0; |
879 | resp->rqstp = rqstp; | 931 | resp->rqstp = rqstp; |
932 | resp->cstate.minorversion = args->minorversion; | ||
933 | resp->cstate.replay_owner = NULL; | ||
934 | fh_init(&resp->cstate.current_fh, NFS4_FHSIZE); | ||
935 | fh_init(&resp->cstate.save_fh, NFS4_FHSIZE); | ||
936 | /* Use the deferral mechanism only for NFSv4.0 compounds */ | ||
937 | rqstp->rq_usedeferral = (args->minorversion == 0); | ||
880 | 938 | ||
881 | /* | 939 | /* |
882 | * According to RFC3010, this takes precedence over all other errors. | 940 | * According to RFC3010, this takes precedence over all other errors. |
883 | */ | 941 | */ |
884 | status = nfserr_minor_vers_mismatch; | 942 | status = nfserr_minor_vers_mismatch; |
885 | if (args->minorversion > NFSD_SUPPORTED_MINOR_VERSION) | 943 | if (args->minorversion > nfsd_supported_minorversion) |
886 | goto out; | 944 | goto out; |
887 | 945 | ||
888 | status = nfserr_resource; | 946 | if (!nfs41_op_ordering_ok(args)) { |
889 | cstate = cstate_alloc(); | 947 | op = &args->ops[0]; |
890 | if (cstate == NULL) | 948 | op->status = nfserr_sequence_pos; |
891 | goto out; | 949 | goto encode_op; |
950 | } | ||
892 | 951 | ||
893 | status = nfs_ok; | 952 | status = nfs_ok; |
894 | while (!status && resp->opcnt < args->opcnt) { | 953 | while (!status && resp->opcnt < args->opcnt) { |
@@ -897,7 +956,6 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
897 | dprintk("nfsv4 compound op #%d/%d: %d (%s)\n", | 956 | dprintk("nfsv4 compound op #%d/%d: %d (%s)\n", |
898 | resp->opcnt, args->opcnt, op->opnum, | 957 | resp->opcnt, args->opcnt, op->opnum, |
899 | nfsd4_op_name(op->opnum)); | 958 | nfsd4_op_name(op->opnum)); |
900 | |||
901 | /* | 959 | /* |
902 | * The XDR decode routines may have pre-set op->status; | 960 | * The XDR decode routines may have pre-set op->status; |
903 | * for example, if there is a miscellaneous XDR error | 961 | * for example, if there is a miscellaneous XDR error |
@@ -938,6 +996,15 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
938 | BUG_ON(op->status == nfs_ok); | 996 | BUG_ON(op->status == nfs_ok); |
939 | 997 | ||
940 | encode_op: | 998 | encode_op: |
999 | /* Only from SEQUENCE or CREATE_SESSION */ | ||
1000 | if (resp->cstate.status == nfserr_replay_cache) { | ||
1001 | dprintk("%s NFS4.1 replay from cache\n", __func__); | ||
1002 | if (nfsd4_not_cached(resp)) | ||
1003 | status = nfsd4_enc_uncached_replay(args, resp); | ||
1004 | else | ||
1005 | status = op->status; | ||
1006 | goto out; | ||
1007 | } | ||
941 | if (op->status == nfserr_replay_me) { | 1008 | if (op->status == nfserr_replay_me) { |
942 | op->replay = &cstate->replay_owner->so_replay; | 1009 | op->replay = &cstate->replay_owner->so_replay; |
943 | nfsd4_encode_replay(resp, op); | 1010 | nfsd4_encode_replay(resp, op); |
@@ -961,15 +1028,24 @@ encode_op: | |||
961 | 1028 | ||
962 | nfsd4_increment_op_stats(op->opnum); | 1029 | nfsd4_increment_op_stats(op->opnum); |
963 | } | 1030 | } |
1031 | if (!rqstp->rq_usedeferral && status == nfserr_dropit) { | ||
1032 | dprintk("%s Dropit - send NFS4ERR_DELAY\n", __func__); | ||
1033 | status = nfserr_jukebox; | ||
1034 | } | ||
964 | 1035 | ||
965 | cstate_free(cstate); | 1036 | resp->cstate.status = status; |
1037 | fh_put(&resp->cstate.current_fh); | ||
1038 | fh_put(&resp->cstate.save_fh); | ||
1039 | BUG_ON(resp->cstate.replay_owner); | ||
966 | out: | 1040 | out: |
967 | nfsd4_release_compoundargs(args); | 1041 | nfsd4_release_compoundargs(args); |
1042 | /* Reset deferral mechanism for RPC deferrals */ | ||
1043 | rqstp->rq_usedeferral = 1; | ||
968 | dprintk("nfsv4 compound returned %d\n", ntohl(status)); | 1044 | dprintk("nfsv4 compound returned %d\n", ntohl(status)); |
969 | return status; | 1045 | return status; |
970 | } | 1046 | } |
971 | 1047 | ||
972 | static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { | 1048 | static struct nfsd4_operation nfsd4_ops[] = { |
973 | [OP_ACCESS] = { | 1049 | [OP_ACCESS] = { |
974 | .op_func = (nfsd4op_func)nfsd4_access, | 1050 | .op_func = (nfsd4op_func)nfsd4_access, |
975 | .op_name = "OP_ACCESS", | 1051 | .op_name = "OP_ACCESS", |
@@ -1045,7 +1121,7 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { | |||
1045 | .op_name = "OP_PUTFH", | 1121 | .op_name = "OP_PUTFH", |
1046 | }, | 1122 | }, |
1047 | [OP_PUTPUBFH] = { | 1123 | [OP_PUTPUBFH] = { |
1048 | /* unsupported, just for future reference: */ | 1124 | .op_func = (nfsd4op_func)nfsd4_putrootfh, |
1049 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, | 1125 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, |
1050 | .op_name = "OP_PUTPUBFH", | 1126 | .op_name = "OP_PUTPUBFH", |
1051 | }, | 1127 | }, |
@@ -1119,6 +1195,28 @@ static struct nfsd4_operation nfsd4_ops[OP_RELEASE_LOCKOWNER+1] = { | |||
1119 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, | 1195 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_ON_ABSENT_FS, |
1120 | .op_name = "OP_RELEASE_LOCKOWNER", | 1196 | .op_name = "OP_RELEASE_LOCKOWNER", |
1121 | }, | 1197 | }, |
1198 | |||
1199 | /* NFSv4.1 operations */ | ||
1200 | [OP_EXCHANGE_ID] = { | ||
1201 | .op_func = (nfsd4op_func)nfsd4_exchange_id, | ||
1202 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, | ||
1203 | .op_name = "OP_EXCHANGE_ID", | ||
1204 | }, | ||
1205 | [OP_CREATE_SESSION] = { | ||
1206 | .op_func = (nfsd4op_func)nfsd4_create_session, | ||
1207 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, | ||
1208 | .op_name = "OP_CREATE_SESSION", | ||
1209 | }, | ||
1210 | [OP_DESTROY_SESSION] = { | ||
1211 | .op_func = (nfsd4op_func)nfsd4_destroy_session, | ||
1212 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, | ||
1213 | .op_name = "OP_DESTROY_SESSION", | ||
1214 | }, | ||
1215 | [OP_SEQUENCE] = { | ||
1216 | .op_func = (nfsd4op_func)nfsd4_sequence, | ||
1217 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, | ||
1218 | .op_name = "OP_SEQUENCE", | ||
1219 | }, | ||
1122 | }; | 1220 | }; |
1123 | 1221 | ||
1124 | static const char *nfsd4_op_name(unsigned opnum) | 1222 | static const char *nfsd4_op_name(unsigned opnum) |