From: Marc Eshel <eshel@xxxxxxxxxxxxxxx> Propagate the minorversion from open to the stateowner. On first open set seqid to 1 and mark state confirmed Skip seqid processing for NFSv4.1 4.1 is allowed to ignore the generation number when it is zero whereas 4.0 returns bad_stateid or stale stateid. Propagate minorversion to all stateful ops and down to check_stateid_generation. Signed-off-by: Benny Halevy <bhalevy@xxxxxxxxxxx> --- fs/nfsd/nfs4proc.c | 20 +++++++++++++++----- fs/nfsd/nfs4state.c | 37 +++++++++++++++++++++++++++++++------ fs/nfsd/nfs4xdr.c | 8 ++++++++ include/linux/nfsd/state.h | 2 ++ include/linux/nfsd/xdr4.h | 5 +++++ 5 files changed, 61 insertions(+), 11 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 876f285..b17948b 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -511,6 +511,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_read *read) { __be32 status; + int flags = 0; /* no need to check permission - this will be done in nfsd_read() */ @@ -518,11 +519,14 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; + flags = CHECK_FH | RD_STATE; + if (read->rd_minorversion == 1) + flags |= NFS_4_1; nfs4_lock_state(); /* check stateid */ if ((status = nfs4_preprocess_stateid_op(&cstate->current_fh, &read->rd_stateid, - CHECK_FH | RD_STATE, &read->rd_filp))) { + flags, &read->rd_filp))) { dprintk("NFSD: nfsd4_read: couldn't process stateid!\n"); goto out; } @@ -649,12 +653,15 @@ static __be32 nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_setattr *setattr) { - __be32 status = nfs_ok; + __be32 status = nfs_ok, flags = 0; if (setattr->sa_iattr.ia_valid & ATTR_SIZE) { + flags = CHECK_FH | WR_STATE; + if (setattr->sa_minorversion == 1) + flags |= NFS_4_1; nfs4_lock_state(); status = nfs4_preprocess_stateid_op(&cstate->current_fh, - &setattr->sa_stateid, CHECK_FH | WR_STATE, NULL); + &setattr->sa_stateid, flags, NULL); nfs4_unlock_state(); if (status) { dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n"); @@ -684,16 +691,19 @@ 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; + __be32 status = nfs_ok, flags = 0; /* no need to check permission - this will be done in nfsd_write() */ if (write->wr_offset >= OFFSET_MAX) return nfserr_inval; + flags = CHECK_FH | WR_STATE; + if (write->wr_minorversion == 1) + flags |= NFS_4_1; nfs4_lock_state(); status = nfs4_preprocess_stateid_op(&cstate->current_fh, stateid, - CHECK_FH | WR_STATE, &filp); + flags, &filp); if (filp) get_file(filp); nfs4_unlock_state(); diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 23e83e2..2e6e9d5 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1115,6 +1115,7 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfs4_client *clp, str sop->so_client = clp; sop->so_seqid = open->op_seqid; sop->so_confirmed = 0; + sop->so_minorversion = open->op_minorversion; rp = &sop->so_replay; rp->rp_status = nfserr_serverfault; rp->rp_buflen = 0; @@ -1524,6 +1525,9 @@ nfsd4_process_open1(struct nfsd4_open *open) open->op_stateowner = NULL; goto renew; } + /* Skip seqid processing for NFSv4.1 */ + if (open->op_minorversion == 1) + goto renew; if (open->op_seqid == sop->so_seqid - 1) { if (sop->so_replay.rp_buflen) return nfserr_replay_me; @@ -1852,9 +1856,14 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf release_stateid(stp, OPEN_STATE); goto out; } + if (open->op_minorversion == 1) + update_stateid(&stp->st_stateid); } memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); + if (open->op_minorversion == 1) + open->op_stateowner->so_confirmed = 1; + /* * Attempt to hand out a delegation. No error return, because the * OPEN succeeds even if we fail. @@ -1875,7 +1884,7 @@ out: * To finish the open response, we just need to set the rflags. */ open->op_rflags = NFS4_OPEN_RESULT_LOCKTYPE_POSIX; - if (!open->op_stateowner->so_confirmed) + if (!open->op_stateowner->so_confirmed && !open->op_minorversion) open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM; return status; @@ -2096,8 +2105,16 @@ io_during_grace_disallowed(struct inode *inode, int flags) && mandatory_lock(inode); } -static int check_stateid_generation(stateid_t *in, stateid_t *ref) +static int check_stateid_generation(stateid_t *in, stateid_t *ref, + u32 minorversion) { + /* + * 4.1 is allowed to ignore the generation number when it is zero + * whereas 4.0 returns bad_stateid or stale stateid. + */ + if (minorversion && in->si_generation == 0) + goto out; + /* If the client sends us a stateid from the future, it's buggy: */ if (in->si_generation > ref->si_generation) return nfserr_bad_stateid; @@ -2113,6 +2130,7 @@ static int check_stateid_generation(stateid_t *in, stateid_t *ref) */ if (in->si_generation < ref->si_generation) return nfserr_old_stateid; +out: return nfs_ok; } @@ -2164,7 +2182,8 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl goto out; stidp = &stp->st_stateid; } - status = check_stateid_generation(stateid, stidp); + status = check_stateid_generation(stateid, stidp, + (flags & NFS_4_1) != 0); if (status) goto out; if (stp) { @@ -2275,7 +2294,7 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei * For the moment, we ignore the possibility of * generation number wraparound. */ - if (seqid != sop->so_seqid) + if (sop->so_minorversion == 0 && seqid != sop->so_seqid) goto check_replay; if (sop->so_confirmed && flags & CONFIRM) { @@ -2288,7 +2307,8 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei " confirmed yet!\n"); return nfserr_bad_stateid; } - status = check_stateid_generation(stateid, &stp->st_stateid); + status = check_stateid_generation(stateid, &stp->st_stateid, + sop->so_minorversion); if (status) return status; renew_client(sop->so_client); @@ -2480,13 +2500,17 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_delegreturn *dr) { __be32 status; + int flags = 0; if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) goto out; nfs4_lock_state(); + flags |= DELEG_RET; + if (dr->dr_minorversion == 1) + flags |= NFS_4_1; status = nfs4_preprocess_stateid_op(&cstate->current_fh, - &dr->dr_stateid, DELEG_RET, NULL); + &dr->dr_stateid, flags, NULL); nfs4_unlock_state(); out: return status; @@ -2659,6 +2683,7 @@ alloc_init_lock_stateowner(unsigned int strhashval, struct nfs4_client *clp, str * case of new lockowners; so increment the lock seqid manually: */ sop->so_seqid = lock->lk_new_lock_seqid + 1; sop->so_confirmed = 1; + sop->so_minorversion = open_stp->st_stateowner->so_minorversion; rp = &sop->so_replay; rp->rp_status = nfserr_serverfault; rp->rp_buflen = 0; diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index f7dec05..9b26ba9 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -633,6 +633,8 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) open->op_iattr.ia_valid = 0; open->op_stateowner = NULL; + open->op_minorversion = argp->minorversion; + /* seqid, share_access, share_deny, clientid, ownerlen */ READ_BUF(16 + sizeof(clientid_t)); READ32(open->op_seqid); @@ -756,6 +758,8 @@ nfsd4_decode_read(struct nfsd4_compoundargs *argp, struct nfsd4_read *read) { DECODE_HEAD; + read->rd_minorversion = argp->minorversion; + status = nfsd4_decode_stateid(argp, &read->rd_stateid); if (status) return status; @@ -850,6 +854,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta { __be32 status; + setattr->sa_minorversion = argp->minorversion; + status = nfsd4_decode_stateid(argp, &setattr->sa_stateid); if (status) return status; @@ -939,6 +945,8 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) int len; DECODE_HEAD; + write->wr_minorversion = argp->minorversion; + status = nfsd4_decode_stateid(argp, &write->wr_stateid); if (status) return status; diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 85cdaf1..5cb4142 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -290,6 +290,7 @@ struct nfs4_stateowner { u32 so_seqid; struct xdr_netobj so_owner; /* open owner name */ int so_confirmed; /* successful OPEN_CONFIRM? */ + u32 so_minorversion; struct nfs4_replay so_replay; }; @@ -349,6 +350,7 @@ struct nfs4_stateid { #define WR_STATE 0x00000020 #define CLOSE_STATE 0x00000040 #define DELEG_RET 0x00000080 +#define NFS_4_1 0x00000100 #define seqid_mutating_err(err) \ (((err) != nfserr_stale_clientid) && \ diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index 88f7cd6..81c249c 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -105,6 +105,7 @@ struct nfsd4_create { struct nfsd4_delegreturn { stateid_t dr_stateid; + u32 dr_minorversion; }; struct nfsd4_getattr { @@ -223,6 +224,7 @@ struct nfsd4_open { u32 op_recall; /* recall */ struct nfsd4_change_info op_cinfo; /* response */ u32 op_rflags; /* response */ + u32 op_minorversion; /* used during processing */ int op_truncate; /* used during processing */ struct nfs4_stateowner *op_stateowner; /* used during processing */ struct nfs4_acl *op_acl; @@ -255,6 +257,7 @@ struct nfsd4_read { struct svc_rqst *rd_rqstp; /* response */ struct svc_fh * rd_fhp; /* response */ + u32 rd_minorversion; /* processing */ }; struct nfsd4_readdir { @@ -304,6 +307,7 @@ struct nfsd4_secinfo { struct nfsd4_setattr { stateid_t sa_stateid; /* request */ + u32 sa_minorversion; /* processing */ u32 sa_bmval[2]; /* request */ struct iattr sa_iattr; /* request */ struct nfs4_acl *sa_acl; @@ -345,6 +349,7 @@ struct nfsd4_write { u32 wr_bytes_written; /* response */ u32 wr_how_written; /* response */ nfs4_verifier wr_verifier; /* response */ + u32 wr_minorversion; /* processing */ }; struct nfsd4_op { -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html