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 18
vgz_msg(const struct vgz *vg)
70
{
71 18
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
72 18
        return vg->vz.msg ? vg->vz.msg : "(null)";
73
}
74
75
/*--------------------------------------------------------------------
76
 * Set up a gunzip instance
77
 */
78
79
static struct vgz *
80 300
vgz_gunzip(struct vsl_log *vsl, const char *id)
81
{
82
        struct vgz *vg;
83
84 300
        ALLOC_OBJ(vg, VGZ_MAGIC);
85 300
        AN(vg);
86 300
        vg->vsl = vsl;
87 300
        vg->id = id;
88 300
        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 300
        assert(Z_OK == inflateInit2(&vg->vz, 31));
97 300
        return (vg);
98
}
99
100
static struct vgz *
101 216
VGZ_NewGunzip(struct vsl_log *vsl, const char *id)
102
{
103 216
        VSC_C_main->n_gunzip++;
104 216
        return (vgz_gunzip(vsl, id));
105
}
106
107
static struct vgz *
108 84
VGZ_NewTestGunzip(struct vsl_log *vsl, const char *id)
109
{
110 84
        VSC_C_main->n_test_gunzip++;
111 84
        return (vgz_gunzip(vsl, id));
112
}
113
114
struct vgz *
115 159
VGZ_NewGzip(struct vsl_log *vsl, const char *id)
116
{
117
        struct vgz *vg;
118
        int i;
119
120 159
        VSC_C_main->n_gzip++;
121 159
        ALLOC_OBJ(vg, VGZ_MAGIC);
122 159
        AN(vg);
123 159
        vg->vsl = vsl;
124 159
        vg->id = id;
125 159
        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 159
        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 159
        assert(Z_OK == i);
144 159
        return (vg);
145
}
146
147
/*--------------------------------------------------------------------
148
 */
149
150
static int
151 342
vgz_getmbuf(struct vgz *vg)
152
{
153
154 342
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
155 342
        AZ(vg->m_sz);
156 342
        AZ(vg->m_len);
157 342
        AZ(vg->m_buf);
158
159 342
        vg->m_sz = cache_param->gzip_buffer;
160 342
        vg->m_buf = malloc(vg->m_sz);
161 342
        if (vg->m_buf == NULL) {
162 0
                vg->m_sz = 0;
163 0
                return (-1);
164
        }
165 342
        return (0);
166
}
167
168
/*--------------------------------------------------------------------*/
169
170
void
171 3319
VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
172
{
173
174 3319
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
175
176 3319
        AZ(vg->vz.avail_in);
177 3319
        vg->vz.next_in = TRUST_ME(ptr);
178 3319
        vg->vz.avail_in = len;
179 3319
}
180
181
int
182 8345
VGZ_IbufEmpty(const struct vgz *vg)
183
{
184
185 8345
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
186 8345
        return (vg->vz.avail_in == 0);
187
}
188
189
/*--------------------------------------------------------------------*/
190
191
void
192 4150
VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
193
{
194
195 4150
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
196
197 4150
        vg->vz.next_out = TRUST_ME(ptr);
198 4150
        vg->vz.avail_out = len;
199 4150
}
200
201
int
202 2470
VGZ_ObufFull(const struct vgz *vg)
203
{
204
205 2470
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
206 2470
        return (vg->vz.avail_out == 0);
207
}
208
209
/*--------------------------------------------------------------------*/
210
211
static enum vgzret_e
212 1629
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 1629
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
219
220 1629
        *pptr = NULL;
221 1629
        *plen = 0;
222 1629
        AN(vg->vz.next_out);
223 1629
        AN(vg->vz.avail_out);
224 1629
        before = vg->vz.next_out;
225 1629
        i = inflate(&vg->vz, 0);
226 1629
        if (i == Z_OK || i == Z_STREAM_END) {
227 1617
                *pptr = before;
228 1617
                l = (const uint8_t *)vg->vz.next_out - before;
229 1617
                *plen = l;
230
        }
231 1629
        vg->last_i = i;
232 1629
        if (i == Z_OK)
233 1215
                return (VGZ_OK);
234 414
        if (i == Z_STREAM_END)
235 402
                return (VGZ_END);
236 12
        if (i == Z_BUF_ERROR)
237 6
                return (VGZ_STUCK);
238 6
        VSLb(vg->vsl, SLT_Gzip, "Gunzip error: %d (%s)", i, vgz_msg(vg));
239 6
        return (VGZ_ERROR);
240
}
241
242
/*--------------------------------------------------------------------*/
243
244
enum vgzret_e
245 2689
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 2689
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
253
254 2689
        *pptr = NULL;
255 2689
        *plen = 0;
256 2689
        AN(vg->vz.next_out);
257 2689
        AN(vg->vz.avail_out);
258 2689
        before = vg->vz.next_out;
259 2689
        switch (flags) {
260 1963
        case VGZ_NORMAL:        zflg = Z_NO_FLUSH; break;
261 237
        case VGZ_ALIGN:         zflg = Z_SYNC_FLUSH; break;
262 240
        case VGZ_RESET:         zflg = Z_FULL_FLUSH; break;
263 249
        case VGZ_FINISH:        zflg = Z_FINISH; break;
264 0
        default:                INCOMPL();
265
        }
266 2689
        i = deflate(&vg->vz, zflg);
267 2689
        if (i == Z_OK || i == Z_STREAM_END) {
268 2689
                *pptr = before;
269 2689
                l = (const uint8_t *)vg->vz.next_out - before;
270 2689
                *plen = l;
271
        }
272 2689
        vg->last_i = i;
273 2689
        if (i == Z_OK)
274 2500
                return (VGZ_OK);
275 189
        if (i == Z_STREAM_END)
276 189
                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_bytes)
288 690
vdp_gunzip(struct req *req, enum vdp_action act, void **priv,
289
    const void *ptr, ssize_t len)
290
{
291
        enum vgzret_e vr;
292
        ssize_t dl;
293
        const void *dp;
294
        struct worker *wrk;
295
        struct vgz *vg;
296
        const char *p;
297
        uint64_t u;
298
299 690
        CHECK_OBJ_NOTNULL(req, REQ_MAGIC);
300 690
        wrk = req->wrk;
301 690
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
302
303 690
        if (act == VDP_INIT) {
304 99
                vg = VGZ_NewGunzip(req->vsl, "U D -");
305 99
                AN(vg);
306 99
                if (vgz_getmbuf(vg)) {
307 0
                        (void)VGZ_Destroy(&vg);
308 0
                        return (-1);
309
                }
310
311 99
                req->res_mode |= RES_GUNZIP;
312 99
                VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
313 99
                *priv = vg;
314
315 99
                http_Unset(req->resp, H_Content_Encoding);
316
317 99
                req->resp_len = -1;
318
319 99
                p = ObjGetAttr(req->wrk, req->objcore, OA_GZIPBITS, &dl);
320 99
                if (p != NULL && dl == 32) {
321 99
                        u = vbe64dec(p + 24);
322
                        /*
323
                         * If the size is non-zero AND we are the top VDP
324
                         * (ie: no ESI), we know what size the output will be.
325
                         */
326 189
                        if (u != 0 &&
327 90
                            VTAILQ_FIRST(&req->vdc->vdp)->vdp == &VDP_gunzip)
328 66
                                req->resp_len = u;
329
                }
330 99
                return (0);
331
        }
332
333 591
        CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
334 591
        AN(vg->m_buf);
335
336 591
        if (act == VDP_FINI) {
337
                /* NB: Gunzip'ing may or may not have completed successfully. */
338 99
                AZ(len);
339 99
                (void)VGZ_Destroy(&vg);
340 99
                *priv = NULL;
341 99
                return (0);
342
        }
343
344 492
        if (len == 0)
345 144
                return (0);
346
347 348
        VGZ_Ibuf(vg, ptr, len);
348
        do {
349 348
                vr = VGZ_Gunzip(vg, &dp, &dl);
350 348
                vg->m_len += dl;
351 348
                if (vr < VGZ_OK)
352 0
                        return (-1);
353 348
                if (vg->m_len == vg->m_sz || vr != VGZ_OK) {
354 93
                        if (VDP_bytes(req, VDP_FLUSH, vg->m_buf, vg->m_len))
355 6
                                return (req->vdc->retval);
356 87
                        vg->m_len = 0;
357 87
                        VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
358
                }
359 342
        } while (!VGZ_IbufEmpty(vg));
360 342
        assert(vr == VGZ_STUCK || vr == VGZ_OK || vr == VGZ_END);
361 342
        return (0);
362
}
363
364
const struct vdp VDP_gunzip = {
365
        .name =         "gunzip",
366
        .func =         vdp_gunzip,
367
};
368
369
/*--------------------------------------------------------------------*/
370
371
void
372 2965
VGZ_UpdateObj(const struct vfp_ctx *vc, struct vgz *vg, enum vgz_ua_e e)
373
{
374
        char *p;
375
        intmax_t ii;
376
377 2965
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
378 2965
        ii = vg->vz.start_bit + vg->vz.last_bit + vg->vz.stop_bit;
379 2965
        if (e == VUA_UPDATE && ii == vg->bits)
380 2389
                return;
381 576
        vg->bits = ii;
382 576
        p = ObjSetAttr(vc->wrk, vc->oc, OA_GZIPBITS, 32, NULL);
383 576
        AN(p);
384 576
        vbe64enc(p, vg->vz.start_bit);
385 576
        vbe64enc(p + 8, vg->vz.last_bit);
386 576
        vbe64enc(p + 16, vg->vz.stop_bit);
387 576
        if (e == VUA_END_GZIP)
388 156
                vbe64enc(p + 24, vg->vz.total_in);
389 576
        if (e == VUA_END_GUNZIP)
390 66
                vbe64enc(p + 24, vg->vz.total_out);
391
}
392
393
/*--------------------------------------------------------------------
394
 */
395
396
enum vgzret_e
397 459
VGZ_Destroy(struct vgz **vgp)
398
{
399
        struct vgz *vg;
400
        enum vgzret_e vr;
401
        int i;
402
403 459
        TAKE_OBJ_NOTNULL(vg, vgp, VGZ_MAGIC);
404 459
        AN(vg->id);
405 2295
        VSLb(vg->vsl, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
406
            vg->id,
407 459
            (intmax_t)vg->vz.total_in,
408 459
            (intmax_t)vg->vz.total_out,
409 459
            (intmax_t)vg->vz.start_bit,
410 459
            (intmax_t)vg->vz.last_bit,
411 459
            (intmax_t)vg->vz.stop_bit);
412 459
        if (vg->dir == VGZ_GZ)
413 159
                i = deflateEnd(&vg->vz);
414
        else
415 300
                i = inflateEnd(&vg->vz);
416 459
        if (vg->last_i == Z_STREAM_END && i == Z_OK)
417 426
                i = Z_STREAM_END;
418 459
        if (vg->m_buf)
419 342
                free(vg->m_buf);
420 459
        if (i == Z_OK)
421 27
                vr = VGZ_OK;
422 432
        else if (i == Z_STREAM_END)
423 426
                vr = VGZ_END;
424 6
        else if (i == Z_BUF_ERROR)
425 0
                vr = VGZ_STUCK;
426
        else {
427 6
                VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
428
                    i, vgz_msg(vg));
429 6
                vr = VGZ_ERROR;
430
        }
431 459
        FREE_OBJ(vg);
432 459
        return (vr);
433
}
434
435
/*--------------------------------------------------------------------*/
436
437
static enum vfp_status v_matchproto_(vfp_init_f)
438 255
vfp_gzip_init(struct vfp_ctx *vc, struct vfp_entry *vfe)
439
{
440
        struct vgz *vg;
441
442 255
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
443 255
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
444
445
        /*
446
         * G(un)zip makes no sence on partial responses, but since
447
         * it is an pure 1:1 transform, we can just ignore it.
448
         */
449 255
        if (http_GetStatus(vc->resp) == 206)
450 12
                return (VFP_NULL);
451
452 243
        if (vfe->vfp == &VFP_gzip) {
453 42
                if (http_GetHdr(vc->resp, H_Content_Encoding, NULL))
454 0
                        return (VFP_NULL);
455 42
                vg = VGZ_NewGzip(vc->wrk->vsl, vfe->vfp->priv1);
456 42
                vc->obj_flags |= OF_GZIPED | OF_CHGGZIP;
457
        } else {
458 201
                if (!http_HdrIs(vc->resp, H_Content_Encoding, "gzip"))
459 0
                        return (VFP_NULL);
460 201
                if (vfe->vfp == &VFP_gunzip) {
461 117
                        vg = VGZ_NewGunzip(vc->wrk->vsl, vfe->vfp->priv1);
462 117
                        vc->obj_flags &= ~OF_GZIPED;
463 117
                        vc->obj_flags |= OF_CHGGZIP;
464
                } else {
465 84
                        vg = VGZ_NewTestGunzip(vc->wrk->vsl, vfe->vfp->priv1);
466 84
                        vc->obj_flags |= OF_GZIPED;
467
                }
468
        }
469 243
        if (vg == NULL)
470 0
                return (VFP_ERROR);
471 243
        vfe->priv1 = vg;
472 243
        if (vgz_getmbuf(vg))
473 0
                return (VFP_ERROR);
474 243
        VGZ_Ibuf(vg, vg->m_buf, 0);
475 243
        AZ(vg->m_len);
476
477 243
        if (vfe->vfp == &VFP_gunzip || vfe->vfp == &VFP_gzip) {
478 159
                http_Unset(vc->resp, H_Content_Encoding);
479 159
                http_Unset(vc->resp, H_Content_Length);
480 159
                RFC2616_Weaken_Etag(vc->resp);
481
        }
482
483 243
        if (vfe->vfp == &VFP_gzip)
484 42
                http_SetHeader(vc->resp, "Content-Encoding: gzip");
485
486 243
        if (vfe->vfp == &VFP_gzip || vfe->vfp == &VFP_testgunzip)
487 126
                RFC2616_Vary_AE(vc->resp);
488
489 243
        return (VFP_OK);
490
}
491
492
/*--------------------------------------------------------------------
493
 * VFP_GUNZIP
494
 *
495
 * A VFP for gunzip'ing an object as we receive it from the backend
496
 */
497
498
static enum vfp_status v_matchproto_(vfp_pull_f)
499 1173
vfp_gunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
500
    ssize_t *lp)
501
{
502
        ssize_t l;
503
        struct vgz *vg;
504 1173
        enum vgzret_e vr = VGZ_ERROR;
505
        const void *dp;
506
        ssize_t dl;
507 1173
        enum vfp_status vp = VFP_OK;
508
509 1173
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
510 1173
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
511 1173
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
512 1173
        AN(p);
513 1173
        AN(lp);
514 1173
        l = *lp;
515 1173
        *lp = 0;
516 1173
        VGZ_Obuf(vg, p, l);
517
        do {
518 1179
                if (VGZ_IbufEmpty(vg)) {
519 231
                        l = vg->m_sz;
520 231
                        vp = VFP_Suck(vc, vg->m_buf, &l);
521 231
                        if (vp == VFP_ERROR)
522 0
                                return (vp);
523 231
                        VGZ_Ibuf(vg, vg->m_buf, l);
524
                }
525 1179
                if (!VGZ_IbufEmpty(vg) || vp == VFP_END) {
526 1179
                        vr = VGZ_Gunzip(vg, &dp, &dl);
527 1179
                        if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
528 3
                                return(VFP_Error(vc, "Junk after gzip data"));
529 1176
                        if (vr < VGZ_OK)
530 6
                                return (VFP_Error(vc,
531
                                    "Invalid Gzip data: %s", vgz_msg(vg)));
532 1170
                        if (dl > 0) {
533 1056
                                *lp = dl;
534 1056
                                assert(dp == p);
535 1056
                                return (VFP_OK);
536
                        }
537
                }
538 114
                AN(VGZ_IbufEmpty(vg));
539 114
        } while (vp == VFP_OK);
540 108
        if (vr != VGZ_END)
541 3
                return(VFP_Error(vc, "Gunzip error at the very end"));
542 105
        return (vp);
543
}
544
545
546
/*--------------------------------------------------------------------
547
 * VFP_GZIP
548
 *
549
 * A VFP for gzip'ing an object as we receive it from the backend
550
 */
551
552
static enum vfp_status v_matchproto_(vfp_pull_f)
553 219
vfp_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
554
    ssize_t *lp)
555
{
556
        ssize_t l;
557
        struct vgz *vg;
558 219
        enum vgzret_e vr = VGZ_ERROR;
559
        const void *dp;
560
        ssize_t dl;
561 219
        enum vfp_status vp = VFP_ERROR;
562
563 219
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
564 219
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
565 219
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
566 219
        AN(p);
567 219
        AN(lp);
568 219
        l = *lp;
569 219
        *lp = 0;
570 219
        VGZ_Obuf(vg, p, l);
571
        do {
572 219
                if (VGZ_IbufEmpty(vg)) {
573 207
                        l = vg->m_sz;
574 207
                        vp = VFP_Suck(vc, vg->m_buf, &l);
575 207
                        if (vp == VFP_ERROR)
576 0
                                break;
577 207
                        if (vp == VFP_END)
578 99
                                vg->flag = VGZ_FINISH;
579 207
                        VGZ_Ibuf(vg, vg->m_buf, l);
580
                }
581 219
                if (!VGZ_IbufEmpty(vg) || vg->flag == VGZ_FINISH) {
582 219
                        vr = VGZ_Gzip(vg, &dp, &dl, vg->flag);
583 219
                        if (vr < VGZ_OK)
584 0
                                return (VFP_Error(vc, "Gzip failed"));
585 219
                        if (dl > 0) {
586 180
                                VGZ_UpdateObj(vc, vg, VUA_UPDATE);
587 180
                                *lp = dl;
588 180
                                assert(dp == p);
589 180
                                return (VFP_OK);
590
                        }
591
                }
592 39
                AN(VGZ_IbufEmpty(vg));
593 39
        } while (vg->flag != VGZ_FINISH);
594
595 39
        if (vr != VGZ_END)
596 0
                return (VFP_Error(vc, "Gzip failed"));
597 39
        VGZ_UpdateObj(vc, vg, VUA_END_GZIP);
598 39
        return (VFP_END);
599
}
600
601
/*--------------------------------------------------------------------
602
 * VFP_TESTGZIP
603
 *
604
 * A VFP for testing that received gzip data is valid, and for
605
 * collecting the magic bits while we're at it.
606
 */
607
608
static enum vfp_status v_matchproto_(vfp_pull_f)
609 108
vfp_testgunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
610
    ssize_t *lp)
611
{
612
        struct vgz *vg;
613 108
        enum vgzret_e vr = VGZ_ERROR;
614
        const void *dp;
615
        ssize_t dl;
616
        enum vfp_status vp;
617
618 108
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
619 108
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
620 108
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
621 108
        AN(p);
622 108
        AN(lp);
623 108
        vp = VFP_Suck(vc, p, lp);
624 108
        if (vp == VFP_ERROR)
625 6
                return (vp);
626 102
        if (*lp > 0 || vp == VFP_END) {
627 102
                VGZ_Ibuf(vg, p, *lp);
628
                do {
629 102
                        VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
630 102
                        vr = VGZ_Gunzip(vg, &dp, &dl);
631 102
                        if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
632 9
                                return(VFP_Error(vc, "Junk after gzip data"));
633 93
                        if (vr < VGZ_OK)
634 0
                                return (VFP_Error(vc,
635
                                    "Invalid Gzip data: %s", vgz_msg(vg)));
636 93
                } while (!VGZ_IbufEmpty(vg));
637
        }
638 93
        VGZ_UpdateObj(vc, vg, VUA_UPDATE);
639 93
        if (vp == VFP_END) {
640 69
                if (vr != VGZ_END)
641 3
                        return (VFP_Error(vc, "tGunzip failed"));
642 66
                VGZ_UpdateObj(vc, vg, VUA_END_GUNZIP);
643
        }
644 90
        return (vp);
645
}
646
647
/*--------------------------------------------------------------------*/
648
649
static void v_matchproto_(vfp_fini_f)
650 255
vfp_gzip_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
651
{
652
        struct vgz *vg;
653
654 255
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
655 255
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
656
657 255
        if (vfe->priv1 != NULL) {
658 243
                CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
659 243
                vfe->priv1 = NULL;
660 243
                (void)VGZ_Destroy(&vg);
661
        }
662 255
}
663
664
/*--------------------------------------------------------------------*/
665
666
const struct vfp VFP_gunzip = {
667
        .name = "gunzip",
668
        .init = vfp_gzip_init,
669
        .pull = vfp_gunzip_pull,
670
        .fini = vfp_gzip_fini,
671
        .priv1 = "U F -",
672
};
673
674
const struct vfp VFP_gzip = {
675
        .name = "gzip",
676
        .init = vfp_gzip_init,
677
        .pull = vfp_gzip_pull,
678
        .fini = vfp_gzip_fini,
679
        .priv1 = "G F -",
680
};
681
682
const struct vfp VFP_testgunzip = {
683
        .name = "testgunzip",
684
        .init = vfp_gzip_init,
685
        .pull = vfp_testgunzip_pull,
686
        .fini = vfp_gzip_fini,
687
        .priv1 = "u F -",
688
};