xzlib.c 22.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
/**
 * xzlib.c: front end for the transparent suport of lzma compression
 *          at the I/O layer, based on an example file from lzma project
 *
 * See Copyright for the status of this software.
 *
 * Anders F Bjorklund <afb@users.sourceforge.net>
 */
#define IN_LIBXML
#include "libxml.h"
#ifdef HAVE_LZMA_H

#include <string.h>
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif


#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#endif
#include <lzma.h>

39
#include "xzlib.h"
40
#include <libxml/xmlmemory.h>
41 42

/* values for xz_state how */
43 44 45 46
#define LOOK 0                  /* look for a gzip/lzma header */
#define COPY 1                  /* copy input directly */
#define GZIP 2                  /* decompress a gzip stream */
#define LZMA 3                  /* decompress a lzma stream */
47 48 49

/* internal lzma file state data structure */
typedef struct {
50 51 52 53
    int mode;                   /* see lzma modes above */
    int fd;                     /* file descriptor */
    char *path;                 /* path or fd for error messages */
    uint64_t pos;               /* current position in uncompressed data */
54 55
    unsigned int size;          /* buffer size, zero if not allocated yet */
    unsigned int want;          /* requested buffer size, default is BUFSIZ */
56 57 58
    unsigned char *in;          /* input buffer */
    unsigned char *out;         /* output buffer (double-sized when reading) */
    unsigned char *next;        /* next output data to deliver or write */
59
    unsigned int have;          /* amount of output data unused at next */
60 61 62 63 64 65 66 67 68 69 70 71
    int eof;                    /* true if end of input file reached */
    uint64_t start;             /* where the lzma data started, for rewinding */
    uint64_t raw;               /* where the raw data started, for seeking */
    int how;                    /* 0: get header, 1: copy, 2: decompress */
    int direct;                 /* true if last read direct, false if lzma */
    /* seek request */
    uint64_t skip;              /* amount to skip (already rewound if backwards) */
    int seek;                   /* true if seek request pending */
    /* error information */
    int err;                    /* error code */
    char *msg;                  /* error message */
    /* lzma stream */
72
    int init;                   /* is the iniflate stream initialized */
73 74 75 76
    lzma_stream strm;           /* stream structure in-place (not a pointer) */
    char padding1[32];          /* padding allowing to cope with possible
                                   extensions of above structure without
				   too much side effect */
77
#ifdef HAVE_ZLIB_H
78 79
    /* zlib inflate or deflate stream */
    z_stream zstrm;             /* stream structure in-place (not a pointer) */
80
#endif
81 82 83
    char padding2[32];          /* padding allowing to cope with possible
                                   extensions of above structure without
				   too much side effect */
84 85
} xz_state, *xz_statep;

86 87
static void
xz_error(xz_statep state, int err, const char *msg)
88 89 90 91
{
    /* free previously allocated message and clear */
    if (state->msg != NULL) {
        if (state->err != LZMA_MEM_ERROR)
92
            xmlFree(state->msg);
93 94 95 96 97 98 99 100 101 102
        state->msg = NULL;
    }

    /* set error code, and if no message, then done */
    state->err = err;
    if (msg == NULL)
        return;

    /* for an out of memory error, save as static string */
    if (err == LZMA_MEM_ERROR) {
103
        state->msg = (char *) msg;
104 105 106 107
        return;
    }

    /* construct error message with path */
108
    if ((state->msg =
109
         xmlMalloc(strlen(state->path) + strlen(msg) + 3)) == NULL) {
110
        state->err = LZMA_MEM_ERROR;
111
        state->msg = (char *) "out of memory";
112 113 114 115 116 117 118 119
        return;
    }
    strcpy(state->msg, state->path);
    strcat(state->msg, ": ");
    strcat(state->msg, msg);
    return;
}

120 121
static void
xz_reset(xz_statep state)
122
{
123 124 125 126 127 128 129 130
    state->have = 0;            /* no output data available */
    state->eof = 0;             /* not at end of file */
    state->how = LOOK;          /* look for gzip header */
    state->direct = 1;          /* default for empty file */
    state->seek = 0;            /* no seek request pending */
    xz_error(state, LZMA_OK, NULL);     /* clear error */
    state->pos = 0;             /* no uncompressed data yet */
    state->strm.avail_in = 0;   /* no input data yet */
131
#ifdef HAVE_ZLIB_H
132
    state->zstrm.avail_in = 0;  /* no input data yet */
133 134 135
#endif
}

136 137
static xzFile
xz_open(const char *path, int fd, const char *mode ATTRIBUTE_UNUSED)
138 139 140 141
{
    xz_statep state;

    /* allocate xzFile structure to return */
142
    state = xmlMalloc(sizeof(xz_state));
143 144 145 146 147
    if (state == NULL)
        return NULL;
    state->size = 0;            /* no buffers allocated yet */
    state->want = BUFSIZ;       /* requested buffer size */
    state->msg = NULL;          /* no error message yet */
148
    state->init = 0;            /* initialization of zlib data */
149

150
    /* save the path name for error messages */
151
    state->path = xmlMalloc(strlen(path) + 1);
152
    if (state->path == NULL) {
153
        xmlFree(state);
154 155 156 157 158
        return NULL;
    }
    strcpy(state->path, path);

    /* open the file with the appropriate mode (or just use fd) */
159
    state->fd = fd != -1 ? fd : open(path,
160
#ifdef O_LARGEFILE
161
                                     O_LARGEFILE |
162 163
#endif
#ifdef O_BINARY
164
                                     O_BINARY |
165
#endif
166
                                     O_RDONLY, 0666);
167
    if (state->fd == -1) {
168 169
        xmlFree(state->path);
        xmlFree(state);
170 171 172 173 174
        return NULL;
    }

    /* save the current position for rewinding (only if reading) */
    state->start = lseek(state->fd, 0, SEEK_CUR);
175 176
    if (state->start == (uint64_t) - 1)
        state->start = 0;
177 178 179 180 181

    /* initialize stream */
    xz_reset(state);

    /* return stream */
182
    return (xzFile) state;
183 184
}

185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
static int
xz_compressed(xzFile f) {
    xz_statep state;

    if (f == NULL)
        return(-1);
    state = (xz_statep) f;
    if (state->init <= 0)
        return(-1);

    switch (state->how) {
        case COPY:
	    return(0);
	case GZIP:
	case LZMA:
	    return(1);
    }
    return(-1);
}

205 206
xzFile
__libxml2_xzopen(const char *path, const char *mode)
207 208 209 210
{
    return xz_open(path, -1, mode);
}

211 212 213 214 215
int
__libxml2_xzcompressed(xzFile f) {
    return xz_compressed(f);
}

216 217
xzFile
__libxml2_xzdopen(int fd, const char *mode)
218
{
219
    char *path;                 /* identifier for error messages */
220 221
    xzFile xz;

222
    if (fd == -1 || (path = xmlMalloc(7 + 3 * sizeof(int))) == NULL)
223
        return NULL;
224
    sprintf(path, "<fd:%d>", fd);       /* for debugging */
225
    xz = xz_open(path, fd, mode);
226
    xmlFree(path);
227 228 229
    return xz;
}

230
static int
231 232
xz_load(xz_statep state, unsigned char *buf, unsigned int len,
        unsigned int *have)
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
{
    int ret;

    *have = 0;
    do {
        ret = read(state->fd, buf + *have, len - *have);
        if (ret <= 0)
            break;
        *have += ret;
    } while (*have < len);
    if (ret < 0) {
        xz_error(state, -1, strerror(errno));
        return -1;
    }
    if (ret == 0)
        state->eof = 1;
    return 0;
}

252 253
static int
xz_avail(xz_statep state)
254 255 256 257 258 259
{
    lzma_stream *strm = &(state->strm);

    if (state->err != LZMA_OK)
        return -1;
    if (state->eof == 0) {
260 261 262 263 264
        /* avail_in is size_t, which is not necessary sizeof(unsigned) */
        unsigned tmp = strm->avail_in;

        if (xz_load(state, state->in, state->size, &tmp) == -1) {
            strm->avail_in = tmp;
265
            return -1;
266 267
        }
        strm->avail_in = tmp;
268 269 270 271 272 273 274 275 276 277
        strm->next_in = state->in;
    }
    return 0;
}

static int
is_format_xz(xz_statep state)
{
    lzma_stream *strm = &(state->strm);

278
    return strm->avail_in >= 6 && memcmp(state->in, "\3757zXZ", 6) == 0;
279 280 281 282 283 284 285
}

static int
is_format_lzma(xz_statep state)
{
    lzma_stream *strm = &(state->strm);

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
    lzma_filter filter;
    lzma_options_lzma *opt;
    uint32_t dict_size;
    uint64_t uncompressed_size;
    size_t i;

    if (strm->avail_in < 13)
        return 0;

    filter.id = LZMA_FILTER_LZMA1;
    if (lzma_properties_decode(&filter, NULL, state->in, 5) != LZMA_OK)
        return 0;

    opt = filter.options;
    dict_size = opt->dict_size;
301
    free(opt); /* we can't use xmlFree on a string returned by zlib */
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333

    /* A hack to ditch tons of false positives: We allow only dictionary
     * sizes that are 2^n or 2^n + 2^(n-1) or UINT32_MAX. LZMA_Alone
     * created only files with 2^n, but accepts any dictionary size.
     * If someone complains, this will be reconsidered.
     */
    if (dict_size != UINT32_MAX) {
        uint32_t d = dict_size - 1;

        d |= d >> 2;
        d |= d >> 3;
        d |= d >> 4;
        d |= d >> 8;
        d |= d >> 16;
        ++d;
        if (d != dict_size || dict_size == 0)
            return 0;
    }

    /* Another hack to ditch false positives: Assume that if the
     * uncompressed size is known, it must be less than 256 GiB.
     * Again, if someone complains, this will be reconsidered.
     */
    uncompressed_size = 0;
    for (i = 0; i < 8; ++i)
        uncompressed_size |= (uint64_t) (state->in[5 + i]) << (i * 8);

    if (uncompressed_size != UINT64_MAX
        && uncompressed_size > (UINT64_C(1) << 38))
        return 0;

    return 1;
334 335 336
}

#ifdef HAVE_ZLIB_H
337

338 339 340 341 342 343 344
/* Get next byte from input, or -1 if end or error. */
#define NEXT() ((strm->avail_in == 0 && xz_avail(state) == -1) ? -1 : \
                (strm->avail_in == 0 ? -1 : \
                 (strm->avail_in--, *(strm->next_in)++)))

/* Get a four-byte little-endian integer and return 0 on success and the value
   in *ret.  Otherwise -1 is returned and *ret is not modified. */
345 346
static int
gz_next4(xz_statep state, unsigned long *ret)
347 348 349 350 351 352
{
    int ch;
    unsigned long val;
    z_streamp strm = &(state->zstrm);

    val = NEXT();
353 354
    val += (unsigned) NEXT() << 8;
    val += (unsigned long) NEXT() << 16;
355 356 357
    ch = NEXT();
    if (ch == -1)
        return -1;
358
    val += (unsigned long) ch << 24;
359 360 361 362 363
    *ret = val;
    return 0;
}
#endif

364 365
static int
xz_head(xz_statep state)
366 367 368 369 370 371 372 373 374
{
    lzma_stream *strm = &(state->strm);
    lzma_stream init = LZMA_STREAM_INIT;
    int flags;
    unsigned len;

    /* allocate read buffers and inflate memory */
    if (state->size == 0) {
        /* allocate buffers */
375 376
        state->in = xmlMalloc(state->want);
        state->out = xmlMalloc(state->want << 1);
377 378
        if (state->in == NULL || state->out == NULL) {
            if (state->out != NULL)
379
                xmlFree(state->out);
380
            if (state->in != NULL)
381
                xmlFree(state->in);
382 383 384 385 386 387 388 389 390 391
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
            return -1;
        }
        state->size = state->want;

        /* allocate decoder memory */
        state->strm = init;
        state->strm.avail_in = 0;
        state->strm.next_in = NULL;
        if (lzma_auto_decoder(&state->strm, UINT64_MAX, 0) != LZMA_OK) {
392 393
            xmlFree(state->out);
            xmlFree(state->in);
394 395 396
            state->size = 0;
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
            return -1;
397
        }
398 399 400 401 402 403 404
#ifdef HAVE_ZLIB_H
        /* allocate inflate memory */
        state->zstrm.zalloc = Z_NULL;
        state->zstrm.zfree = Z_NULL;
        state->zstrm.opaque = Z_NULL;
        state->zstrm.avail_in = 0;
        state->zstrm.next_in = Z_NULL;
405 406 407 408 409 410 411 412 413
        if (state->init == 0) {
            if (inflateInit2(&(state->zstrm), -15) != Z_OK) {/* raw inflate */
                xmlFree(state->out);
                xmlFree(state->in);
                state->size = 0;
                xz_error(state, LZMA_MEM_ERROR, "out of memory");
                return -1;
            }
            state->init = 1;
414 415 416 417 418 419 420 421 422 423 424 425
        }
#endif
    }

    /* get some data in the input buffer */
    if (strm->avail_in == 0) {
        if (xz_avail(state) == -1)
            return -1;
        if (strm->avail_in == 0)
            return 0;
    }

426
    /* look for the xz magic header bytes */
427
    if (is_format_xz(state) || is_format_lzma(state)) {
428 429 430
        state->how = LZMA;
        state->direct = 0;
        return 0;
431 432
    }
#ifdef HAVE_ZLIB_H
433
    /* look for the gzip magic header bytes 31 and 139 */
434 435 436 437 438 439 440 441 442 443 444
    if (strm->next_in[0] == 31) {
        strm->avail_in--;
        strm->next_in++;
        if (strm->avail_in == 0 && xz_avail(state) == -1)
            return -1;
        if (strm->avail_in && strm->next_in[0] == 139) {
            /* we have a gzip header, woo hoo! */
            strm->avail_in--;
            strm->next_in++;

            /* skip rest of header */
445 446 447
            if (NEXT() != 8) {  /* compression method */
                xz_error(state, LZMA_DATA_ERROR,
                         "unknown compression method");
448 449 450
                return -1;
            }
            flags = NEXT();
451 452 453
            if (flags & 0xe0) { /* reserved flag bits */
                xz_error(state, LZMA_DATA_ERROR,
                         "unknown header flags set");
454 455
                return -1;
            }
456
            NEXT();             /* modification time */
457 458 459
            NEXT();
            NEXT();
            NEXT();
460 461 462 463 464
            NEXT();             /* extra flags */
            NEXT();             /* operating system */
            if (flags & 4) {    /* extra field */
                len = (unsigned) NEXT();
                len += (unsigned) NEXT() << 8;
465 466 467 468
                while (len--)
                    if (NEXT() < 0)
                        break;
            }
469 470 471 472 473
            if (flags & 8)      /* file name */
                while (NEXT() > 0) ;
            if (flags & 16)     /* comment */
                while (NEXT() > 0) ;
            if (flags & 2) {    /* header crc */
474 475 476 477
                NEXT();
                NEXT();
            }
            /* an unexpected end of file is not checked for here -- it will be
478
             * noticed on the first request for uncompressed data */
479 480 481 482 483 484 485

            /* set up for decompression */
            inflateReset(&state->zstrm);
            state->zstrm.adler = crc32(0L, Z_NULL, 0);
            state->how = GZIP;
            state->direct = 0;
            return 0;
486
        } else {
487 488 489 490 491 492 493 494
            /* not a gzip file -- save first byte (31) and fall to raw i/o */
            state->out[0] = 31;
            state->have = 1;
        }
    }
#endif

    /* doing raw i/o, save start of raw data for seeking, copy any leftover
495 496
     * input to output -- this assumes that the output buffer is larger than
     * the input buffer, which also assures space for gzungetc() */
497 498 499 500 501 502 503 504 505 506 507 508
    state->raw = state->pos;
    state->next = state->out;
    if (strm->avail_in) {
        memcpy(state->next + state->have, strm->next_in, strm->avail_in);
        state->have += strm->avail_in;
        strm->avail_in = 0;
    }
    state->how = COPY;
    state->direct = 1;
    return 0;
}

509 510
static int
xz_decomp(xz_statep state)
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
{
    int ret;
    unsigned had;
    unsigned long crc, len;
    lzma_stream *strm = &(state->strm);

    lzma_action action = LZMA_RUN;

    /* fill output buffer up to end of deflate stream */
    had = strm->avail_out;
    do {
        /* get more input for inflate() */
        if (strm->avail_in == 0 && xz_avail(state) == -1)
            return -1;
        if (strm->avail_in == 0) {
            xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
            return -1;
        }
        if (state->eof)
            action = LZMA_FINISH;
531

532 533 534
        /* decompress and handle errors */
#ifdef HAVE_ZLIB_H
        if (state->how == GZIP) {
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
            state->zstrm.avail_in = (uInt) state->strm.avail_in;
            state->zstrm.next_in = (Bytef *) state->strm.next_in;
            state->zstrm.avail_out = (uInt) state->strm.avail_out;
            state->zstrm.next_out = (Bytef *) state->strm.next_out;
            ret = inflate(&state->zstrm, Z_NO_FLUSH);
            if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
                xz_error(state, Z_STREAM_ERROR,
                         "internal error: inflate stream corrupt");
                return -1;
            }
            if (ret == Z_MEM_ERROR)
                ret = LZMA_MEM_ERROR;
            if (ret == Z_DATA_ERROR)
                ret = LZMA_DATA_ERROR;
            if (ret == Z_STREAM_END)
                ret = LZMA_STREAM_END;
            state->strm.avail_in = state->zstrm.avail_in;
            state->strm.next_in = state->zstrm.next_in;
            state->strm.avail_out = state->zstrm.avail_out;
            state->strm.next_out = state->zstrm.next_out;
        } else                  /* state->how == LZMA */
556
#endif
557
            ret = lzma_code(strm, action);
558 559 560 561 562 563 564 565 566 567 568 569 570 571
        if (ret == LZMA_MEM_ERROR) {
            xz_error(state, LZMA_MEM_ERROR, "out of memory");
            return -1;
        }
        if (ret == LZMA_DATA_ERROR) {
            xz_error(state, LZMA_DATA_ERROR, "compressed data error");
            return -1;
        }
    } while (strm->avail_out && ret != LZMA_STREAM_END);

    /* update available output and crc check value */
    state->have = had - strm->avail_out;
    state->next = strm->next_out - state->have;
#ifdef HAVE_ZLIB_H
572 573
    state->zstrm.adler =
        crc32(state->zstrm.adler, state->next, state->have);
574 575 576 577 578
#endif

    if (ret == LZMA_STREAM_END) {
#ifdef HAVE_ZLIB_H
        if (state->how == GZIP) {
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595
            if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
                xz_error(state, LZMA_DATA_ERROR, "unexpected end of file");
                return -1;
            }
            if (crc != state->zstrm.adler) {
                xz_error(state, LZMA_DATA_ERROR, "incorrect data check");
                return -1;
            }
            if (len != (state->zstrm.total_out & 0xffffffffL)) {
                xz_error(state, LZMA_DATA_ERROR, "incorrect length check");
                return -1;
            }
            state->strm.avail_in = 0;
            state->strm.next_in = NULL;
            state->strm.avail_out = 0;
            state->strm.next_out = NULL;
        } else
596
#endif
597
        if (strm->avail_in != 0 || !state->eof) {
598 599 600 601
            xz_error(state, LZMA_DATA_ERROR, "trailing garbage");
            return -1;
        }
        state->how = LOOK;      /* ready for next stream, once have is 0 (leave
602
                                 * state->direct unchanged to remember how) */
603 604 605 606 607 608
    }

    /* good decompression */
    return 0;
}

609 610
static int
xz_make(xz_statep state)
611 612 613
{
    lzma_stream *strm = &(state->strm);

614
    if (state->how == LOOK) {   /* look for lzma / gzip header */
615 616
        if (xz_head(state) == -1)
            return -1;
617
        if (state->have)        /* got some data from xz_head() */
618 619
            return 0;
    }
620 621 622
    if (state->how == COPY) {   /* straight copy */
        if (xz_load(state, state->out, state->size << 1, &(state->have)) ==
            -1)
623 624
            return -1;
        state->next = state->out;
625
    } else if (state->how == LZMA || state->how == GZIP) {      /* decompress */
626 627 628 629 630 631 632 633
        strm->avail_out = state->size << 1;
        strm->next_out = state->out;
        if (xz_decomp(state) == -1)
            return -1;
    }
    return 0;
}

634 635
static int
xz_skip(xz_statep state, uint64_t len)
636 637 638 639 640 641 642
{
    unsigned n;

    /* skip over len bytes or reach end-of-file, whichever comes first */
    while (len)
        /* skip over whatever is in output buffer */
        if (state->have) {
643 644
            n = (uint64_t) state->have > len ?
                (unsigned) len : state->have;
645 646 647 648 649 650
            state->have -= n;
            state->next += n;
            state->pos += n;
            len -= n;
        }

651
    /* output buffer empty -- return if we're at the end of the input */
652 653 654
        else if (state->eof && state->strm.avail_in == 0)
            break;

655
    /* need more data to skip -- load up output buffer */
656 657 658 659 660 661 662 663
        else {
            /* get more output, looking for header if required */
            if (xz_make(state) == -1)
                return -1;
        }
    return 0;
}

664 665
int
__libxml2_xzread(xzFile file, void *buf, unsigned len)
666 667 668 669 670 671 672 673
{
    unsigned got, n;
    xz_statep state;
    lzma_stream *strm;

    /* get internal structure */
    if (file == NULL)
        return -1;
674
    state = (xz_statep) file;
675 676 677 678 679 680 681
    strm = &(state->strm);

    /* check that we're reading and that there's no error */
    if (state->err != LZMA_OK)
        return -1;

    /* since an int is returned, make sure len fits in one, otherwise return
682 683 684 685
     * with an error (this avoids the flaw in the interface) */
    if ((int) len < 0) {
        xz_error(state, LZMA_BUF_ERROR,
                 "requested length does not fit in int");
686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715
        return -1;
    }

    /* if len is zero, avoid unnecessary operations */
    if (len == 0)
        return 0;

    /* process a skip request */
    if (state->seek) {
        state->seek = 0;
        if (xz_skip(state, state->skip) == -1)
            return -1;
    }

    /* get len bytes to buf, or less than len if at the end */
    got = 0;
    do {
        /* first just try copying data from the output buffer */
        if (state->have) {
            n = state->have > len ? len : state->have;
            memcpy(buf, state->next, n);
            state->next += n;
            state->have -= n;
        }

        /* output buffer empty -- return if we're at the end of the input */
        else if (state->eof && strm->avail_in == 0)
            break;

        /* need output data -- for small len or new stream load up our output
716
         * buffer */
717 718 719 720
        else if (state->how == LOOK || len < (state->size << 1)) {
            /* get more output, looking for header if required */
            if (xz_make(state) == -1)
                return -1;
721
            continue;           /* no progress yet -- go back to memcpy() above */
722
            /* the copy above assures that we will leave with space in the
723
             * output buffer, allowing at least one gzungetc() to succeed */
724 725 726
        }

        /* large len -- read directly into user buffer */
727
        else if (state->how == COPY) {  /* read directly */
728 729 730 731 732
            if (xz_load(state, buf, len, &n) == -1)
                return -1;
        }

        /* large len -- decompress directly into user buffer */
733
        else {                  /* state->how == LZMA */
734 735 736 737 738 739 740 741 742 743
            strm->avail_out = len;
            strm->next_out = buf;
            if (xz_decomp(state) == -1)
                return -1;
            n = state->have;
            state->have = 0;
        }

        /* update progress */
        len -= n;
744
        buf = (char *) buf + n;
745 746 747 748 749
        got += n;
        state->pos += n;
    } while (len);

    /* return number of bytes read into user buffer (will fit in int) */
750
    return (int) got;
751 752
}

753 754
int
__libxml2_xzclose(xzFile file)
755 756 757 758 759 760 761
{
    int ret;
    xz_statep state;

    /* get internal structure */
    if (file == NULL)
        return LZMA_DATA_ERROR;
762
    state = (xz_statep) file;
763 764 765 766

    /* free memory and close file */
    if (state->size) {
        lzma_end(&(state->strm));
767 768 769 770
#ifdef HAVE_ZLIB_H
        if (state->init == 1)
            inflateEnd(&(state->zstrm));
        state->init = 0;
771
#endif
772 773
        xmlFree(state->out);
        xmlFree(state->in);
774
    }
775
    xmlFree(state->path);
776
    ret = close(state->fd);
777
    xmlFree(state);
778 779
    return ret ? ret : LZMA_OK;
}
780
#endif /* HAVE_LZMA_H */