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