Coverage Report

Created: 2026-03-30 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/wsutil/file_compressed.c
Line
Count
Source
1
/* file_compressed.c
2
 * Code for writing compressed files.
3
 *
4
 * Wireshark - Network traffic analyzer
5
 * By Gerald Combs <gerald@wireshark.org>
6
 * Copyright 1998 Gerald Combs
7
 *
8
 * Derived from code in the Wiretap Library
9
 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
10
 *
11
 * SPDX-License-Identifier: GPL-2.0-or-later
12
 */
13
14
#include <config.h>
15
16
#include <wireshark.h>
17
18
#include <errno.h>
19
20
#include <wsutil/file_util.h>
21
#include <wsutil/zlib_compat.h>
22
23
#ifdef HAVE_LZ4FRAME_H
24
#include <lz4frame.h>
25
#endif /* HAVE_LZ4FRAME_H */
26
27
#include "file_compressed.h"
28
29
/*
30
 * List of compression types supported.
31
 * This includes compression types that can only be read, not written.
32
 */
33
static const struct compression_type {
34
    ws_compression_type  type;
35
    const char          *extension;
36
    const char          *description;
37
    const char          *name;
38
    const bool           can_write_compressed;
39
} compression_types[] = {
40
#ifdef USE_ZLIB_OR_ZLIBNG
41
    { WS_FILE_GZIP_COMPRESSED, "gz", "gzip compressed", "gzip", true },
42
#endif /* USE_ZLIB_OR_ZLIBNG */
43
#ifdef HAVE_ZSTD
44
    { WS_FILE_ZSTD_COMPRESSED, "zst", "zstd compressed", "zstd", false },
45
#endif /* HAVE_ZSTD */
46
#ifdef HAVE_LZ4FRAME_H
47
    { WS_FILE_LZ4_COMPRESSED, "lz4", "lz4 compressed", "lz4", true },
48
#endif /* HAVE_LZ4FRAME_H */
49
    { WS_FILE_UNCOMPRESSED, NULL, NULL, "none", true },
50
    { WS_FILE_UNKNOWN_COMPRESSION, NULL, NULL, NULL, false },
51
};
52
53
ws_compression_type
54
ws_name_to_compression_type(const char *name)
55
0
{
56
0
    for (const struct compression_type *p = compression_types;
57
0
   p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) {
58
0
        if (!g_strcmp0(name, p->name))
59
0
            return p->type;
60
0
    }
61
0
    return WS_FILE_UNKNOWN_COMPRESSION;
62
0
}
63
64
ws_compression_type
65
ws_extension_to_compression_type(const char *ext)
66
0
{
67
0
    for (const struct compression_type *p = compression_types;
68
0
         p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) {
69
0
        if (g_strcmp0(ext, p->extension) == 0)
70
0
            return p->type;
71
0
    }
72
0
    return WS_FILE_UNKNOWN_COMPRESSION;
73
0
}
74
75
bool
76
ws_can_write_compression_type(ws_compression_type compression_type)
77
0
{
78
0
    for (const struct compression_type *p = compression_types;
79
0
         p->type != WS_FILE_UNKNOWN_COMPRESSION; p++) {
80
0
        if (compression_type == p->type)
81
0
            return p->can_write_compressed;
82
0
    }
83
84
0
    return false;
85
0
}
86
87
const char *
88
ws_compression_type_description(ws_compression_type compression_type)
89
0
{
90
0
    for (const struct compression_type *p = compression_types;
91
0
         p->type != WS_FILE_UNCOMPRESSED; p++) {
92
0
        if (p->type == compression_type)
93
0
            return p->description;
94
0
    }
95
0
    return NULL;
96
0
}
97
98
const char *
99
ws_compression_type_extension(ws_compression_type compression_type)
100
0
{
101
0
    for (const struct compression_type *p = compression_types;
102
0
         p->type != WS_FILE_UNCOMPRESSED; p++) {
103
0
        if (p->type == compression_type)
104
0
            return p->extension;
105
0
    }
106
0
    return NULL;
107
0
}
108
109
const char *
110
ws_compression_type_name(ws_compression_type compression_type)
111
0
{
112
0
    for (const struct compression_type *p = compression_types;
113
0
         p->type != WS_FILE_UNCOMPRESSED; p++) {
114
0
        if (p->type == compression_type)
115
0
            return p->name;
116
0
    }
117
0
    return NULL;
118
0
}
119
120
GSList *
121
ws_get_all_compression_type_extensions_list(void)
122
0
{
123
0
    GSList *extensions;
124
125
0
    extensions = NULL; /* empty list, to start with */
126
127
0
    for (const struct compression_type *p = compression_types;
128
0
         p->type != WS_FILE_UNCOMPRESSED; p++)
129
0
        extensions = g_slist_prepend(extensions, (void *)p->extension);
130
131
0
    return extensions;
132
0
}
133
134
GSList *
135
ws_get_all_output_compression_type_names_list(void)
136
0
{
137
0
    GSList *names;
138
139
0
    names = NULL; /* empty list, to start with */
140
141
0
    for (const struct compression_type *p = compression_types;
142
0
         p->type != WS_FILE_UNCOMPRESSED; p++) {
143
0
        if (p->can_write_compressed)
144
0
            names = g_slist_prepend(names, (void *)p->name);
145
0
    }
146
147
0
    return names;
148
0
}
149
150
typedef void* WFILE_T;
151
152
struct ws_cwstream {
153
    WFILE_T fh;
154
    char* io_buffer;
155
    ws_compression_type ctype;
156
};
157
158
static WFILE_T
159
writecap_file_open(ws_cwstream* pfile, const char *filename)
160
0
{
161
0
    WFILE_T fh;
162
0
    switch (pfile->ctype) {
163
0
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
164
0
        case WS_FILE_GZIP_COMPRESSED:
165
0
            return gzwfile_open(filename);
166
0
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
167
#ifdef HAVE_LZ4FRAME_H
168
        case WS_FILE_LZ4_COMPRESSED:
169
            return lz4wfile_open(filename);
170
#endif /* HAVE_LZ4FRAME_H */
171
0
        default:
172
0
            fh = ws_fopen(filename, "wb");
173
            /* Increase the size of the IO buffer if uncompressed.
174
             * Compression has its own buffer that reduces writes.
175
             */
176
0
            if (fh != NULL) {
177
0
                size_t buffsize = IO_BUF_SIZE;
178
0
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
179
0
                ws_statb64 statb;
180
181
0
                if (ws_stat64(filename, &statb) == 0) {
182
0
                    if (statb.st_blksize > IO_BUF_SIZE) {
183
0
                        buffsize = (size_t)statb.st_blksize;
184
0
                    }
185
0
                }
186
0
#endif
187
0
                pfile->io_buffer = (char *)g_malloc(buffsize);
188
0
                setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize);
189
                //ws_debug("buffsize %zu", buffsize);
190
0
            }
191
0
            return fh;
192
0
    }
193
0
}
194
195
static WFILE_T
196
writecap_file_fdopen(ws_cwstream* pfile, int fd)
197
0
{
198
0
    WFILE_T fh;
199
0
    switch (pfile->ctype) {
200
0
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
201
0
        case WS_FILE_GZIP_COMPRESSED:
202
0
            return gzwfile_fdopen(fd);
203
0
#endif /* defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG) */
204
#ifdef HAVE_LZ4FRAME_H
205
        case WS_FILE_LZ4_COMPRESSED:
206
            return lz4wfile_fdopen(fd);
207
#endif /* HAVE_LZ4FRAME_H */
208
0
        default:
209
0
            fh = ws_fdopen(fd, "wb");
210
            /* Increase the size of the IO buffer if uncompressed.
211
             * Compression has its own buffer that reduces writes.
212
             */
213
0
            if (fh != NULL) {
214
0
                size_t buffsize = IO_BUF_SIZE;
215
0
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
216
0
                ws_statb64 statb;
217
218
0
                if (ws_fstat64(fd, &statb) == 0) {
219
0
                    if (statb.st_blksize > IO_BUF_SIZE) {
220
0
                        buffsize = (size_t)statb.st_blksize;
221
0
                    }
222
0
                }
223
0
#endif
224
0
                pfile->io_buffer = (char *)g_malloc(buffsize);
225
0
                setvbuf(fh, pfile->io_buffer, _IOFBF, buffsize);
226
                //ws_debug("buffsize %zu", buffsize);
227
0
            }
228
0
            return fh;
229
0
    }
230
0
}
231
232
ws_cwstream*
233
ws_cwstream_open(const char *filename, ws_compression_type ctype, int *err)
234
0
{
235
0
    ws_cwstream* pfile;
236
0
    *err = 0;
237
238
0
    pfile = g_new0(struct ws_cwstream, 1);
239
0
    if (pfile == NULL) {
240
0
        *err = errno;
241
0
        return NULL;
242
0
    }
243
0
    pfile->ctype = ctype;
244
    /* XXX - Callers call g_strerror on failure, so should "standard" errors be used? */
245
0
    errno = FILE_ERR_CANT_OPEN;
246
0
    void* fh = writecap_file_open(pfile, filename);
247
0
    if (fh == NULL) {
248
0
        *err = errno;
249
0
  g_free(pfile);
250
0
  return NULL;
251
0
    }
252
253
0
    pfile->fh = fh;
254
0
    return pfile;
255
0
}
256
257
ws_cwstream*
258
ws_cwstream_fdopen(int fd, ws_compression_type ctype, int *err)
259
0
{
260
0
    ws_cwstream* pfile;
261
0
    *err = 0;
262
263
0
    pfile = g_new0(struct ws_cwstream, 1);
264
0
    if (pfile == NULL) {
265
0
        *err = errno;
266
0
        return NULL;
267
0
    }
268
0
    pfile->ctype = ctype;
269
    /* XXX - Callers call g_strerror on failure, so should "standard" errors be used? */
270
0
    errno = FILE_ERR_CANT_OPEN;
271
0
    WFILE_T fh = writecap_file_fdopen(pfile, fd);
272
0
    if (fh == NULL) {
273
0
        *err = errno;
274
0
        g_free(pfile);
275
0
        return NULL;
276
0
    }
277
278
0
    pfile->fh = fh;
279
0
    return pfile;
280
0
}
281
282
ws_cwstream*
283
ws_cwstream_open_stdout(ws_compression_type ctype, int *err)
284
0
{
285
0
    int new_fd;
286
0
    ws_cwstream* pfile;
287
288
0
    new_fd = ws_dup(1);
289
0
    if (new_fd == -1) {
290
0
        *err = errno;
291
0
        return NULL;
292
0
    }
293
#ifdef _WIN32
294
    /*
295
     * Put the new descriptor into binary mode.
296
     *
297
     * XXX - even if the file format we're writing is a text
298
     * format?
299
     */
300
    if (_setmode(new_fd, O_BINARY) == -1) {
301
        /* "Should not happen" */
302
        *err = errno;
303
        ws_close(new_fd);
304
        return NULL;
305
    }
306
#endif
307
308
0
    pfile = ws_cwstream_fdopen(new_fd, ctype, err);
309
0
    if (pfile == NULL) {
310
        /* Failed; close the new fd */
311
0
        ws_close(new_fd);
312
0
        return NULL;
313
0
    }
314
0
    return pfile;
315
0
}
316
317
/* Write to file */
318
bool
319
ws_cwstream_write(ws_cwstream* pfile, const uint8_t* data, size_t data_length,
320
                  uint64_t *bytes_written, int *err)
321
0
{
322
0
    size_t nwritten;
323
324
0
    switch (pfile->ctype) {
325
0
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
326
0
        case WS_FILE_GZIP_COMPRESSED:
327
0
            nwritten = gzwfile_write(pfile->fh, data, (unsigned)data_length);
328
            /*
329
             * gzwfile_write() returns 0 on error.
330
             */
331
0
            if (nwritten == 0) {
332
0
                *err = gzwfile_geterr(pfile->fh);
333
0
                return false;
334
0
            }
335
0
            break;
336
0
#endif
337
#ifdef HAVE_LZ4FRAME_H
338
        case WS_FILE_LZ4_COMPRESSED:
339
            nwritten = lz4wfile_write(pfile->fh, data, data_length);
340
            /*
341
             * lz4wfile_write() returns 0 on error.
342
             */
343
            if (nwritten == 0) {
344
                *err = lz4wfile_geterr(pfile->fh);
345
                return false;
346
            }
347
            break;
348
#endif /* HAVE_LZ4FRAME_H */
349
0
        default:
350
0
            nwritten = fwrite(data, data_length, 1, pfile->fh);
351
0
            if (nwritten != 1) {
352
0
                if (ferror((FILE *)pfile->fh)) {
353
0
                    *err = errno;
354
0
                } else {
355
0
                    *err = FILE_ERR_SHORT_WRITE;
356
0
                }
357
0
                return false;
358
0
            }
359
0
            break;
360
0
    }
361
362
0
    (*bytes_written) += data_length;
363
0
    return true;
364
0
}
365
366
bool
367
ws_cwstream_flush(ws_cwstream* pfile, int *err)
368
0
{
369
0
    switch (pfile->ctype) {
370
0
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
371
0
        case WS_FILE_GZIP_COMPRESSED:
372
0
            if (gzwfile_flush((GZWFILE_T)pfile->fh) == -1) {
373
0
                if (err) {
374
0
                    *err = gzwfile_geterr((GZWFILE_T)pfile->fh);
375
0
                }
376
0
                return false;
377
0
            }
378
0
            break;
379
0
#endif
380
#ifdef HAVE_LZ4FRAME_H
381
        case WS_FILE_LZ4_COMPRESSED:
382
            if (lz4wfile_flush((LZ4WFILE_T)pfile->fh) == -1) {
383
                if (err) {
384
                    *err = lz4wfile_geterr((LZ4WFILE_T)pfile->fh);
385
                }
386
                return false;
387
            }
388
            break;
389
#endif /* HAVE_LZ4FRAME_H */
390
0
        default:
391
0
            if (fflush((FILE*)pfile->fh) == EOF) {
392
0
                if (err) {
393
0
                    *err = errno;
394
0
                }
395
0
                return false;
396
0
            }
397
0
    }
398
0
    return true;
399
0
}
400
401
bool
402
ws_cwstream_close(ws_cwstream* pfile, int *errp)
403
0
{
404
0
    int err = 0;
405
406
0
    errno = FILE_ERR_CANT_CLOSE;
407
0
    switch (pfile->ctype) {
408
0
#if defined (HAVE_ZLIB) || defined (HAVE_ZLIBNG)
409
0
        case WS_FILE_GZIP_COMPRESSED:
410
0
            err = gzwfile_close(pfile->fh);
411
0
            break;
412
0
#endif
413
#ifdef HAVE_LZ4FRAME_H
414
        case WS_FILE_LZ4_COMPRESSED:
415
            err = lz4wfile_close(pfile->fh);
416
            break;
417
#endif /* HAVE_LZ4FRAME_H */
418
0
        default:
419
0
            if (fclose(pfile->fh) == EOF) {
420
0
                err = errno;
421
0
            }
422
0
    }
423
424
0
    g_free(pfile->io_buffer);
425
0
    g_free(pfile);
426
0
    if (errp) {
427
0
        *errp = err;
428
0
    }
429
0
    return err == 0;
430
0
}
431
432
#ifdef USE_ZLIB_OR_ZLIBNG
433
434
0
#define GZBUFSIZE 4096
435
436
/* internal gzip file state data structure for writing */
437
struct gzip_writer {
438
    int fd;                 /* file descriptor */
439
    int64_t pos;            /* current position in uncompressed data */
440
    unsigned size;          /* buffer size, zero if not allocated yet */
441
    unsigned want;          /* requested buffer size, default is GZBUFSIZE */
442
    unsigned char *in;      /* input buffer */
443
    unsigned char *out;     /* output buffer (double-sized when reading) */
444
    unsigned char *next;    /* next output data to deliver or write */
445
    int level;              /* compression level */
446
    int strategy;           /* compression strategy */
447
    int err;                /* error code */
448
    const char *err_info;   /* additional error information string for some errors */
449
    /* zlib deflate stream */
450
    zlib_stream strm;          /* stream structure in-place (not a pointer) */
451
};
452
453
GZWFILE_T
454
gzwfile_open(const char *path)
455
0
{
456
0
    int fd;
457
0
    GZWFILE_T state;
458
0
    int save_errno;
459
460
0
    fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
461
0
    if (fd == -1)
462
0
        return NULL;
463
0
    state = gzwfile_fdopen(fd);
464
0
    if (state == NULL) {
465
0
        save_errno = errno;
466
0
        ws_close(fd);
467
0
        errno = save_errno;
468
0
    }
469
0
    return state;
470
0
}
471
472
GZWFILE_T
473
gzwfile_fdopen(int fd)
474
0
{
475
0
    GZWFILE_T state;
476
477
    /* allocate zlib_writer structure to return */
478
0
    state = (GZWFILE_T)g_try_malloc(sizeof *state);
479
0
    if (state == NULL)
480
0
        return NULL;
481
0
    state->fd = fd;
482
0
    state->size = 0;            /* no buffers allocated yet */
483
0
    state->want = GZBUFSIZE;    /* requested buffer size */
484
485
0
    state->level = Z_DEFAULT_COMPRESSION;
486
0
    state->strategy = Z_DEFAULT_STRATEGY;
487
488
    /* initialize stream */
489
0
    state->err = Z_OK;              /* clear error */
490
0
    state->err_info = NULL;         /* clear additional error information */
491
0
    state->pos = 0;                 /* no uncompressed data yet */
492
0
    state->strm.avail_in = 0;       /* no input data yet */
493
494
    /* return stream */
495
0
    return state;
496
0
}
497
498
/* Initialize state for writing a gzip file.  Mark initialization by setting
499
   state->size to non-zero.  Return -1, and set state->err and possibly
500
   state->err_info, on failure; return 0 on success. */
501
static int
502
gz_init(GZWFILE_T state)
503
0
{
504
0
    int ret;
505
0
    zlib_streamp strm = &(state->strm);
506
507
    /* allocate input and output buffers */
508
0
    state->in = (unsigned char *)g_try_malloc(state->want);
509
0
    state->out = (unsigned char *)g_try_malloc(state->want);
510
0
    if (state->in == NULL || state->out == NULL) {
511
0
        g_free(state->out);
512
0
        g_free(state->in);
513
0
        state->err = ENOMEM;
514
0
        return -1;
515
0
    }
516
517
    /* allocate deflate memory, set up for gzip compression */
518
0
    strm->zalloc = Z_NULL;
519
0
    strm->zfree = Z_NULL;
520
0
    strm->opaque = Z_NULL;
521
0
    ret = ZLIB_PREFIX(deflateInit2)(strm, state->level, Z_DEFLATED,
522
0
                       15 + 16, 8, state->strategy);
523
0
    if (ret != Z_OK) {
524
0
        g_free(state->out);
525
0
        g_free(state->in);
526
0
        if (ret == Z_MEM_ERROR) {
527
            /* This means "not enough memory". */
528
0
            state->err = ENOMEM;
529
0
        } else {
530
            /* This "shouldn't happen". */
531
0
            state->err = FILE_ERR_INTERNAL;
532
0
            state->err_info = "Unknown error from deflateInit2()";
533
0
        }
534
0
        return -1;
535
0
    }
536
537
    /* mark state as initialized */
538
0
    state->size = state->want;
539
540
    /* initialize write buffer */
541
0
    strm->avail_out = state->size;
542
0
    strm->next_out = state->out;
543
0
    state->next = strm->next_out;
544
0
    return 0;
545
0
}
546
547
/* Compress whatever is at avail_in and next_in and write to the output file.
548
   Return -1, and set state->err and possibly state->err_info, if there is
549
   an error writing to the output file; return 0 on success.
550
   flush is assumed to be a valid deflate() flush value.  If flush is Z_FINISH,
551
   then the deflate() state is reset to start a new gzip stream. */
552
static int
553
gz_comp(GZWFILE_T state, int flush)
554
0
{
555
0
    int ret;
556
0
    ssize_t got;
557
0
    ptrdiff_t have;
558
0
    zlib_streamp strm = &(state->strm);
559
    /* allocate memory if this is the first time through */
560
0
    if (state->size == 0 && gz_init(state) == -1)
561
0
        return -1;
562
563
    /* run deflate() on provided input until it produces no more output */
564
0
    ret = Z_OK;
565
0
    do {
566
        /* write out current buffer contents if full, or if flushing, but if
567
           doing Z_FINISH then don't write until we get to Z_STREAM_END */
568
0
        if (strm->avail_out == 0 || (flush != Z_NO_FLUSH &&
569
0
                                     (flush != Z_FINISH || ret == Z_STREAM_END))) {
570
0
            have = strm->next_out - state->next;
571
0
            if (have) {
572
0
                got = ws_write(state->fd, state->next, (unsigned int)have);
573
0
                if (got < 0) {
574
0
                    state->err = errno;
575
0
                    return -1;
576
0
                }
577
0
                if ((ptrdiff_t)got != have) {
578
0
                    state->err = FILE_ERR_SHORT_WRITE;
579
0
                    return -1;
580
0
                }
581
0
            }
582
0
            if (strm->avail_out == 0) {
583
0
                strm->avail_out = state->size;
584
0
                strm->next_out = state->out;
585
0
            }
586
0
            state->next = strm->next_out;
587
0
        }
588
589
        /* compress */
590
0
        have = strm->avail_out;
591
0
        ret = ZLIB_PREFIX(deflate)(strm, flush);
592
0
        if (ret == Z_STREAM_ERROR) {
593
            /* This "shouldn't happen". */
594
0
            state->err = FILE_ERR_INTERNAL;
595
0
            state->err_info = "Z_STREAM_ERROR from deflate()";
596
0
            return -1;
597
0
        }
598
0
        have -= strm->avail_out;
599
0
    } while (have);
600
601
    /* if that completed a deflate stream, allow another to start */
602
0
    if (flush == Z_FINISH)
603
0
        ZLIB_PREFIX(deflateReset)(strm);
604
605
    /* all done, no errors */
606
0
    return 0;
607
0
}
608
609
/* Write out len bytes from buf.  Return 0, and set state->err, on
610
   failure or on an attempt to write 0 bytes (in which case state->err
611
   is Z_OK); return the number of bytes written on success. */
612
unsigned
613
gzwfile_write(GZWFILE_T state, const void *buf, unsigned len)
614
0
{
615
0
    unsigned put = len;
616
0
    unsigned n;
617
0
    zlib_streamp strm;
618
619
0
    strm = &(state->strm);
620
621
    /* check that there's no error */
622
0
    if (state->err != Z_OK)
623
0
        return 0;
624
625
    /* if len is zero, avoid unnecessary operations */
626
0
    if (len == 0)
627
0
        return 0;
628
629
    /* allocate memory if this is the first time through */
630
0
    if (state->size == 0 && gz_init(state) == -1)
631
0
        return 0;
632
633
    /* for small len, copy to input buffer, otherwise compress directly */
634
0
    if (len < state->size) {
635
        /* copy to input buffer, compress when full */
636
0
        do {
637
0
            if (strm->avail_in == 0)
638
0
                strm->next_in = state->in;
639
0
            n = state->size - strm->avail_in;
640
0
            if (n > len)
641
0
                n = len;
642
0
#ifdef z_const
643
0
DIAG_OFF(cast-qual)
644
0
            memcpy((Bytef *)strm->next_in + strm->avail_in, buf, n);
645
0
DIAG_ON(cast-qual)
646
#else /* z_const */
647
            memcpy(strm->next_in + strm->avail_in, buf, n);
648
#endif /* z_const */
649
0
            strm->avail_in += n;
650
0
            state->pos += n;
651
0
            buf = (const char *)buf + n;
652
0
            len -= n;
653
0
            if (len && gz_comp(state, Z_NO_FLUSH) == -1)
654
0
                return 0;
655
0
        } while (len);
656
0
    }
657
0
    else {
658
        /* consume whatever's left in the input buffer */
659
0
        if (strm->avail_in != 0 && gz_comp(state, Z_NO_FLUSH) == -1)
660
0
            return 0;
661
662
        /* directly compress user buffer to file */
663
0
        strm->avail_in = len;
664
0
#ifdef z_const
665
0
        strm->next_in = (z_const Bytef *)buf;
666
#else /* z_const */
667
DIAG_OFF(cast-qual)
668
        strm->next_in = (Bytef *)buf;
669
DIAG_ON(cast-qual)
670
#endif /* z_const */
671
0
        state->pos += len;
672
0
        if (gz_comp(state, Z_NO_FLUSH) == -1)
673
0
            return 0;
674
0
    }
675
676
    /* input was all buffered or compressed */
677
0
    return put;
678
0
}
679
680
/* Flush out what we've written so far.  Returns -1, and sets state->err,
681
   on failure; returns 0 on success. */
682
int
683
gzwfile_flush(GZWFILE_T state)
684
0
{
685
    /* check that there's no error */
686
0
    if (state->err != Z_OK)
687
0
        return -1;
688
689
    /* compress remaining data with Z_SYNC_FLUSH */
690
0
    gz_comp(state, Z_SYNC_FLUSH);
691
0
    if (state->err != Z_OK)
692
0
        return -1;
693
0
    return 0;
694
0
}
695
696
/* Flush out all data written, and close the file.  Returns a Wiretap
697
   error on failure; returns 0 on success. */
698
int
699
gzwfile_close(GZWFILE_T state)
700
0
{
701
0
    int ret = 0;
702
703
    /* flush, free memory, and close file */
704
0
    if (gz_comp(state, Z_FINISH) == -1)
705
0
        ret = state->err;
706
0
    (void)ZLIB_PREFIX(deflateEnd)(&(state->strm));
707
0
    g_free(state->out);
708
0
    g_free(state->in);
709
0
    state->err = Z_OK;
710
0
    if (ws_close(state->fd) == -1 && ret == 0)
711
0
        ret = errno;
712
0
    g_free(state);
713
0
    return ret;
714
0
}
715
716
int
717
gzwfile_geterr(GZWFILE_T state)
718
0
{
719
0
    return state->err;
720
0
}
721
#endif /* USE_ZLIB_OR_ZLIBNG */
722
723
#ifdef HAVE_LZ4FRAME_H
724
725
#define LZ4BUFSIZE 4194304 // 4MiB, maximum block size
726
727
/* internal lz4 file state data structure for writing */
728
struct lz4_writer {
729
    int fd;                 /* file descriptor */
730
    int64_t pos;            /* current position in uncompressed data */
731
    int64_t pos_out;
732
    size_t size_out;      /* buffer size, zero if not allocated yet */
733
    size_t want;          /* requested buffer size, default is LZ4BUFSIZE */
734
    size_t want_out;      /* requested output buffer size, determined from want */
735
    unsigned char *out; /* output buffer, containing uncompressed data */
736
    int err;                /* error code */
737
    const char *err_info;   /* additional error information string for some errors */
738
    LZ4F_preferences_t lz4_prefs;
739
    LZ4F_cctx *lz4_cctx;
740
};
741
742
LZ4WFILE_T
743
lz4wfile_open(const char *path)
744
{
745
    int fd;
746
    LZ4WFILE_T state;
747
    int save_errno;
748
749
    fd = ws_open(path, O_BINARY|O_WRONLY|O_CREAT|O_TRUNC, 0666);
750
    if (fd == -1)
751
        return NULL;
752
    state = lz4wfile_fdopen(fd);
753
    if (state == NULL) {
754
        save_errno = errno;
755
        ws_close(fd);
756
        errno = save_errno;
757
    }
758
    return state;
759
}
760
761
LZ4WFILE_T
762
lz4wfile_fdopen(int fd)
763
{
764
    LZ4WFILE_T state;
765
766
    /* allocate lz4_writer structure to return */
767
    state = (LZ4WFILE_T)g_try_malloc(sizeof *state);
768
    if (state == NULL)
769
        return NULL;
770
    state->fd = fd;
771
    state->size_out = 0;         /* no buffer allocated yet */
772
    state->want = LZ4BUFSIZE;    /* max input size (a block) */
773
774
    memset(&state->lz4_prefs, 0, sizeof(LZ4F_preferences_t));
775
    /* Use the same prefs as the lz4 command line utility defaults. */
776
    state->lz4_prefs.frameInfo.blockMode = LZ4F_blockIndependent; /* Allows fast seek */
777
    /* We could use LZ4F_blockLinked but start a new frame every so often
778
     * in order to allow fast seek. (Or implement fast seek for linked
779
     * blocks via dictionary loading.) Linked blocks have better compression
780
     * when blocks are small, as happens when flushing during live capture.
781
     */
782
    state->lz4_prefs.frameInfo.contentChecksumFlag = 1;
783
    state->lz4_prefs.frameInfo.blockSizeID = LZ4F_max4MB;
784
    /* XXX - What should we set state->lz4_prefs.compressionLevel to?
785
     * The command line utility uses 1, recommends 9 as another option, and
786
     * also there's 12 (max).
787
     *
788
     * We could provide an API call or perhaps two or three preset options.
789
     */
790
    state->lz4_prefs.compressionLevel = 1;
791
792
    state->want_out = LZ4F_compressBound(state->want, &state->lz4_prefs);
793
    /*
794
     * This size guarantees that we will always have enough room to
795
     * write the result of LZ4F_compressUpdate (or Flush or End),
796
     * so long as the output buffer is empty (i.e., we immediately
797
     * write to the output file anything the compressor hands back
798
     * instead of buffering.)
799
     */
800
801
    /* initialize stream */
802
    state->err = 0;              /* clear error */
803
    state->err_info = NULL;         /* clear additional error information */
804
    state->pos = 0;                 /* no uncompressed data yet */
805
    state->pos_out = 0;
806
807
    /* return stream */
808
    return state;
809
}
810
811
/* Writes len bytes from the output buffer to the file.
812
 * Return true on success; returns false and sets state->err on failure.
813
 */
814
static bool
815
lz4_write_out(LZ4WFILE_T state, size_t len)
816
{
817
    if (len > 0) {
818
        ssize_t got = ws_write(state->fd, state->out, (unsigned)len);
819
        if (got < 0) {
820
            state->err = errno;
821
            return false;
822
        }
823
        if ((unsigned)got != len) {
824
            state->err = FILE_ERR_SHORT_WRITE;
825
            return false;
826
        }
827
        state->pos_out += got;
828
    }
829
    return true;
830
}
831
832
/* Initialize state for writing an lz4 file.  Mark initialization by setting
833
   state->size to non-zero.  Return -1, and set state->err and possibly
834
   state->err_info, on failure; return 0 on success. */
835
static int
836
lz4_init(LZ4WFILE_T state)
837
{
838
    LZ4F_errorCode_t ret;
839
840
    /* create Compression context */
841
    ret = LZ4F_createCompressionContext(&state->lz4_cctx, LZ4F_VERSION);
842
    if (LZ4F_isError(ret)) {
843
        state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS?
844
        state->err_info = LZ4F_getErrorName(ret);
845
        return -1;
846
    }
847
848
    /* allocate buffer */
849
    state->out = (unsigned char *)g_try_malloc(state->want_out);
850
    if (state->out == NULL) {
851
        g_free(state->out);
852
        LZ4F_freeCompressionContext(state->lz4_cctx);
853
        state->err = ENOMEM;
854
        return -1;
855
    }
856
857
    ret = LZ4F_compressBegin(state->lz4_cctx, state->out, state->want_out, &state->lz4_prefs);
858
    if (LZ4F_isError(ret)) {
859
        state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS?
860
        state->err_info = LZ4F_getErrorName(ret);
861
        return -1;
862
    }
863
    if (!lz4_write_out(state, ret)) {
864
        return -1;
865
    }
866
867
    /* mark state as initialized */
868
    state->size_out = state->want_out;
869
870
    return 0;
871
}
872
873
/* Write out len bytes from buf.  Return 0, and set state->err, on
874
   failure or on an attempt to write 0 bytes (in which case state->err
875
   is 0); return the number of bytes written on success. */
876
size_t
877
lz4wfile_write(LZ4WFILE_T state, const void *buf, size_t len)
878
{
879
    size_t to_write;
880
    size_t put = len;
881
882
    /* check that there's no error */
883
    if (state->err != 0)
884
        return 0;
885
886
    /* if len is zero, avoid unnecessary operations */
887
    if (len == 0)
888
        return 0;
889
890
    /* allocate memory if this is the first time through */
891
    if (state->size_out == 0 && lz4_init(state) == -1)
892
        return 0;
893
894
    do {
895
        to_write = MIN(len, state->want);
896
        size_t bytesWritten = LZ4F_compressUpdate(state->lz4_cctx, state->out, state->size_out,
897
            buf, to_write, NULL);
898
        if (LZ4F_isError(bytesWritten)) {
899
            state->err = FILE_ERR_CANT_WRITE; // XXX - FILE_ERR_COMPRESS?
900
            state->err_info = LZ4F_getErrorName(bytesWritten);
901
            return 0;
902
        }
903
        if (!lz4_write_out(state, bytesWritten)) {
904
            return 0;
905
        }
906
        state->pos += to_write;
907
        len -= to_write;
908
    } while (len);
909
910
    /* input was all buffered or compressed */
911
    return put;
912
}
913
914
/* Flush out what we've written so far.  Returns -1, and sets state->err,
915
   on failure; returns 0 on success. */
916
int
917
lz4wfile_flush(LZ4WFILE_T state)
918
{
919
    size_t bytesWritten;
920
    /* check that there's no error */
921
    if (state->err != 0)
922
        return -1;
923
924
    bytesWritten = LZ4F_flush(state->lz4_cctx, state->out, state->size_out, NULL);
925
    if (LZ4F_isError(bytesWritten)) {
926
        // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr)
927
        state->err = FILE_ERR_INTERNAL;
928
        return -1;
929
    }
930
    if (!lz4_write_out(state, bytesWritten)) {
931
        return -1;
932
    }
933
    return 0;
934
}
935
936
/* Flush out all data written, and close the file.  Returns a Wiretap
937
   error on failure; returns 0 on success. */
938
int
939
lz4wfile_close(LZ4WFILE_T state)
940
{
941
    int ret = 0;
942
943
    /* flush, free memory, and close file */
944
    size_t bytesWritten = LZ4F_compressEnd(state->lz4_cctx, state->out, state->size_out, NULL);
945
    if (LZ4F_isError(bytesWritten)) {
946
        // Should never happen if size_out >= LZ4F_compressBound(0, prefsPtr)
947
        ret = FILE_ERR_INTERNAL;
948
    }
949
    if (!lz4_write_out(state, bytesWritten)) {
950
        ret = state->err;
951
    }
952
    g_free(state->out);
953
    LZ4F_freeCompressionContext(state->lz4_cctx);
954
    if (ws_close(state->fd) == -1 && ret == 0)
955
        ret = errno;
956
    g_free(state);
957
    return ret;
958
}
959
960
int
961
lz4wfile_geterr(LZ4WFILE_T state)
962
{
963
    return state->err;
964
}
965
#endif /* HAVE_LZ4FRAME_H */