diff options
-rw-r--r-- | file.c | 27 | ||||
-rw-r--r-- | wiretap/file_access.c | 2 | ||||
-rw-r--r-- | wiretap/file_wrappers.c | 32 | ||||
-rw-r--r-- | wiretap/file_wrappers.h | 6 | ||||
-rw-r--r-- | wiretap/wtap.c | 129 | ||||
-rw-r--r-- | wiretap/wtap.def | 16 | ||||
-rw-r--r-- | wiretap/wtap.h | 7 |
7 files changed, 189 insertions, 30 deletions
@@ -3850,13 +3850,9 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format, convert it to a file descriptor with _open_osfhandle(), that would allow the file to be renamed out from under us. - It would also allow it to be deleted out from under us; according - to the MSDN documentation on DeleteFile(), "The DeleteFile function - marks a file for deletion on close. Therefore, the file deletion - does not occur until the last handle to the file is closed. - Subsequent calls to CreateFile to open the file fail with - ERROR_ACCESS_DENIED.", so it sounds as if deleting it out from - under us would be safe. */ + However, that doesn't work in practice. Perhaps the problem + is that the process doing the rename is the process that + has the file open. */ #ifndef _WIN32 if (ws_rename(cf->filename, fname) == 0) { /* That succeeded - there's no need to copy the source file. */ @@ -3975,12 +3971,25 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format, if (fname_new != NULL) { /* We wrote out to fname_new, and should rename it on top of - fname; fname is now closed, so that should be possible even - on Windows. Do the rename. */ + fname. fname_new is now closed, so that should be possible even + on Windows. However, on Windows, we first need to close whatever + file descriptors we have open for fname. */ +#ifdef _WIN32 + wtap_fdclose(cf->wth); +#endif + /* Now do the rename. */ if (ws_rename(fname_new, fname) == -1) { /* Well, the rename failed. */ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, file_rename_error_message(errno), fname); +#ifdef _WIN32 + /* Attempt to reopen the file descriptors using fname. */ + if (!wtap_fdreopen(cf->wth, fname, &err)) { + /* Oh, well, we're screwed. */ + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + file_open_error_message(err, FALSE), fname); + } +#endif goto fail; } } diff --git a/wiretap/file_access.c b/wiretap/file_access.c index b6525f07e8..6e88b69d96 100644 --- a/wiretap/file_access.c +++ b/wiretap/file_access.c @@ -317,7 +317,7 @@ wtap* wtap_open_offline(const char *filename, int *err, char **err_info, return NULL; } #endif - if (!(wth->fh = filed_open(fd))) { + if (!(wth->fh = file_fdopen(fd))) { *err = errno; ws_close(fd); g_free(wth); diff --git a/wiretap/file_wrappers.c b/wiretap/file_wrappers.c index 67437acee9..537cc1a18e 100644 --- a/wiretap/file_wrappers.c +++ b/wiretap/file_wrappers.c @@ -766,7 +766,7 @@ gz_reset(FILE_T state) } FILE_T -filed_open(int fd) +file_fdopen(int fd) { #ifdef _STATBUF_ST_BLKSIZE /* XXX, _STATBUF_ST_BLKSIZE portable? */ struct stat st; @@ -862,7 +862,7 @@ file_open(const char *path) return NULL; /* open file handle */ - ft = filed_open(fd); + ft = file_fdopen(fd); if (ft == NULL) { ws_close(fd); return NULL; @@ -1274,7 +1274,25 @@ file_clearerr(FILE_T stream) stream->eof = 0; } -int +void +file_fdclose(FILE_T file) +{ + ws_close(file->fd); + file->fd = -1; +} + +gboolean +file_fdreopen(FILE_T file, const char *path) +{ + int fd; + + if ((fd = ws_open(path, O_RDONLY|O_BINARY, 0000)) == -1) + return FALSE; + file->fd = fd; + return TRUE; +} + +void file_close(FILE_T file) { int fd = file->fd; @@ -1291,7 +1309,13 @@ file_close(FILE_T file) file->err = 0; file->err_info = NULL; g_free(file); - return ws_close(fd); + /* + * If fd is -1, somebody's done a file_closefd() on us, so + * we don't need to close the FD itself, and shouldn't do + * so. + */ + if (fd != -1) + ws_close(fd); } #ifdef HAVE_LIBZ diff --git a/wiretap/file_wrappers.h b/wiretap/file_wrappers.h index 4ef46fb297..efdb9c2950 100644 --- a/wiretap/file_wrappers.h +++ b/wiretap/file_wrappers.h @@ -28,7 +28,7 @@ #include <wsutil/file_util.h> extern FILE_T file_open(const char *path); -extern FILE_T filed_open(int fildes); +extern FILE_T file_fdopen(int fildes); extern void file_set_random_access(FILE_T stream, gboolean random, GPtrArray *seek); extern gint64 file_seek(FILE_T stream, gint64 offset, int whence, int *err); extern gint64 file_skip(FILE_T file, gint64 delta, int *err); @@ -42,7 +42,9 @@ extern char *file_gets(char *buf, int len, FILE_T stream); extern int file_eof(FILE_T stream); extern int file_error(FILE_T fh, gchar **err_info); extern void file_clearerr(FILE_T stream); -extern int file_close(FILE_T file); +extern void file_fdclose(FILE_T file); +extern int file_fdreopen(FILE_T file, const char *path); +extern void file_close(FILE_T file); #ifdef HAVE_LIBZ typedef struct wtap_writer *GZWFILE_T; diff --git a/wiretap/wtap.c b/wiretap/wtap.c index cf63c9c053..0fd64180d3 100644 --- a/wiretap/wtap.c +++ b/wiretap/wtap.c @@ -739,6 +739,135 @@ g_fast_seek_item_free(gpointer data, gpointer user_data _U_) g_free(data); } +/* + * Close the file descriptors for the sequential and random streams, but + * don't discard any information about those streams. Used on Windows if + * we need to rename a file that we have open or if we need to rename on + * top of a file we have open. + */ +void +wtap_fdclose(wtap *wth) +{ + if (wth->fh != NULL) + file_fdclose(wth->fh); + if (wth->random_fh != NULL) + file_fdclose(wth->random_fh); +} + +/* + * Given the pathname of the file we just closed with wtap_fdclose(), attempt + * to reopen that file and assign the new file descriptor(s) to the sequential + * stream and, if do_random is TRUE, to the random stream. Used on Windows + * after the rename of a file we had open was done or if the rename of a + * file on top of a file we had open failed. + */ +gboolean +wtap_fdreopen(wtap *wth, const char *filename, int *err, gboolean do_random) +{ + int fd; + ws_statb64 statb; + gboolean use_stdin = FALSE; + + /* open standard input if filename is '-' */ + if (strcmp(filename, "-") == 0) + use_stdin = TRUE; + + /* First, make sure the file is valid */ + if (use_stdin) { + if (ws_fstat64(0, &statb) < 0) { + *err = errno; + return FALSE; + } + } else { + if (ws_stat64(filename, &statb) < 0) { + *err = errno; + return FALSE; + } + } + if (S_ISFIFO(statb.st_mode)) { + /* + * Opens of FIFOs are allowed only when not opening + * for random access. + * + * XXX - currently, we do seeking when trying to find + * out the file type, so we don't actually support + * opening FIFOs. However, we may eventually + * do buffering that allows us to do at least some + * file type determination even on pipes, so we + * allow FIFO opens and let things fail later when + * we try to seek. + */ + if (do_random) { + *err = WTAP_ERR_RANDOM_OPEN_PIPE; + return FALSE; + } + } else if (S_ISDIR(statb.st_mode)) { + /* + * Return different errors for "this is a directory" + * and "this is some random special file type", so + * the user can get a potentially more helpful error. + */ + *err = EISDIR; + return FALSE; + } else if (! S_ISREG(statb.st_mode)) { + *err = WTAP_ERR_NOT_REGULAR_FILE; + return FALSE; + } + + /* + * We need two independent descriptors for random access, so + * they have different file positions. If we're opening the + * standard input, we can only dup it to get additional + * descriptors, so we can't have two independent descriptors, + * and thus can't do random access. + */ + if (use_stdin && do_random) { + *err = WTAP_ERR_RANDOM_OPEN_STDIN; + return FALSE; + } + + /* Open the file */ + errno = WTAP_ERR_CANT_OPEN; + if (use_stdin) { + /* + * We dup FD 0, so that we don't have to worry about + * a file_close of wth->fh closing the standard + * input of the process. + */ + fd = ws_dup(0); + if (fd < 0) { + *err = errno; + return FALSE; + } +#ifdef _WIN32 + if (_setmode(fd, O_BINARY) == -1) { + /* "Shouldn't happen" */ + *err = errno; + return FALSE; + } +#endif + if (!(wth->fh = file_fdopen(fd))) { + *err = errno; + ws_close(fd); + return FALSE; + } + } else { + if (!file_fdreopen(wth->fh, filename)) { + *err = errno; + return FALSE; + } + } + + if (do_random) { + if (!file_fdreopen(wth->random_fh, filename)) { + *err = errno; + file_fdclose(wth->fh); + return FALSE; + } + } + return TRUE; +} + void wtap_close(wtap *wth) { diff --git a/wiretap/wtap.def b/wiretap/wtap.def index 48b8316265..62035e439c 100644 --- a/wiretap/wtap.def +++ b/wiretap/wtap.def @@ -15,20 +15,6 @@ buffer_free buffer_init buffer_remove_start -file_seek -file_tell -file_error -file_gets -file_open -filed_open -file_read -file_close -file_getc -file_gets -file_eof -file_clearerr -file_set_random_access - wtap_buf_ptr wtap_cleareof wtap_close @@ -46,6 +32,8 @@ wtap_dump_open_ng wtap_dump_set_addrinfo_list wtap_encap_short_string wtap_encap_string +wtap_fdclose +wtap_fdreopen wtap_file_encap wtap_get_savable_file_types wtap_get_file_extensions_list diff --git a/wiretap/wtap.h b/wiretap/wtap.h index 7cab0f79ef..69a5858745 100644 --- a/wiretap/wtap.h +++ b/wiretap/wtap.h @@ -1083,6 +1083,13 @@ wtapng_section_t* wtap_file_get_shb_info(wtap *wth); wtapng_iface_descriptions_t *wtap_file_get_idb_info(wtap *wth); void wtap_write_shb_comment(wtap *wth, gchar *comment); +/*** close the file descriptors for the current file ***/ +void wtap_fdclose(wtap *wth); + +/*** reopen the file descriptors for the current file ***/ +gboolean wtap_fdreopen(wtap *wth, const char *filename, int *err, + gboolean do_random); + /*** close the current file ***/ void wtap_sequential_close(wtap *wth); void wtap_close(wtap *wth); |