On at least some platforms, a #define of O_BINARY is needed even if
[obnox/wireshark/wip.git] / wiretap / file_access.c
1 /* file_access.c
2  *
3  * $Id: file_access.c,v 1.14 2004/06/30 06:58:59 guy Exp $
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 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30
31 #ifdef HAVE_FCNTL_H
32 #include <fcntl.h>
33 #endif
34
35 #ifdef HAVE_SYS_STAT_H
36 #include <sys/stat.h>
37 #endif
38
39 #ifdef HAVE_UNISTD_H
40 #include <unistd.h>
41 #endif
42
43 #include <errno.h>
44
45 #ifdef HAVE_IO_H
46 #include <io.h> /* open/close on win32 */
47 #endif
48
49 #include "wtap-int.h"
50 #include "file_wrappers.h"
51 #include "buffer.h"
52 #include "lanalyzer.h"
53 #include "airopeek9.h"
54 #include "ngsniffer.h"
55 #include "radcom.h"
56 #include "ascend.h"
57 #include "nettl.h"
58 #include "libpcap.h"
59 #include "snoop.h"
60 #include "iptrace.h"
61 #include "netmon.h"
62 #include "netxray.h"
63 #include "toshiba.h"
64 #include "eyesdn.h"
65 #include "i4btrace.h"
66 #include "csids.h"
67 #include "pppdump.h"
68 #include "etherpeek.h"
69 #include "vms.h"
70 #include "dbs-etherwatch.h"
71 #include "visual.h"
72 #include "cosine.h"
73 #include "5views.h"
74 #include "erf.h"
75 #include "hcidump.h"
76 #include "network_instruments.h"
77
78 /* The open_file_* routines should return:
79  *
80  *      -1 on an I/O error;
81  *
82  *      1 if the file they're reading is one of the types it handles;
83  *
84  *      0 if the file they're reading isn't the type they're checking for.
85  *
86  * If the routine handles this type of file, it should set the "file_type"
87  * field in the "struct wtap" to the type of the file.
88  *
89  * Put the trace files that are merely saved telnet-sessions last, since it's
90  * possible that you could have captured someone a router telnet-session
91  * using another tool. So, a libpcap trace of an toshiba "snoop" session
92  * should be discovered as a libpcap file, not a toshiba file.
93  */
94
95 static int (*const open_routines[])(wtap *, int *, char **) = {
96         /* Files that have magic bytes in fixed locations. These
97          * are easy to identify.
98          */
99         libpcap_open,
100         lanalyzer_open,
101         ngsniffer_open,
102         snoop_open,
103         iptrace_open,
104         netmon_open,
105         netxray_open,
106         radcom_open,
107         nettl_open,
108         visual_open,
109         _5views_open,
110         network_instruments_open,
111         airopeek9_open,
112
113         /* Files that don't have magic bytes at a fixed location,
114          * but that instead require a heuristic of some sort to
115          * identify them.  This includes the ASCII trace files that
116          * would be, for example, saved copies of a Telnet session
117          * to some box.
118          */
119         etherpeek_open,
120         pppdump_open,
121         ascend_open,
122         eyesdn_open,
123         toshiba_open,
124         i4btrace_open,
125         csids_open,
126         vms_open,
127         dbs_etherwatch_open,
128         cosine_open,
129         erf_open,
130         hcidump_open,
131 };
132
133 #define N_FILE_TYPES    (sizeof open_routines / sizeof open_routines[0])
134
135 /*
136  * Visual C++ on Win32 systems doesn't define these.  (Old UNIX systems don't
137  * define them either.)
138  *
139  * Visual C++ on Win32 systems doesn't define S_IFIFO, it defines _S_IFIFO.
140  */
141 #ifndef S_ISREG
142 #define S_ISREG(mode)   (((mode) & S_IFMT) == S_IFREG)
143 #endif
144 #ifndef S_IFIFO
145 #define S_IFIFO _S_IFIFO
146 #endif
147 #ifndef S_ISFIFO
148 #define S_ISFIFO(mode)  (((mode) & S_IFMT) == S_IFIFO)
149 #endif
150 #ifndef S_ISDIR
151 #define S_ISDIR(mode)   (((mode) & S_IFMT) == S_IFDIR)
152 #endif
153
154 /* Opens a file and prepares a wtap struct.
155    If "do_random" is TRUE, it opens the file twice; the second open
156    allows the application to do random-access I/O without moving
157    the seek offset for sequential I/O, which is used by Ethereal
158    so that it can do sequential I/O to a capture file that's being
159    written to as new packets arrive independently of random I/O done
160    to display protocol trees for packets when they're selected. */
161 wtap* wtap_open_offline(const char *filename, int *err, char **err_info,
162     gboolean do_random)
163 {
164         struct stat statb;
165         wtap    *wth;
166         unsigned int    i;
167
168         /* First, make sure the file is valid */
169         if (stat(filename, &statb) < 0) {
170                 *err = errno;
171                 return NULL;
172         }
173         if (S_ISFIFO(statb.st_mode)) {
174                 /*
175                  * Opens of FIFOs are allowed only when not opening
176                  * for random access.
177                  *
178                  * XXX - currently, we do seeking when trying to find
179                  * out the file type, so we don't actually support
180                  * opening FIFOs.  However, we may eventually
181                  * do buffering that allows us to do at least some
182                  * file type determination even on pipes, so we
183                  * allow FIFO opens and let things fail later when
184                  * we try to seek.
185                  */
186                 if (do_random) {
187                         *err = WTAP_ERR_RANDOM_OPEN_PIPE;
188                         return NULL;
189                 }
190         } else if (S_ISDIR(statb.st_mode)) {
191                 /*
192                  * Return different errors for "this is a directory"
193                  * and "this is some random special file type", so
194                  * the user can get a potentially more helpful error.
195                  */
196                 *err = EISDIR;
197                 return NULL;
198         } else if (! S_ISREG(statb.st_mode)) {
199                 *err = WTAP_ERR_NOT_REGULAR_FILE;
200                 return NULL;
201         }
202
203         errno = ENOMEM;
204         wth = g_malloc(sizeof(wtap));
205         if (wth == NULL) {
206                 *err = errno;
207                 return NULL;
208         }
209
210 /* Win32 needs the O_BINARY flag for open() */
211 #ifndef O_BINARY
212 #define O_BINARY        0
213 #endif
214
215         /* Open the file */
216         errno = WTAP_ERR_CANT_OPEN;
217         wth->fd = open(filename, O_RDONLY|O_BINARY);
218         if (wth->fd < 0) {
219                 *err = errno;
220                 g_free(wth);
221                 return NULL;
222         }
223         if (!(wth->fh = filed_open(wth->fd, "rb"))) {
224                 *err = errno;
225                 g_free(wth);
226                 return NULL;
227         }
228
229         if (do_random) {
230                 if (!(wth->random_fh = file_open(filename, "rb"))) {
231                         *err = errno;
232                         file_close(wth->fh);
233                         g_free(wth);
234                         return NULL;
235                 }
236         } else
237                 wth->random_fh = NULL;
238
239         /* initialization */
240         wth->file_encap = WTAP_ENCAP_UNKNOWN;
241         wth->data_offset = 0;
242         wth->subtype_sequential_close = NULL;
243         wth->subtype_close = NULL;
244
245         /* Try all file types */
246         for (i = 0; i < N_FILE_TYPES; i++) {
247                 /* Seek back to the beginning of the file; the open routine
248                    for the previous file type may have left the file
249                    position somewhere other than the beginning, and the
250                    open routine for this file type will probably want
251                    to start reading at the beginning.
252
253                    Initialize the data offset while we're at it. */
254                 if (file_seek(wth->fh, 0, SEEK_SET, err) == -1) {
255                         /* I/O error - give up */
256                         if (wth->random_fh != NULL)
257                                 file_close(wth->random_fh);
258                         file_close(wth->fh);
259                         g_free(wth);
260                         return NULL;
261                 }
262                 wth->data_offset = 0;
263                 switch ((*open_routines[i])(wth, err, err_info)) {
264
265                 case -1:
266                         /* I/O error - give up */
267                         if (wth->random_fh != NULL)
268                                 file_close(wth->random_fh);
269                         file_close(wth->fh);
270                         g_free(wth);
271                         return NULL;
272
273                 case 0:
274                         /* No I/O error, but not that type of file */
275                         break;
276
277                 case 1:
278                         /* We found the file type */
279                         goto success;
280                 }
281         }
282
283         /* Well, it's not one of the types of file we know about. */
284         if (wth->random_fh != NULL)
285                 file_close(wth->random_fh);
286         file_close(wth->fh);
287         g_free(wth);
288         *err = WTAP_ERR_FILE_UNKNOWN_FORMAT;
289         return NULL;
290
291 success:
292         wth->frame_buffer = g_malloc(sizeof(struct Buffer));
293         buffer_init(wth->frame_buffer, 1500);
294         return wth;
295 }
296
297 /* Table of the file types we know about. */
298 static const struct file_type_info {
299         const char *name;
300         const char *short_name;
301         int     (*can_write_encap)(int);
302         int     (*dump_open)(wtap_dumper *, gboolean, int *);
303 } dump_open_table[WTAP_NUM_FILE_TYPES] = {
304         /* WTAP_FILE_UNKNOWN */
305         { NULL, NULL,
306           NULL, NULL },
307
308         /* WTAP_FILE_WTAP */
309         { "Wiretap (Ethereal)", NULL,
310           NULL, NULL },
311
312         /* WTAP_FILE_PCAP */
313         { "libpcap (tcpdump, Ethereal, etc.)", "libpcap",
314           libpcap_dump_can_write_encap, libpcap_dump_open },
315
316         /* WTAP_FILE_PCAP_SS990417 */
317         { "RedHat Linux 6.1 libpcap (tcpdump)", "rh6_1libpcap",
318           libpcap_dump_can_write_encap, libpcap_dump_open },
319
320         /* WTAP_FILE_PCAP_SS990915 */
321         { "SuSE Linux 6.3 libpcap (tcpdump)", "suse6_3libpcap",
322           libpcap_dump_can_write_encap, libpcap_dump_open },
323
324         /* WTAP_FILE_PCAP_SS991029 */
325         { "modified libpcap (tcpdump)", "modlibpcap",
326           libpcap_dump_can_write_encap, libpcap_dump_open },
327
328         /* WTAP_FILE_PCAP_NOKIA */
329         { "Nokia libpcap (tcpdump)", "nokialibpcap",
330           libpcap_dump_can_write_encap, libpcap_dump_open },
331
332         /* WTAP_FILE_PCAP_AIX */
333         { "AIX libpcap (tcpdump)", NULL,
334           NULL, NULL },
335
336         /* WTAP_FILE_LANALYZER */
337         { "Novell LANalyzer","lanalyzer",
338           lanalyzer_dump_can_write_encap, lanalyzer_dump_open },
339
340         /* WTAP_FILE_NGSNIFFER_UNCOMPRESSED */
341         { "Network Associates Sniffer (DOS-based)", "ngsniffer",
342           ngsniffer_dump_can_write_encap, ngsniffer_dump_open },
343
344         /* WTAP_FILE_NGSNIFFER_COMPRESSED */
345         { "Network Associates Sniffer (DOS-based), compressed", "ngsniffer_comp",
346           NULL, NULL },
347
348         /* WTAP_FILE_SNOOP */
349         { "Sun snoop", "snoop",
350           snoop_dump_can_write_encap, snoop_dump_open },
351
352         /* WTAP_FILE_SHOMITI */
353         { "Shomiti/Finisar Surveyor", "shomiti",
354           NULL, NULL },
355
356         /* WTAP_FILE_IPTRACE_1_0 */
357         { "AIX iptrace 1.0", NULL,
358           NULL, NULL },
359
360         /* WTAP_FILE_IPTRACE_2_0 */
361         { "AIX iptrace 2.0", NULL,
362           NULL, NULL },
363
364         /* WTAP_FILE_NETMON_1_x */
365         { "Microsoft Network Monitor 1.x", "netmon1",
366           netmon_dump_can_write_encap, netmon_dump_open },
367
368         /* WTAP_FILE_NETMON_2_x */
369         { "Microsoft Network Monitor 2.x", "netmon2",
370           netmon_dump_can_write_encap, netmon_dump_open },
371
372         /* WTAP_FILE_NETXRAY_OLD */
373         { "Cinco Networks NetXRay 1.x", NULL,
374           NULL, NULL },
375
376         /* WTAP_FILE_NETXRAY_1_0 */
377         { "Cinco Networks NetXRay 2.0 or later", NULL,
378           NULL, NULL },
379
380         /* WTAP_FILE_NETXRAY_1_1 */
381         { "Network Associates Sniffer (Windows-based) 1.1", "ngwsniffer_1_1",
382           netxray_dump_can_write_encap_1_1, netxray_dump_open_1_1 },
383
384         /* WTAP_FILE_NETXRAY_2_00x */
385         { "Network Associates Sniffer (Windows-based) 2.00x", "ngwsniffer_2_0",
386           netxray_dump_can_write_encap_2_0, netxray_dump_open_2_0 },
387
388         /* WTAP_FILE_RADCOM */
389         { "RADCOM WAN/LAN analyzer", NULL,
390           NULL, NULL },
391
392         /* WTAP_FILE_ASCEND */
393         { "Lucent/Ascend access server trace", NULL,
394           NULL, NULL },
395
396         /* WTAP_FILE_NETTL */
397         { "HP-UX nettl trace", NULL,
398           NULL, NULL },
399
400         /* WTAP_FILE_TOSHIBA */
401         { "Toshiba Compact ISDN Router snoop trace", NULL,
402           NULL, NULL },
403
404         /* WTAP_FILE_I4BTRACE */
405         { "I4B ISDN trace", NULL,
406           NULL, NULL },
407
408         /* WTAP_FILE_CSIDS */
409         { "CSIDS IPLog", NULL,
410           NULL, NULL },
411
412         /* WTAP_FILE_PPPDUMP */
413         { "pppd log (pppdump format)", NULL,
414           NULL, NULL },
415
416         /* WTAP_FILE_ETHERPEEK_V56 */
417         { "EtherPeek/TokenPeek trace (V5 & V6 file format)", NULL,
418           NULL, NULL },
419
420         /* WTAP_FILE_ETHERPEEK_V7 */
421         { "EtherPeek/TokenPeek/AiroPeek trace (V7 file format)", NULL,
422           NULL, NULL },
423
424         /* WTAP_FILE_VMS */
425         { "TCPIPtrace (VMS)", NULL,
426           NULL, NULL},
427
428         /* WTAP_FILE_DBS_ETHERWATCH */
429         { "DBS Etherwatch (VMS)", NULL,
430           NULL, NULL},
431
432         /* WTAP_FILE_VISUAL_NETWORKS */
433         { "Visual Networks traffic capture", "visual",
434           visual_dump_can_write_encap, visual_dump_open },
435
436         /* WTAP_FILE_COSINE */
437         { "CoSine IPSX L2 capture", "cosine",
438           NULL, NULL },
439
440         /* WTAP_FILE_5VIEWS */
441         { "Accellent 5Views capture", "5views",
442           _5views_dump_can_write_encap, _5views_dump_open },
443
444         /* WTAP_FILE_ERF */
445         { "Endace DAG capture", "erf",
446           NULL, NULL },
447
448         /* WTAP_FILE_HCIDUMP */
449         { "Bluetooth HCI dump", "hcidump",
450           NULL, NULL },
451
452         /* WTAP_FILE_NETWORK_INSTRUMENTS_V9 */
453         { "Network Instruments Observer version 9", "niobserverv9",
454           network_instruments_dump_can_write_encap, network_instruments_dump_open },
455
456         /* WTAP_FILE_AIROPEEK_V9 */
457         { "EtherPeek/AiroPeek trace (V9 file format)", NULL,
458           NULL, NULL },
459     
460         /* WTAP_FILE_EYESDN */
461         { "EyeSDN USB S0/E1 ISDN trace format", NULL,
462           NULL, NULL },
463 };
464
465 /* Name that should be somewhat descriptive. */
466 const char *wtap_file_type_string(int filetype)
467 {
468         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES) {
469                 g_error("Unknown capture file type %d", filetype);
470                 return NULL;
471         } else
472                 return dump_open_table[filetype].name;
473 }
474
475 /* Name to use in, say, a command-line flag specifying the type. */
476 const char *wtap_file_type_short_string(int filetype)
477 {
478         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES)
479                 return NULL;
480         else
481                 return dump_open_table[filetype].short_name;
482 }
483
484 /* Translate a short name to a capture file type. */
485 int wtap_short_string_to_file_type(const char *short_name)
486 {
487         int filetype;
488
489         for (filetype = 0; filetype < WTAP_NUM_FILE_TYPES; filetype++) {
490                 if (dump_open_table[filetype].short_name != NULL &&
491                     strcmp(short_name, dump_open_table[filetype].short_name) == 0)
492                         return filetype;
493         }
494         return -1;      /* no such file type, or we can't write it */
495 }
496
497 gboolean wtap_dump_can_open(int filetype)
498 {
499         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
500             || dump_open_table[filetype].dump_open == NULL)
501                 return FALSE;
502
503         return TRUE;
504 }
505
506 gboolean wtap_dump_can_write_encap(int filetype, int encap)
507 {
508         if (filetype < 0 || filetype >= WTAP_NUM_FILE_TYPES
509             || dump_open_table[filetype].can_write_encap == NULL)
510                 return FALSE;
511
512         if ((*dump_open_table[filetype].can_write_encap)(encap) != 0)
513                 return FALSE;
514
515         return TRUE;
516 }
517
518 static gboolean wtap_dump_open_check(int filetype, int encap, int *err);
519 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
520     int *err);
521 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, int *err);
522
523 wtap_dumper* wtap_dump_open(const char *filename, int filetype, int encap,
524                                 int snaplen, int *err)
525 {
526         wtap_dumper *wdh;
527         FILE *fh;
528
529         /* Check whether we can open a capture file with that file type
530            and that encapsulation. */
531         if (!wtap_dump_open_check(filetype, encap, err))
532                 return NULL;
533
534         /* Allocate a data structure for the output stream. */
535         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, err);
536         if (wdh == NULL)
537                 return NULL;    /* couldn't allocate it */
538
539         /* Empty filename means stdout */
540         if (*filename == '\0') {
541 #ifdef _WIN32
542                 setmode(fileno(stdout), O_BINARY);
543 #endif
544                 wdh->fh = stdout;
545         } else {
546                 /* In case "fopen()" fails but doesn't set "errno", set "errno"
547                    to a generic "the open failed" error. */
548                 errno = WTAP_ERR_CANT_OPEN;
549                 fh = fopen(filename, "wb");
550                 if (fh == NULL) {
551                         *err = errno;
552                         return NULL;    /* can't create file */
553                 }
554                 wdh->fh = fh;
555         }
556
557         if (!wtap_dump_open_finish(wdh, filetype, err)) {
558                 /* Get rid of the file we created; we couldn't finish
559                    opening it. */
560                 if (wdh->fh != stdout)
561                         unlink(filename);
562                 return NULL;
563         }
564         return wdh;
565 }
566
567 wtap_dumper* wtap_dump_fdopen(int fd, int filetype, int encap, int snaplen,
568                                 int *err)
569 {
570         wtap_dumper *wdh;
571         FILE *fh;
572
573         /* Check whether we can open a capture file with that file type
574            and that encapsulation. */
575         if (!wtap_dump_open_check(filetype, encap, err))
576                 return NULL;
577
578         /* Allocate a data structure for the output stream. */
579         wdh = wtap_dump_alloc_wdh(filetype, encap, snaplen, err);
580         if (wdh == NULL)
581                 return NULL;    /* couldn't allocate it */
582
583         /* In case "fopen()" fails but doesn't set "errno", set "errno"
584            to a generic "the open failed" error. */
585         errno = WTAP_ERR_CANT_OPEN;
586         fh = fdopen(fd, "wb");
587         if (fh == NULL) {
588                 *err = errno;
589                 return NULL;    /* can't create standard I/O stream */
590         }
591         wdh->fh = fh;
592
593         if (!wtap_dump_open_finish(wdh, filetype, err))
594                 return NULL;
595         return wdh;
596 }
597
598 static gboolean wtap_dump_open_check(int filetype, int encap, int *err)
599 {
600         if (!wtap_dump_can_open(filetype)) {
601                 /* Invalid type, or type we don't know how to write. */
602                 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
603                 return FALSE;
604         }
605
606         /* OK, we know how to write that type; can we write the specified
607            encapsulation type? */
608         *err = (*dump_open_table[filetype].can_write_encap)(encap);
609         if (*err != 0)
610                 return FALSE;
611
612         /* All systems go! */
613         return TRUE;
614 }
615
616 static wtap_dumper* wtap_dump_alloc_wdh(int filetype, int encap, int snaplen,
617                                         int *err)
618 {
619         wtap_dumper *wdh;
620
621         wdh = g_malloc(sizeof (wtap_dumper));
622         if (wdh == NULL) {
623                 *err = errno;
624                 return NULL;
625         }
626         wdh->fh = NULL;
627         wdh->file_type = filetype;
628         wdh->snaplen = snaplen;
629         wdh->encap = encap;
630         wdh->bytes_dumped = 0;
631         wdh->dump.opaque = NULL;
632         wdh->subtype_write = NULL;
633         wdh->subtype_close = NULL;
634         return wdh;
635 }
636
637 static gboolean wtap_dump_open_finish(wtap_dumper *wdh, int filetype, int *err)
638 {
639         int fd;
640         gboolean cant_seek;
641
642         /* Can we do a seek on the file descriptor?
643            If not, note that fact. */
644         fd = fileno(wdh->fh);
645         if (lseek(fd, 1, SEEK_CUR) == -1)
646           cant_seek = TRUE;
647         else {
648           /* Undo the seek. */
649           lseek(fd, 0, SEEK_SET);
650           cant_seek = FALSE;
651         }
652
653         /* Now try to open the file for writing. */
654         if (!(*dump_open_table[filetype].dump_open)(wdh, cant_seek, err)) {
655                 /* The attempt failed.  Close the stream for the file.
656                    NOTE: this means the FD handed to "wtap_dump_fdopen()"
657                    will be closed if the open fails. */
658                 if (wdh->fh != stdout)
659                         fclose(wdh->fh);
660
661                 /* Now free up the dumper handle. */
662                 g_free(wdh);
663                 return FALSE;
664         }
665
666         return TRUE;    /* success! */
667 }
668
669 FILE* wtap_dump_file(wtap_dumper *wdh)
670 {
671         return wdh->fh;
672 }
673
674 gboolean wtap_dump(wtap_dumper *wdh, const struct wtap_pkthdr *phdr,
675     const union wtap_pseudo_header *pseudo_header, const guchar *pd, int *err)
676 {
677         return (wdh->subtype_write)(wdh, phdr, pseudo_header, pd, err);
678 }
679
680 gboolean wtap_dump_close(wtap_dumper *wdh, int *err)
681 {
682         gboolean ret = TRUE;
683
684         if (wdh->subtype_close != NULL) {
685                 /* There's a close routine for this dump stream. */
686                 if (!(wdh->subtype_close)(wdh, err))
687                         ret = FALSE;
688         }
689         errno = WTAP_ERR_CANT_CLOSE;
690         /* Don't close stdout */
691         if (wdh->fh != stdout) {
692                 if (fclose(wdh->fh) == EOF) {
693                         if (ret) {
694                                 /* The per-format close function succeeded,
695                                    but the fclose didn't.  Save the reason
696                                    why, if our caller asked for it. */
697                                 if (err != NULL)
698                                         *err = errno;
699                         }
700                         ret = FALSE;
701                 }
702         }
703         if (wdh->dump.opaque != NULL)
704                 g_free(wdh->dump.opaque);
705         g_free(wdh);
706         return ret;
707 }
708
709 long wtap_get_bytes_dumped(wtap_dumper *wdh)
710 {
711         return wdh->bytes_dumped;
712 }
713
714 void wtap_set_bytes_dumped(wtap_dumper *wdh, long bytes_dumped)
715 {
716         wdh->bytes_dumped = bytes_dumped;
717 }
718