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