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

Commit e7616923 authored by Trond Myklebust's avatar Trond Myklebust
Browse files

NFSv4: Make nfs4_state track O_RDWR, O_RDONLY and O_WRONLY separately



 A closer reading of RFC3530 reveals that OPEN_DOWNGRADE must always
 specify a access modes that have been the argument of a previous OPEN
 operation.
 IOW: doing OPEN(O_RDWR) and then OPEN_DOWNGRADE(O_WRONLY) is forbidden
 unless the user called OPEN(O_WRONLY)

 In order to fix that, we really need to track the three possible open
 states separately.

 Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent cdd4e68b
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -182,8 +182,9 @@ struct nfs4_state {

	nfs4_stateid stateid;

	unsigned int nreaders;
	unsigned int nwriters;
	unsigned int n_rdonly;
	unsigned int n_wronly;
	unsigned int n_rdwr;
	int state;			/* State on the server (R,W, or RW) */
	atomic_t count;
};
+22 −12
Original line number Diff line number Diff line
@@ -297,6 +297,20 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
	return ret;
}

static inline void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
{
	switch (open_flags) {
		case FMODE_WRITE:
			state->n_wronly++;
			break;
		case FMODE_READ:
			state->n_rdonly++;
			break;
		case FMODE_READ|FMODE_WRITE:
			state->n_rdwr++;
	}
}

static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
{
	struct inode *inode = state->inode;
@@ -306,10 +320,7 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
	spin_lock(&state->owner->so_lock);
	spin_lock(&inode->i_lock);
	memcpy(&state->stateid, stateid, sizeof(state->stateid));
	if ((open_flags & FMODE_WRITE))
		state->nwriters++;
	if (open_flags & FMODE_READ)
		state->nreaders++;
	update_open_stateflags(state, open_flags);
	nfs4_state_set_mode_locked(state, state->state | open_flags);
	spin_unlock(&inode->i_lock);
	spin_unlock(&state->owner->so_lock);
@@ -822,10 +833,7 @@ static int _nfs4_open_delegated(struct inode *inode, int flags, struct rpc_cred
	err = -ENOENT;
	if ((state->state & open_flags) == open_flags) {
		spin_lock(&inode->i_lock);
		if (open_flags & FMODE_READ)
			state->nreaders++;
		if (open_flags & FMODE_WRITE)
			state->nwriters++;
		update_open_stateflags(state, open_flags);
		spin_unlock(&inode->i_lock);
		goto out_ok;
	} else if (state->state != 0)
@@ -1082,10 +1090,12 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
	spin_lock(&state->owner->so_lock);
	spin_lock(&calldata->inode->i_lock);
	mode = old_mode = state->state;
	if (state->nreaders == 0)
	if (state->n_rdwr == 0) {
		if (state->n_rdonly == 0)
			mode &= ~FMODE_READ;
	if (state->nwriters == 0)
		if (state->n_wronly == 0)
			mode &= ~FMODE_WRITE;
	}
	nfs4_state_set_mode_locked(state, mode);
	spin_unlock(&calldata->inode->i_lock);
	spin_unlock(&state->owner->so_lock);
+17 −14
Original line number Diff line number Diff line
@@ -349,14 +349,9 @@ nfs4_alloc_open_state(void)
{
	struct nfs4_state *state;

	state = kmalloc(sizeof(*state), GFP_KERNEL);
	state = kzalloc(sizeof(*state), GFP_KERNEL);
	if (!state)
		return NULL;
	state->state = 0;
	state->nreaders = 0;
	state->nwriters = 0;
	state->flags = 0;
	memset(state->stateid.data, 0, sizeof(state->stateid.data));
	atomic_set(&state->count, 1);
	INIT_LIST_HEAD(&state->lock_states);
	spin_lock_init(&state->state_lock);
@@ -475,15 +470,23 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode)
	/* Protect against nfs4_find_state() */
	spin_lock(&owner->so_lock);
	spin_lock(&inode->i_lock);
	if (mode & FMODE_READ)
		state->nreaders--;
	if (mode & FMODE_WRITE)
		state->nwriters--;
	switch (mode & (FMODE_READ | FMODE_WRITE)) {
		case FMODE_READ:
			state->n_rdonly--;
			break;
		case FMODE_WRITE:
			state->n_wronly--;
			break;
		case FMODE_READ|FMODE_WRITE:
			state->n_rdwr--;
	}
	oldstate = newstate = state->state;
	if (state->nreaders == 0)
	if (state->n_rdwr == 0) {
		if (state->n_rdonly == 0)
			newstate &= ~FMODE_READ;
	if (state->nwriters == 0)
		if (state->n_wronly == 0)
			newstate &= ~FMODE_WRITE;
	}
	if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
		nfs4_state_set_mode_locked(state, newstate);
		oldstate = newstate;