From: Bruno Haible Date: Fri, 6 Oct 2006 12:17:22 +0000 (+0000) Subject: Have clean-temp register file open descriptors to temporary files. X-Git-Tag: cvs-readonly~1782 X-Git-Url: http://erislabs.org.uk/gitweb/?a=commitdiff_plain;h=bd63935b8ac05a50706b7b56540cd0396b528c77;p=gnulib.git Have clean-temp register file open descriptors to temporary files. --- diff --git a/ChangeLog b/ChangeLog index ef72a0a74..23dae8a3a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2006-10-05 Bruno Haible + + * modules/fwriteerror (configure.ac): Define GNULIB_FWRITEERROR. + 2006-10-02 Eric Blake * modules/strnlen (Depends-on): Add extensions. diff --git a/lib/ChangeLog b/lib/ChangeLog index 0f958a25a..6e58ab995 100644 --- a/lib/ChangeLog +++ b/lib/ChangeLog @@ -1,3 +1,13 @@ +2006-10-05 Bruno Haible + + * clean-temp.h (open_temp, fopen_temp, close_temp, fclose_temp, + fwriteerror_temp): New declarations. + * clean-temp.c (uintptr_t): Provide fallback definition. + (descriptors): New variable. + (cleanup): First, close the descriptors. + (register_fd, unregister_fd, open_temp, fopen_temp, close_temp, + fclose_temp, fwriteerror_temp): New functions. + 2006-10-03 Bruno Haible * gl_list.h (gl_sortedlist_search_from_to, diff --git a/lib/clean-temp.c b/lib/clean-temp.c index 2322e7a0d..1d1e07ccc 100644 --- a/lib/clean-temp.c +++ b/lib/clean-temp.c @@ -41,6 +41,10 @@ #define _(str) gettext (str) +#ifndef uintptr_t +# define uintptr_t unsigned long +#endif + /* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5)) ensure that while constructing or modifying the data structures, the field @@ -70,6 +74,9 @@ static struct size_t tempdir_allocated; } cleanup_list /* = { NULL, 0, 0 } */; +/* List of all open file descriptors to temporary files. */ +static gl_list_t /* */ volatile descriptors; + /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH. Why? We need a data structure that @@ -154,6 +161,25 @@ cleanup () { size_t i; + /* First close all file descriptors to temporary files. */ + { + gl_list_t fds = descriptors; + + if (fds != NULL) + { + gl_list_iterator_t iter; + const void *element; + + iter = gl_list_iterator (fds); + while (gl_list_iterator_next (&iter, &element, NULL)) + { + int fd = (int) (uintptr_t) element; + close (fd); + } + gl_list_iterator_free (&iter); + } + } + for (i = 0; i < cleanup_list.tempdir_count; i++) { struct tempdir *dir = cleanup_list.tempdir_list[i]; @@ -481,3 +507,140 @@ cleanup_temp_dir (struct temp_dir *dir) /* The user passed an invalid DIR argument. */ abort (); } + + +/* Register a file descriptor to be closed. */ +static void +register_fd (int fd) +{ + if (descriptors == NULL) + descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, false); + gl_list_add_first (descriptors, (void *) (uintptr_t) fd); +} + +/* Unregister a file descriptor to be closed. */ +static void +unregister_fd (int fd) +{ + gl_list_t fds = descriptors; + gl_list_node_t node; + + if (fds == NULL) + /* descriptors should already contain fd. */ + abort (); + node = gl_list_search (fds, (void *) (uintptr_t) fd); + if (node == NULL) + /* descriptors should already contain fd. */ + abort (); + gl_list_remove_node (fds, node); +} + +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +int +open_temp (const char *file_name, int flags, mode_t mode) +{ + int fd; + int saved_errno; + + block_fatal_signals (); + fd = open (file_name, flags, mode); + saved_errno = errno; + if (fd >= 0) + register_fd (fd); + unblock_fatal_signals (); + errno = saved_errno; + return fd; +} + +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +FILE * +fopen_temp (const char *file_name, const char *mode) +{ + FILE *fp; + int saved_errno; + + block_fatal_signals (); + fp = fopen (file_name, mode); + saved_errno = errno; + if (fp != NULL) + { + /* It is sufficient to register fileno (fp) instead of the entire fp, + because at cleanup time there is no need to do an fflush (fp); a + close (fileno (fp)) will be enough. */ + int fd = fileno (fp); + if (!(fd >= 0)) + abort (); + register_fd (fd); + } + unblock_fatal_signals (); + errno = saved_errno; + return fp; +} + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +int +close_temp (int fd) +{ + if (fd >= 0) + { + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = close (fd); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; + } + else + return close (fd); +} + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +int +fclose_temp (FILE *fp) +{ + int fd = fileno (fp); + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = fclose (fp); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; +} + +#if GNULIB_FWRITEERROR +/* Like fwriteerror. + Unregisters the previously registered file descriptor. */ +int +fwriteerror_temp (FILE *fp) +{ + int fd = fileno (fp); + /* No blocking of signals is needed here, since a double close of a + file descriptor is harmless. */ + int result = fwriteerror (fp); + int saved_errno = errno; + + /* No race condition here: we assume a single-threaded program, hence + fd cannot be re-opened here. */ + + unregister_fd (fd); + + errno = saved_errno; + return result; +} +#endif diff --git a/lib/clean-temp.h b/lib/clean-temp.h index c564addac..aa5e8d3d5 100644 --- a/lib/clean-temp.h +++ b/lib/clean-temp.h @@ -20,6 +20,8 @@ #define _CLEAN_TEMP_H #include +#include +#include #ifdef __cplusplus extern "C" { @@ -32,6 +34,10 @@ extern "C" { also - if no signal blocking is used - leaves a time window where a fatal signal would not clean up the temporary file. + Also, open file descriptors need to be closed before the temporary files + and the temporary directories can be removed, because only on Unix + (excluding Cygwin) one can remove directories containing open files. + This module provides support for temporary directories and temporary files inside these temporary directories. Temporary files without temporary directories are not supported here. */ @@ -98,6 +104,20 @@ extern void cleanup_temp_dir_contents (struct temp_dir *dir); DIR cannot be used any more after this call. */ extern void cleanup_temp_dir (struct temp_dir *dir); +/* Open a temporary file in a temporary directory. + Registers the resulting file descriptor to be closed. */ +extern int open_temp (const char *file_name, int flags, mode_t mode); +extern FILE * fopen_temp (const char *file_name, const char *mode); + +/* Close a temporary file in a temporary directory. + Unregisters the previously registered file descriptor. */ +extern int close_temp (int fd); +extern int fclose_temp (FILE *fp); + +/* Like fwriteerror. + Unregisters the previously registered file descriptor. */ +extern int fwriteerror_temp (FILE *fp); + #ifdef __cplusplus } diff --git a/modules/fwriteerror b/modules/fwriteerror index f03112e00..b67a45aa8 100644 --- a/modules/fwriteerror +++ b/modules/fwriteerror @@ -9,6 +9,8 @@ Depends-on: stdbool configure.ac: +AC_DEFINE([GNULIB_FWRITEERROR], 1, + [Define to 1 when using the gnulib fwriteerror module.]) Makefile.am: lib_SOURCES += fwriteerror.h fwriteerror.c