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