Commit d530838b authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Fix problem with OPEN_DOWNGRADE

 RFC 3530 states that for OPEN_DOWNGRADE "The share_access and share_deny
 bits specified must be exactly equal to the union of the share_access and
 share_deny bits specified for some subset of the OPENs in effect for
 current openowner on the current file.

 Setattr is currently violating the NFSv4 rules for OPEN_DOWNGRADE in that
 it may cause a downgrade from OPEN4_SHARE_ACCESS_BOTH to
 OPEN4_SHARE_ACCESS_WRITE despite the fact that there exists no open file
 with O_WRONLY access mode.

 Fix the problem by replacing nfs4_find_state() with a modified version of
 nfs_find_open_context().
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 4cecb76f
...@@ -1009,13 +1009,18 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx) ...@@ -1009,13 +1009,18 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
spin_unlock(&inode->i_lock); spin_unlock(&inode->i_lock);
} }
struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode) /*
* Given an inode, search for an open context with the desired characteristics
*/
struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
{ {
struct nfs_inode *nfsi = NFS_I(inode); struct nfs_inode *nfsi = NFS_I(inode);
struct nfs_open_context *pos, *ctx = NULL; struct nfs_open_context *pos, *ctx = NULL;
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
list_for_each_entry(pos, &nfsi->open_files, list) { list_for_each_entry(pos, &nfsi->open_files, list) {
if (cred != NULL && pos->cred != cred)
continue;
if ((pos->mode & mode) == mode) { if ((pos->mode & mode) == mode) {
ctx = get_nfs_open_context(pos); ctx = get_nfs_open_context(pos);
break; break;
......
...@@ -247,7 +247,6 @@ extern void nfs4_drop_state_owner(struct nfs4_state_owner *); ...@@ -247,7 +247,6 @@ extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
extern void nfs4_put_open_state(struct nfs4_state *); extern void nfs4_put_open_state(struct nfs4_state *);
extern void nfs4_close_state(struct nfs4_state *, mode_t); extern void nfs4_close_state(struct nfs4_state *, mode_t);
extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
extern void nfs4_schedule_state_recovery(struct nfs4_client *); extern void nfs4_schedule_state_recovery(struct nfs4_client *);
extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
......
...@@ -214,7 +214,7 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, ...@@ -214,7 +214,7 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
struct inode *inode = state->inode; struct inode *inode = state->inode;
open_flags &= (FMODE_READ|FMODE_WRITE); open_flags &= (FMODE_READ|FMODE_WRITE);
/* Protect against nfs4_find_state() */ /* Protect against nfs4_find_state_byowner() */
spin_lock(&state->owner->so_lock); spin_lock(&state->owner->so_lock);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
memcpy(&state->stateid, stateid, sizeof(state->stateid)); memcpy(&state->stateid, stateid, sizeof(state->stateid));
...@@ -1274,7 +1274,8 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -1274,7 +1274,8 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
{ {
struct rpc_cred *cred; struct rpc_cred *cred;
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct nfs4_state *state; struct nfs_open_context *ctx;
struct nfs4_state *state = NULL;
int status; int status;
nfs_fattr_init(fattr); nfs_fattr_init(fattr);
...@@ -1282,22 +1283,18 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, ...@@ -1282,22 +1283,18 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
if (IS_ERR(cred)) if (IS_ERR(cred))
return PTR_ERR(cred); return PTR_ERR(cred);
/* Search for an existing WRITE delegation first */
state = nfs4_open_delegated(inode, FMODE_WRITE, cred); /* Search for an existing open(O_WRITE) file */
if (!IS_ERR(state)) { ctx = nfs_find_open_context(inode, cred, FMODE_WRITE);
/* NB: nfs4_open_delegated() bumps the inode->i_count */ if (ctx != NULL)
iput(inode); state = ctx->state;
} else {
/* Search for an existing open(O_WRITE) stateid */
state = nfs4_find_state(inode, cred, FMODE_WRITE);
}
status = nfs4_do_setattr(NFS_SERVER(inode), fattr, status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
NFS_FH(inode), sattr, state); NFS_FH(inode), sattr, state);
if (status == 0) if (status == 0)
nfs_setattr_update_inode(inode, sattr); nfs_setattr_update_inode(inode, sattr);
if (state != NULL) if (ctx != NULL)
nfs4_close_state(state, FMODE_WRITE); put_nfs_open_context(ctx);
put_rpccred(cred); put_rpccred(cred);
return status; return status;
} }
......
...@@ -383,28 +383,6 @@ nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode) ...@@ -383,28 +383,6 @@ nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode)
state->state = mode; state->state = mode;
} }
static struct nfs4_state *
__nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode)
{
struct nfs_inode *nfsi = NFS_I(inode);
struct nfs4_state *state;
mode &= (FMODE_READ|FMODE_WRITE);
list_for_each_entry(state, &nfsi->open_states, inode_states) {
if (state->owner->so_cred != cred)
continue;
if ((state->state & mode) != mode)
continue;
atomic_inc(&state->count);
if (mode & FMODE_READ)
state->nreaders++;
if (mode & FMODE_WRITE)
state->nwriters++;
return state;
}
return NULL;
}
static struct nfs4_state * static struct nfs4_state *
__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
{ {
...@@ -423,17 +401,6 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) ...@@ -423,17 +401,6 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
return NULL; return NULL;
} }
struct nfs4_state *
nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode)
{
struct nfs4_state *state;
spin_lock(&inode->i_lock);
state = __nfs4_find_state(inode, cred, mode);
spin_unlock(&inode->i_lock);
return state;
}
static void static void
nfs4_free_open_state(struct nfs4_state *state) nfs4_free_open_state(struct nfs4_state *state)
{ {
......
...@@ -507,7 +507,7 @@ int nfs_readpage(struct file *file, struct page *page) ...@@ -507,7 +507,7 @@ int nfs_readpage(struct file *file, struct page *page)
goto out_error; goto out_error;
if (file == NULL) { if (file == NULL) {
ctx = nfs_find_open_context(inode, FMODE_READ); ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
if (ctx == NULL) if (ctx == NULL)
return -EBADF; return -EBADF;
} else } else
...@@ -576,7 +576,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping, ...@@ -576,7 +576,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
nr_pages); nr_pages);
if (filp == NULL) { if (filp == NULL) {
desc.ctx = nfs_find_open_context(inode, FMODE_READ); desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
if (desc.ctx == NULL) if (desc.ctx == NULL)
return -EBADF; return -EBADF;
} else } else
......
...@@ -294,7 +294,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc) ...@@ -294,7 +294,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
if (page->index >= end_index+1 || !offset) if (page->index >= end_index+1 || !offset)
goto out; goto out;
do_it: do_it:
ctx = nfs_find_open_context(inode, FMODE_WRITE); ctx = nfs_find_open_context(inode, NULL, FMODE_WRITE);
if (ctx == NULL) { if (ctx == NULL) {
err = -EBADF; err = -EBADF;
goto out; goto out;
......
...@@ -316,7 +316,7 @@ extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, st ...@@ -316,7 +316,7 @@ extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, st
extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
extern void put_nfs_open_context(struct nfs_open_context *ctx); extern void put_nfs_open_context(struct nfs_open_context *ctx);
extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx);
extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode); extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode);
extern void nfs_file_clear_open_context(struct file *filp); extern void nfs_file_clear_open_context(struct file *filp);
/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment