Have clean-temp register file open descriptors to temporary files.
authorBruno Haible <bruno@clisp.org>
Fri, 6 Oct 2006 12:17:22 +0000 (12:17 +0000)
committerBruno Haible <bruno@clisp.org>
Fri, 6 Oct 2006 12:17:22 +0000 (12:17 +0000)
ChangeLog
lib/ChangeLog
lib/clean-temp.c
lib/clean-temp.h
modules/fwriteerror

index ef72a0a..23dae8a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2006-10-05  Bruno Haible  <bruno@clisp.org>
+
+       * modules/fwriteerror (configure.ac): Define GNULIB_FWRITEERROR.
+
 2006-10-02  Eric Blake  <ebb9@byu.net>
 
        * modules/strnlen (Depends-on): Add extensions.
index 0f958a2..6e58ab9 100644 (file)
@@ -1,3 +1,13 @@
+2006-10-05  Bruno Haible  <bruno@clisp.org>
+
+       * 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,
index 2322e7a..1d1e07c 100644 (file)
 
 #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 /* <int> */ 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
index c564add..aa5e8d3 100644 (file)
@@ -20,6 +20,8 @@
 #define _CLEAN_TEMP_H
 
 #include <stdbool.h>
+#include <stdio.h>
+#include <sys/types.h>
 
 #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
 }
index f03112e..b67a45a 100644 (file)
@@ -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