Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4c4cd222 authored by NeilBrown's avatar NeilBrown Committed by Linus Torvalds
Browse files

[PATCH] nfsd4: check lock type against openmode.



We shouldn't be allowing, e.g., write locks on files not open for read.  To
enforce this, we add a pointer from the lock stateid back to the open stateid
it came from, so that the check will continue to be correct even after the
open is upgraded or downgraded.

Signed-off-by: default avatarAndy Adamson <andros@citi.umich.edu>
Signed-off-by: default avatarJ. Bruce Fields <bfields@citi.umich.edu>
Signed-off-by: default avatarNeil Brown <neilb@cse.unsw.edu.au>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 3a4f98bb
Loading
Loading
Loading
Loading
+33 −16
Original line number Diff line number Diff line
@@ -1160,6 +1160,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *
	stp->st_deny_bmap = 0;
	__set_bit(open->op_share_access, &stp->st_access_bmap);
	__set_bit(open->op_share_deny, &stp->st_deny_bmap);
	stp->st_openstp = NULL;
}

static void
@@ -2158,12 +2159,18 @@ nfs4_preprocess_stateid_op(struct svc_fh *current_fh, stateid_t *stateid, int fl
	return status;
}

static inline int
setlkflg (int type)
{
	return (type == NFS4_READW_LT || type == NFS4_READ_LT) ?
		RD_STATE : WR_STATE;
}

/* 
 * Checks for sequence id mutating operations. 
 */
static int
nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *stateid, int flags, struct nfs4_stateowner **sopp, struct nfs4_stateid **stpp, clientid_t *lockclid)
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)
{
	struct nfs4_stateid *stp;
	struct nfs4_stateowner *sop;
@@ -2201,21 +2208,31 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
		goto check_replay;
	}

	/* for new lock stateowners:
	 * check that the lock->v.new.open_stateid
	 * refers to an open stateowner
	 *
	 * check that the lockclid (nfs4_lock->v.new.clientid) is the same
	 * as the open_stateid->st_stateowner->so_client->clientid
	 */
	if (lockclid) {
	if (lock) {
		struct nfs4_stateowner *sop = stp->st_stateowner;
		clientid_t *lockclid = &lock->v.new.clientid;
		struct nfs4_client *clp = sop->so_client;
		int lkflg = 0;
		int status;

		lkflg = setlkflg(lock->lk_type);

		if (lock->lk_is_new) {
                       if (!sop->so_is_open_owner)
			       return nfserr_bad_stateid;
                       if (!cmp_clid(&clp->cl_clientid, lockclid))
			       return nfserr_bad_stateid;
                       /* stp is the open stateid */
                       status = nfs4_check_openmode(stp, lkflg);
                       if (status)
			       return status;
               } else {
                       /* stp is the lock stateid */
                       status = nfs4_check_openmode(stp->st_openstp, lkflg);
                       if (status)
			       return status;
               }

	}

	if ((flags & CHECK_FH) && nfs4_check_fh(current_fh, stp)) {
@@ -2642,6 +2659,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
	stp->st_vfs_file = open_stp->st_vfs_file; /* FIXME refcount?? */
	stp->st_access_bmap = open_stp->st_access_bmap;
	stp->st_deny_bmap = open_stp->st_deny_bmap;
	stp->st_openstp = open_stp;

out:
	return stp;
@@ -2697,8 +2715,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
				        lock->lk_new_open_seqid,
		                        &lock->lk_new_open_stateid,
		                        CHECK_FH | OPEN_STATE,
		                        &open_sop, &open_stp,
					&lock->v.new.clientid);
		                        &open_sop, &open_stp, lock);
		if (status)
			goto out;
		/* create lockowner and lock stateid */
@@ -2726,7 +2743,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_lock
				       lock->lk_old_lock_seqid, 
				       &lock->lk_old_lock_stateid, 
				       CHECK_FH | LOCK_STATE, 
				       &lock->lk_stateowner, &lock_stp, NULL);
				       &lock->lk_stateowner, &lock_stp, lock);
		if (status)
			goto out;
	}
+5 −0
Original line number Diff line number Diff line
@@ -237,6 +237,10 @@ struct nfs4_file {
*       st_perlockowner: (open stateid) list of lock nfs4_stateowners
* 	st_access_bmap: used only for open stateid
* 	st_deny_bmap: used only for open stateid
*	st_openstp: open stateid lock stateid was derived from
*
* XXX: open stateids and lock stateids have diverged sufficiently that
* we should consider defining separate structs for the two cases.
*/

struct nfs4_stateid {
@@ -250,6 +254,7 @@ struct nfs4_stateid {
	struct file                 * st_vfs_file;
	unsigned long                 st_access_bmap;
	unsigned long                 st_deny_bmap;
	struct nfs4_stateid         * st_openstp;
};

/* flags for preprocess_seqid_op() */