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