diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-01-03 09:55:13 +0100 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-01-06 20:58:43 +0100 |
commit | e761692381f294ea079d2e869fcd7c0afc79e394 (patch) | |
tree | 34ebc00c001cddc94866936c460547d311e5dabb | |
parent | NFSv4: Make open_confirm() asynchronous too (diff) | |
download | linux-e761692381f294ea079d2e869fcd7c0afc79e394.tar.xz linux-e761692381f294ea079d2e869fcd7c0afc79e394.zip |
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: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/nfs4_fs.h | 5 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 34 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 31 |
3 files changed, 42 insertions, 28 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index b7f262dcb6e3..4ad5981f9375 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h @@ -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; }; diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 8154f2579469..e494cc2ea986 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -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) - mode &= ~FMODE_READ; - if (state->nwriters == 0) - mode &= ~FMODE_WRITE; + if (state->n_rdwr == 0) { + if (state->n_rdonly == 0) + mode &= ~FMODE_READ; + 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); diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 5ef4c57618fe..41a5f1a8a984 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c @@ -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) - newstate &= ~FMODE_READ; - if (state->nwriters == 0) - newstate &= ~FMODE_WRITE; + if (state->n_rdwr == 0) { + if (state->n_rdonly == 0) + newstate &= ~FMODE_READ; + 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; |