diff options
Diffstat (limited to 'fs/nfsd/nfs4xdr.c')
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 128 |
1 files changed, 50 insertions, 78 deletions
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 944275c8f56d..f9821ce6658a 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -181,28 +181,43 @@ static int zero_clientid(clientid_t *clid) | |||
181 | } | 181 | } |
182 | 182 | ||
183 | /** | 183 | /** |
184 | * defer_free - mark an allocation as deferred freed | 184 | * svcxdr_tmpalloc - allocate memory to be freed after compound processing |
185 | * @argp: NFSv4 compound argument structure to be freed with | 185 | * @argp: NFSv4 compound argument structure |
186 | * @release: release callback to free @p, typically kfree() | 186 | * @p: pointer to be freed (with kfree()) |
187 | * @p: pointer to be freed | ||
188 | * | 187 | * |
189 | * Marks @p to be freed when processing the compound operation | 188 | * Marks @p to be freed when processing the compound operation |
190 | * described in @argp finishes. | 189 | * described in @argp finishes. |
191 | */ | 190 | */ |
192 | static int | 191 | static void * |
193 | defer_free(struct nfsd4_compoundargs *argp, | 192 | svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len) |
194 | void (*release)(const void *), void *p) | ||
195 | { | 193 | { |
196 | struct tmpbuf *tb; | 194 | struct svcxdr_tmpbuf *tb; |
197 | 195 | ||
198 | tb = kmalloc(sizeof(*tb), GFP_KERNEL); | 196 | tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL); |
199 | if (!tb) | 197 | if (!tb) |
200 | return -ENOMEM; | 198 | return NULL; |
201 | tb->buf = p; | ||
202 | tb->release = release; | ||
203 | tb->next = argp->to_free; | 199 | tb->next = argp->to_free; |
204 | argp->to_free = tb; | 200 | argp->to_free = tb; |
205 | return 0; | 201 | return tb->buf; |
202 | } | ||
203 | |||
204 | /* | ||
205 | * For xdr strings that need to be passed to other kernel api's | ||
206 | * as null-terminated strings. | ||
207 | * | ||
208 | * Note null-terminating in place usually isn't safe since the | ||
209 | * buffer might end on a page boundary. | ||
210 | */ | ||
211 | static char * | ||
212 | svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len) | ||
213 | { | ||
214 | char *p = svcxdr_tmpalloc(argp, len + 1); | ||
215 | |||
216 | if (!p) | ||
217 | return NULL; | ||
218 | memcpy(p, buf, len); | ||
219 | p[len] = '\0'; | ||
220 | return p; | ||
206 | } | 221 | } |
207 | 222 | ||
208 | /** | 223 | /** |
@@ -217,19 +232,13 @@ defer_free(struct nfsd4_compoundargs *argp, | |||
217 | */ | 232 | */ |
218 | static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) | 233 | static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) |
219 | { | 234 | { |
220 | if (p == argp->tmp) { | 235 | void *ret; |
221 | p = kmemdup(argp->tmp, nbytes, GFP_KERNEL); | 236 | |
222 | if (!p) | 237 | ret = svcxdr_tmpalloc(argp, nbytes); |
223 | return NULL; | 238 | if (!ret) |
224 | } else { | ||
225 | BUG_ON(p != argp->tmpp); | ||
226 | argp->tmpp = NULL; | ||
227 | } | ||
228 | if (defer_free(argp, kfree, p)) { | ||
229 | kfree(p); | ||
230 | return NULL; | 239 | return NULL; |
231 | } else | 240 | memcpy(ret, p, nbytes); |
232 | return (char *)p; | 241 | return ret; |
233 | } | 242 | } |
234 | 243 | ||
235 | static __be32 | 244 | static __be32 |
@@ -292,12 +301,10 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, | |||
292 | if (nace > NFS4_ACL_MAX) | 301 | if (nace > NFS4_ACL_MAX) |
293 | return nfserr_fbig; | 302 | return nfserr_fbig; |
294 | 303 | ||
295 | *acl = nfs4_acl_new(nace); | 304 | *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace)); |
296 | if (*acl == NULL) | 305 | if (*acl == NULL) |
297 | return nfserr_jukebox; | 306 | return nfserr_jukebox; |
298 | 307 | ||
299 | defer_free(argp, kfree, *acl); | ||
300 | |||
301 | (*acl)->naces = nace; | 308 | (*acl)->naces = nace; |
302 | for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { | 309 | for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) { |
303 | READ_BUF(16); len += 16; | 310 | READ_BUF(16); len += 16; |
@@ -418,12 +425,10 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, | |||
418 | return nfserr_badlabel; | 425 | return nfserr_badlabel; |
419 | len += (XDR_QUADLEN(dummy32) << 2); | 426 | len += (XDR_QUADLEN(dummy32) << 2); |
420 | READMEM(buf, dummy32); | 427 | READMEM(buf, dummy32); |
421 | label->data = kzalloc(dummy32 + 1, GFP_KERNEL); | 428 | label->len = dummy32; |
429 | label->data = svcxdr_dupstr(argp, buf, dummy32); | ||
422 | if (!label->data) | 430 | if (!label->data) |
423 | return nfserr_jukebox; | 431 | return nfserr_jukebox; |
424 | label->len = dummy32; | ||
425 | defer_free(argp, kfree, label->data); | ||
426 | memcpy(label->data, buf, dummy32); | ||
427 | } | 432 | } |
428 | #endif | 433 | #endif |
429 | 434 | ||
@@ -598,20 +603,11 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create | |||
598 | switch (create->cr_type) { | 603 | switch (create->cr_type) { |
599 | case NF4LNK: | 604 | case NF4LNK: |
600 | READ_BUF(4); | 605 | READ_BUF(4); |
601 | create->cr_linklen = be32_to_cpup(p++); | 606 | create->cr_datalen = be32_to_cpup(p++); |
602 | READ_BUF(create->cr_linklen); | 607 | READ_BUF(create->cr_datalen); |
603 | /* | 608 | create->cr_data = svcxdr_dupstr(argp, p, create->cr_datalen); |
604 | * The VFS will want a null-terminated string, and | 609 | if (!create->cr_data) |
605 | * null-terminating in place isn't safe since this might | ||
606 | * end on a page boundary: | ||
607 | */ | ||
608 | create->cr_linkname = | ||
609 | kmalloc(create->cr_linklen + 1, GFP_KERNEL); | ||
610 | if (!create->cr_linkname) | ||
611 | return nfserr_jukebox; | 610 | return nfserr_jukebox; |
612 | memcpy(create->cr_linkname, p, create->cr_linklen); | ||
613 | create->cr_linkname[create->cr_linklen] = '\0'; | ||
614 | defer_free(argp, kfree, create->cr_linkname); | ||
615 | break; | 611 | break; |
616 | case NF4BLK: | 612 | case NF4BLK: |
617 | case NF4CHR: | 613 | case NF4CHR: |
@@ -1481,13 +1477,12 @@ nfsd4_decode_test_stateid(struct nfsd4_compoundargs *argp, struct nfsd4_test_sta | |||
1481 | INIT_LIST_HEAD(&test_stateid->ts_stateid_list); | 1477 | INIT_LIST_HEAD(&test_stateid->ts_stateid_list); |
1482 | 1478 | ||
1483 | for (i = 0; i < test_stateid->ts_num_ids; i++) { | 1479 | for (i = 0; i < test_stateid->ts_num_ids; i++) { |
1484 | stateid = kmalloc(sizeof(struct nfsd4_test_stateid_id), GFP_KERNEL); | 1480 | stateid = svcxdr_tmpalloc(argp, sizeof(*stateid)); |
1485 | if (!stateid) { | 1481 | if (!stateid) { |
1486 | status = nfserrno(-ENOMEM); | 1482 | status = nfserrno(-ENOMEM); |
1487 | goto out; | 1483 | goto out; |
1488 | } | 1484 | } |
1489 | 1485 | ||
1490 | defer_free(argp, kfree, stateid); | ||
1491 | INIT_LIST_HEAD(&stateid->ts_id_list); | 1486 | INIT_LIST_HEAD(&stateid->ts_id_list); |
1492 | list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); | 1487 | list_add_tail(&stateid->ts_id_list, &test_stateid->ts_stateid_list); |
1493 | 1488 | ||
@@ -1640,7 +1635,7 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | |||
1640 | goto xdr_error; | 1635 | goto xdr_error; |
1641 | 1636 | ||
1642 | if (argp->opcnt > ARRAY_SIZE(argp->iops)) { | 1637 | if (argp->opcnt > ARRAY_SIZE(argp->iops)) { |
1643 | argp->ops = kmalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); | 1638 | argp->ops = kzalloc(argp->opcnt * sizeof(*argp->ops), GFP_KERNEL); |
1644 | if (!argp->ops) { | 1639 | if (!argp->ops) { |
1645 | argp->ops = argp->iops; | 1640 | argp->ops = argp->iops; |
1646 | dprintk("nfsd: couldn't allocate room for COMPOUND\n"); | 1641 | dprintk("nfsd: couldn't allocate room for COMPOUND\n"); |
@@ -3077,11 +3072,8 @@ static __be32 nfsd4_encode_splice_read( | |||
3077 | __be32 nfserr; | 3072 | __be32 nfserr; |
3078 | __be32 *p = xdr->p - 2; | 3073 | __be32 *p = xdr->p - 2; |
3079 | 3074 | ||
3080 | /* | 3075 | /* Make sure there will be room for padding if needed */ |
3081 | * Don't inline pages unless we know there's room for eof, | 3076 | if (xdr->end - xdr->p < 1) |
3082 | * count, and possible padding: | ||
3083 | */ | ||
3084 | if (xdr->end - xdr->p < 3) | ||
3085 | return nfserr_resource; | 3077 | return nfserr_resource; |
3086 | 3078 | ||
3087 | nfserr = nfsd_splice_read(read->rd_rqstp, file, | 3079 | nfserr = nfsd_splice_read(read->rd_rqstp, file, |
@@ -3147,9 +3139,7 @@ static __be32 nfsd4_encode_readv(struct nfsd4_compoundres *resp, | |||
3147 | len = maxcount; | 3139 | len = maxcount; |
3148 | v = 0; | 3140 | v = 0; |
3149 | 3141 | ||
3150 | thislen = (void *)xdr->end - (void *)xdr->p; | 3142 | thislen = min_t(long, len, ((void *)xdr->end - (void *)xdr->p)); |
3151 | if (len < thislen) | ||
3152 | thislen = len; | ||
3153 | p = xdr_reserve_space(xdr, (thislen+3)&~3); | 3143 | p = xdr_reserve_space(xdr, (thislen+3)&~3); |
3154 | WARN_ON_ONCE(!p); | 3144 | WARN_ON_ONCE(!p); |
3155 | resp->rqstp->rq_vec[v].iov_base = p; | 3145 | resp->rqstp->rq_vec[v].iov_base = p; |
@@ -3216,10 +3206,8 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
3216 | xdr_commit_encode(xdr); | 3206 | xdr_commit_encode(xdr); |
3217 | 3207 | ||
3218 | maxcount = svc_max_payload(resp->rqstp); | 3208 | maxcount = svc_max_payload(resp->rqstp); |
3219 | if (maxcount > xdr->buf->buflen - xdr->buf->len) | 3209 | maxcount = min_t(unsigned long, maxcount, (xdr->buf->buflen - xdr->buf->len)); |
3220 | maxcount = xdr->buf->buflen - xdr->buf->len; | 3210 | maxcount = min_t(unsigned long, maxcount, read->rd_length); |
3221 | if (maxcount > read->rd_length) | ||
3222 | maxcount = read->rd_length; | ||
3223 | 3211 | ||
3224 | if (!read->rd_filp) { | 3212 | if (!read->rd_filp) { |
3225 | err = nfsd_get_tmp_read_open(resp->rqstp, read->rd_fhp, | 3213 | err = nfsd_get_tmp_read_open(resp->rqstp, read->rd_fhp, |
@@ -3937,8 +3925,6 @@ status: | |||
3937 | * | 3925 | * |
3938 | * XDR note: do not encode rp->rp_buflen: the buffer contains the | 3926 | * XDR note: do not encode rp->rp_buflen: the buffer contains the |
3939 | * previously sent already encoded operation. | 3927 | * previously sent already encoded operation. |
3940 | * | ||
3941 | * called with nfs4_lock_state() held | ||
3942 | */ | 3928 | */ |
3943 | void | 3929 | void |
3944 | nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op) | 3930 | nfsd4_encode_replay(struct xdr_stream *xdr, struct nfsd4_op *op) |
@@ -3977,9 +3963,8 @@ int nfsd4_release_compoundargs(void *rq, __be32 *p, void *resp) | |||
3977 | kfree(args->tmpp); | 3963 | kfree(args->tmpp); |
3978 | args->tmpp = NULL; | 3964 | args->tmpp = NULL; |
3979 | while (args->to_free) { | 3965 | while (args->to_free) { |
3980 | struct tmpbuf *tb = args->to_free; | 3966 | struct svcxdr_tmpbuf *tb = args->to_free; |
3981 | args->to_free = tb->next; | 3967 | args->to_free = tb->next; |
3982 | tb->release(tb->buf); | ||
3983 | kfree(tb); | 3968 | kfree(tb); |
3984 | } | 3969 | } |
3985 | return 1; | 3970 | return 1; |
@@ -4012,7 +3997,6 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo | |||
4012 | /* | 3997 | /* |
4013 | * All that remains is to write the tag and operation count... | 3998 | * All that remains is to write the tag and operation count... |
4014 | */ | 3999 | */ |
4015 | struct nfsd4_compound_state *cs = &resp->cstate; | ||
4016 | struct xdr_buf *buf = resp->xdr.buf; | 4000 | struct xdr_buf *buf = resp->xdr.buf; |
4017 | 4001 | ||
4018 | WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len + | 4002 | WARN_ON_ONCE(buf->len != buf->head[0].iov_len + buf->page_len + |
@@ -4026,19 +4010,7 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo | |||
4026 | p += XDR_QUADLEN(resp->taglen); | 4010 | p += XDR_QUADLEN(resp->taglen); |
4027 | *p++ = htonl(resp->opcnt); | 4011 | *p++ = htonl(resp->opcnt); |
4028 | 4012 | ||
4029 | if (nfsd4_has_session(cs)) { | 4013 | nfsd4_sequence_done(resp); |
4030 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
4031 | struct nfs4_client *clp = cs->session->se_client; | ||
4032 | if (cs->status != nfserr_replay_cache) { | ||
4033 | nfsd4_store_cache_entry(resp); | ||
4034 | cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE; | ||
4035 | } | ||
4036 | /* Renew the clientid on success and on replay */ | ||
4037 | spin_lock(&nn->client_lock); | ||
4038 | nfsd4_put_session(cs->session); | ||
4039 | spin_unlock(&nn->client_lock); | ||
4040 | put_client_renew(clp); | ||
4041 | } | ||
4042 | return 1; | 4014 | return 1; |
4043 | } | 4015 | } |
4044 | 4016 | ||