xref: /freebsd/sys/contrib/zlib/gzlib.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
1 /* gzlib.c -- zlib functions common to reading and writing gzip files
2  * Copyright (C) 2004-2024 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(_WIN32) && !defined(__BORLANDC__)
11 #  define LSEEK _lseeki64
12 #else
13 #if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0
14 #  define LSEEK lseek64
15 #else
16 #  define LSEEK lseek
17 #endif
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. */
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);
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 */
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     }
78     else                            /* for writing ... */
79         state->reset = 0;           /* no deflateReset pending */
80     state->seek = 0;                /* no seek request pending */
81     gz_error(state, Z_OK, NULL);    /* clear error */
82     state->x.pos = 0;               /* no uncompressed data yet */
83     state->strm.avail_in = 0;       /* no input data yet */
84 }
85 
86 /* Open a gzip file either by name or file descriptor. */
87 local gzFile gz_open(const void *path, int fd, const char *mode) {
88     gz_statep state;
89     z_size_t len;
90     int oflag;
91 #ifdef O_CLOEXEC
92     int cloexec = 0;
93 #endif
94 #ifdef O_EXCL
95     int exclusive = 0;
96 #endif
97 
98     /* check input */
99     if (path == NULL)
100         return NULL;
101 
102     /* allocate gzFile structure to return */
103     state = (gz_statep)malloc(sizeof(gz_state));
104     if (state == NULL)
105         return NULL;
106     state->size = 0;            /* no buffers allocated yet */
107     state->want = GZBUFSIZE;    /* requested buffer size */
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                 cloexec = 1;
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 'T':
159                 state->direct = 1;
160                 break;
161             default:        /* could consider as an error, but just ignore */
162                 ;
163             }
164         mode++;
165     }
166 
167     /* must provide an "r", "w", or "a" */
168     if (state->mode == GZ_NONE) {
169         free(state);
170         return NULL;
171     }
172 
173     /* can't force transparent read */
174     if (state->mode == GZ_READ) {
175         if (state->direct) {
176             free(state);
177             return NULL;
178         }
179         state->direct = 1;      /* for empty file */
180     }
181 
182     /* save the path name for error messages */
183 #ifdef WIDECHAR
184     if (fd == -2) {
185         len = wcstombs(NULL, path, 0);
186         if (len == (z_size_t)-1)
187             len = 0;
188     }
189     else
190 #endif
191         len = strlen((const char *)path);
192     state->path = (char *)malloc(len + 1);
193     if (state->path == NULL) {
194         free(state);
195         return NULL;
196     }
197 #ifdef WIDECHAR
198     if (fd == -2)
199         if (len)
200             wcstombs(state->path, path, len + 1);
201         else
202             *(state->path) = 0;
203     else
204 #endif
205 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
206         (void)snprintf(state->path, len + 1, "%s", (const char *)path);
207 #else
208         strcpy(state->path, path);
209 #endif
210 
211     /* compute the flags for open() */
212     oflag =
213 #ifdef O_LARGEFILE
214         O_LARGEFILE |
215 #endif
216 #ifdef O_BINARY
217         O_BINARY |
218 #endif
219 #ifdef O_CLOEXEC
220         (cloexec ? O_CLOEXEC : 0) |
221 #endif
222         (state->mode == GZ_READ ?
223          O_RDONLY :
224          (O_WRONLY | O_CREAT |
225 #ifdef O_EXCL
226           (exclusive ? O_EXCL : 0) |
227 #endif
228           (state->mode == GZ_WRITE ?
229            O_TRUNC :
230            O_APPEND)));
231 
232     /* open the file with the appropriate flags (or just use fd) */
233     state->fd = fd > -1 ? fd : (
234 #ifdef WIDECHAR
235         fd == -2 ? _wopen(path, oflag, 0666) :
236 #endif
237         open((const char *)path, oflag, 0666));
238     if (state->fd == -1) {
239         free(state->path);
240         free(state);
241         return NULL;
242     }
243     if (state->mode == GZ_APPEND) {
244         LSEEK(state->fd, 0, SEEK_END);  /* so gzoffset() is correct */
245         state->mode = GZ_WRITE;         /* simplify later checks */
246     }
247 
248     /* save the current position for rewinding (only if reading) */
249     if (state->mode == GZ_READ) {
250         state->start = LSEEK(state->fd, 0, SEEK_CUR);
251         if (state->start == -1) state->start = 0;
252     }
253 
254     /* initialize stream */
255     gz_reset(state);
256 
257     /* return stream */
258     return (gzFile)state;
259 }
260 
261 /* -- see zlib.h -- */
262 gzFile ZEXPORT gzopen(const char *path, const char *mode) {
263     return gz_open(path, -1, mode);
264 }
265 
266 /* -- see zlib.h -- */
267 gzFile ZEXPORT gzopen64(const char *path, const char *mode) {
268     return gz_open(path, -1, mode);
269 }
270 
271 /* -- see zlib.h -- */
272 gzFile ZEXPORT gzdopen(int fd, const char *mode) {
273     char *path;         /* identifier for error messages */
274     gzFile gz;
275 
276     if (fd == -1 || (path = (char *)malloc(7 + 3 * sizeof(int))) == NULL)
277         return NULL;
278 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
279     (void)snprintf(path, 7 + 3 * sizeof(int), "<fd:%d>", fd);
280 #else
281     sprintf(path, "<fd:%d>", fd);   /* for debugging */
282 #endif
283     gz = gz_open(path, fd, mode);
284     free(path);
285     return gz;
286 }
287 
288 /* -- see zlib.h -- */
289 #ifdef WIDECHAR
290 gzFile ZEXPORT gzopen_w(const wchar_t *path, const char *mode) {
291     return gz_open(path, -2, mode);
292 }
293 #endif
294 
295 /* -- see zlib.h -- */
296 int ZEXPORT gzbuffer(gzFile file, unsigned size) {
297     gz_statep state;
298 
299     /* get internal structure and check integrity */
300     if (file == NULL)
301         return -1;
302     state = (gz_statep)file;
303     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
304         return -1;
305 
306     /* make sure we haven't already allocated memory */
307     if (state->size != 0)
308         return -1;
309 
310     /* check and set requested size */
311     if ((size << 1) < size)
312         return -1;              /* need to be able to double it */
313     if (size < 8)
314         size = 8;               /* needed to behave well with flushing */
315     state->want = size;
316     return 0;
317 }
318 
319 /* -- see zlib.h -- */
320 int ZEXPORT gzrewind(gzFile file) {
321     gz_statep state;
322 
323     /* get internal structure */
324     if (file == NULL)
325         return -1;
326     state = (gz_statep)file;
327 
328     /* check that we're reading and that there's no error */
329     if (state->mode != GZ_READ ||
330             (state->err != Z_OK && state->err != Z_BUF_ERROR))
331         return -1;
332 
333     /* back up and start over */
334     if (LSEEK(state->fd, state->start, SEEK_SET) == -1)
335         return -1;
336     gz_reset(state);
337     return 0;
338 }
339 
340 /* -- see zlib.h -- */
341 z_off64_t ZEXPORT gzseek64(gzFile file, z_off64_t offset, int whence) {
342     unsigned n;
343     z_off64_t ret;
344     gz_statep state;
345 
346     /* get internal structure and check integrity */
347     if (file == NULL)
348         return -1;
349     state = (gz_statep)file;
350     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
351         return -1;
352 
353     /* check that there's no error */
354     if (state->err != Z_OK && state->err != Z_BUF_ERROR)
355         return -1;
356 
357     /* can only seek from start or relative to current position */
358     if (whence != SEEK_SET && whence != SEEK_CUR)
359         return -1;
360 
361     /* normalize offset to a SEEK_CUR specification */
362     if (whence == SEEK_SET)
363         offset -= state->x.pos;
364     else if (state->seek)
365         offset += state->skip;
366     state->seek = 0;
367 
368     /* if within raw area while reading, just go there */
369     if (state->mode == GZ_READ && state->how == COPY &&
370             state->x.pos + offset >= 0) {
371         ret = LSEEK(state->fd, offset - (z_off64_t)state->x.have, SEEK_CUR);
372         if (ret == -1)
373             return -1;
374         state->x.have = 0;
375         state->eof = 0;
376         state->past = 0;
377         state->seek = 0;
378         gz_error(state, Z_OK, NULL);
379         state->strm.avail_in = 0;
380         state->x.pos += offset;
381         return state->x.pos;
382     }
383 
384     /* calculate skip amount, rewinding if needed for back seek when reading */
385     if (offset < 0) {
386         if (state->mode != GZ_READ)         /* writing -- can't go backwards */
387             return -1;
388         offset += state->x.pos;
389         if (offset < 0)                     /* before start of file! */
390             return -1;
391         if (gzrewind(file) == -1)           /* rewind, then skip to offset */
392             return -1;
393     }
394 
395     /* if reading, skip what's in output buffer (one less gzgetc() check) */
396     if (state->mode == GZ_READ) {
397         n = GT_OFF(state->x.have) || (z_off64_t)state->x.have > offset ?
398             (unsigned)offset : state->x.have;
399         state->x.have -= n;
400         state->x.next += n;
401         state->x.pos += n;
402         offset -= n;
403     }
404 
405     /* request skip (if not zero) */
406     if (offset) {
407         state->seek = 1;
408         state->skip = offset;
409     }
410     return state->x.pos + offset;
411 }
412 
413 /* -- see zlib.h -- */
414 z_off_t ZEXPORT gzseek(gzFile file, z_off_t offset, int whence) {
415     z_off64_t ret;
416 
417     ret = gzseek64(file, (z_off64_t)offset, whence);
418     return ret == (z_off_t)ret ? (z_off_t)ret : -1;
419 }
420 
421 /* -- see zlib.h -- */
422 z_off64_t ZEXPORT gztell64(gzFile file) {
423     gz_statep state;
424 
425     /* get internal structure and check integrity */
426     if (file == NULL)
427         return -1;
428     state = (gz_statep)file;
429     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
430         return -1;
431 
432     /* return position */
433     return state->x.pos + (state->seek ? state->skip : 0);
434 }
435 
436 /* -- see zlib.h -- */
437 z_off_t ZEXPORT gztell(gzFile file) {
438     z_off64_t ret;
439 
440     ret = gztell64(file);
441     return ret == (z_off_t)ret ? (z_off_t)ret : -1;
442 }
443 
444 /* -- see zlib.h -- */
445 z_off64_t ZEXPORT gzoffset64(gzFile file) {
446     z_off64_t offset;
447     gz_statep state;
448 
449     /* get internal structure and check integrity */
450     if (file == NULL)
451         return -1;
452     state = (gz_statep)file;
453     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
454         return -1;
455 
456     /* compute and return effective offset in file */
457     offset = LSEEK(state->fd, 0, SEEK_CUR);
458     if (offset == -1)
459         return -1;
460     if (state->mode == GZ_READ)             /* reading */
461         offset -= state->strm.avail_in;     /* don't count buffered input */
462     return offset;
463 }
464 
465 /* -- see zlib.h -- */
466 z_off_t ZEXPORT gzoffset(gzFile file) {
467     z_off64_t ret;
468 
469     ret = gzoffset64(file);
470     return ret == (z_off_t)ret ? (z_off_t)ret : -1;
471 }
472 
473 /* -- see zlib.h -- */
474 int ZEXPORT gzeof(gzFile file) {
475     gz_statep state;
476 
477     /* get internal structure and check integrity */
478     if (file == NULL)
479         return 0;
480     state = (gz_statep)file;
481     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
482         return 0;
483 
484     /* return end-of-file state */
485     return state->mode == GZ_READ ? state->past : 0;
486 }
487 
488 /* -- see zlib.h -- */
489 const char * ZEXPORT gzerror(gzFile file, int *errnum) {
490     gz_statep state;
491 
492     /* get internal structure and check integrity */
493     if (file == NULL)
494         return NULL;
495     state = (gz_statep)file;
496     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
497         return NULL;
498 
499     /* return error information */
500     if (errnum != NULL)
501         *errnum = state->err;
502     return state->err == Z_MEM_ERROR ? "out of memory" :
503                                        (state->msg == NULL ? "" : state->msg);
504 }
505 
506 /* -- see zlib.h -- */
507 void ZEXPORT gzclearerr(gzFile file) {
508     gz_statep state;
509 
510     /* get internal structure and check integrity */
511     if (file == NULL)
512         return;
513     state = (gz_statep)file;
514     if (state->mode != GZ_READ && state->mode != GZ_WRITE)
515         return;
516 
517     /* clear error and end-of-file */
518     if (state->mode == GZ_READ) {
519         state->eof = 0;
520         state->past = 0;
521     }
522     gz_error(state, Z_OK, NULL);
523 }
524 
525 /* Create an error message in allocated memory and set state->err and
526    state->msg accordingly.  Free any previous error message already there.  Do
527    not try to free or allocate space if the error is Z_MEM_ERROR (out of
528    memory).  Simply save the error message as a static string.  If there is an
529    allocation failure constructing the error message, then convert the error to
530    out of memory. */
531 void ZLIB_INTERNAL gz_error(gz_statep state, int err, const char *msg) {
532     /* free previously allocated message and clear */
533     if (state->msg != NULL) {
534         if (state->err != Z_MEM_ERROR)
535             free(state->msg);
536         state->msg = NULL;
537     }
538 
539     /* if fatal, set state->x.have to 0 so that the gzgetc() macro fails */
540     if (err != Z_OK && err != Z_BUF_ERROR)
541         state->x.have = 0;
542 
543     /* set error code, and if no message, then done */
544     state->err = err;
545     if (msg == NULL)
546         return;
547 
548     /* for an out of memory error, return literal string when requested */
549     if (err == Z_MEM_ERROR)
550         return;
551 
552     /* construct error message with path */
553     if ((state->msg = (char *)malloc(strlen(state->path) + strlen(msg) + 3)) ==
554             NULL) {
555         state->err = Z_MEM_ERROR;
556         return;
557     }
558 #if !defined(NO_snprintf) && !defined(NO_vsnprintf)
559     (void)snprintf(state->msg, strlen(state->path) + strlen(msg) + 3,
560                    "%s%s%s", state->path, ": ", msg);
561 #else
562     strcpy(state->msg, state->path);
563     strcat(state->msg, ": ");
564     strcat(state->msg, msg);
565 #endif
566 }
567 
568 /* portably return maximum value for an int (when limits.h presumed not
569    available) -- we need to do this to cover cases where 2's complement not
570    used, since C standard permits 1's complement and sign-bit representations,
571    otherwise we could just use ((unsigned)-1) >> 1 */
572 unsigned ZLIB_INTERNAL gz_intmax(void) {
573 #ifdef INT_MAX
574     return INT_MAX;
575 #else
576     unsigned p = 1, q;
577     do {
578         q = p;
579         p <<= 1;
580         p++;
581     } while (p > q);
582     return q >> 1;
583 #endif
584 }
585