diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-11-04 15:33:38 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2005-11-04 15:33:38 -0500 |
commit | d530838bfa507d67b40d13b00d9cbd7a46a47e78 (patch) | |
tree | f4b2be26c0a7b9ed3233a2be016b7e97427f8705 | |
parent | 4cecb76ff86db46d2823550256c828b6597f418e (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.c | 7 | ||||
-rw-r--r-- | fs/nfs/nfs4_fs.h | 1 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 23 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 33 | ||||
-rw-r--r-- | fs/nfs/read.c | 4 | ||||
-rw-r--r-- | fs/nfs/write.c | 2 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 2 |
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 | ||
1012 | struct 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 | */ | ||
1015 | struct 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 *); | |||
247 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); | 247 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); |
248 | extern void nfs4_put_open_state(struct nfs4_state *); | 248 | extern void nfs4_put_open_state(struct nfs4_state *); |
249 | extern void nfs4_close_state(struct nfs4_state *, mode_t); | 249 | extern void nfs4_close_state(struct nfs4_state *, mode_t); |
250 | extern struct nfs4_state *nfs4_find_state(struct inode *, struct rpc_cred *, mode_t mode); | ||
251 | extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); | 250 | extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); |
252 | extern void nfs4_schedule_state_recovery(struct nfs4_client *); | 251 | extern void nfs4_schedule_state_recovery(struct nfs4_client *); |
253 | extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); | 252 | extern 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 | ||
386 | static struct nfs4_state * | 386 | static 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 | |||
408 | static 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 | ||
426 | struct nfs4_state * | ||
427 | nfs4_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 | |||
437 | static void | 404 | static void |
438 | nfs4_free_open_state(struct nfs4_state *state) | 405 | nfs4_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; |
296 | do_it: | 296 | do_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 | |||
316 | extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); | 316 | extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx); |
317 | extern void put_nfs_open_context(struct nfs_open_context *ctx); | 317 | extern void put_nfs_open_context(struct nfs_open_context *ctx); |
318 | extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); | 318 | extern void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx); |
319 | extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode); | 319 | extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode); |
320 | extern void nfs_file_clear_open_context(struct file *filp); | 320 | extern 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. */ |