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
49
#include "storage/storage.h"
50
51
#include "vend.h"
52
53
#include "vgz.h"
54
55
struct vgz {
56
        unsigned                magic;
57
#define VGZ_MAGIC               0x162df0cb
58
        enum {VGZ_GZ,VGZ_UN}    dir;
59
        struct vsl_log          *vsl;
60
        const char              *id;
61
        int                     last_i;
62
        enum vgz_flag           flag;
63
64
        struct stv_buffer       *stvbuf;
65
        char                    *m_buf;
66
        ssize_t                 m_sz;
67
        ssize_t                 m_len;
68
69
        intmax_t                bits;
70
71
        z_stream                vz;
72
};
73
74
static const char *
75 240
vgz_msg(const struct vgz *vg)
76
{
77 240
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
78 240
        return (vg->vz.msg ? vg->vz.msg : "(null)");
79
}
80
81
/*--------------------------------------------------------------------
82
 * Set up a gunzip instance
83
 */
84
85
static struct vgz *
86 9240
vgz_gunzip(struct vsl_log *vsl, const char *id)
87
{
88
        struct vgz *vg;
89
90 9240
        ALLOC_OBJ(vg, VGZ_MAGIC);
91 9240
        AN(vg);
92 9240
        vg->vsl = vsl;
93 9240
        vg->id = id;
94 9240
        vg->dir = VGZ_UN;
95
96
        /*
97
         * Max memory usage according to zonf.h:
98
         *      mem_needed = "a few kb" + (1 << (windowBits))
99
         * Since we don't control windowBits, we have to assume
100
         * it is 15, so 34-35KB or so.
101
         */
102 9240
        assert(Z_OK == inflateInit2(&vg->vz, 31));
103 9240
        return (vg);
104
}
105
106
static struct vgz *
107 8040
VGZ_NewGunzip(struct vsl_log *vsl, const char *id)
108
{
109 8040
        VSC_C_main->n_gunzip++;
110 8040
        return (vgz_gunzip(vsl, id));
111
}
112
113
static struct vgz *
114 1200
VGZ_NewTestGunzip(struct vsl_log *vsl, const char *id)
115
{
116 1200
        VSC_C_main->n_test_gunzip++;
117 1200
        return (vgz_gunzip(vsl, id));
118
}
119
120
struct vgz *
121 3680
VGZ_NewGzip(struct vsl_log *vsl, const char *id)
122
{
123
        struct vgz *vg;
124
        int i;
125
126 3680
        VSC_C_main->n_gzip++;
127 3680
        ALLOC_OBJ(vg, VGZ_MAGIC);
128 3680
        AN(vg);
129 3680
        vg->vsl = vsl;
130 3680
        vg->id = id;
131 3680
        vg->dir = VGZ_GZ;
132
133
        /*
134
         * From zconf.h:
135
         *
136
         *      mem_needed = "a few kb"
137
         *              + (1 << (windowBits+2))
138
         *              +  (1 << (memLevel+9))
139
         *
140
         * windowBits [8..15] (-> 1K..128K)
141
         * memLevel [1..9] (-> 1K->256K)
142
         */
143 3680
        i = deflateInit2(&vg->vz,
144
            cache_param->gzip_level,            /* Level */
145
            Z_DEFLATED,                         /* Method */
146
            16 + 15,                            /* Window bits (16=gzip) */
147
            cache_param->gzip_memlevel,         /* memLevel */
148
            Z_DEFAULT_STRATEGY);
149 3680
        assert(Z_OK == i);
150 3680
        return (vg);
151
}
152
153
/*--------------------------------------------------------------------
154
 */
155
156
static int
157 9880
vgz_getmbuf(struct worker *wrk, struct vgz *vg)
158
{
159
        size_t sz;
160
161 9880
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
162 9880
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
163 9880
        AZ(vg->m_sz);
164 9880
        AZ(vg->m_len);
165 9880
        AZ(vg->m_buf);
166 9880
        AZ(vg->stvbuf);
167
168 9880
        vg->stvbuf = STV_AllocBuf(wrk, stv_transient, cache_param->gzip_buffer);
169 9880
        if (vg->stvbuf == NULL)
170 0
                return (-1);
171 9880
        vg->m_buf = STV_GetBufPtr(vg->stvbuf, &sz);
172 9880
        vg->m_sz = sz;
173 9880
        AN(vg->m_buf);
174 9880
        assert(vg->m_sz > 0);
175 9880
        return (0);
176 9880
}
177
178
/*--------------------------------------------------------------------*/
179
180
void
181 67048
VGZ_Ibuf(struct vgz *vg, const void *ptr, ssize_t len)
182
{
183
184 67048
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
185 67048
        AZ(vg->vz.avail_in);
186 67048
        assert(len >= 0);
187 67048
        if (len > 0)
188 45008
                AN(ptr);
189
190 67048
        vg->vz.next_in = TRUST_ME(ptr);
191 67048
        vg->vz.avail_in = len;
192 67048
}
193
194
int
195 150941
VGZ_IbufEmpty(const struct vgz *vg)
196
{
197
198 150941
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
199 150941
        return (vg->vz.avail_in == 0);
200
}
201
202
/*--------------------------------------------------------------------*/
203
204
void
205 71096
VGZ_Obuf(struct vgz *vg, void *ptr, ssize_t len)
206
{
207
208 71096
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
209 71096
        AN(ptr);
210 71096
        assert(len > 0);
211
212 71096
        vg->vz.next_out = ptr;
213 71096
        vg->vz.avail_out = len;
214 71096
}
215
216
int
217 42837
VGZ_ObufFull(const struct vgz *vg)
218
{
219
220 42837
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
221 42837
        return (vg->vz.avail_out == 0);
222
}
223
224
/*--------------------------------------------------------------------*/
225
226
static enum vgzret_e
227 32739
VGZ_Gunzip(struct vgz *vg, const void **pptr, ssize_t *plen)
228
{
229
        int i;
230
        ssize_t l;
231
        const uint8_t *before;
232
233 32739
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
234
235 32739
        *pptr = NULL;
236 32739
        *plen = 0;
237 32739
        AN(vg->vz.next_out);
238 32739
        AN(vg->vz.avail_out);
239 32739
        before = vg->vz.next_out;
240 32739
        i = inflate(&vg->vz, 0);
241 32739
        if (i == Z_OK || i == Z_STREAM_END) {
242 32579
                *pptr = before;
243 32579
                l = (const uint8_t *)vg->vz.next_out - before;
244 32579
                *plen = l;
245 32579
        }
246 32739
        vg->last_i = i;
247 32739
        if (i == Z_OK)
248 22659
                return (VGZ_OK);
249 10080
        if (i == Z_STREAM_END)
250 9920
                return (VGZ_END);
251 160
        if (i == Z_BUF_ERROR)
252 80
                return (VGZ_STUCK);
253 80
        VSLb(vg->vsl, SLT_Gzip, "Gunzip error: %d (%s)", i, vgz_msg(vg));
254 80
        return (VGZ_ERROR);
255 32739
}
256
257
/*--------------------------------------------------------------------*/
258
259
enum vgzret_e
260 45517
VGZ_Gzip(struct vgz *vg, const void **pptr, ssize_t *plen, enum vgz_flag flags)
261
{
262
        int i;
263
        int zflg;
264
        ssize_t l;
265
        const uint8_t *before;
266
267 45517
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
268
269 45517
        *pptr = NULL;
270 45517
        *plen = 0;
271 45517
        AN(vg->vz.next_out);
272 45517
        AN(vg->vz.avail_out);
273 45517
        before = vg->vz.next_out;
274 45517
        switch (flags) {
275 29997
        case VGZ_NORMAL:        zflg = Z_NO_FLUSH; break;
276 5000
        case VGZ_ALIGN:         zflg = Z_SYNC_FLUSH; break;
277 5040
        case VGZ_RESET:         zflg = Z_FULL_FLUSH; break;
278 5480
        case VGZ_FINISH:        zflg = Z_FINISH; break;
279 0
        default:                WRONG("Invalid VGZ flag");
280 0
        }
281 45517
        i = deflate(&vg->vz, zflg);
282 45517
        if (i == Z_OK || i == Z_STREAM_END) {
283 45517
                *pptr = before;
284 45517
                l = (const uint8_t *)vg->vz.next_out - before;
285 45517
                *plen = l;
286 45517
        }
287 45517
        vg->last_i = i;
288 45517
        if (i == Z_OK)
289 42037
                return (VGZ_OK);
290 3480
        if (i == Z_STREAM_END)
291 3480
                return (VGZ_END);
292 0
        if (i == Z_BUF_ERROR)
293 0
                return (VGZ_STUCK);
294 0
        VSLb(vg->vsl, SLT_Gzip, "Gzip error: %d (%s)", i, vgz_msg(vg));
295 0
        return (VGZ_ERROR);
296 45517
}
297
298
/*--------------------------------------------------------------------
299
 * VDP for gunzip'ing
300
 */
301
302
static int v_matchproto_(vdp_init_f)
303 3040
vdp_gunzip_init(VRT_CTX, struct vdp_ctx *vdc, void **priv)
304
{
305
        struct vgz *vg;
306
        struct boc *boc;
307
        enum boc_state_e bos;
308
        const char *p;
309
        ssize_t dl;
310
        uint64_t u;
311
312 3040
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
313 3040
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
314 3040
        CHECK_OBJ_ORNULL(vdc->oc, OBJCORE_MAGIC);
315 3040
        CHECK_OBJ_NOTNULL(vdc->hp, HTTP_MAGIC);
316 3040
        AN(vdc->clen);
317 3040
        AN(priv);
318
319 3040
        vg = VGZ_NewGunzip(vdc->vsl, "U D -");
320 3040
        AN(vg);
321 3040
        if (vgz_getmbuf(vdc->wrk, vg)) {
322 0
                (void)VGZ_Destroy(vdc->wrk, &vg);
323 0
                return (-1);
324
        }
325
326 3040
        VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
327 3040
        *priv = vg;
328
329 3040
        http_Unset(vdc->hp, H_Content_Encoding);
330
331 3040
        *vdc->clen = -1;
332
333 3040
        if (vdc->oc == NULL)
334 680
                return (0);
335
336 2360
        boc = HSH_RefBoc(vdc->oc);
337 2360
        if (boc != NULL) {
338 440
                CHECK_OBJ(boc, BOC_MAGIC);
339 440
                bos = boc->state;
340 440
                HSH_DerefBoc(vdc->wrk, vdc->oc);
341 440
                if (bos < BOS_FINISHED)
342 440
                        return (0); /* OA_GZIPBITS is not stable yet */
343 0
        }
344
345 1920
        p = ObjGetAttr(vdc->wrk, vdc->oc, OA_GZIPBITS, &dl);
346 1920
        if (p != NULL && dl == 32) {
347 1920
                u = vbe64dec(p + 24);
348 1920
                if (u != 0)
349 1880
                        *vdc->clen = u;
350 1920
        }
351 1920
        return (0);
352 3040
}
353
354
static int v_matchproto_(vdp_fini_f)
355 3040
vdp_gunzip_fini(struct vdp_ctx *vdc, void **priv)
356
{
357
        struct vgz *vg;
358
359 3040
        (void)vdc;
360 3040
        TAKE_OBJ_NOTNULL(vg, priv, VGZ_MAGIC);
361 3040
        AN(vg->m_buf);
362 3040
        (void)VGZ_Destroy(vdc->wrk, &vg);
363 3040
        return (0);
364
}
365
366
static int v_matchproto_(vdp_bytes_f)
367 17400
vdp_gunzip_bytes(struct vdp_ctx *vdc, enum vdp_action act, void **priv,
368
    const void *ptr, ssize_t len)
369
{
370
        enum vgzret_e vr;
371
        ssize_t dl;
372
        const void *dp;
373
        struct worker *wrk;
374
        struct vgz *vg;
375
376 17400
        CHECK_OBJ_NOTNULL(vdc, VDP_CTX_MAGIC);
377 17400
        wrk = vdc->wrk;
378 17400
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
379 17400
        (void)act;
380
381 17400
        CAST_OBJ_NOTNULL(vg, *priv, VGZ_MAGIC);
382 17400
        AN(vg->m_buf);
383
384 17400
        if (len == 0)
385 4680
                return (0);
386
387 12720
        VGZ_Ibuf(vg, ptr, len);
388 12720
        do {
389 12720
                vr = VGZ_Gunzip(vg, &dp, &dl);
390 12720
                if (vr == VGZ_END && !VGZ_IbufEmpty(vg)) {
391 80
                        VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
392 40
                             vr, "junk after VGZ_END");
393 40
                        return (-1);
394
                }
395 12680
                vg->m_len += dl;
396 12680
                if (vr < VGZ_OK)
397 0
                        return (-1);
398 12680
                if (vg->m_len == vg->m_sz || vr != VGZ_OK) {
399 5840
                        if (VDP_bytes(vdc, vr == VGZ_END ? VDP_END : VDP_FLUSH,
400 2920
                            vg->m_buf, vg->m_len))
401 160
                                return (vdc->retval);
402 2760
                        vg->m_len = 0;
403 2760
                        VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
404 2760
                }
405 12520
        } while (!VGZ_IbufEmpty(vg));
406 12520
        assert(vr == VGZ_STUCK || vr == VGZ_OK || vr == VGZ_END);
407 12520
        return (0);
408 17400
}
409
410
const struct vdp VDP_gunzip = {
411
        .name =         "gunzip",
412
        .init =         vdp_gunzip_init,
413
        .bytes =        vdp_gunzip_bytes,
414
        .fini =         vdp_gunzip_fini,
415
};
416
417
/*--------------------------------------------------------------------*/
418
419
void
420 50208
VGZ_UpdateObj(const struct vfp_ctx *vc, struct vgz *vg, enum vgzret_e e)
421
{
422
        char *p;
423
        intmax_t ii;
424
425 50208
        CHECK_OBJ_NOTNULL(vg, VGZ_MAGIC);
426 50208
        if (e < VGZ_OK)
427 0
                return;
428 50208
        ii = vg->vz.start_bit + vg->vz.last_bit + vg->vz.stop_bit;
429 50208
        if (e != VGZ_END && ii == vg->bits)
430 36917
                return;
431 13291
        vg->bits = ii;
432 13291
        p = ObjSetAttr(vc->wrk, vc->oc, OA_GZIPBITS, 32, NULL);
433 13291
        AN(p);
434 13291
        vbe64enc(p, vg->vz.start_bit);
435 13291
        vbe64enc(p + 8, vg->vz.last_bit);
436 13291
        vbe64enc(p + 16, vg->vz.stop_bit);
437 13291
        if (e != VGZ_END)
438 5091
                return;
439 8200
        if (vg->dir == VGZ_GZ)
440 6960
                vbe64enc(p + 24, vg->vz.total_in);
441 8200
        if (vg->dir == VGZ_UN)
442 1240
                vbe64enc(p + 24, vg->vz.total_out);
443 50208
}
444
445
/*--------------------------------------------------------------------
446
 */
447
448
enum vgzret_e
449 12920
VGZ_Destroy(struct worker *wrk, struct vgz **vgp)
450
{
451
        struct vgz *vg;
452
        enum vgzret_e vr;
453
        int i;
454
455 12920
        CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC);
456 12920
        TAKE_OBJ_NOTNULL(vg, vgp, VGZ_MAGIC);
457 12920
        AN(vg->id);
458 25840
        VSLb(vg->vsl, SLT_Gzip, "%s %jd %jd %jd %jd %jd",
459 12920
            vg->id,
460 12920
            (intmax_t)vg->vz.total_in,
461 12920
            (intmax_t)vg->vz.total_out,
462 12920
            (intmax_t)vg->vz.start_bit,
463 12920
            (intmax_t)vg->vz.last_bit,
464 12920
            (intmax_t)vg->vz.stop_bit);
465 12920
        if (vg->dir == VGZ_GZ)
466 3680
                i = deflateEnd(&vg->vz);
467
        else
468 9240
                i = inflateEnd(&vg->vz);
469 12920
        if (vg->last_i == Z_STREAM_END && i == Z_OK)
470 10320
                i = Z_STREAM_END;
471 12920
        if (vg->m_buf != NULL) {
472 9880
                AN(vg->stvbuf);
473 9880
                STV_FreeBuf(wrk, &vg->stvbuf);
474 9880
        }
475 12920
        AZ(vg->stvbuf);
476 12920
        if (i == Z_OK)
477 2520
                vr = VGZ_OK;
478 10400
        else if (i == Z_STREAM_END)
479 10320
                vr = VGZ_END;
480 80
        else if (i == Z_BUF_ERROR)
481 0
                vr = VGZ_STUCK;
482
        else {
483 160
                VSLb(vg->vsl, SLT_Gzip, "G(un)zip error: %d (%s)",
484 80
                    i, vgz_msg(vg));
485 80
                vr = VGZ_ERROR;
486
        }
487 12920
        FREE_OBJ(vg);
488 12920
        return (vr);
489
}
490
491
/*--------------------------------------------------------------------*/
492
493
static enum vfp_status v_matchproto_(vfp_init_f)
494 7000
vfp_gzip_init(VRT_CTX, struct vfp_ctx *vc, struct vfp_entry *vfe)
495
{
496
        struct vgz *vg;
497
498 7000
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
499 7000
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
500 7000
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
501
502
        /*
503
         * G(un)zip makes no sence on partial responses, but since
504
         * it is an pure 1:1 transform, we can just ignore it.
505
         */
506 7000
        if (http_GetStatus(vc->resp) == 206)
507 160
                return (VFP_NULL);
508
509 6840
        if (vfe->vfp == &VFP_gzip) {
510 640
                if (http_GetHdr(vc->resp, H_Content_Encoding, NULL))
511 0
                        return (VFP_NULL);
512 640
                vg = VGZ_NewGzip(vc->wrk->vsl, vfe->vfp->priv1);
513 640
                vc->obj_flags |= OF_GZIPED | OF_CHGCE;
514 640
        } else {
515 6200
                if (!http_HdrIs(vc->resp, H_Content_Encoding, "gzip"))
516 0
                        return (VFP_NULL);
517 6200
                if (vfe->vfp == &VFP_gunzip) {
518 5000
                        vg = VGZ_NewGunzip(vc->wrk->vsl, vfe->vfp->priv1);
519 5000
                        vc->obj_flags &= ~OF_GZIPED;
520 5000
                        vc->obj_flags |= OF_CHGCE;
521 5000
                } else {
522 1200
                        vg = VGZ_NewTestGunzip(vc->wrk->vsl, vfe->vfp->priv1);
523 1200
                        vc->obj_flags |= OF_GZIPED;
524
                }
525
        }
526 6840
        AN(vg);
527 6840
        vfe->priv1 = vg;
528 6840
        if (vgz_getmbuf(vc->wrk, vg))
529 0
                return (VFP_ERROR);
530 6840
        VGZ_Ibuf(vg, vg->m_buf, 0);
531 6840
        AZ(vg->m_len);
532
533 6840
        if (vfe->vfp == &VFP_gunzip || vfe->vfp == &VFP_gzip) {
534 5640
                http_Unset(vc->resp, H_Content_Encoding);
535 5640
                http_Unset(vc->resp, H_Content_Length);
536 5640
                RFC2616_Weaken_Etag(vc->resp);
537 5640
        }
538
539 6840
        if (vfe->vfp == &VFP_gzip)
540 640
                http_SetHeader(vc->resp, "Content-Encoding: gzip");
541
542 6840
        if (vfe->vfp == &VFP_gzip || vfe->vfp == &VFP_testgunzip)
543 1840
                RFC2616_Vary_AE(vc->resp);
544
545 6840
        return (VFP_OK);
546 7000
}
547
548
/*--------------------------------------------------------------------
549
 * VFP_GUNZIP
550
 *
551
 * A VFP for gunzip'ing an object as we receive it from the backend
552
 */
553
554
static enum vfp_status v_matchproto_(vfp_pull_f)
555 18448
vfp_gunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
556
    ssize_t *lp)
557
{
558
        ssize_t l;
559
        struct vgz *vg;
560 18448
        enum vgzret_e vr = VGZ_ERROR;
561
        const void *dp;
562
        ssize_t dl;
563 18448
        enum vfp_status vp = VFP_OK;
564
565 18448
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
566 18448
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
567 18448
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
568 18448
        AN(p);
569 18448
        AN(lp);
570 18448
        l = *lp;
571 18448
        *lp = 0;
572 18448
        VGZ_Obuf(vg, p, l);
573 18448
        do {
574 18528
                if (VGZ_IbufEmpty(vg)) {
575 5800
                        l = vg->m_sz;
576 5800
                        vp = VFP_Suck(vc, vg->m_buf, &l);
577 5800
                        if (vp == VFP_ERROR)
578 0
                                return (vp);
579 5800
                        VGZ_Ibuf(vg, vg->m_buf, l);
580 5800
                }
581 18528
                if (!VGZ_IbufEmpty(vg) || vp == VFP_END) {
582 18528
                        vr = VGZ_Gunzip(vg, &dp, &dl);
583 18528
                        if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
584 40
                                return (VFP_Error(vc, "Junk after gzip data"));
585 18488
                        if (vr < VGZ_OK)
586 160
                                return (VFP_Error(vc,
587 80
                                    "Invalid Gzip data: %s", vgz_msg(vg)));
588 18408
                        if (dl > 0) {
589 15528
                                *lp = dl;
590 15528
                                assert(dp == p);
591 15528
                                return (VFP_OK);
592
                        }
593 2880
                }
594 2880
                AN(VGZ_IbufEmpty(vg));
595 2880
        } while (vp == VFP_OK);
596 2800
        if (vr != VGZ_END)
597 40
                return (VFP_Error(vc, "Gunzip error at the very end"));
598 2760
        return (vp);
599 18448
}
600
601
602
/*--------------------------------------------------------------------
603
 * VFP_GZIP
604
 *
605
 * A VFP for gzip'ing an object as we receive it from the backend
606
 */
607
608
static enum vfp_status v_matchproto_(vfp_pull_f)
609 2520
vfp_gzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
610
    ssize_t *lp)
611
{
612
        ssize_t l;
613
        struct vgz *vg;
614 2520
        enum vgzret_e vr = VGZ_ERROR;
615
        const void *dp;
616
        ssize_t dl;
617 2520
        enum vfp_status vp = VFP_ERROR;
618
619 2520
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
620 2520
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
621 2520
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
622 2520
        AN(p);
623 2520
        AN(lp);
624 2520
        l = *lp;
625 2520
        *lp = 0;
626 2520
        VGZ_Obuf(vg, p, l);
627 2520
        do {
628 2680
                if (VGZ_IbufEmpty(vg)) {
629 2520
                        l = vg->m_sz;
630 2520
                        vp = VFP_Suck(vc, vg->m_buf, &l);
631 2520
                        if (vp == VFP_ERROR)
632 0
                                break;
633 2520
                        if (vp == VFP_END)
634 880
                                vg->flag = VGZ_FINISH;
635 2520
                        VGZ_Ibuf(vg, vg->m_buf, l);
636 2520
                }
637 2680
                if (!VGZ_IbufEmpty(vg) || vg->flag == VGZ_FINISH) {
638 2680
                        vr = VGZ_Gzip(vg, &dp, &dl, vg->flag);
639 2680
                        if (vr < VGZ_OK)
640 0
                                return (VFP_Error(vc, "Gzip failed"));
641 2680
                        if (dl > 0) {
642 2520
                                VGZ_UpdateObj(vc, vg, vr);
643 2520
                                *lp = dl;
644 2520
                                assert(dp == p);
645 2520
                                if (vr != VGZ_END || !VGZ_IbufEmpty(vg))
646 1920
                                        return (VFP_OK);
647 600
                        }
648 760
                }
649 760
                AN(VGZ_IbufEmpty(vg));
650 760
        } while (vg->flag != VGZ_FINISH);
651
652 600
        if (vr != VGZ_END)
653 0
                return (VFP_Error(vc, "Gzip failed"));
654 600
        VGZ_UpdateObj(vc, vg, VGZ_END);
655 600
        return (VFP_END);
656 2520
}
657
658
/*--------------------------------------------------------------------
659
 * VFP_TESTGZIP
660
 *
661
 * A VFP for testing that received gzip data is valid, and for
662
 * collecting the magic bits while we're at it.
663
 */
664
665
static enum vfp_status v_matchproto_(vfp_pull_f)
666 1571
vfp_testgunzip_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p,
667
    ssize_t *lp)
668
{
669
        struct vgz *vg;
670 1571
        enum vgzret_e vr = VGZ_ERROR;
671
        const void *dp;
672
        ssize_t dl;
673
        enum vfp_status vp;
674
675 1571
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
676 1571
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
677 1571
        CAST_OBJ_NOTNULL(vg, vfe->priv1, VGZ_MAGIC);
678 1571
        AN(p);
679 1571
        AN(lp);
680 1571
        vp = VFP_Suck(vc, p, lp);
681 1571
        if (vp == VFP_ERROR)
682 80
                return (vp);
683 1491
        if (*lp > 0 || vp == VFP_END) {
684 1491
                VGZ_Ibuf(vg, p, *lp);
685 1491
                do {
686 1491
                        VGZ_Obuf(vg, vg->m_buf, vg->m_sz);
687 1491
                        vr = VGZ_Gunzip(vg, &dp, &dl);
688 1491
                        if (vr == VGZ_END && !VGZ_IbufEmpty(vg))
689 120
                                return (VFP_Error(vc, "Junk after gzip data"));
690 1371
                        if (vr < VGZ_OK)
691 0
                                return (VFP_Error(vc,
692 0
                                    "Invalid Gzip data: %s", vgz_msg(vg)));
693 1371
                } while (!VGZ_IbufEmpty(vg));
694 1371
        }
695 1371
        VGZ_UpdateObj(vc, vg, vr);
696 1371
        if (vp == VFP_END && vr != VGZ_END)
697 40
                return (VFP_Error(vc, "tGunzip failed"));
698 1331
        return (vp);
699 1571
}
700
701
/*--------------------------------------------------------------------*/
702
703
static void v_matchproto_(vfp_fini_f)
704 7440
vfp_gzip_fini(struct vfp_ctx *vc, struct vfp_entry *vfe)
705
{
706
        struct vgz *vg;
707
708 7440
        CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC);
709 7440
        CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC);
710
711 7440
        if (vfe->priv1 != NULL) {
712 6840
                TAKE_OBJ_NOTNULL(vg, &vfe->priv1, VGZ_MAGIC);
713 6840
                (void)VGZ_Destroy(vc->wrk, &vg);
714 6840
        }
715 7440
}
716
717
/*--------------------------------------------------------------------*/
718
719
const struct vfp VFP_gunzip = {
720
        .name = "gunzip",
721
        .init = vfp_gzip_init,
722
        .pull = vfp_gunzip_pull,
723
        .fini = vfp_gzip_fini,
724
        .priv1 = "U F -",
725
};
726
727
const struct vfp VFP_gzip = {
728
        .name = "gzip",
729
        .init = vfp_gzip_init,
730
        .pull = vfp_gzip_pull,
731
        .fini = vfp_gzip_fini,
732
        .priv1 = "G F -",
733
};
734
735
const struct vfp VFP_testgunzip = {
736
        .name = "testgunzip",
737
        .init = vfp_gzip_init,
738
        .pull = vfp_testgunzip_pull,
739
        .fini = vfp_gzip_fini,
740
        .priv1 = "u F -",
741
};