dp->dl_recall.cbr_dp = NULL;
dp->dl_recall.cbr_ident = cb->cb_ident;
dp->dl_recall.cbr_trunc = 0;
- dp->dl_stateid.si_boot = boot_time;
+ dp->dl_stateid.si_boot = get_seconds();
dp->dl_stateid.si_stateownerid = current_delegid++;
dp->dl_stateid.si_fileid = 0;
dp->dl_stateid.si_generation = 0;
{
if (clid->cl_boot == boot_time)
return 0;
- dprintk("NFSD stale clientid (%08x/%08x)\n",
- clid->cl_boot, clid->cl_id);
+ dprintk("NFSD stale clientid (%08x/%08x) boot_time %08lx\n",
+ clid->cl_boot, clid->cl_id, boot_time);
return 1;
}
struct nfsd4_compound_state *cstate,
struct nfsd4_destroy_session *sessionid)
{
- return -1; /* stub */
+ struct nfsd4_session *ses;
+ u32 status = nfserr_badsession;
+
+ /* Notes:
+ * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid
+ * - Should we return nfserr_back_chan_busy if waiting for
+ * callbacks on to-be-destroyed session?
+ * - Do we need to clear any callback info from previous session?
+ */
+
+ dump_sessionid(__func__, &sessionid->sessionid);
+ spin_lock(&sessionid_lock);
+ ses = find_in_sessionid_hashtbl(&sessionid->sessionid);
+ if (!ses) {
+ spin_unlock(&sessionid_lock);
+ goto out;
+ }
+
+ unhash_session(ses);
+ spin_unlock(&sessionid_lock);
+
+ /* wait for callbacks */
+ shutdown_callback_client(ses->se_client);
+ nfsd4_put_session(ses);
+ status = nfs_ok;
+out:
+ dprintk("%s returns %d\n", __func__, ntohl(status));
+ return status;
}
__be32
stp->st_stateowner = sop;
get_nfs4_file(fp);
stp->st_file = fp;
- stp->st_stateid.si_boot = boot_time;
+ stp->st_stateid.si_boot = get_seconds();
stp->st_stateid.si_stateownerid = sop->so_id;
stp->st_stateid.si_fileid = fp->fi_id;
stp->st_stateid.si_generation = 0;
stp->st_access_bmap = 0;
stp->st_deny_bmap = 0;
- __set_bit(open->op_share_access, &stp->st_access_bmap);
+ __set_bit(open->op_share_access & ~NFS4_SHARE_WANT_MASK,
+ &stp->st_access_bmap);
__set_bit(open->op_share_deny, &stp->st_deny_bmap);
stp->st_openstp = NULL;
}
return NULL;
}
-static inline int access_valid(u32 x)
+static inline int access_valid(u32 x, u32 minorversion)
{
- if (x < NFS4_SHARE_ACCESS_READ)
+ if ((x & NFS4_SHARE_ACCESS_MASK) < NFS4_SHARE_ACCESS_READ)
return 0;
- if (x > NFS4_SHARE_ACCESS_BOTH)
+ if ((x & NFS4_SHARE_ACCESS_MASK) > NFS4_SHARE_ACCESS_BOTH)
+ return 0;
+ x &= ~NFS4_SHARE_ACCESS_MASK;
+ if (minorversion && x) {
+ if ((x & NFS4_SHARE_WANT_MASK) > NFS4_SHARE_WANT_CANCEL)
+ return 0;
+ if ((x & NFS4_SHARE_WHEN_MASK) > NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED)
+ return 0;
+ x &= ~(NFS4_SHARE_WANT_MASK | NFS4_SHARE_WHEN_MASK);
+ }
+ if (x)
return 0;
return 1;
}
__be32
-nfsd4_process_open1(struct nfsd4_open *open)
+nfsd4_process_open1(struct nfsd4_compound_state *cstate,
+ struct nfsd4_open *open)
{
clientid_t *clientid = &open->op_clientid;
struct nfs4_client *clp = NULL;
return nfserr_expired;
goto renew;
}
+ /* When sessions are used, skip open sequenceid processing */
+ if (nfsd4_has_session(cstate))
+ goto renew;
if (!sop->so_confirmed) {
/* Replace unconfirmed owners without checking for replay. */
clp = sop->so_client;
__be32
nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
{
+ struct nfsd4_compoundres *resp = rqstp->rq_resp;
struct nfs4_file *fp = NULL;
struct inode *ino = current_fh->fh_dentry->d_inode;
struct nfs4_stateid *stp = NULL;
__be32 status;
status = nfserr_inval;
- if (!access_valid(open->op_share_access)
+ if (!access_valid(open->op_share_access, resp->cstate.minorversion)
|| !deny_valid(open->op_share_deny))
goto out;
/*
release_open_stateid(stp);
goto out;
}
+ if (nfsd4_has_session(&resp->cstate))
+ update_stateid(&stp->st_stateid);
}
memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t));
+ if (nfsd4_has_session(&resp->cstate))
+ open->op_stateowner->so_confirmed = 1;
+
/*
* Attempt to hand out a delegation. No error return, because the
* OPEN succeeds even if we fail.
* 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 &&
+ !nfsd4_has_session(&resp->cstate))
open->op_rflags |= NFS4_OPEN_RESULT_CONFIRM;
return status;
static int
STALE_STATEID(stateid_t *stateid)
{
- if (stateid->si_boot == boot_time)
- return 0;
- dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
- stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
- stateid->si_generation);
- return 1;
+ if (time_after((unsigned long)boot_time,
+ (unsigned long)stateid->si_boot)) {
+ dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
+ stateid->si_boot, stateid->si_stateownerid,
+ stateid->si_fileid, stateid->si_generation);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+EXPIRED_STATEID(stateid_t *stateid)
+{
+ if (time_before((unsigned long)boot_time,
+ ((unsigned long)stateid->si_boot)) &&
+ time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) {
+ dprintk("NFSD: expired stateid (%08x/%08x/%08x/%08x)!\n",
+ stateid->si_boot, stateid->si_stateownerid,
+ stateid->si_fileid, stateid->si_generation);
+ return 1;
+ }
+ return 0;
+}
+
+static __be32
+stateid_error_map(stateid_t *stateid)
+{
+ if (STALE_STATEID(stateid))
+ return nfserr_stale_stateid;
+ if (EXPIRED_STATEID(stateid))
+ return nfserr_expired;
+
+ dprintk("NFSD: bad stateid (%08x/%08x/%08x/%08x)!\n",
+ stateid->si_boot, stateid->si_stateownerid,
+ stateid->si_fileid, stateid->si_generation);
+ return nfserr_bad_stateid;
}
static inline int
return locks_in_grace() && 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, int flags)
{
+ /*
+ * When sessions are used the stateid generation number is ignored
+ * when it is zero.
+ */
+ if ((flags & HAS_SESSION) && 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;
*/
if (in->si_generation < ref->si_generation)
return nfserr_old_stateid;
+out:
return nfs_ok;
}
* Checks for stateid operations
*/
__be32
-nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int flags, struct file **filpp)
+nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
+ stateid_t *stateid, int flags, struct file **filpp)
{
struct nfs4_stateid *stp = NULL;
struct nfs4_delegation *dp = NULL;
+ struct svc_fh *current_fh = &cstate->current_fh;
struct inode *ino = current_fh->fh_dentry->d_inode;
__be32 status;
if (grace_disallows_io(ino))
return nfserr_grace;
+ if (nfsd4_has_session(cstate))
+ flags |= HAS_SESSION;
+
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
return check_special_stateids(current_fh, stateid, flags);
status = nfserr_bad_stateid;
if (is_delegation_stateid(stateid)) {
dp = find_delegation_stateid(ino, stateid);
- if (!dp)
+ if (!dp) {
+ status = stateid_error_map(stateid);
goto out;
- status = check_stateid_generation(stateid, &dp->dl_stateid);
+ }
+ status = check_stateid_generation(stateid, &dp->dl_stateid,
+ flags);
if (status)
goto out;
status = nfs4_check_delegmode(dp, flags);
*filpp = dp->dl_vfs_file;
} else { /* open or lock stateid */
stp = find_stateid(stateid, flags);
- if (!stp)
+ if (!stp) {
+ status = stateid_error_map(stateid);
goto out;
+ }
if (nfs4_check_fh(current_fh, stp))
goto out;
if (!stp->st_stateowner->so_confirmed)
goto out;
- status = check_stateid_generation(stateid, &stp->st_stateid);
+ status = check_stateid_generation(stateid, &stp->st_stateid,
+ flags);
if (status)
goto out;
status = nfs4_check_openmode(stp, flags);
* Checks for sequence id mutating operations.
*/
static __be32
-nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
+nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
+ stateid_t *stateid, int flags,
+ struct nfs4_stateowner **sopp,
+ struct nfs4_stateid **stpp, struct nfsd4_lock *lock)
{
struct nfs4_stateid *stp;
struct nfs4_stateowner *sop;
+ struct svc_fh *current_fh = &cstate->current_fh;
__be32 status;
dprintk("NFSD: preprocess_seqid_op: seqid=%d "
if (STALE_STATEID(stateid))
return nfserr_stale_stateid;
+
+ if (nfsd4_has_session(cstate))
+ flags |= HAS_SESSION;
+
/*
* We return BAD_STATEID if filehandle doesn't match stateid,
* the confirmed flag is incorrecly set, or the generation
*/
sop = search_close_lru(stateid->si_stateownerid, flags);
if (sop == NULL)
- return nfserr_bad_stateid;
+ return stateid_error_map(stateid);
*sopp = sop;
goto check_replay;
}
if (lock->lk_is_new) {
if (!sop->so_is_open_owner)
return nfserr_bad_stateid;
- if (!same_clid(&clp->cl_clientid, lockclid))
- return nfserr_bad_stateid;
+ if (!(flags & HAS_SESSION) &&
+ !same_clid(&clp->cl_clientid, lockclid))
+ return nfserr_bad_stateid;
/* stp is the open stateid */
status = nfs4_check_openmode(stp, lkflg);
if (status)
* For the moment, we ignore the possibility of
* generation number wraparound.
*/
- if (seqid != sop->so_seqid)
+ if (!(flags & HAS_SESSION) && seqid != sop->so_seqid)
goto check_replay;
if (sop->so_confirmed && flags & CONFIRM) {
" confirmed yet!\n");
return nfserr_bad_stateid;
}
- status = check_stateid_generation(stateid, &stp->st_stateid);
+ status = check_stateid_generation(stateid, &stp->st_stateid, flags);
if (status)
return status;
renew_client(sop->so_client);
nfs4_lock_state();
- if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ if ((status = nfs4_preprocess_seqid_op(cstate,
oc->oc_seqid, &oc->oc_req_stateid,
CONFIRM | OPEN_STATE,
&oc->oc_stateowner, &stp, NULL)))
(int)cstate->current_fh.fh_dentry->d_name.len,
cstate->current_fh.fh_dentry->d_name.name);
- if (!access_valid(od->od_share_access)
+ if (!access_valid(od->od_share_access, cstate->minorversion)
|| !deny_valid(od->od_share_deny))
return nfserr_inval;
nfs4_lock_state();
- if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ if ((status = nfs4_preprocess_seqid_op(cstate,
od->od_seqid,
&od->od_stateid,
OPEN_STATE,
nfs4_lock_state();
/* check close_lru for replay */
- if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ if ((status = nfs4_preprocess_seqid_op(cstate,
close->cl_seqid,
&close->cl_stateid,
OPEN_STATE | CLOSE_STATE,
stateid_t *stateid = &dr->dr_stateid;
struct inode *inode;
__be32 status;
+ int flags = 0;
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0)))
return status;
inode = cstate->current_fh.fh_dentry->d_inode;
+ if (nfsd4_has_session(cstate))
+ flags |= HAS_SESSION;
nfs4_lock_state();
status = nfserr_bad_stateid;
if (ZERO_STATEID(stateid) || ONE_STATEID(stateid))
if (!is_delegation_stateid(stateid))
goto out;
dp = find_delegation_stateid(inode, stateid);
- if (!dp)
+ if (!dp) {
+ status = stateid_error_map(stateid);
goto out;
- status = check_stateid_generation(stateid, &dp->dl_stateid);
+ }
+ status = check_stateid_generation(stateid, &dp->dl_stateid, flags);
if (status)
goto out;
renew_client(dp->dl_client);
stp->st_stateowner = sop;
get_nfs4_file(fp);
stp->st_file = fp;
- stp->st_stateid.si_boot = boot_time;
+ stp->st_stateid.si_boot = get_seconds();
stp->st_stateid.si_stateownerid = sop->so_id;
stp->st_stateid.si_fileid = fp->fi_id;
stp->st_stateid.si_generation = 0;
struct nfs4_file *fp;
status = nfserr_stale_clientid;
- if (STALE_CLIENTID(&lock->lk_new_clientid))
+ if (!nfsd4_has_session(cstate) &&
+ STALE_CLIENTID(&lock->lk_new_clientid))
goto out;
/* validate and update open stateid and open seqid */
- status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ status = nfs4_preprocess_seqid_op(cstate,
lock->lk_new_open_seqid,
&lock->lk_new_open_stateid,
OPEN_STATE,
goto out;
} else {
/* lock (lock owner + lock stateid) already exists */
- status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ status = nfs4_preprocess_seqid_op(cstate,
lock->lk_old_lock_seqid,
&lock->lk_old_lock_stateid,
LOCK_STATE,
nfs4_lock_state();
status = nfserr_stale_clientid;
- if (STALE_CLIENTID(&lockt->lt_clientid))
+ if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid))
goto out;
if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) {
nfs4_lock_state();
- if ((status = nfs4_preprocess_seqid_op(&cstate->current_fh,
+ if ((status = nfs4_preprocess_seqid_op(cstate,
locku->lu_seqid,
&locku->lu_stateid,
LOCK_STATE,