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