1 /*-
2 * Copyright (c) 2013-2015 Varnish Software AS
3 * All rights reserved.
4 *
5 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6 *
7 * SPDX-License-Identifier: BSD-2-Clause
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 * Interaction with the linvgz (zlib) library.
31 *
32 * The zlib library pollutes namespace a LOT when you include the "vgz.h"
33 * (aka (zlib.h") file so we contain the damage by vectoring all access
34 * to libz through this source file.
35 *
36 * The API defined by this file, will also insulate the rest of the code,
37 * should we find a better gzip library at a later date.
38 *
39 */
40
41 #include "config.h"
42
43 #include <stdlib.h>
44
45 #include "cache_varnishd.h"
46 #include "cache_filter.h"
47 #include "cache_objhead.h"
48 #include "cache_vgz.h"
49 #include "vend.h"
50
51 #include "vgz.h"
52
53 struct vgz {
54 unsigned magic;
55 #define VGZ_MAGIC 0x162df0cb
56 enum {VGZ_GZ,VGZ_UN} dir;
57 struct vsl_log *vsl;
58 const char *id;
59 int last_i;
60 enum vgz_flag flag;
61
62 char *m_buf;
63 ssize_t m_sz;
64 ssize_t m_len;
65
66 intmax_t bits;
67
68 z_stream vz;
69 };
70
71 static const char *
vgz_msg(const struct vgz * vg)72 vgz_msg(const struct vgz *vg)
73 {
74 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
75 return (vg->vz.msg ? vg->vz.msg : "(null)");
76 }
77
78 /*--------------------------------------------------------------------
79 * Set up a gunzip instance
80 */
81
82 static struct vgz *
vgz_gunzip(struct vsl_log * vsl,const char * id)83 vgz_gunzip(struct vsl_log *vsl, const char *id)
84 {
85 struct vgz *vg;
86
87 ALLOC_OBJ(vg, VGZ_MAGIC);
88 AN(vg);
89 vg->vsl = vsl;
90 vg->id = id;
91 vg->dir = VGZ_UN;
92
93 /*
94 * Max memory usage according to zonf.h:
95 * mem_needed = "a few kb" + (1 << (windowBits))
96 * Since we don't control windowBits, we have to assume
97 * it is 15, so 34-35KB or so.
98 */
99 assert(Z_OK == inflateInit2(&vg->vz, 31));
100 return (vg);
101 }
102
103 static struct vgz *
VGZ_NewGunzip(struct vsl_log * vsl,const char * id)104 VGZ_NewGunzip(struct vsl_log *vsl, const char *id)
105 {
106 VSC_C_main->n_gunzip++;
107 return (vgz_gunzip(vsl, id));
108 }
109
110 static struct vgz *
VGZ_NewTestGunzip(struct vsl_log * vsl,const char * id)111 VGZ_NewTestGunzip(struct vsl_log *vsl, const char *id)
112 {
113 VSC_C_main->n_test_gunzip++;
114 return (vgz_gunzip(vsl, id));
115 }
116
117 struct vgz *
VGZ_NewGzip(struct vsl_log * vsl,const char * id)118 VGZ_NewGzip(struct vsl_log *vsl, const char *id)
119 {
120 struct vgz *vg;
121 int i;
122
123 VSC_C_main->n_gzip++;
124 ALLOC_OBJ(vg, VGZ_MAGIC);
125 AN(vg);
126 vg->vsl = vsl;
127 vg->id = id;
128 vg->dir = VGZ_GZ;
129
130 /*
131 * From zconf.h:
132 *
133 * mem_needed = "a few kb"
134 * + (1 << (windowBits+2))
135 * + (1 << (memLevel+9))
136 *
137 * windowBits [8..15] (-> 1K..128K)
138 * memLevel [1..9] (-> 1K->256K)
139 */
140 i = deflateInit2(&vg->vz,
141 cache_param->gzip_level, /* Level */
142 Z_DEFLATED, /* Method */
143 16 + 15, /* Window bits (16=gzip) */
144 cache_param->gzip_memlevel, /* memLevel */
145 Z_DEFAULT_STRATEGY);
146 assert(Z_OK == i);
147 return (vg);
148 }
149
150 /*--------------------------------------------------------------------
151 */
152
153 static int
vgz_getmbuf(struct vgz * vg)154 vgz_getmbuf(struct vgz *vg)
155 {
156
157 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
158 AZ(vg->m_sz);
159 AZ(vg->m_len);
160 AZ(vg->m_buf);
161
162 vg->m_sz = cache_param->gzip_buffer;
163 vg->m_buf = malloc(vg->m_sz);
164 if (vg->m_buf == NULL) {
165 vg->m_sz = 0;
166 return (-1);
167 }
168 return (0);
169 }
170
171 /*--------------------------------------------------------------------*/
172
173 void
VGZ_Ibuf(struct vgz * vg,const void * ptr,ssize_t len)174 VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
175 {
176
177 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
178
179 AZ(vg->vz.avail_in);
180 vg->vz.next_in = TRUST_ME(ptr);
181 vg->vz.avail_in = len;
182 }
183
184 int
VGZ_IbufEmpty(const struct vgz * vg)185 VGZ_IbufEmpty(const struct vgz *vg)
186 {
187
188 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
189 return (vg->vz.avail_in == 0);
190 }
191
192 /*--------------------------------------------------------------------*/
193
194 void
VGZ_Obuf(struct vgz * vg,void * ptr,ssize_t len)195 VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
196 {
197
198 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
199
200 vg->vz.next_out = TRUST_ME(ptr);
201 vg->vz.avail_out = len;
202 }
203
204 int
VGZ_ObufFull(const struct vgz * vg)205 VGZ_ObufFull(const struct vgz *vg)
206 {
207
208 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
209 return (vg->vz.avail_out == 0);
210 }
211
212 /*--------------------------------------------------------------------*/
213
214 static enum vgzret_e
VGZ_Gunzip(struct vgz * vg,const void ** pptr,ssize_t * plen)215 VGZ_Gunzip(struct vgz *vg, const void **pptr, ssize_t *plen)
216 {
217 int i;
218 ssize_t l;
219 const uint8_t *before;
220
221 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
222
223 *pptr = NULL;
224 *plen = 0;
225 AN(vg->vz.next_out);
226 AN(vg->vz.avail_out);
227 before = vg->vz.next_out;
228 i = inflate(&vg->vz, 0);
229 if (i == Z_OK || i == Z_STREAM_END) {
230 *pptr = before;
231 l = (const uint8_t *)vg->vz.next_out - before;
232 *plen = l;
233 }
234 vg->last_i = i;
235 if (i == Z_OK)
236 return (VGZ_OK);
237 if (i == Z_STREAM_END)
238 return (VGZ_END);
239 if (i == Z_BUF_ERROR)
240 return (VGZ_STUCK);
241 VSLb(vg->vsl, SLT_Gzip, "Gunzip error: %d (%s)", i, vgz_msg(vg));
242 return (VGZ_ERROR);
243 }
244
245 /*--------------------------------------------------------------------*/
246
247 enum vgzret_e
VGZ_Gzip(struct vgz * vg,const void ** pptr,ssize_t * plen,enum vgz_flag flags)248 VGZ_Gzip(struct vgz *vg, const void **pptr, ssize_t *plen, enum vgz_flag flags)
249 {
250 int i;
251 int zflg;
252 ssize_t l;
253 const uint8_t *before;
254
255 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
256
257 *pptr = NULL;
258 *plen = 0;
259 AN(vg->vz.next_out);
260 AN(vg->vz.avail_out);
261 before = vg->vz.next_out;
262 switch (flags) {
263 case VGZ_NORMAL: zflg = Z_NO_FLUSH; break;
264 case VGZ_ALIGN: zflg = Z_SYNC_FLUSH; break;
265 case VGZ_RESET: zflg = Z_FULL_FLUSH; break;
266 case VGZ_FINISH: zflg = Z_FINISH; break;
267 default: INCOMPL();
268 }
269 i = deflate(&vg->vz, zflg);
270 if (i == Z_OK || i == Z_STREAM_END) {
271 *pptr = before;
272 l = (const uint8_t *)vg->vz.next_out - before;
273 *plen = l;
274 }
275 vg->last_i = i;
276 if (i == Z_OK)
277 return (VGZ_OK);
278 if (i == Z_STREAM_END)
279 return (VGZ_END);
280 if (i == Z_BUF_ERROR)
281 return (VGZ_STUCK);
282 VSLb(vg->vsl, SLT_Gzip, "Gzip error: %d (%s)", i, vgz_msg(vg));
283 return (VGZ_ERROR);
284 }
285
286 /*--------------------------------------------------------------------
287 * VDP for gunzip'ing
288 */
289
v_matchproto_(vdp_init_f)290 static int v_matchproto_(vdp_init_f)
291 vdp_gunzip_init(struct vdp_ctx *vdp, void **priv, struct objcore *oc)
292 {
293 struct vgz *vg;
294 struct boc *boc;
295 struct req *req;
296 enum boc_state_e bos;
297 const char *p;
298 ssize_t dl;
299 uint64_t u;
300
301 CHECK_OBJ_NOTNULL(vdp, VDP_CTX_MAGIC);
302 CHECK_OBJ_ORNULL(oc, OBJCORE_MAGIC);
303 req = vdp->req;
304 CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
305
306 vg = VGZ_NewGunzip(req->vsl, "U D -");
307 AN(vg);
308 if (vgz_getmbuf(vg)) {
309 (void)VGZ_Destroy(&vg);
310 return (-1);
311 }
312
313 VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
314 *priv = vg;
315
316 http_Unset(req->resp, H_Content_Encoding);
317
318 req->resp_len = -1;
319
320 if (oc == NULL)
321 return (0);
322
323 boc = HSH_RefBoc(oc);
324 if (boc != NULL) {
325 CHECK_OBJ(boc, BOC_MAGIC);
326 bos = boc->state;
327 HSH_DerefBoc(req->wrk, oc);
328 if (bos < BOS_FINISHED)
329 return (0); /* OA_GZIPBITS is not stable yet */
330 }
331
332 p = ObjGetAttr(req->wrk, oc, OA_GZIPBITS, &dl);
333 if (p != NULL && dl == 32) {
334 u = vbe64dec(p + 24);
335 if (u != 0)
336 req->resp_len = u;
337 }
338 return (0);
339 }
340
v_matchproto_(vdp_fini_f)341 static int v_matchproto_(vdp_fini_f)
342 vdp_gunzip_fini(struct vdp_ctx *vdc, void **priv)
343 {
344 struct vgz *vg;
345
346 (void)vdc;
347 CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
348 AN(vg->m_buf);
349 (void)VGZ_Destroy(&vg);
350 *priv = NULL;
351 return (0);
352 }
353
v_matchproto_(vdp_bytes_f)354 static int v_matchproto_(vdp_bytes_f)
355 vdp_gunzip_bytes(struct vdp_ctx *vdx, enum vdp_action act, void **priv,
356 const void *ptr, ssize_t len)
357 {
358 enum vgzret_e vr;
359 ssize_t dl;
360 const void *dp;
361 struct worker *wrk;
362 struct vgz *vg;
363
364 CHECK_OBJ_NOTNULL(vdx, VDP_CTX_MAGIC);
365 wrk = vdx->wrk;
366 CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
367 (void)act;
368
369 CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
370 AN(vg->m_buf);
371
372 if (len == 0)
373 return (0);
374
375 VGZ_Ibuf(vg, ptr, len);
376 do {
377 vr = VGZ_Gunzip(vg, &dp, &dl);
378 if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) {
379 VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
380 vr, "junk after VGZ_END");
381 return (-1);
382 }
383 vg->m_len += dl;
384 if (vr < VGZ_OK)
385 return (-1);
386 if (vg->m_len == vg->m_sz || vr != VGZ_OK) {
387 if (VDP_bytes(vdx, vr == VGZ_END ? VDP_END : VDP_FLUSH,
388 vg->m_buf, vg->m_len))
389 return (vdx->retval);
390 vg->m_len = 0;
391 VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
392 }
393 } while (!VGZ_IbufEmpty(vg));
394 assert(vr == VGZ_STUCK || vr == VGZ_OK || vr == VGZ_END);
395 return (0);
396 }
397
398 const struct vdp VDP_gunzip = {
399 .name = "gunzip",
400 .init = vdp_gunzip_init,
401 .bytes = vdp_gunzip_bytes,
402 .fini = vdp_gunzip_fini,
403 };
404
405 /*--------------------------------------------------------------------*/
406
407 void
VGZ_UpdateObj(const struct vfp_ctx * vc,struct vgz * vg,enum vgz_ua_e e)408 VGZ_UpdateObj(const struct vfp_ctx *vc, struct vgz *vg, enum vgz_ua_e e)
409 {
410 char *p;
411 intmax_t ii;
412
413 CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
414 ii = vg->vz.start_bit + vg->vz.last_bit + vg->vz.stop_bit;
415 if (e == VUA_UPDATE && ii == vg->bits)
416 return;
417 vg->bits = ii;
418 p = ObjSetAttr(vc->wrk, vc->oc, OA_GZIPBITS, 32, NULL);
419 AN(p);
420 vbe64enc(p, vg->vz.start_bit);
421 vbe64enc(p + 8, vg->vz.last_bit);
422 vbe64enc(p + 16, vg->vz.stop_bit);
423 if (e == VUA_END_GZIP)
424 vbe64enc(p + 24, vg->vz.total_in);
425 if (e == VUA_END_GUNZIP)
426 vbe64enc(p + 24, vg->vz.total_out);
427 }
428
429 /*--------------------------------------------------------------------
430 */
431
432 enum vgzret_e
VGZ_Destroy(struct vgz ** vgp)433 VGZ_Destroy(struct vgz **vgp)
434 {
435 struct vgz *vg;
436 enum vgzret_e vr;
437 int i;
438
439 TAKE_OBJ_NOTNULL(vg, vgp, VGZ_MAGIC);
440 AN(vg->id);
441 VSLb(vg->vsl, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
442 vg->id,
443 (intmax_t)vg->vz.total_in,
444 (intmax_t)vg->vz.total_out,
445 (intmax_t)vg->vz.start_bit,
446 (intmax_t)vg->vz.last_bit,
447 (intmax_t)vg->vz.stop_bit);
448 if (vg->dir == VGZ_GZ)
449 i = deflateEnd(&vg->vz);
450 else
451 i = inflateEnd(&vg->vz);
452 if (vg->last_i == Z_STREAM_END && i == Z_OK)
453 i = Z_STREAM_END;
454 if (vg->m_buf)
455 free(vg->m_buf);
456 if (i == Z_OK)
457 vr = VGZ_OK;
458 else if (i == Z_STREAM_END)
459 vr = VGZ_END;
460 else if (i == Z_BUF_ERROR)
461 vr = VGZ_STUCK;
462 else {
463 VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
464 i, vgz_msg(vg));
465 vr = VGZ_ERROR;
466 }
467 FREE_OBJ(vg);
468 return (vr);
469 }
470
471 /*--------------------------------------------------------------------*/
472
v_matchproto_(vfp_init_f)473 static enum vfp_status v_matchproto_(vfp_init_f)
474 vfp_gzip_init(struct vfp_ctx *vc, struct vfp_entry *vfe)
475 {
476 struct vgz *vg;
477
478 CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
479 CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
480
481 /*
482 * G(un)zip makes no sence on partial responses, but since
483 * it is an pure 1:1 transform, we can just ignore it.
484 */
485 if (http_GetStatus(vc->resp) == 206)
486 return (VFP_NULL);
487
488 if (vfe->vfp == &VFP_gzip) {
489 if (http_GetHdr(vc->resp, H_Content_Encoding, NULL))
490 return (VFP_NULL);
491 vg = VGZ_NewGzip(vc->wrk->vsl, vfe->vfp->priv1);
492 vc->obj_flags |= OF_GZIPED | OF_CHGCE;
493 } else {
494 if (!http_HdrIs(vc->resp, H_Content_Encoding, "gzip"))
495 return (VFP_NULL);
496 if (vfe->vfp == &VFP_gunzip) {
497 vg = VGZ_NewGunzip(vc->wrk->vsl, vfe->vfp->priv1);
498 vc->obj_flags &= ~OF_GZIPED;
499 vc->obj_flags |= OF_CHGCE;
500 } else {
501 vg = VGZ_NewTestGunzip(vc->wrk->vsl, vfe->vfp->priv1);
502 vc->obj_flags |= OF_GZIPED;
503 }
504 }
505 AN(vg);
506 vfe->priv1 = vg;
507 if (vgz_getmbuf(vg))
508 return (VFP_ERROR);
509 VGZ_Ibuf(vg, vg->m_buf, 0);
510 AZ(vg->m_len);
511
512 if (vfe->vfp == &VFP_gunzip || vfe->vfp == &VFP_gzip) {
513 http_Unset(vc->resp, H_Content_Encoding);
514 http_Unset(vc->resp, H_Content_Length);
515 RFC2616_Weaken_Etag(vc->resp);
516 }
517
518 if (vfe->vfp == &VFP_gzip)
519 http_SetHeader(vc->resp, "Content-Encoding: gzip");
520
521 if (vfe->vfp == &VFP_gzip || vfe->vfp == &VFP_testgunzip)
522 RFC2616_Vary_AE(vc->resp);
523
524 return (VFP_OK);
525 }
526
527 /*--------------------------------------------------------------------
528 * VFP_GUNZIP
529 *
530 * A VFP for gunzip'ing an object as we receive it from the backend
531 */
532
v_matchproto_(vfp_pull_f)533 static enum vfp_status v_matchproto_(vfp_pull_f)
534 vfp_gunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
535 ssize_t *lp)
536 {
537 ssize_t l;
538 struct vgz *vg;
539 enum vgzret_e vr = VGZ_ERROR;
540 const void *dp;
541 ssize_t dl;
542 enum vfp_status vp = VFP_OK;
543
544 CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
545 CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
546 CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
547 AN(p);
548 AN(lp);
549 l = *lp;
550 *lp = 0;
551 VGZ_Obuf(vg, p, l);
552 do {
553 if (VGZ_IbufEmpty(vg)) {
554 l = vg->m_sz;
555 vp = VFP_Suck(vc, vg->m_buf, &l);
556 if (vp == VFP_ERROR)
557 return (vp);
558 VGZ_Ibuf(vg, vg->m_buf, l);
559 }
560 if (!VGZ_IbufEmpty(vg) || vp == VFP_END) {
561 vr = VGZ_Gunzip(vg, &dp, &dl);
562 if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
563 return (VFP_Error(vc, "Junk after gzip data"));
564 if (vr < VGZ_OK)
565 return (VFP_Error(vc,
566 "Invalid Gzip data: %s", vgz_msg(vg)));
567 if (dl > 0) {
568 *lp = dl;
569 assert(dp == p);
570 return (VFP_OK);
571 }
572 }
573 AN(VGZ_IbufEmpty(vg));
574 } while (vp == VFP_OK);
575 if (vr != VGZ_END)
576 return (VFP_Error(vc, "Gunzip error at the very end"));
577 return (vp);
578 }
579
580
581 /*--------------------------------------------------------------------
582 * VFP_GZIP
583 *
584 * A VFP for gzip'ing an object as we receive it from the backend
585 */
586
v_matchproto_(vfp_pull_f)587 static enum vfp_status v_matchproto_(vfp_pull_f)
588 vfp_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
589 ssize_t *lp)
590 {
591 ssize_t l;
592 struct vgz *vg;
593 enum vgzret_e vr = VGZ_ERROR;
594 const void *dp;
595 ssize_t dl;
596 enum vfp_status vp = VFP_ERROR;
597
598 CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
599 CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
600 CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
601 AN(p);
602 AN(lp);
603 l = *lp;
604 *lp = 0;
605 VGZ_Obuf(vg, p, l);
606 do {
607 if (VGZ_IbufEmpty(vg)) {
608 l = vg->m_sz;
609 vp = VFP_Suck(vc, vg->m_buf, &l);
610 if (vp == VFP_ERROR)
611 break;
612 if (vp == VFP_END)
613 vg->flag = VGZ_FINISH;
614 VGZ_Ibuf(vg, vg->m_buf, l);
615 }
616 if (!VGZ_IbufEmpty(vg) || vg->flag == VGZ_FINISH) {
617 vr = VGZ_Gzip(vg, &dp, &dl, vg->flag);
618 if (vr < VGZ_OK)
619 return (VFP_Error(vc, "Gzip failed"));
620 if (dl > 0) {
621 VGZ_UpdateObj(vc, vg, VUA_UPDATE);
622 *lp = dl;
623 assert(dp == p);
624 return (VFP_OK);
625 }
626 }
627 AN(VGZ_IbufEmpty(vg));
628 } while (vg->flag != VGZ_FINISH);
629
630 if (vr != VGZ_END)
631 return (VFP_Error(vc, "Gzip failed"));
632 VGZ_UpdateObj(vc, vg, VUA_END_GZIP);
633 return (VFP_END);
634 }
635
636 /*--------------------------------------------------------------------
637 * VFP_TESTGZIP
638 *
639 * A VFP for testing that received gzip data is valid, and for
640 * collecting the magic bits while we're at it.
641 */
642
v_matchproto_(vfp_pull_f)643 static enum vfp_status v_matchproto_(vfp_pull_f)
644 vfp_testgunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
645 ssize_t *lp)
646 {
647 struct vgz *vg;
648 enum vgzret_e vr = VGZ_ERROR;
649 const void *dp;
650 ssize_t dl;
651 enum vfp_status vp;
652
653 CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
654 CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
655 CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
656 AN(p);
657 AN(lp);
658 vp = VFP_Suck(vc, p, lp);
659 if (vp == VFP_ERROR)
660 return (vp);
661 if (*lp > 0 || vp == VFP_END) {
662 VGZ_Ibuf(vg, p, *lp);
663 do {
664 VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
665 vr = VGZ_Gunzip(vg, &dp, &dl);
666 if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
667 return (VFP_Error(vc, "Junk after gzip data"));
668 if (vr < VGZ_OK)
669 return (VFP_Error(vc,
670 "Invalid Gzip data: %s", vgz_msg(vg)));
671 } while (!VGZ_IbufEmpty(vg));
672 }
673 VGZ_UpdateObj(vc, vg, VUA_UPDATE);
674 if (vp == VFP_END) {
675 if (vr != VGZ_END)
676 return (VFP_Error(vc, "tGunzip failed"));
677 VGZ_UpdateObj(vc, vg, VUA_END_GUNZIP);
678 }
679 return (vp);
680 }
681
682 /*--------------------------------------------------------------------*/
683
v_matchproto_(vfp_fini_f)684 static void v_matchproto_(vfp_fini_f)
685 vfp_gzip_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
686 {
687 struct vgz *vg;
688
689 CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
690 CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
691
692 if (vfe->priv1 != NULL) {
693 CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
694 vfe->priv1 = NULL;
695 (void)VGZ_Destroy(&vg);
696 }
697 }
698
699 /*--------------------------------------------------------------------*/
700
701 const struct vfp VFP_gunzip = {
702 .name = "gunzip",
703 .init = vfp_gzip_init,
704 .pull = vfp_gunzip_pull,
705 .fini = vfp_gzip_fini,
706 .priv1 = "U F -",
707 };
708
709 const struct vfp VFP_gzip = {
710 .name = "gzip",
711 .init = vfp_gzip_init,
712 .pull = vfp_gzip_pull,
713 .fini = vfp_gzip_fini,
714 .priv1 = "G F -",
715 };
716
717 const struct vfp VFP_testgunzip = {
718 .name = "testgunzip",
719 .init = vfp_gzip_init,
720 .pull = vfp_testgunzip_pull,
721 .fini = vfp_gzip_fini,
722 .priv1 = "u F -",
723 };
724