aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4state.c
diff options
context:
space:
mode:
authorChristoph Hellwig <hch@infradead.org>2014-06-30 11:48:30 -0400
committerJ. Bruce Fields <bfields@redhat.com>2014-07-08 17:14:29 -0400
commit7e6a72e5f1d42768a9949d73d3337277ff96e026 (patch)
tree934632aa5d8d84374a927ddf1855dee9d5c58485 /fs/nfsd/nfs4state.c
parent1055414fe19db2db6c8947c0b9ee9c8fe07beea1 (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.c62
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
3049static inline __be32
3050nfsd4_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
3049static __be32 nfs4_get_vfs_file(struct svc_rqst *rqstp, struct nfs4_file *fp, 3064static __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
3067static inline __be32 3085out_put_access:
3068nfsd4_truncate(struct svc_rqst *rqstp, struct svc_fh *fh, 3086 nfs4_file_put_access(fp, oflag);
3069 struct nfsd4_open *open) 3087out:
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
3082static __be32 3091static __be32
3083nfs4_upgrade_open(struct svc_rqst *rqstp, struct nfs4_file *fp, struct svc_fh *cur_fh, struct nfs4_ol_stateid *stp, struct nfsd4_open *open) 3092nfs4_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);