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