aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2012-06-01 08:05:12 +0000
committerGuy Harris <guy@alum.mit.edu>2012-06-01 08:05:12 +0000
commit129c881fcf7c8707f379a09d2ed2cdfcc178b807 (patch)
treec3e2e1a9b39235d5b89594d3947e849d9152c09a
parent86c69b01e7965f37eecbe6195e797a52ab81e23e (diff)
Sigh. There appears to be no way to get Windows to allow us to rename a
file that we ourselves have open. In the "safe save" code path for capture files, on Windows temporarily close the file descriptors for the currently-open capture before doing the rename and then, if the rename failed, reopen them, leaving the rest of the wtap and capture_file structures intact. Rename filed_open() to file_fdopen(), to make its name match what it does a bit better (it's an fdopen()-style routine, i.e. do the equivalent of an open with an already-open file descriptor rather than a pathname, in the file_wrappers.c set of routines). Remove the file_ routines from the .def file for Wiretap - they should only be called by code inside Wiretap. Closing a descriptor open for input has no reason to fail (closing a descriptor open for *writing* could fail if the file is on a server and dirty pages are pushed asynchronously to the server and synchronously on a close), so just have file_close() return void. svn path=/trunk/; revision=42961
-rw-r--r--file.c27
-rw-r--r--wiretap/file_access.c2
-rw-r--r--wiretap/file_wrappers.c32
-rw-r--r--wiretap/file_wrappers.h6
-rw-r--r--wiretap/wtap.c129
-rw-r--r--wiretap/wtap.def16
-rw-r--r--wiretap/wtap.h7
7 files changed, 189 insertions, 30 deletions
diff --git a/file.c b/file.c
index 89b03937ff..4438257a39 100644
--- a/file.c
+++ b/file.c
@@ -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);