aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 15:33:38 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-11-04 15:33:38 -0500
commitd530838bfa507d67b40d13b00d9cbd7a46a47e78 (patch)
treef4b2be26c0a7b9ed3233a2be016b7e97427f8705
parent4cecb76ff86db46d2823550256c828b6597f418e (diff)
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: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/inode.c7
-rw-r--r--fs/nfs/nfs4_fs.h1
-rw-r--r--fs/nfs/nfs4proc.c23
-rw-r--r--fs/nfs/nfs4state.c33
-rw-r--r--fs/nfs/read.c4
-rw-r--r--fs/nfs/write.c2
-rw-r--r--include/linux/nfs_fs.h2
7 files changed, 20 insertions, 52 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index fc0f12ba89cc..24d2fbf549bd 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1009,13 +1009,18 @@ void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
1009 spin_unlock(&inode->i_lock); 1009 spin_unlock(&inode->i_lock);
1010} 1010}
1011 1011
1012struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode) 1012/*
1013 * Given an inode, search for an open context with the desired characteristics
1014 */
1015struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
1013{ 1016{
1014 struct nfs_inode *nfsi = NFS_I(inode); 1017 struct nfs_inode *nfsi = NFS_I(inode);
1015 struct nfs_open_context *pos, *ctx = NULL; 1018 struct nfs_open_context *pos, *ctx = NULL;
1016 1019
1017 spin_lock(&inode->i_lock); 1020 spin_lock(&inode->i_lock);
1018 list_for_each_entry(pos, &nfsi->open_files, list) { 1021 list_for_each_entry(pos, &nfsi->open_files, list) {
1022 if (cred != NULL && pos->cred != cred)
1023 continue;
1019 if ((pos->mode & mode) == mode) { 1024 if ((pos->mode & mode) == mode) {
1020 ctx = get_nfs_open_context(pos); 1025 ctx = get_nfs_open_context(pos);
1021 break; 1026 break;
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 53969022d239..b7f262dcb6e3 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -247,7 +247,6 @@ extern void nfs4_drop_state_owner(struct nfs4_state_owner *);
247extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); 247extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
248extern void nfs4_put_open_state(struct nfs4_state *); 248extern void nfs4_put_open_state(struct nfs4_state *);
249extern void nfs4_close_state(struct nfs4_state *, mode_t); 249extern void nfs4_close_state(struct nfs4_state *, mode_t);
250extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode);
251extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); 250extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
252extern void nfs4_schedule_state_recovery(struct nfs4_client *); 251extern void nfs4_schedule_state_recovery(struct nfs4_client *);
253extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); 252extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 02fddd0e27e8..9e492c2261c0 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -214,7 +214,7 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid,
214 struct inode *inode = state->inode; 214 struct inode *inode = state->inode;
215 215
216 open_flags &= (FMODE_READ|FMODE_WRITE); 216 open_flags &= (FMODE_READ|FMODE_WRITE);
217 /* Protect against nfs4_find_state() */ 217 /* Protect against nfs4_find_state_byowner() */
218 spin_lock(&state->owner->so_lock); 218 spin_lock(&state->owner->so_lock);
219 spin_lock(&inode->i_lock); 219 spin_lock(&inode->i_lock);
220 memcpy(&state->stateid, stateid, sizeof(state->stateid)); 220 memcpy(&state->stateid, stateid, sizeof(state->stateid));
@@ -1274,7 +1274,8 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
1274{ 1274{
1275 struct rpc_cred *cred; 1275 struct rpc_cred *cred;
1276 struct inode *inode = dentry->d_inode; 1276 struct inode *inode = dentry->d_inode;
1277 struct nfs4_state *state; 1277 struct nfs_open_context *ctx;
1278 struct nfs4_state *state = NULL;
1278 int status; 1279 int status;
1279 1280
1280 nfs_fattr_init(fattr); 1281 nfs_fattr_init(fattr);
@@ -1282,22 +1283,18 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
1282 cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); 1283 cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
1283 if (IS_ERR(cred)) 1284 if (IS_ERR(cred))
1284 return PTR_ERR(cred); 1285 return PTR_ERR(cred);
1285 /* Search for an existing WRITE delegation first */ 1286
1286 state = nfs4_open_delegated(inode, FMODE_WRITE, cred); 1287 /* Search for an existing open(O_WRITE) file */
1287 if (!IS_ERR(state)) { 1288 ctx = nfs_find_open_context(inode, cred, FMODE_WRITE);
1288 /* NB: nfs4_open_delegated() bumps the inode->i_count */ 1289 if (ctx != NULL)
1289 iput(inode); 1290 state = ctx->state;
1290 } else {
1291 /* Search for an existing open(O_WRITE) stateid */
1292 state = nfs4_find_state(inode, cred, FMODE_WRITE);
1293 }
1294 1291
1295 status = nfs4_do_setattr(NFS_SERVER(inode), fattr, 1292 status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
1296 NFS_FH(inode), sattr, state); 1293 NFS_FH(inode), sattr, state);
1297 if (status == 0) 1294 if (status == 0)
1298 nfs_setattr_update_inode(inode, sattr); 1295 nfs_setattr_update_inode(inode, sattr);
1299 if (state != NULL) 1296 if (ctx != NULL)
1300 nfs4_close_state(state, FMODE_WRITE); 1297 put_nfs_open_context(ctx);
1301 put_rpccred(cred); 1298 put_rpccred(cred);
1302 return status; 1299 return status;
1303} 1300}
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 959374d833a7..81d964bfd8a7 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -384,28 +384,6 @@ nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode)
384} 384}
385 385
386static struct nfs4_state * 386static struct nfs4_state *
387__nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode)
388{
389 struct nfs_inode *nfsi = NFS_I(inode);
390 struct nfs4_state *state;
391
392 mode &= (FMODE_READ|FMODE_WRITE);
393 list_for_each_entry(state, &nfsi->open_states, inode_states) {
394 if (state->owner->so_cred != cred)
395 continue;
396 if ((state->state & mode) != mode)
397 continue;
398 atomic_inc(&state->count);
399 if (mode & FMODE_READ)
400 state->nreaders++;
401 if (mode & FMODE_WRITE)
402 state->nwriters++;
403 return state;
404 }
405 return NULL;
406}
407
408static struct nfs4_state *
409__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner) 387__nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
410{ 388{
411 struct nfs_inode *nfsi = NFS_I(inode); 389 struct nfs_inode *nfsi = NFS_I(inode);
@@ -423,17 +401,6 @@ __nfs4_find_state_byowner(struct inode *inode, struct nfs4_state_owner *owner)
423 return NULL; 401 return NULL;
424} 402}
425 403
426struct nfs4_state *
427nfs4_find_state(struct inode *inode, struct rpc_cred *cred, mode_t mode)
428{
429 struct nfs4_state *state;
430
431 spin_lock(&inode->i_lock);
432 state = __nfs4_find_state(inode, cred, mode);
433 spin_unlock(&inode->i_lock);
434 return state;
435}
436
437static void 404static void
438nfs4_free_open_state(struct nfs4_state *state) 405nfs4_free_open_state(struct nfs4_state *state)
439{ 406{
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index 43b03b19731b..5f20eafba8ec 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -507,7 +507,7 @@ int nfs_readpage(struct file *file, struct page *page)
507 goto out_error; 507 goto out_error;
508 508
509 if (file == NULL) { 509 if (file == NULL) {
510 ctx = nfs_find_open_context(inode, FMODE_READ); 510 ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
511 if (ctx == NULL) 511 if (ctx == NULL)
512 return -EBADF; 512 return -EBADF;
513 } else 513 } else
@@ -576,7 +576,7 @@ int nfs_readpages(struct file *filp, struct address_space *mapping,
576 nr_pages); 576 nr_pages);
577 577
578 if (filp == NULL) { 578 if (filp == NULL) {
579 desc.ctx = nfs_find_open_context(inode, FMODE_READ); 579 desc.ctx = nfs_find_open_context(inode, NULL, FMODE_READ);
580 if (desc.ctx == NULL) 580 if (desc.ctx == NULL)
581 return -EBADF; 581 return -EBADF;
582 } else 582 } else
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 819a65f5071f..1bdbd4f9fdd2 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -294,7 +294,7 @@ int nfs_writepage(struct page *page, struct writeback_control *wbc)
294 if (page->index >= end_index+1 || !offset) 294 if (page->index >= end_index+1 || !offset)
295 goto out; 295 goto out;
296do_it: 296do_it:
297 ctx = nfs_find_open_context(inode, FMODE_WRITE); 297 ctx = nfs_find_open_context(inode, NULL, FMODE_WRITE);
298 if (ctx == NULL) { 298 if (ctx == NULL) {
299 err = -EBADF; 299 err = -EBADF;
300 goto out; 300 goto out;
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 325fe7ae49bb..12787a9b0259 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -316,7 +316,7 @@ extern struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, st
316extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); 316extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
317extern void put_nfs_open_context(struct nfs_open_context *ctx); 317extern void put_nfs_open_context(struct nfs_open_context *ctx);
318extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); 318extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx);
319extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode); 319extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode);
320extern void nfs_file_clear_open_context(struct file *filp); 320extern void nfs_file_clear_open_context(struct file *filp);
321 321
322/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */ 322/* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */