1/* gzlib.c -- zlib functions common to reading and writing gzip files
2 * Copyright (C) 2004-2017 Mark Adler
3 * For conditions of distribution and use, see copyright notice in zlib.h
4 */
5
6/* $FreeBSD: stable/11/sys/contrib/zlib/gzlib.c 312335 2017-01-17 05:55:47Z delphij $ */
7
8#include "gzguts.h"
9#include "zutil.h"
10
11#if defined(_WIN32) && !defined(__BORLANDC__) && !defined(__MINGW32__)
12#  define LSEEK _lseeki64
13#else
14#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
15#  define LSEEK lseek64
16#else
17#  define LSEEK lseek
18#endif
19#endif
20
21/* Local functions */
22local void gz_reset OF((gz_statep));
23local gzFile gz_open OF((const void *, int, const char *));
24
25#if defined UNDER_CE
26
27/* Map the Windows error number in ERROR to a locale-dependent error message
28   string and return a pointer to it.  Typically, the values for ERROR come
29   from GetLastError.
30
31   The string pointed to shall not be modified by the application, but may be
32   overwritten by a subsequent call to gz_strwinerror
33
34   The gz_strwinerror function does not change the current setting of
35   GetLastError. */
36char ZLIB_INTERNAL *gz_strwinerror (error)
37     DWORD error;
38{
39    static char buf[1024];
40
41    wchar_t *msgbuf;
42    DWORD lasterr = GetLastError();
43    DWORD chars = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
44        | FORMAT_MESSAGE_ALLOCATE_BUFFER,
45        NULL,
46        error,
47        0, /* Default language */
48        (LPVOID)&msgbuf,
49        0,
50        NULL);
51    if (chars != 0) {
52        /* If there is an \r\n appended, zap it.  */
53        if (chars >= 2
54            && msgbuf[chars - 2] == '\r' && msgbuf[chars - 1] == '\n') {
55            chars -= 2;
56            msgbuf[chars] = 0;
57        }
58
59        if (chars > sizeof (buf) - 1) {
60            chars = sizeof (buf) - 1;
61            msgbuf[chars] = 0;
62        }
63
64        wcstombs(buf, msgbuf, chars + 1);
65        LocalFree(msgbuf);
66    }
67    else {
68        sprintf(buf, "unknown win32 error (%ld)", error);
69    }
70
71    SetLastError(lasterr);
72    return buf;
73}
74
75#endif /* UNDER_CE */
76
77/* Reset gzip file state */
78local void gz_reset(state)
79    gz_statep state;
80{
81    state->x.have = 0;              /* no output data available */
82    if (state->mode == GZ_READ) {   /* for reading ... */
83        state->eof = 0;             /* not at end of file */
84        state->past = 0;            /* have not read past end yet */
85        state->how = LOOK;          /* look for gzip header */
86    }
87    state->seek = 0;                /* no seek request pending */
88    gz_error(state, Z_OK, NULL);    /* clear error */
89    state->x.pos = 0;               /* no uncompressed data yet */
90    state->strm.avail_in = 0;       /* no input data yet */
91}
92
93/* Open a gzip file either by name or file descriptor. */
94local gzFile gz_open(path, fd, mode)
95    const void *path;
96    int fd;
97    const char *mode;
98{
99    gz_statep state;
100    z_size_t len;
101    int oflag;
102#ifdef O_CLOEXEC
103    int cloexec = 0;
104#endif
105#ifdef O_EXCL
106    int exclusive = 0;
107#endif
108
109    /* check input */
110    if (path == NULL)
111        return NULL;
112
113    /* allocate gzFile structure to return */
114    state = (gz_statep)malloc(sizeof(gz_state));
115    if (state == NULL)
116        return NULL;
117    state->size = 0;            /* no buffers allocated yet */
118    state->want = GZBUFSIZE;    /* requested buffer size */
119    state->msg = NULL;          /* no error message yet */
120
121    /* interpret mode */
122    state->mode = GZ_NONE;
123    state->level = Z_DEFAULT_COMPRESSION;
124    state->strategy = Z_DEFAULT_STRATEGY;
125    state->direct = 0;
126    while (*mode) {
127        if (*mode >= '0' && *mode <= '9')
128            state->level = *mode - '0';
129        else
130            switch (*mode) {
131            case 'r':
132                state->mode = GZ_READ;
133                break;
134#ifndef NO_GZCOMPRESS
135            case 'w':
136                state->mode = GZ_WRITE;
137                break;
138            case 'a':
139                state->mode = GZ_APPEND;
140                break;
141#endif
142            case '+':       /* can't read and write at the same time */
143                free(state);
144                return NULL;
145            case 'b':       /* ignore -- will request binary anyway */
146                break;
147#ifdef O_CLOEXEC
148            case 'e':
149                cloexec = 1;
150                break;
151#endif
152#ifdef O_EXCL
153            case 'x':
154                exclusive = 1;
155                break;
156#endif
157            case 'f':
158                state->strategy = Z_FILTERED;
159                break;
160            case 'h':
161                state->strategy = Z_HUFFMAN_ONLY;
162                break;
163            case 'R':
164                state->strategy = Z_RLE;
165                break;
166            case 'F':
167                state->strategy = Z_FIXED;
168                break;
169            case 'T':
170                state->direct = 1;
171                break;
172            default:        /* could consider as an error, but just ignore */
173                ;
174            }
175        mode++;
176    }
177
178    /* must provide an "r", "w", or "a" */
179    if (state->mode == GZ_NONE) {
180        free(state);
181        return NULL;
182    }
183
184    /* can't force transparent read */
185    if (state->mode == GZ_READ) {
186        if (state->direct) {
187            free(state);
188            return NULL;
189        }
190        state->direct = 1;      /* for empty file */
191    }
192
193    /* save the path name for error messages */
194#ifdef WIDECHAR
195    if (fd == -2) {
196        len = wcstombs(NULL, path, 0);
197        if (len == (z_size_t)-1)
198            len = 0;
199    }
200    else
201#endif
202        len = strlen((const char *)path);
203    state->path = (char *)malloc(len + 1);
204    if (state->path == NULL) {
205        free(state);
206        return NULL;
207    }
208#ifdef WIDECHAR
209    if (fd == -2)
210        if (len)
211            wcstombs(state->path, path, len + 1);
212        else
213            *(state->path) = 0;
214    else
215#endif
216#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
217        (void)snprintf(state->path, len + 1, "%s", (const char *)path);
218#else
219        strcpy(state->path, path);
220#endif
221
222    /* compute the flags for open() */
223    oflag =
224#ifdef O_LARGEFILE
225        O_LARGEFILE |
226#endif
227#ifdef O_BINARY
228        O_BINARY |
229#endif
230#ifdef O_CLOEXEC
231        (cloexec ? O_CLOEXEC : 0) |
232#endif
233        (state->mode == GZ_READ ?
234         O_RDONLY :
235         (O_WRONLY | O_CREAT |
236#ifdef O_EXCL
237          (exclusive ? O_EXCL : 0) |
238#endif
239          (state->mode == GZ_WRITE ?
240           O_TRUNC :
241           O_APPEND)));
242
243    /* open the file with the appropriate flags (or just use fd) */
244    state->fd = fd > -1 ? fd : (
245#ifdef WIDECHAR
246        fd == -2 ? _wopen(path, oflag, 0666) :
247#endif
248        open((const char *)path, oflag, 0666));
249    if (state->fd == -1) {
250        free(state->path);
251        free(state);
252        return NULL;
253    }
254    if (state->mode == GZ_APPEND) {
255        LSEEK(state->fd, 0, SEEK_END);  /* so gzoffset() is correct */
256        state->mode = GZ_WRITE;         /* simplify later checks */
257    }
258
259    /* save the current position for rewinding (only if reading) */
260    if (state->mode == GZ_READ) {
261        state->start = LSEEK(state->fd, 0, SEEK_CUR);
262        if (state->start == -1) state->start = 0;
263    }
264
265    /* initialize stream */
266    gz_reset(state);
267
268    /* return stream */
269    return (gzFile)state;
270}
271
272/* -- see zlib.h -- */
273gzFile ZEXPORT gzopen(path, mode)
274    const char *path;
275    const char *mode;
276{
277    return gz_open(path, -1, mode);
278}
279
280/* -- see zlib.h -- */
281gzFile ZEXPORT gzopen64(path, mode)
282    const char *path;
283    const char *mode;
284{
285    return gz_open(path, -1, mode);
286}
287
288/* -- see zlib.h -- */
289gzFile ZEXPORT gzdopen(fd, mode)
290    int fd;
291    const char *mode;
292{
293    char *path;         /* identifier for error messages */
294    gzFile gz;
295
296    if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)
297        return NULL;
298#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
299    (void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd);
300#else
301    sprintf(path, "<fd:%d>", fd);   /* for debugging */
302#endif
303    gz = gz_open(path, fd, mode);
304    free(path);
305    return gz;
306}
307
308/* -- see zlib.h -- */
309#ifdef WIDECHAR
310gzFile ZEXPORT gzopen_w(path, mode)
311    const wchar_t *path;
312    const char *mode;
313{
314    return gz_open(path, -2, mode);
315}
316#endif
317
318/* -- see zlib.h -- */
319int ZEXPORT gzbuffer(file, size)
320    gzFile file;
321    unsigned size;
322{
323    gz_statep state;
324
325    /* get internal structure and check integrity */
326    if (file == NULL)
327        return -1;
328    state = (gz_statep)file;
329    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
330        return -1;
331
332    /* make sure we haven't already allocated memory */
333    if (state->size != 0)
334        return -1;
335
336    /* check and set requested size */
337    if ((size << 1) < size)
338        return -1;              /* need to be able to double it */
339    if (size < 2)
340        size = 2;               /* need two bytes to check magic header */
341    state->want = size;
342    return 0;
343}
344
345/* -- see zlib.h -- */
346int ZEXPORT gzrewind(file)
347    gzFile file;
348{
349    gz_statep state;
350
351    /* get internal structure */
352    if (file == NULL)
353        return -1;
354    state = (gz_statep)file;
355
356    /* check that we're reading and that there's no error */
357    if (state->mode != GZ_READ ||
358            (state->err != Z_OK && state->err != Z_BUF_ERROR))
359        return -1;
360
361    /* back up and start over */
362    if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
363        return -1;
364    gz_reset(state);
365    return 0;
366}
367
368/* -- see zlib.h -- */
369z_off64_t ZEXPORT gzseek64(file, offset, whence)
370    gzFile file;
371    z_off64_t offset;
372    int whence;
373{
374    unsigned n;
375    z_off64_t ret;
376    gz_statep state;
377
378    /* get internal structure and check integrity */
379    if (file == NULL)
380        return -1;
381    state = (gz_statep)file;
382    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
383        return -1;
384
385    /* check that there's no error */
386    if (state->err != Z_OK && state->err != Z_BUF_ERROR)
387        return -1;
388
389    /* can only seek from start or relative to current position */
390    if (whence != SEEK_SET && whence != SEEK_CUR)
391        return -1;
392
393    /* normalize offset to a SEEK_CUR specification */
394    if (whence == SEEK_SET)
395        offset -= state->x.pos;
396    else if (state->seek)
397        offset += state->skip;
398    state->seek = 0;
399
400    /* if within raw area while reading, just go there */
401    if (state->mode == GZ_READ && state->how == COPY &&
402            state->x.pos + offset >= 0) {
403        ret = LSEEK(state->fd, offset - state->x.have, SEEK_CUR);
404        if (ret == -1)
405            return -1;
406        state->x.have = 0;
407        state->eof = 0;
408        state->past = 0;
409        state->seek = 0;
410        gz_error(state, Z_OK, NULL);
411        state->strm.avail_in = 0;
412        state->x.pos += offset;
413        return state->x.pos;
414    }
415
416    /* calculate skip amount, rewinding if needed for back seek when reading */
417    if (offset < 0) {
418        if (state->mode != GZ_READ)         /* writing -- can't go backwards */
419            return -1;
420        offset += state->x.pos;
421        if (offset < 0)                     /* before start of file! */
422            return -1;
423        if (gzrewind(file) == -1)           /* rewind, then skip to offset */
424            return -1;
425    }
426
427    /* if reading, skip what's in output buffer (one less gzgetc() check) */
428    if (state->mode == GZ_READ) {
429        n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
430            (unsigned)offset : state->x.have;
431        state->x.have -= n;
432        state->x.next += n;
433        state->x.pos += n;
434        offset -= n;
435    }
436
437    /* request skip (if not zero) */
438    if (offset) {
439        state->seek = 1;
440        state->skip = offset;
441    }
442    return state->x.pos + offset;
443}
444
445/* -- see zlib.h -- */
446z_off_t ZEXPORT gzseek(file, offset, whence)
447    gzFile file;
448    z_off_t offset;
449    int whence;
450{
451    z_off64_t ret;
452
453    ret = gzseek64(file, (z_off64_t)offset, whence);
454    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
455}
456
457/* -- see zlib.h -- */
458z_off64_t ZEXPORT gztell64(file)
459    gzFile file;
460{
461    gz_statep state;
462
463    /* get internal structure and check integrity */
464    if (file == NULL)
465        return -1;
466    state = (gz_statep)file;
467    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
468        return -1;
469
470    /* return position */
471    return state->x.pos + (state->seek ? state->skip : 0);
472}
473
474/* -- see zlib.h -- */
475z_off_t ZEXPORT gztell(file)
476    gzFile file;
477{
478    z_off64_t ret;
479
480    ret = gztell64(file);
481    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
482}
483
484/* -- see zlib.h -- */
485z_off64_t ZEXPORT gzoffset64(file)
486    gzFile file;
487{
488    z_off64_t offset;
489    gz_statep state;
490
491    /* get internal structure and check integrity */
492    if (file == NULL)
493        return -1;
494    state = (gz_statep)file;
495    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
496        return -1;
497
498    /* compute and return effective offset in file */
499    offset = LSEEK(state->fd, 0, SEEK_CUR);
500    if (offset == -1)
501        return -1;
502    if (state->mode == GZ_READ)             /* reading */
503        offset -= state->strm.avail_in;     /* don't count buffered input */
504    return offset;
505}
506
507/* -- see zlib.h -- */
508z_off_t ZEXPORT gzoffset(file)
509    gzFile file;
510{
511    z_off64_t ret;
512
513    ret = gzoffset64(file);
514    return ret == (z_off_t)ret ? (z_off_t)ret : -1;
515}
516
517/* -- see zlib.h -- */
518int ZEXPORT gzeof(file)
519    gzFile file;
520{
521    gz_statep state;
522
523    /* get internal structure and check integrity */
524    if (file == NULL)
525        return 0;
526    state = (gz_statep)file;
527    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
528        return 0;
529
530    /* return end-of-file state */
531    return state->mode == GZ_READ ? state->past : 0;
532}
533
534/* -- see zlib.h -- */
535const char * ZEXPORT gzerror(file, errnum)
536    gzFile file;
537    int *errnum;
538{
539    gz_statep state;
540
541    /* get internal structure and check integrity */
542    if (file == NULL)
543        return NULL;
544    state = (gz_statep)file;
545    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
546        return NULL;
547
548    /* return error information */
549    if (errnum != NULL)
550        *errnum = state->err;
551    return state->err == Z_MEM_ERROR ? "out of memory" :
552                                       (state->msg == NULL ? "" : state->msg);
553}
554
555/* -- see zlib.h -- */
556void ZEXPORT gzclearerr(file)
557    gzFile file;
558{
559    gz_statep state;
560
561    /* get internal structure and check integrity */
562    if (file == NULL)
563        return;
564    state = (gz_statep)file;
565    if (state->mode != GZ_READ && state->mode != GZ_WRITE)
566        return;
567
568    /* clear error and end-of-file */
569    if (state->mode == GZ_READ) {
570        state->eof = 0;
571        state->past = 0;
572    }
573    gz_error(state, Z_OK, NULL);
574}
575
576/* Create an error message in allocated memory and set state->err and
577   state->msg accordingly.  Free any previous error message already there.  Do
578   not try to free or allocate space if the error is Z_MEM_ERROR (out of
579   memory).  Simply save the error message as a static string.  If there is an
580   allocation failure constructing the error message, then convert the error to
581   out of memory. */
582void ZLIB_INTERNAL gz_error(state, err, msg)
583    gz_statep state;
584    int err;
585    const char *msg;
586{
587    /* free previously allocated message and clear */
588    if (state->msg != NULL) {
589        if (state->err != Z_MEM_ERROR)
590            free(state->msg);
591        state->msg = NULL;
592    }
593
594    /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
595    if (err != Z_OK && err != Z_BUF_ERROR)
596        state->x.have = 0;
597
598    /* set error code, and if no message, then done */
599    state->err = err;
600    if (msg == NULL)
601        return;
602
603    /* for an out of memory error, return literal string when requested */
604    if (err == Z_MEM_ERROR)
605        return;
606
607    /* construct error message with path */
608    if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==
609            NULL) {
610        state->err = Z_MEM_ERROR;
611        return;
612    }
613#if !defined(NO_snprintf) && !defined(NO_vsnprintf)
614    (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,
615                   "%s%s%s", state->path, ": ", msg);
616#else
617    strcpy(state->msg, state->path);
618    strcat(state->msg, ": ");
619    strcat(state->msg, msg);
620#endif
621}
622
623#ifndef INT_MAX
624/* portably return maximum value for an int (when limits.h presumed not
625   available) -- we need to do this to cover cases where 2's complement not
626   used, since C standard permits 1's complement and sign-bit representations,
627   otherwise we could just use ((unsigned)-1) >> 1 */
628unsigned ZLIB_INTERNAL gz_intmax()
629{
630    unsigned p, q;
631
632    p = 1;
633    do {
634        q = p;
635        p <<= 1;
636        p++;
637    } while (p > q);
638    return q >> 1;
639}
640#endif
641