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
This commit is contained in:
Guy Harris 2012-06-01 08:05:12 +00:00
parent 86c69b01e7
commit 129c881fcf
7 changed files with 189 additions and 30 deletions

27
file.c
View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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)
{

View File

@ -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

View File

@ -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);