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