Coverage Report

Created: 2023-06-07 06:50

/src/libxml2/xzlib.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * xzlib.c: front end for the transparent support of lzma compression
3
 *          at the I/O layer, based on an example file from lzma project
4
 *
5
 * See Copyright for the status of this software.
6
 *
7
 * Anders F Bjorklund <afb@users.sourceforge.net>
8
 */
9
#define IN_LIBXML
10
#include "libxml.h"
11
#ifdef LIBXML_LZMA_ENABLED
12
13
#include <string.h>
14
#include <stdlib.h>
15
#include <errno.h>
16
17
#ifdef HAVE_SYS_STAT_H
18
#include <sys/stat.h>
19
#endif
20
#ifdef HAVE_FCNTL_H
21
#include <fcntl.h>
22
#endif
23
#ifdef HAVE_UNISTD_H
24
#include <unistd.h>
25
#elif defined (_WIN32)
26
#include <io.h>
27
#endif
28
#ifdef LIBXML_ZLIB_ENABLED
29
#include <zlib.h>
30
#endif
31
#ifdef LIBXML_LZMA_ENABLED
32
#include <lzma.h>
33
#endif
34
35
#include "private/xzlib.h"
36
#include <libxml/xmlmemory.h>
37
38
/* values for xz_state how */
39
0
#define LOOK 0                  /* look for a gzip/lzma header */
40
0
#define COPY 1                  /* copy input directly */
41
0
#define GZIP 2                  /* decompress a gzip stream */
42
0
#define LZMA 3                  /* decompress a lzma stream */
43
44
/* internal lzma file state data structure */
45
typedef struct {
46
    int mode;                   /* see lzma modes above */
47
    int fd;                     /* file descriptor */
48
    char *path;                 /* path or fd for error messages */
49
    uint64_t pos;               /* current position in uncompressed data */
50
    unsigned int size;          /* buffer size, zero if not allocated yet */
51
    unsigned int want;          /* requested buffer size, default is BUFSIZ */
52
    unsigned char *in;          /* input buffer */
53
    unsigned char *out;         /* output buffer (double-sized when reading) */
54
    unsigned char *next;        /* next output data to deliver or write */
55
    unsigned int have;          /* amount of output data unused at next */
56
    int eof;                    /* true if end of input file reached */
57
    uint64_t start;             /* where the lzma data started, for rewinding */
58
    uint64_t raw;               /* where the raw data started, for seeking */
59
    int how;                    /* 0: get header, 1: copy, 2: decompress */
60
    int direct;                 /* true if last read direct, false if lzma */
61
    /* seek request */
62
    uint64_t skip;              /* amount to skip (already rewound if backwards) */
63
    int seek;                   /* true if seek request pending */
64
    /* error information */
65
    int err;                    /* error code */
66
    char *msg;                  /* error message */
67
    /* lzma stream */
68
    int init;                   /* is the inflate stream initialized */
69
    lzma_stream strm;           /* stream structure in-place (not a pointer) */
70
    char padding1[32];          /* padding allowing to cope with possible
71
                                   extensions of above structure without
72
           too much side effect */
73
#ifdef LIBXML_ZLIB_ENABLED
74
    /* zlib inflate or deflate stream */
75
    z_stream zstrm;             /* stream structure in-place (not a pointer) */
76
#endif
77
    char padding2[32];          /* padding allowing to cope with possible
78
                                   extensions of above structure without
79
           too much side effect */
80
} xz_state, *xz_statep;
81
82
static void
83
xz_error(xz_statep state, int err, const char *msg)
84
0
{
85
    /* free previously allocated message and clear */
86
0
    if (state->msg != NULL) {
87
0
        if (state->err != LZMA_MEM_ERROR)
88
0
            xmlFree(state->msg);
89
0
        state->msg = NULL;
90
0
    }
91
92
    /* set error code, and if no message, then done */
93
0
    state->err = err;
94
0
    if (msg == NULL)
95
0
        return;
96
97
    /* for an out of memory error, save as static string */
98
0
    if (err == LZMA_MEM_ERROR) {
99
0
        state->msg = (char *) msg;
100
0
        return;
101
0
    }
102
103
    /* construct error message with path */
104
0
    if ((state->msg =
105
0
         xmlMalloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
106
0
        state->err = LZMA_MEM_ERROR;
107
0
        state->msg = (char *) "out of memory";
108
0
        return;
109
0
    }
110
0
    strcpy(state->msg, state->path);
111
0
    strcat(state->msg, ": ");
112
0
    strcat(state->msg, msg);
113
0
    return;
114
0
}
115
116
static void
117
xz_reset(xz_statep state)
118
0
{
119
0
    state->have = 0;            /* no output data available */
120
0
    state->eof = 0;             /* not at end of file */
121
0
    state->how = LOOK;          /* look for gzip header */
122
0
    state->direct = 1;          /* default for empty file */
123
0
    state->seek = 0;            /* no seek request pending */
124
0
    xz_error(state, LZMA_OK, NULL);     /* clear error */
125
0
    state->pos = 0;             /* no uncompressed data yet */
126
0
    state->strm.avail_in = 0;   /* no input data yet */
127
0
#ifdef LIBXML_ZLIB_ENABLED
128
0
    state->zstrm.avail_in = 0;  /* no input data yet */
129
0
#endif
130
0
}
131
132
static xzFile
133
xz_open(const char *path, int fd, const char *mode ATTRIBUTE_UNUSED)
134
0
{
135
0
    xz_statep state;
136
0
    off_t offset;
137
138
    /* allocate xzFile structure to return */
139
0
    state = xmlMalloc(sizeof(xz_state));
140
0
    if (state == NULL)
141
0
        return NULL;
142
0
    state->size = 0;            /* no buffers allocated yet */
143
0
    state->want = BUFSIZ;       /* requested buffer size */
144
0
    state->msg = NULL;          /* no error message yet */
145
0
    state->init = 0;            /* initialization of zlib data */
146
147
    /* save the path name for error messages */
148
0
    state->path = xmlMalloc(strlen(path) + 1);
149
0
    if (state->path == NULL) {
150
0
        xmlFree(state);
151
0
        return NULL;
152
0
    }
153
0
    strcpy(state->path, path);
154
155
    /* open the file with the appropriate mode (or just use fd) */
156
0
    state->fd = fd != -1 ? fd : open(path,
157
#ifdef O_LARGEFILE
158
                                     O_LARGEFILE |
159
#endif
160
#ifdef O_BINARY
161
                                     O_BINARY |
162
#endif
163
0
                                     O_RDONLY, 0666);
164
0
    if (state->fd == -1) {
165
0
        xmlFree(state->path);
166
0
        xmlFree(state);
167
0
        return NULL;
168
0
    }
169
170
    /* save the current position for rewinding (only if reading) */
171
0
    offset = lseek(state->fd, 0, SEEK_CUR);
172
0
    if (offset == -1)
173
0
        state->start = 0;
174
0
    else
175
0
        state->start = offset;
176
177
    /* initialize stream */
178
0
    xz_reset(state);
179
180
    /* return stream */
181
0
    return (xzFile) state;
182
0
}
183
184
static int
185
0
xz_compressed(xzFile f) {
186
0
    xz_statep state;
187
188
0
    if (f == NULL)
189
0
        return(-1);
190
0
    state = (xz_statep) f;
191
0
    if (state->init <= 0)
192
0
        return(-1);
193
194
0
    switch (state->how) {
195
0
        case COPY:
196
0
      return(0);
197
0
  case GZIP:
198
0
  case LZMA:
199
0
      return(1);
200
0
    }
201
0
    return(-1);
202
0
}
203
204
xzFile
205
__libxml2_xzopen(const char *path, const char *mode)
206
0
{
207
0
    return xz_open(path, -1, mode);
208
0
}
209
210
int
211
0
__libxml2_xzcompressed(xzFile f) {
212
0
    return xz_compressed(f);
213
0
}
214
215
xzFile
216
__libxml2_xzdopen(int fd, const char *mode)
217
0
{
218
0
    char *path;                 /* identifier for error messages */
219
0
    size_t path_size = 7 + 3 * sizeof(int);
220
0
    xzFile xz;
221
222
0
    if (fd == -1 || (path = xmlMalloc(path_size)) == NULL)
223
0
        return NULL;
224
0
    snprintf(path, path_size, "<fd:%d>", fd);       /* for debugging */
225
0
    xz = xz_open(path, fd, mode);
226
0
    xmlFree(path);
227
0
    return xz;
228
0
}
229
230
static int
231
xz_load(xz_statep state, unsigned char *buf, unsigned int len,
232
        unsigned int *have)
233
0
{
234
0
    int ret;
235
236
0
    *have = 0;
237
0
    do {
238
0
        ret = read(state->fd, buf + *have, len - *have);
239
0
        if (ret <= 0)
240
0
            break;
241
0
        *have += ret;
242
0
    } while (*have < len);
243
0
    if (ret < 0) {
244
0
        xz_error(state, -1, strerror(errno));
245
0
        return -1;
246
0
    }
247
0
    if (ret == 0)
248
0
        state->eof = 1;
249
0
    return 0;
250
0
}
251
252
static int
253
xz_avail(xz_statep state)
254
0
{
255
0
    lzma_stream *strm = &(state->strm);
256
257
0
    if (state->err != LZMA_OK)
258
0
        return -1;
259
0
    if (state->eof == 0) {
260
        /* avail_in is size_t, which is not necessary sizeof(unsigned) */
261
0
        unsigned tmp = strm->avail_in;
262
263
0
        if (xz_load(state, state->in, state->size, &tmp) == -1) {
264
0
            strm->avail_in = tmp;
265
0
            return -1;
266
0
        }
267
0
        strm->avail_in = tmp;
268
0
        strm->next_in = state->in;
269
0
    }
270
0
    return 0;
271
0
}
272
273
#ifdef LIBXML_ZLIB_ENABLED
274
static int
275
xz_avail_zstrm(xz_statep state)
276
0
{
277
0
    int ret;
278
0
    state->strm.avail_in = state->zstrm.avail_in;
279
0
    state->strm.next_in = state->zstrm.next_in;
280
0
    ret = xz_avail(state);
281
0
    state->zstrm.avail_in = (uInt) state->strm.avail_in;
282
0
    state->zstrm.next_in = (Bytef *) state->strm.next_in;
283
0
    return ret;
284
0
}
285
#endif
286
287
static int
288
is_format_xz(xz_statep state)
289
0
{
290
0
    lzma_stream *strm = &(state->strm);
291
292
0
    return strm->avail_in >= 6 && memcmp(state->in, "\3757zXZ", 6) == 0;
293
0
}
294
295
static int
296
is_format_lzma(xz_statep state)
297
0
{
298
0
    lzma_stream *strm = &(state->strm);
299
300
0
    lzma_filter filter;
301
0
    lzma_options_lzma *opt;
302
0
    uint32_t dict_size;
303
0
    uint64_t uncompressed_size;
304
0
    size_t i;
305
306
0
    if (strm->avail_in < 13)
307
0
        return 0;
308
309
0
    filter.id = LZMA_FILTER_LZMA1;
310
0
    if (lzma_properties_decode(&filter, NULL, state->in, 5) != LZMA_OK)
311
0
        return 0;
312
313
0
    opt = filter.options;
314
0
    dict_size = opt->dict_size;
315
0
    free(opt); /* we can't use xmlFree on a string returned by zlib */
316
317
    /* A hack to ditch tons of false positives: We allow only dictionary
318
     * sizes that are 2^n or 2^n + 2^(n-1) or UINT32_MAX. LZMA_Alone
319
     * created only files with 2^n, but accepts any dictionary size.
320
     * If someone complains, this will be reconsidered.
321
     */
322
0
    if (dict_size != UINT32_MAX) {
323
0
        uint32_t d = dict_size - 1;
324
325
0
        d |= d >> 2;
326
0
        d |= d >> 3;
327
0
        d |= d >> 4;
328
0
        d |= d >> 8;
329
0
        d |= d >> 16;
330
0
        ++d;
331
0
        if (d != dict_size || dict_size == 0)
332
0
            return 0;
333
0
    }
334
335
    /* Another hack to ditch false positives: Assume that if the
336
     * uncompressed size is known, it must be less than 256 GiB.
337
     * Again, if someone complains, this will be reconsidered.
338
     */
339
0
    uncompressed_size = 0;
340
0
    for (i = 0; i < 8; ++i)
341
0
        uncompressed_size |= (uint64_t) (state->in[5 + i]) << (i * 8);
342
343
0
    if (uncompressed_size != UINT64_MAX
344
0
        && uncompressed_size > (UINT64_C(1) << 38))
345
0
        return 0;
346
347
0
    return 1;
348
0
}
349
350
#ifdef LIBXML_ZLIB_ENABLED
351
352
/* Get next byte from input, or -1 if end or error. */
353
0
#define NEXT() ((strm->avail_in == 0 && xz_avail(state) == -1) ? -1 : \
354
0
                (strm->avail_in == 0 ? -1 : \
355
0
                 (strm->avail_in--, *(strm->next_in)++)))
356
/* Same thing, but from zstrm */
357
0
#define NEXTZ() ((strm->avail_in == 0 && xz_avail_zstrm(state) == -1) ? -1 : \
358
0
                (strm->avail_in == 0 ? -1 : \
359
0
                 (strm->avail_in--, *(strm->next_in)++)))
360
361
/* Get a four-byte little-endian integer and return 0 on success and the value
362
   in *ret.  Otherwise -1 is returned and *ret is not modified. */
363
static int
364
gz_next4(xz_statep state, unsigned long *ret)
365
0
{
366
0
    int ch;
367
0
    unsigned long val;
368
0
    z_streamp strm = &(state->zstrm);
369
370
0
    val = NEXTZ();
371
0
    val += (unsigned) NEXTZ() << 8;
372
0
    val += (unsigned long) NEXTZ() << 16;
373
0
    ch = NEXTZ();
374
0
    if (ch == -1)
375
0
        return -1;
376
0
    val += (unsigned long) ch << 24;
377
0
    *ret = val;
378
0
    return 0;
379
0
}
380
#endif
381
382
static int
383
xz_head(xz_statep state)
384
0
{
385
0
    lzma_stream *strm = &(state->strm);
386
0
    lzma_stream init = LZMA_STREAM_INIT;
387
0
    int flags;
388
0
    unsigned len;
389
390
    /* Avoid unused variable warning if features are disabled. */
391
0
    (void) flags;
392
0
    (void) len;
393
394
    /* allocate read buffers and inflate memory */
395
0
    if (state->size == 0) {
396
        /* allocate buffers */
397
0
        state->in = xmlMalloc(state->want);
398
0
        state->out = xmlMalloc(state->want << 1);
399
0
        if (state->in == NULL || state->out == NULL) {
400
0
            if (state->out != NULL)
401
0
                xmlFree(state->out);
402
0
            if (state->in != NULL)
403
0
                xmlFree(state->in);
404
0
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
405
0
            return -1;
406
0
        }
407
0
        state->size = state->want;
408
409
        /* allocate decoder memory */
410
0
        state->strm = init;
411
0
        state->strm.avail_in = 0;
412
0
        state->strm.next_in = NULL;
413
0
        if (lzma_auto_decoder(&state->strm, 100000000, 0) != LZMA_OK) {
414
0
            xmlFree(state->out);
415
0
            xmlFree(state->in);
416
0
            state->size = 0;
417
0
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
418
0
            return -1;
419
0
        }
420
0
#ifdef LIBXML_ZLIB_ENABLED
421
        /* allocate inflate memory */
422
0
        state->zstrm.zalloc = Z_NULL;
423
0
        state->zstrm.zfree = Z_NULL;
424
0
        state->zstrm.opaque = Z_NULL;
425
0
        state->zstrm.avail_in = 0;
426
0
        state->zstrm.next_in = Z_NULL;
427
0
        if (state->init == 0) {
428
0
            if (inflateInit2(&(state->zstrm), -15) != Z_OK) {/* raw inflate */
429
0
                xmlFree(state->out);
430
0
                xmlFree(state->in);
431
0
                state->size = 0;
432
0
                xz_error(state, LZMA_MEM_ERROR, "out of memory");
433
0
                return -1;
434
0
            }
435
0
            state->init = 1;
436
0
        }
437
0
#endif
438
0
    }
439
440
    /* get some data in the input buffer */
441
0
    if (strm->avail_in == 0) {
442
0
        if (xz_avail(state) == -1)
443
0
            return -1;
444
0
        if (strm->avail_in == 0)
445
0
            return 0;
446
0
    }
447
448
    /* look for the xz magic header bytes */
449
0
    if (is_format_xz(state) || is_format_lzma(state)) {
450
0
        state->how = LZMA;
451
0
        state->direct = 0;
452
0
        return 0;
453
0
    }
454
0
#ifdef LIBXML_ZLIB_ENABLED
455
    /* look for the gzip magic header bytes 31 and 139 */
456
0
    if (strm->next_in[0] == 31) {
457
0
        strm->avail_in--;
458
0
        strm->next_in++;
459
0
        if (strm->avail_in == 0 && xz_avail(state) == -1)
460
0
            return -1;
461
0
        if (strm->avail_in && strm->next_in[0] == 139) {
462
            /* we have a gzip header, woo hoo! */
463
0
            strm->avail_in--;
464
0
            strm->next_in++;
465
466
            /* skip rest of header */
467
0
            if (NEXT() != 8) {  /* compression method */
468
0
                xz_error(state, LZMA_DATA_ERROR,
469
0
                         "unknown compression method");
470
0
                return -1;
471
0
            }
472
0
            flags = NEXT();
473
0
            if (flags & 0xe0) { /* reserved flag bits */
474
0
                xz_error(state, LZMA_DATA_ERROR,
475
0
                         "unknown header flags set");
476
0
                return -1;
477
0
            }
478
0
            NEXT();             /* modification time */
479
0
            NEXT();
480
0
            NEXT();
481
0
            NEXT();
482
0
            NEXT();             /* extra flags */
483
0
            NEXT();             /* operating system */
484
0
            if (flags & 4) {    /* extra field */
485
0
                len = (unsigned) NEXT();
486
0
                len += (unsigned) NEXT() << 8;
487
0
                while (len--)
488
0
                    if (NEXT() < 0)
489
0
                        break;
490
0
            }
491
0
            if (flags & 8)      /* file name */
492
0
                while (NEXT() > 0) ;
493
0
            if (flags & 16)     /* comment */
494
0
                while (NEXT() > 0) ;
495
0
            if (flags & 2) {    /* header crc */
496
0
                NEXT();
497
0
                NEXT();
498
0
            }
499
            /* an unexpected end of file is not checked for here -- it will be
500
             * noticed on the first request for uncompressed data */
501
502
            /* set up for decompression */
503
0
            inflateReset(&state->zstrm);
504
0
            state->zstrm.adler = crc32(0L, Z_NULL, 0);
505
0
            state->how = GZIP;
506
0
            state->direct = 0;
507
0
            return 0;
508
0
        } else {
509
            /* not a gzip file -- save first byte (31) and fall to raw i/o */
510
0
            state->out[0] = 31;
511
0
            state->have = 1;
512
0
        }
513
0
    }
514
0
#endif
515
516
    /* doing raw i/o, save start of raw data for seeking, copy any leftover
517
     * input to output -- this assumes that the output buffer is larger than
518
     * the input buffer, which also assures space for gzungetc() */
519
0
    state->raw = state->pos;
520
0
    state->next = state->out;
521
0
    if (strm->avail_in) {
522
0
        memcpy(state->next + state->have, strm->next_in, strm->avail_in);
523
0
        state->have += strm->avail_in;
524
0
        strm->avail_in = 0;
525
0
    }
526
0
    state->how = COPY;
527
0
    state->direct = 1;
528
0
    return 0;
529
0
}
530
531
static int
532
xz_decomp(xz_statep state)
533
0
{
534
0
    int ret;
535
0
    unsigned had;
536
0
    unsigned long crc, len;
537
0
    lzma_stream *strm = &(state->strm);
538
539
0
    lzma_action action = LZMA_RUN;
540
541
    /* Avoid unused variable warning if features are disabled. */
542
0
    (void) crc;
543
0
    (void) len;
544
545
    /* fill output buffer up to end of deflate stream */
546
0
    had = strm->avail_out;
547
0
    do {
548
        /* get more input for inflate() */
549
0
        if (strm->avail_in == 0 && xz_avail(state) == -1)
550
0
            return -1;
551
0
        if (strm->avail_in == 0) {
552
0
            xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
553
0
            return -1;
554
0
        }
555
0
        if (state->eof)
556
0
            action = LZMA_FINISH;
557
558
        /* decompress and handle errors */
559
0
#ifdef LIBXML_ZLIB_ENABLED
560
0
        if (state->how == GZIP) {
561
0
            state->zstrm.avail_in = (uInt) state->strm.avail_in;
562
0
            state->zstrm.next_in = (Bytef *) state->strm.next_in;
563
0
            state->zstrm.avail_out = (uInt) state->strm.avail_out;
564
0
            state->zstrm.next_out = (Bytef *) state->strm.next_out;
565
0
            ret = inflate(&state->zstrm, Z_NO_FLUSH);
566
0
            if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
567
0
                xz_error(state, Z_STREAM_ERROR,
568
0
                         "internal error: inflate stream corrupt");
569
0
                return -1;
570
0
            }
571
            /*
572
             * FIXME: Remapping a couple of error codes and falling through
573
             * to the LZMA error handling looks fragile.
574
             */
575
0
            if (ret == Z_MEM_ERROR)
576
0
                ret = LZMA_MEM_ERROR;
577
0
            if (ret == Z_DATA_ERROR)
578
0
                ret = LZMA_DATA_ERROR;
579
0
            if (ret == Z_STREAM_END)
580
0
                ret = LZMA_STREAM_END;
581
0
            state->strm.avail_in = state->zstrm.avail_in;
582
0
            state->strm.next_in = state->zstrm.next_in;
583
0
            state->strm.avail_out = state->zstrm.avail_out;
584
0
            state->strm.next_out = state->zstrm.next_out;
585
0
        } else                  /* state->how == LZMA */
586
0
#endif
587
0
            ret = lzma_code(strm, action);
588
0
        if (ret == LZMA_MEM_ERROR) {
589
0
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
590
0
            return -1;
591
0
        }
592
0
        if (ret == LZMA_DATA_ERROR) {
593
0
            xz_error(state, LZMA_DATA_ERROR, "compressed data error");
594
0
            return -1;
595
0
        }
596
0
        if (ret == LZMA_PROG_ERROR) {
597
0
            xz_error(state, LZMA_PROG_ERROR, "compression error");
598
0
            return -1;
599
0
        }
600
0
        if ((state->how != GZIP) &&
601
0
            (ret != LZMA_OK) && (ret != LZMA_STREAM_END)) {
602
0
            xz_error(state, ret, "lzma error");
603
0
            return -1;
604
0
        }
605
0
    } while (strm->avail_out && ret != LZMA_STREAM_END);
606
607
    /* update available output and crc check value */
608
0
    state->have = had - strm->avail_out;
609
0
    state->next = strm->next_out - state->have;
610
0
#ifdef LIBXML_ZLIB_ENABLED
611
0
    state->zstrm.adler =
612
0
        crc32(state->zstrm.adler, state->next, state->have);
613
0
#endif
614
615
0
    if (ret == LZMA_STREAM_END) {
616
0
#ifdef LIBXML_ZLIB_ENABLED
617
0
        if (state->how == GZIP) {
618
0
            if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
619
0
                xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
620
0
                return -1;
621
0
            }
622
0
            if (crc != state->zstrm.adler) {
623
0
                xz_error(state, LZMA_DATA_ERROR, "incorrect data check");
624
0
                return -1;
625
0
            }
626
0
            if (len != (state->zstrm.total_out & 0xffffffffL)) {
627
0
                xz_error(state, LZMA_DATA_ERROR, "incorrect length check");
628
0
                return -1;
629
0
            }
630
0
            state->strm.avail_in = 0;
631
0
            state->strm.next_in = NULL;
632
0
            state->strm.avail_out = 0;
633
0
            state->strm.next_out = NULL;
634
0
        } else
635
0
#endif
636
0
        if (strm->avail_in != 0 || !state->eof) {
637
0
            xz_error(state, LZMA_DATA_ERROR, "trailing garbage");
638
0
            return -1;
639
0
        }
640
0
        state->how = LOOK;      /* ready for next stream, once have is 0 (leave
641
                                 * state->direct unchanged to remember how) */
642
0
    }
643
644
    /* good decompression */
645
0
    return 0;
646
0
}
647
648
static int
649
xz_make(xz_statep state)
650
0
{
651
0
    lzma_stream *strm = &(state->strm);
652
653
0
    if (state->how == LOOK) {   /* look for lzma / gzip header */
654
0
        if (xz_head(state) == -1)
655
0
            return -1;
656
0
        if (state->have)        /* got some data from xz_head() */
657
0
            return 0;
658
0
    }
659
0
    if (state->how == COPY) {   /* straight copy */
660
0
        if (xz_load(state, state->out, state->size << 1, &(state->have)) ==
661
0
            -1)
662
0
            return -1;
663
0
        state->next = state->out;
664
0
    } else if (state->how == LZMA || state->how == GZIP) {      /* decompress */
665
0
        strm->avail_out = state->size << 1;
666
0
        strm->next_out = state->out;
667
0
        if (xz_decomp(state) == -1)
668
0
            return -1;
669
0
    }
670
0
    return 0;
671
0
}
672
673
static int
674
xz_skip(xz_statep state, uint64_t len)
675
0
{
676
0
    unsigned n;
677
678
    /* skip over len bytes or reach end-of-file, whichever comes first */
679
0
    while (len)
680
        /* skip over whatever is in output buffer */
681
0
        if (state->have) {
682
0
            n = (uint64_t) state->have > len ?
683
0
                (unsigned) len : state->have;
684
0
            state->have -= n;
685
0
            state->next += n;
686
0
            state->pos += n;
687
0
            len -= n;
688
0
        }
689
690
    /* output buffer empty -- return if we're at the end of the input */
691
0
        else if (state->eof && state->strm.avail_in == 0)
692
0
            break;
693
694
    /* need more data to skip -- load up output buffer */
695
0
        else {
696
            /* get more output, looking for header if required */
697
0
            if (xz_make(state) == -1)
698
0
                return -1;
699
0
        }
700
0
    return 0;
701
0
}
702
703
int
704
__libxml2_xzread(xzFile file, void *buf, unsigned len)
705
0
{
706
0
    unsigned got, n;
707
0
    xz_statep state;
708
0
    lzma_stream *strm;
709
710
    /* get internal structure */
711
0
    if (file == NULL)
712
0
        return -1;
713
0
    state = (xz_statep) file;
714
0
    strm = &(state->strm);
715
716
    /* check that we're reading and that there's no error */
717
0
    if (state->err != LZMA_OK)
718
0
        return -1;
719
720
    /* since an int is returned, make sure len fits in one, otherwise return
721
     * with an error (this avoids the flaw in the interface) */
722
0
    if ((int) len < 0) {
723
0
        xz_error(state, LZMA_BUF_ERROR,
724
0
                 "requested length does not fit in int");
725
0
        return -1;
726
0
    }
727
728
    /* if len is zero, avoid unnecessary operations */
729
0
    if (len == 0)
730
0
        return 0;
731
732
    /* process a skip request */
733
0
    if (state->seek) {
734
0
        state->seek = 0;
735
0
        if (xz_skip(state, state->skip) == -1)
736
0
            return -1;
737
0
    }
738
739
    /* get len bytes to buf, or less than len if at the end */
740
0
    got = 0;
741
0
    do {
742
        /* first just try copying data from the output buffer */
743
0
        if (state->have) {
744
0
            n = state->have > len ? len : state->have;
745
0
            memcpy(buf, state->next, n);
746
0
            state->next += n;
747
0
            state->have -= n;
748
0
        }
749
750
        /* output buffer empty -- return if we're at the end of the input */
751
0
        else if (state->eof && strm->avail_in == 0)
752
0
            break;
753
754
        /* need output data -- for small len or new stream load up our output
755
         * buffer */
756
0
        else if (state->how == LOOK || len < (state->size << 1)) {
757
            /* get more output, looking for header if required */
758
0
            if (xz_make(state) == -1)
759
0
                return -1;
760
0
            continue;           /* no progress yet -- go back to memcpy() above */
761
            /* the copy above assures that we will leave with space in the
762
             * output buffer, allowing at least one gzungetc() to succeed */
763
0
        }
764
765
        /* large len -- read directly into user buffer */
766
0
        else if (state->how == COPY) {  /* read directly */
767
0
            if (xz_load(state, buf, len, &n) == -1)
768
0
                return -1;
769
0
        }
770
771
        /* large len -- decompress directly into user buffer */
772
0
        else {                  /* state->how == LZMA */
773
0
            strm->avail_out = len;
774
0
            strm->next_out = buf;
775
0
            if (xz_decomp(state) == -1)
776
0
                return -1;
777
0
            n = state->have;
778
0
            state->have = 0;
779
0
        }
780
781
        /* update progress */
782
0
        len -= n;
783
0
        buf = (char *) buf + n;
784
0
        got += n;
785
0
        state->pos += n;
786
0
    } while (len);
787
788
    /* return number of bytes read into user buffer (will fit in int) */
789
0
    return (int) got;
790
0
}
791
792
int
793
__libxml2_xzclose(xzFile file)
794
0
{
795
0
    int ret;
796
0
    xz_statep state;
797
798
    /* get internal structure */
799
0
    if (file == NULL)
800
0
        return LZMA_DATA_ERROR;
801
0
    state = (xz_statep) file;
802
803
    /* free memory and close file */
804
0
    if (state->size) {
805
0
        lzma_end(&(state->strm));
806
0
#ifdef LIBXML_ZLIB_ENABLED
807
0
        if (state->init == 1)
808
0
            inflateEnd(&(state->zstrm));
809
0
        state->init = 0;
810
0
#endif
811
0
        xmlFree(state->out);
812
0
        xmlFree(state->in);
813
0
    }
814
0
    xmlFree(state->path);
815
0
    if ((state->msg != NULL) && (state->err != LZMA_MEM_ERROR))
816
0
        xmlFree(state->msg);
817
0
    ret = close(state->fd);
818
0
    xmlFree(state);
819
0
    return ret ? ret : LZMA_OK;
820
0
}
821
#endif /* LIBXML_LZMA_ENABLED */