diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-09-19 15:07:41 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-11 12:35:58 -0500 |
commit | 279b483174560147e9f56f79e9b1fd3bd4fc8f40 (patch) | |
tree | e6493e4e2a8fd9a7d7fdef939bdf83e683c44d24 | |
parent | 0a52cb1083a662d4c417638ccdaa81df0e67494b (diff) |
nfsd4: fix open downgrade, again
commit 3d02fa29dec920c597dd7b7db608a4bc71f088ce upstream.
Yet another open-management regression:
- nfs4_file_downgrade() doesn't remove the BOTH access bit on
downgrade, so the server's idea of the stateid's access gets
out of sync with the client's. If we want to keep an O_RDWR
open in this case, we should do that in the file_put_access
logic rather than here.
- We forgot to convert v4 access to an open mode here.
This logic has proven too hard to get right. In the future we may
consider:
- reexamining the lock/openowner relationship (locks probably
don't really need to take their own references here).
- adding open upgrade/downgrade support to the vfs.
- removing the atomic operations. They're redundant as long as
this is all under some other lock.
Also, maybe some kind of additional static checking would help catch
O_/NFS4_SHARE_ACCESS confusion.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | fs/nfsd/nfs4state.c | 14 |
1 files changed, 11 insertions, 3 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index bfe577c7676..3d893645cc6 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -188,8 +188,15 @@ static void nfs4_file_put_fd(struct nfs4_file *fp, int oflag) | |||
188 | static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag) | 188 | static void __nfs4_file_put_access(struct nfs4_file *fp, int oflag) |
189 | { | 189 | { |
190 | if (atomic_dec_and_test(&fp->fi_access[oflag])) { | 190 | if (atomic_dec_and_test(&fp->fi_access[oflag])) { |
191 | nfs4_file_put_fd(fp, O_RDWR); | ||
192 | nfs4_file_put_fd(fp, oflag); | 191 | nfs4_file_put_fd(fp, oflag); |
192 | /* | ||
193 | * It's also safe to get rid of the RDWR open *if* | ||
194 | * we no longer have need of the other kind of access | ||
195 | * or if we already have the other kind of open: | ||
196 | */ | ||
197 | if (fp->fi_fds[1-oflag] | ||
198 | || atomic_read(&fp->fi_access[1 - oflag]) == 0) | ||
199 | nfs4_file_put_fd(fp, O_RDWR); | ||
193 | } | 200 | } |
194 | } | 201 | } |
195 | 202 | ||
@@ -3381,8 +3388,9 @@ static inline void nfs4_file_downgrade(struct nfs4_stateid *stp, unsigned int to | |||
3381 | int i; | 3388 | int i; |
3382 | 3389 | ||
3383 | for (i = 1; i < 4; i++) { | 3390 | for (i = 1; i < 4; i++) { |
3384 | if (test_bit(i, &stp->st_access_bmap) && !(i & to_access)) { | 3391 | if (test_bit(i, &stp->st_access_bmap) |
3385 | nfs4_file_put_access(stp->st_file, i); | 3392 | && ((i & to_access) != i)) { |
3393 | nfs4_file_put_access(stp->st_file, nfs4_access_to_omode(i)); | ||
3386 | __clear_bit(i, &stp->st_access_bmap); | 3394 | __clear_bit(i, &stp->st_access_bmap); |
3387 | } | 3395 | } |
3388 | } | 3396 | } |