changeset 7411:67235cf9199a

Have clean-temp register file open descriptors to temporary files.
author Bruno Haible <bruno@clisp.org>
date Fri, 06 Oct 2006 12:17:22 +0000
parents 9704ff2cbdfe
children 066c3e27ecd1
files ChangeLog lib/ChangeLog lib/clean-temp.c lib/clean-temp.h modules/fwriteerror
diffstat 5 files changed, 199 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- 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.
--- a/lib/ChangeLog
+++ b/lib/ChangeLog
@@ -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,
--- 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 @@
   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 @@
 {
   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 @@
   /* 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
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -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 @@
    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 @@
    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
 }
--- a/modules/fwriteerror
+++ b/modules/fwriteerror
@@ -9,6 +9,8 @@
 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