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