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