diff options
author | Christoph Hellwig <hch@infradead.org> | 2014-06-30 11:48:30 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2014-07-08 17:14:29 -0400 |
commit | 7e6a72e5f1d42768a9949d73d3337277ff96e026 (patch) | |
tree | 934632aa5d8d84374a927ddf1855dee9d5c58485 /fs/nfsd/nfs4state.c | |
parent | 1055414fe19db2db6c8947c0b9ee9c8fe07beea1 (diff) |
nfsd: fix file access refcount leak when nfsd4_truncate fails
nfsd4_process_open2 will currently will get access to the file, and then
call nfsd4_truncate to (possibly) truncate it. If that operation fails
though, then the access references will never be released as the
nfs4_ol_stateid is never initialized.
Fix by moving the nfsd4_truncate call into nfs4_get_vfs_file, ensuring
that the refcounts are properly put if the truncate fails.
Signed-off-by: Jeff Layton <jlayton@primarydata.com>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r-- | fs/nfsd/nfs4state.c | 62 |
1 files changed, 30 insertions, 32 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8242385a249c..c473bd6d52c8 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -3046,6 +3046,21 @@ static inline int nfs4_access_to_access(u32 nfs4_access) | |||
3046 | return flags; | 3046 | return flags; |
3047 | } | 3047 | } |
3048 | 3048 | ||
3049 | static inline __be32 | ||
3050 | nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, | ||
3051 | struct nfsd4_open *open) | ||
3052 | { | ||
3053 | struct iattr iattr = { | ||
3054 | .ia_valid = ATTR_SIZE, | ||
3055 | .ia_size = 0, | ||
3056 | }; | ||
3057 | if (!open->op_truncate) | ||
3058 | return 0; | ||
3059 | if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) | ||
3060 | return nfserr_inval; | ||
3061 | return nfsd_setattr(rqstp, fh, &iattr, 0, (time_t)0); | ||
3062 | } | ||
3063 | |||
3049 | static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, | 3064 | static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, |
3050 | struct svc_fh *cur_fh, struct nfsd4_open *open) | 3065 | struct svc_fh *cur_fh, struct nfsd4_open *open) |
3051 | { | 3066 | { |
@@ -3057,53 +3072,39 @@ static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, | |||
3057 | status = nfsd_open(rqstp, cur_fh, S_IFREG, access, | 3072 | status = nfsd_open(rqstp, cur_fh, S_IFREG, access, |
3058 | &fp->fi_fds[oflag]); | 3073 | &fp->fi_fds[oflag]); |
3059 | if (status) | 3074 | if (status) |
3060 | return status; | 3075 | goto out; |
3061 | } | 3076 | } |
3062 | nfs4_file_get_access(fp, oflag); | 3077 | nfs4_file_get_access(fp, oflag); |
3063 | 3078 | ||
3079 | status = nfsd4_truncate(rqstp, cur_fh, open); | ||
3080 | if (status) | ||
3081 | goto out_put_access; | ||
3082 | |||
3064 | return nfs_ok; | 3083 | return nfs_ok; |
3065 | } | ||
3066 | 3084 | ||
3067 | static inline __be32 | 3085 | out_put_access: |
3068 | nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, | 3086 | nfs4_file_put_access(fp, oflag); |
3069 | struct nfsd4_open *open) | 3087 | out: |
3070 | { | 3088 | return status; |
3071 | struct iattr iattr = { | ||
3072 | .ia_valid = ATTR_SIZE, | ||
3073 | .ia_size = 0, | ||
3074 | }; | ||
3075 | if (!open->op_truncate) | ||
3076 | return 0; | ||
3077 | if (!(open->op_share_access & NFS4_SHARE_ACCESS_WRITE)) | ||
3078 | return nfserr_inval; | ||
3079 | return nfsd_setattr(rqstp, fh, &iattr, 0, (time_t)0); | ||
3080 | } | 3089 | } |
3081 | 3090 | ||
3082 | static __be32 | 3091 | static __be32 |
3083 | nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) | 3092 | nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) |
3084 | { | 3093 | { |
3085 | u32 op_share_access = open->op_share_access; | 3094 | u32 op_share_access = open->op_share_access; |
3086 | bool new_access; | ||
3087 | __be32 status; | 3095 | __be32 status; |
3088 | 3096 | ||
3089 | new_access = !test_access(op_share_access, stp); | 3097 | if (!test_access(op_share_access, stp)) |
3090 | if (new_access) { | ||
3091 | status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); | 3098 | status = nfs4_get_vfs_file(rqstp, fp, cur_fh, open); |
3092 | if (status) | 3099 | else |
3093 | return status; | 3100 | status = nfsd4_truncate(rqstp, cur_fh, open); |
3094 | } | 3101 | |
3095 | status = nfsd4_truncate(rqstp, cur_fh, open); | 3102 | if (status) |
3096 | if (status) { | ||
3097 | if (new_access) { | ||
3098 | int oflag = nfs4_access_to_omode(op_share_access); | ||
3099 | nfs4_file_put_access(fp, oflag); | ||
3100 | } | ||
3101 | return status; | 3103 | return status; |
3102 | } | 3104 | |
3103 | /* remember the open */ | 3105 | /* remember the open */ |
3104 | set_access(op_share_access, stp); | 3106 | set_access(op_share_access, stp); |
3105 | set_deny(open->op_share_deny, stp); | 3107 | set_deny(open->op_share_deny, stp); |
3106 | |||
3107 | return nfs_ok; | 3108 | return nfs_ok; |
3108 | } | 3109 | } |
3109 | 3110 | ||
@@ -3354,9 +3355,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
3354 | status = nfs4_get_vfs_file(rqstp, fp, current_fh, open); | 3355 | status = nfs4_get_vfs_file(rqstp, fp, current_fh, open); |
3355 | if (status) | 3356 | if (status) |
3356 | goto out; | 3357 | goto out; |
3357 | status = nfsd4_truncate(rqstp, current_fh, open); | ||
3358 | if (status) | ||
3359 | goto out; | ||
3360 | stp = open->op_stp; | 3358 | stp = open->op_stp; |
3361 | open->op_stp = NULL; | 3359 | open->op_stp = NULL; |
3362 | init_open_stateid(stp, fp, open); | 3360 | init_open_stateid(stp, fp, open); |