2bb2ce78f39235f0b7baa59f5f3f65af06b93dde
[obnox/wireshark/wip.git] / wiretap / file_wrappers.c
1 /* file_wrappers.c
2  *
3  * $Id$
4  *
5  * Wiretap Library
6  * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21  */
22
23 /* file_access interface based heavily on zlib gzread.c and gzlib.c from zlib
24  * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
25  * under licence:
26  *
27  *  This software is provided 'as-is', without any express or implied
28  *  warranty.  In no event will the authors be held liable for any damages
29  *  arising from the use of this software.
30  *
31  *  Permission is granted to anyone to use this software for any purpose,
32  *  including commercial applications, and to alter it and redistribute it
33  *  freely, subject to the following restrictions:
34  *
35  *  1. The origin of this software must not be misrepresented; you must not
36  *     claim that you wrote the original software. If you use this software
37  *     in a product, an acknowledgment in the product documentation would be
38  *     appreciated but is not required.
39  *  2. Altered source versions must be plainly marked as such, and must not be
40  *     misrepresented as being the original software.
41  *  3. This notice may not be removed or altered from any source distribution.
42 */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
48 #ifdef HAVE_UNISTD_H
49 #include <unistd.h>
50 #endif /* HAVE_UNISTD_H */
51
52 #include <errno.h>
53 #include <stdio.h>
54 #ifdef HAVE_FCNTL_H
55 #include <fcntl.h>
56 #endif /* HAVE_FCNTL_H */
57 #include <string.h>
58 #include "wtap-int.h"
59 #include "file_wrappers.h"
60 #include <wsutil/file_util.h>
61
62 /*
63  * See RFC 1952 for a description of the gzip file format.
64  *
65  * Some other compressed file formats we might want to support:
66  *
67  *      XZ format: http://tukaani.org/xz/
68  *
69  *      Bzip2 format: http://bzip.org/
70  */
71
72 /* #define GZBUFSIZE 8192 */
73 #define GZBUFSIZE 4096
74
75 struct wtap_reader {
76         int fd;                 /* file descriptor */
77         gint64 pos;             /* current position in uncompressed data */
78         unsigned size;          /* buffer size */
79         unsigned char *in;      /* input buffer */
80         unsigned char *out;     /* output buffer (double-sized when reading) */
81         unsigned char *next;    /* next output data to deliver or write */
82
83         unsigned have;          /* amount of output data unused at next */
84         int eof;                /* true if end of input file reached */
85         gint64 start;           /* where the gzip data started, for rewinding */
86         gint64 raw;             /* where the raw data started, for seeking */
87         int compression;        /* 0: ?, 1: uncompressed, 2: zlib */
88         /* seek request */
89         gint64 skip;            /* amount to skip (already rewound if backwards) */
90         int seek;               /* true if seek request pending */
91         /* error information */
92         int err;                /* error code */
93
94         unsigned int  avail_in;  /* number of bytes available at next_in */
95         unsigned char *next_in;  /* next input byte */
96 #ifdef HAVE_LIBZ
97         /* zlib inflate stream */
98         z_stream strm;          /* stream structure in-place (not a pointer) */
99 #endif
100 };
101
102 /* values for gz_state compression */
103 #define UNKNOWN         0       /* look for a gzip header */
104 #define UNCOMPRESSED    1       /* copy input directly */
105 #ifdef HAVE_LIBZ
106 #define ZLIB            2       /* decompress a zlib stream */
107 #endif
108
109 static int      /* gz_load */
110 raw_read(FILE_T state, unsigned char *buf, unsigned int count, unsigned *have)
111 {
112         int ret;
113
114         *have = 0;
115         do {
116                 ret = read(state->fd, buf + *have, count - *have);
117                 if (ret <= 0)
118                         break;
119                 *have += ret;
120         } while (*have < count);
121         if (ret < 0) {
122                 state->err = errno;
123                 return -1;
124         }
125         if (ret == 0)
126                 state->eof = 1;
127         return 0;
128 }
129
130 static int /* gz_avail */
131 fill_in_buffer(FILE_T state)
132 {
133         if (state->err)
134                 return -1;
135         if (state->eof == 0) {
136                 if (raw_read(state, state->in, state->size, (unsigned *)&(state->avail_in)) == -1)
137                         return -1;
138                 state->next_in = state->in;
139         }
140         return 0;
141 }
142
143 #ifdef HAVE_LIBZ
144
145 /* Get next byte from input, or -1 if end or error. */
146 #define NEXT() ((state->avail_in == 0 && fill_in_buffer(state) == -1) ? -1 : \
147                 (state->avail_in == 0 ? -1 : \
148                  (state->avail_in--, *(state->next_in)++)))
149
150 /* Get a four-byte little-endian integer and return 0 on success and the value
151    in *ret.  Otherwise -1 is returned and *ret is not modified. */
152 static int
153 gz_next4(FILE_T state, guint32 *ret)
154 {
155         guint32 val;
156         int ch;
157
158         val = NEXT();
159         val += (unsigned)NEXT() << 8;
160         val += (guint32)NEXT() << 16;
161         ch = NEXT();
162         if (ch == -1)
163                 return -1;
164         val += (guint32)ch << 24;
165         *ret = val;
166         return 0;
167 }
168
169 static int /* gz_decomp */
170 zlib_read(FILE_T state, unsigned char *buf, unsigned int count)
171 {
172         int ret;
173         guint32 crc, len;
174         z_streamp strm = &(state->strm);
175
176         strm->avail_out = count;
177         strm->next_out = buf;
178
179         /* fill output buffer up to end of deflate stream */
180         do {
181                 /* get more input for inflate() */
182                 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
183                         return -1;
184                 if (state->avail_in == 0) {
185                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
186                         return -1;
187                 }
188
189                 strm->avail_in = state->avail_in;
190                 strm->next_in = state->next_in;
191                 /* decompress and handle errors */
192                 ret = inflate(strm, Z_NO_FLUSH);
193                 state->avail_in = strm->avail_in;
194                 state->next_in = strm->next_in;
195                 if (ret == Z_STREAM_ERROR || ret == Z_NEED_DICT) {
196                         state->err = WTAP_ERR_ZLIB + Z_STREAM_ERROR;
197                         return -1;
198                 }
199                 if (ret == Z_MEM_ERROR) {
200                         state->err = WTAP_ERR_ZLIB + Z_MEM_ERROR; /* ENOMEM? */
201                         return -1;
202                 }
203                 if (ret == Z_DATA_ERROR) {              /* deflate stream invalid */
204                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
205                         return -1;
206                 }
207         } while (strm->avail_out && ret != Z_STREAM_END);
208
209         /* update available output and crc check value */
210         state->next = buf;
211         state->have = count - strm->avail_out;
212         strm->adler = crc32(strm->adler, state->next, state->have);
213
214         /* check gzip trailer if at end of deflate stream */
215         if (ret == Z_STREAM_END) {
216                 if (gz_next4(state, &crc) == -1 || gz_next4(state, &len) == -1) {
217                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
218                         return -1;
219                 }
220                 if (crc != strm->adler) {
221                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
222                         return -1;
223                 }
224                 if (len != (strm->total_out & 0xffffffffL)) {
225                         state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
226                         return -1;
227                 }
228                 state->compression = UNKNOWN;      /* ready for next stream, once have is 0 */
229         }
230
231         /* good decompression */
232         return 0;
233 }
234 #endif
235
236 static int
237 gz_head(FILE_T state)
238 {
239         /* get some data in the input buffer */
240         if (state->avail_in == 0) {
241                 if (fill_in_buffer(state) == -1)
242                         return -1;
243                 if (state->avail_in == 0)
244                         return 0;
245         }
246
247         /* look for the gzip magic header bytes 31 and 139 */
248 #ifdef HAVE_LIBZ
249         if (state->next_in[0] == 31) {
250                 state->avail_in--;
251                 state->next_in++;
252                 if (state->avail_in == 0 && fill_in_buffer(state) == -1)
253                         return -1;
254                 if (state->avail_in && state->next_in[0] == 139) {
255                         unsigned len;
256                         int flags;
257
258                         /* we have a gzip header, woo hoo! */
259                         state->avail_in--;
260                         state->next_in++;
261
262                         /* skip rest of header */
263                         if (NEXT() != 8) {      /* compression method */
264                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
265                                 return -1;
266                         }
267                         flags = NEXT();
268                         if (flags & 0xe0) {     /* reserved flag bits */
269                                 state->err = WTAP_ERR_ZLIB + Z_DATA_ERROR;
270                                 return -1;
271                         }
272                         NEXT();                 /* modification time */
273                         NEXT();
274                         NEXT();
275                         NEXT();
276                         NEXT();                 /* extra flags */
277                         NEXT();                 /* operating system */
278                         if (flags & 4) {        /* extra field */
279                                 len = (unsigned)NEXT();
280                                 len += (unsigned)NEXT() << 8;
281                                 while (len--)
282                                         if (NEXT() < 0)
283                                                 break;
284                         }
285                         if (flags & 8)          /* file name */
286                                 while (NEXT() > 0)
287                                         ;
288                         if (flags & 16)         /* comment */
289                                 while (NEXT() > 0)
290                                         ;
291                         if (flags & 2) {        /* header crc */
292                                 NEXT();
293                                 NEXT();
294                         }
295                         /* an unexpected end of file is not checked for here -- it will be
296                            noticed on the first request for uncompressed data */
297
298                         /* set up for decompression */
299                         inflateReset(&(state->strm));
300                         state->strm.adler = crc32(0L, Z_NULL, 0);
301                         state->compression = ZLIB;
302                         return 0;
303                 }
304                 else {
305                         /* not a gzip file -- save first byte (31) and fall to raw i/o */
306                         state->out[0] = 31;
307                         state->have = 1;
308                 }
309         }
310 #endif
311 #ifdef HAVE_LIBXZ
312         /* { 0xFD, '7', 'z', 'X', 'Z', 0x00 } */
313         /* FD 37 7A 58 5A 00 */
314 #endif
315
316         /* doing raw i/o, save start of raw data for seeking, copy any leftover
317            input to output -- this assumes that the output buffer is larger than
318            the input buffer, which also assures space for gzungetc() */
319         state->raw = state->pos;
320         state->next = state->out;
321         if (state->avail_in) {
322                 memcpy(state->next + state->have, state->next_in, state->avail_in);
323                 state->have += state->avail_in;
324                 state->avail_in = 0;
325         }
326         state->compression = UNCOMPRESSED;
327         return 0;
328 }
329
330 static int /* gz_make */
331 fill_out_buffer(FILE_T state)
332 {
333         if (state->compression == UNKNOWN) {           /* look for gzip header */
334                 if (gz_head(state) == -1)
335                         return -1;
336                 if (state->have)                /* got some data from gz_head() */
337                         return 0;
338         }
339         if (state->compression == UNCOMPRESSED) {           /* straight copy */
340                 if (raw_read(state, state->out, state->size /* << 1 */, &(state->have)) == -1)
341                         return -1;
342                 state->next = state->out;
343         }
344 #ifdef HAVE_LIBZ
345         else if (state->compression == ZLIB) {      /* decompress */
346                 if (zlib_read(state, state->out, state->size << 1) == -1)
347                         return -1;
348         }
349 #endif
350         return 0;
351 }
352
353 static int
354 gz_skip(FILE_T state, gint64 len)
355 {
356         unsigned n;
357
358         /* skip over len bytes or reach end-of-file, whichever comes first */
359         while (len)
360                 /* skip over whatever is in output buffer */
361                 if (state->have) {
362                         n = (gint64)state->have > len ? (unsigned)len : state->have;
363                         state->have -= n;
364                         state->next += n;
365                         state->pos += n;
366                         len -= n;
367                 }
368
369         /* output buffer empty -- return if we're at the end of the input */
370                 else if (state->eof && state->avail_in == 0)
371                         break;
372
373         /* need more data to skip -- load up output buffer */
374                 else {
375                         /* get more output, looking for header if required */
376                         if (fill_out_buffer(state) == -1)
377                                 return -1;
378                 }
379         return 0;
380 }
381
382 static void
383 gz_reset(FILE_T state)
384 {
385         state->have = 0;              /* no output data available */
386         state->eof = 0;               /* not at end of file */
387         state->compression = UNKNOWN; /* look for gzip header */
388
389         state->seek = 0;              /* no seek request pending */
390         state->err = 0;               /* clear error */
391         state->pos = 0;               /* no uncompressed data yet */
392         state->avail_in = 0;          /* no input data yet */
393 }
394
395 FILE_T
396 filed_open(int fd)
397 {
398 #ifdef _STATBUF_ST_BLKSIZE      /* XXX, _STATBUF_ST_BLKSIZE portable? */
399         struct stat st;
400 #endif
401         int want = GZBUFSIZE;
402         FILE_T state;
403
404         if (fd == -1)
405                 return NULL;
406
407         /* allocate gzFile structure to return */
408         state = g_try_malloc(sizeof *state);
409         if (state == NULL)
410                 return NULL;
411
412         /* open the file with the appropriate mode (or just use fd) */
413         state->fd = fd;
414
415         /* save the current position for rewinding (only if reading) */
416         state->start = ws_lseek64(state->fd, 0, SEEK_CUR);
417         if (state->start == -1) state->start = 0;
418
419         /* initialize stream */
420         gz_reset(state);
421
422 #ifdef _STATBUF_ST_BLKSIZE
423         if (fstat(fd, &st) >= 0) {
424                 want = st.st_blksize;
425                 /* XXX, verify result? */
426         }
427 #endif
428
429         /* allocate buffers */
430         state->in = g_try_malloc(want);
431         state->out = g_try_malloc(want << 1);
432         state->size = want;
433         if (state->in == NULL || state->out == NULL) {
434                 g_free(state->out);
435                 g_free(state->in);
436                 g_free(state);
437                 errno = ENOMEM;
438                 return NULL;
439         }
440
441 #ifdef HAVE_LIBZ
442         /* allocate inflate memory */
443         state->strm.zalloc = Z_NULL;
444         state->strm.zfree = Z_NULL;
445         state->strm.opaque = Z_NULL;
446         state->strm.avail_in = 0;
447         state->strm.next_in = Z_NULL;
448         if (inflateInit2(&(state->strm), -15) != Z_OK) {    /* raw inflate */
449                 g_free(state->out);
450                 g_free(state->in);
451                 g_free(state);
452                 errno = ENOMEM;
453                 return NULL;
454         }
455 #endif
456         gz_head(state); /* read first chunk */
457
458         /* return stream */
459         return state;
460 }
461
462 FILE_T
463 file_open(const char *path)
464 {
465         int fd;
466         FILE_T ft;
467         int oflag;
468
469         /* O_LARGEFILE? */
470         oflag = O_RDONLY;
471 #ifdef _WIN32
472         oflag |= O_BINARY;
473 #endif
474
475         /* open file and do correct filename conversions */
476         if ((fd = ws_open(path, oflag, 0666)) == -1)
477                 return NULL;
478
479         /* open file handle */
480         ft = filed_open(fd);
481         if (ft == NULL) {
482                 ws_close(fd);
483                 return NULL;
484         }
485
486         return ft;
487 }
488
489 gint64
490 file_seek(FILE_T file, gint64 offset, int whence, int *err)
491 {
492         unsigned n;
493
494         /* check that there's no error */
495         if (file->err) {
496                 *err = file->err;
497                 return -1;
498         }
499
500         /* can only seek from start or relative to current position */
501         if (whence != SEEK_SET && whence != SEEK_CUR) {
502                 g_assert_not_reached();
503 /*
504                 *err = EINVAL;
505                 return -1;
506  */
507         }
508
509         /* normalize offset to a SEEK_CUR specification */
510         if (whence == SEEK_SET)
511                 offset -= file->pos;
512         else if (file->seek)
513                 offset += file->skip;
514         file->seek = 0;
515
516         /* if within raw area while reading, just go there */
517         if (file->compression == UNCOMPRESSED && file->pos + offset >= file->raw) {
518                 if (ws_lseek64(file->fd, offset - file->have, SEEK_CUR) == -1) {
519                         *err = errno;
520                         return -1;
521                 }
522                 file->have = 0;
523                 file->eof = 0;
524                 file->seek = 0;
525                 file->err = 0;
526                 file->avail_in = 0;
527                 file->pos += offset;
528                 return file->pos;
529         }
530
531         /* calculate skip amount, rewinding if needed for back seek when reading */
532         if (offset < 0) {
533                 offset += file->pos;
534                 if (offset < 0) {                    /* before start of file! */
535                         /* *err = ???; */
536                         return -1;
537                 }
538                 /* rewind, then skip to offset */
539
540                 /* back up and start over */
541                 if (ws_lseek64(file->fd, file->start, SEEK_SET) == -1) {
542                         *err = errno;
543                         return -1;
544                 }
545                 gz_reset(file);
546         }
547
548         /* skip what's in output buffer (one less gzgetc() check) */
549         n = (gint64)file->have > offset ? (unsigned)offset : file->have;
550         file->have -= n;
551         file->next += n;
552         file->pos += n;
553         offset -= n;
554
555         /* request skip (if not zero) */
556         if (offset) {
557                 file->seek = 1;
558                 file->skip = offset;
559         }
560         return file->pos + offset;
561 }
562
563 gint64
564 file_tell(FILE_T stream)
565 {
566         /* return position */
567         return stream->pos + (stream->seek ? stream->skip : 0);
568 }
569
570 int 
571 file_read(void *buf, unsigned int len, FILE_T file)
572 {
573         unsigned got, n;
574
575         /* check that we're reading and that there's no error */
576         if (file->err)
577                 return -1;
578
579         /* if len is zero, avoid unnecessary operations */
580         if (len == 0)
581                 return 0;
582
583         /* process a skip request */
584         if (file->seek) {
585                 file->seek = 0;
586                 if (gz_skip(file, file->skip) == -1)
587                         return -1;
588         }
589
590         /* get len bytes to buf, or less than len if at the end */
591         got = 0;
592         do {
593                 /* first just try copying data from the output buffer */
594                 if (file->have) {
595                         n = file->have > len ? len : file->have;
596                         memcpy(buf, file->next, n);
597                         file->next += n;
598                         file->have -= n;
599                 }
600
601                 /* output buffer empty -- return if we're at the end of the input */
602                 else if (file->eof && file->avail_in == 0)
603                         break;
604
605                 /* need output data -- for small len or new stream load up our output buffer */
606                 else if (file->compression == UNKNOWN  || len < (file->size << 1)) {
607                         /* get more output, looking for header if required */
608                         if (fill_out_buffer(file) == -1)
609                                 return -1;
610                         continue;       /* no progress yet -- go back to memcpy() above */
611
612                 } else if (file->compression == UNCOMPRESSED) { /* large len -- read directly into user buffer */
613                         if (raw_read(file, buf, len, &n) == -1)
614                                 return -1;
615                 }
616 #ifdef HAVE_LIBZ
617                 /* large len -- decompress directly into user buffer */
618                 else {  /* file->compression == ZLIB */
619                         if (zlib_read(file, buf, len) == -1)
620                                 return -1;
621                         n = file->have;
622                         file->have = 0;
623                 }
624 #endif
625                 /* update progress */
626                 len -= n;
627                 buf = (char *)buf + n;
628                 got += n;
629                 file->pos += n;
630         } while (len);
631
632         return (int)got;
633 }
634
635 int
636 file_getc(FILE_T file)
637 {
638         unsigned char buf[1];
639         int ret;
640
641         /* check that we're reading and that there's no error */
642         if (file->err)
643                 return -1;
644
645         /* try output buffer (no need to check for skip request) */
646         if (file->have) {
647                 file->have--;
648                 file->pos++;
649                 return *(file->next)++;
650         }
651
652         ret = file_read(buf, 1, file);
653         return ret < 1 ? -1 : buf[0];
654 }
655
656 char *
657 file_gets(char *buf, int len, FILE_T file)
658 {
659         unsigned left, n;
660         char *str;
661         unsigned char *eol;
662
663         /* check parameters */
664         if (buf == NULL || len < 1)
665                 return NULL;
666
667         /* check that there's no error */
668         if (file->err)
669                 return NULL;
670
671         /* process a skip request */
672         if (file->seek) {
673                 file->seek = 0;
674                 if (gz_skip(file, file->skip) == -1)
675                         return NULL;
676         }
677
678         /* copy output bytes up to new line or len - 1, whichever comes first --
679            append a terminating zero to the string (we don't check for a zero in
680            the contents, let the user worry about that) */
681         str = buf;
682         left = (unsigned)len - 1;
683         if (left) do {
684                 /* assure that something is in the output buffer */
685                 if (file->have == 0) {
686                         if (fill_out_buffer(file) == -1)
687                                 return NULL;            /* error */
688                         if (file->have == 0)  {     /* end of file */
689                                 if (buf == str)         /* got bupkus */
690                                         return NULL;
691                                 break;                  /* got something -- return it */
692                         }
693                 }
694
695                 /* look for end-of-line in current output buffer */
696                 n = file->have > left ? left : file->have;
697                 eol = memchr(file->next, '\n', n);
698                 if (eol != NULL)
699                         n = (unsigned)(eol - file->next) + 1;
700
701                 /* copy through end-of-line, or remainder if not found */
702                 memcpy(buf, file->next, n);
703                 file->have -= n;
704                 file->next += n;
705                 file->pos += n;
706                 left -= n;
707                 buf += n;
708         } while (left && eol == NULL);
709
710         /* found end-of-line or out of space -- terminate string and return it */
711         buf[0] = 0;
712         return str;
713 }
714
715 int 
716 file_eof(FILE_T file)
717 {
718         /* return end-of-file state */
719         return (file->eof && file->avail_in == 0 && file->have == 0);
720 }
721
722 /*
723  * Routine to return a Wiretap error code (0 for no error, an errno
724  * for a file error, or a WTAP_ERR_ code for other errors) for an
725  * I/O stream.
726  */
727 int
728 file_error(FILE_T fh)
729 {
730         return fh->err;
731 }
732
733 void
734 file_clearerr(FILE_T stream)
735 {
736         /* clear error and end-of-file */
737         stream->err = 0;
738         stream->eof = 0;
739 }
740
741 int 
742 file_close(FILE_T file)
743 {
744         int fd = file->fd;
745
746         /* free memory and close file */
747         if (file->size) {
748 #ifdef HAVE_LIBZ
749                 inflateEnd(&(file->strm));
750 #endif
751                 g_free(file->out);
752                 g_free(file->in);
753         }
754         file->err = 0;
755         g_free(file);
756         return close(fd);
757 }
758