bug-gnulib@gnu.org mirror (unofficial)
 help / color / mirror / Atom feed
* clean-temp: add support for temporary files anywhere
@ 2020-07-04 13:30 Bruno Haible
  2020-07-04 16:08 ` Bruno Haible
  0 siblings, 1 reply; 2+ messages in thread
From: Bruno Haible @ 2020-07-04 13:30 UTC (permalink / raw)
  To: bug-gnulib

[-- Attachment #1: Type: text/plain, Size: 1695 bytes --]

Sometimes, temporary files are better placed in temporary subdirectories,
so that you have full control over the file names and access permissions.

But sometimes, temporary files are better placed in a given directory,
so that you can be sure that a 'rename' to another name in the same
directory will succeed.

These two patches add support for creating temporary files at a given
place, with automatic cleanup.

And some documentation improvement.


2020-07-04  Bruno Haible  <bruno@clisp.org>

	clean-temp: Document limitations.
	* lib/clean-temp.h: Document limitations.

2020-07-04  Bruno Haible  <bruno@clisp.org>

	clean-temp: Add support for temporary files with unpredictable names.
	* lib/clean-temp.h (gen_register_open_temp): New declaration.
	* lib/clean-temp.c: Include tempname.h.
	(gen_register_open_temp): New function.
	* modules/tempname (configure.ac): Define a module indicator.

2020-07-04  Bruno Haible  <bruno@clisp.org>

	clean-temp: Add support for temporary files anywhere in the file system.
	* lib/clean-temp.h (register_temporary_file, unregister_temporary_file,
	cleanup_temporary_file): New declarations.
	* lib/clean-temp.c (file_cleanup_list_lock, file_cleanup_list): New
	variables.
	(dir_cleanup_list_lock): Renamed from cleanup_list_lock.
	(dir_cleanup_list): Renamed from cleanup_list.
	(cleanup_action): Process the file_cleanup_list as well.
	(do_init_clean_temp): New function.
	(clean_temp_once): New variable.
	(init_clean_temp): New function.
	(create_temp_dir): Invoke it.
	(register_temporary_file, unregister_temporary_file,
	cleanup_temporary_file): New functions.
	(do_unlink, do_rmdir): Remove 'dir' argument. Add 'cleanup_verbose'
	argument.


[-- Attachment #2: 0001-clean-temp-Add-support-for-temporary-files-anywhere-.patch --]
[-- Type: text/x-patch, Size: 22245 bytes --]

From 629e348003127a1d780fc2edf4f5d38f42e09fb9 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Sat, 4 Jul 2020 15:17:32 +0200
Subject: [PATCH 1/3] clean-temp: Add support for temporary files anywhere in
 the file system.

* lib/clean-temp.h (register_temporary_file, unregister_temporary_file,
cleanup_temporary_file): New declarations.
* lib/clean-temp.c (file_cleanup_list_lock, file_cleanup_list): New
variables.
(dir_cleanup_list_lock): Renamed from cleanup_list_lock.
(dir_cleanup_list): Renamed from cleanup_list.
(cleanup_action): Process the file_cleanup_list as well.
(do_init_clean_temp): New function.
(clean_temp_once): New variable.
(init_clean_temp): New function.
(create_temp_dir): Invoke it.
(register_temporary_file, unregister_temporary_file,
cleanup_temporary_file): New functions.
(do_unlink, do_rmdir): Remove 'dir' argument. Add 'cleanup_verbose'
argument.
---
 ChangeLog        |  19 +++++
 lib/clean-temp.c | 244 +++++++++++++++++++++++++++++++++++++++++--------------
 lib/clean-temp.h |  42 ++++++++--
 3 files changed, 233 insertions(+), 72 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index aa31cfa..4c6319f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 2020-07-04  Bruno Haible  <bruno@clisp.org>
 
+	clean-temp: Add support for temporary files anywhere in the file system.
+	* lib/clean-temp.h (register_temporary_file, unregister_temporary_file,
+	cleanup_temporary_file): New declarations.
+	* lib/clean-temp.c (file_cleanup_list_lock, file_cleanup_list): New
+	variables.
+	(dir_cleanup_list_lock): Renamed from cleanup_list_lock.
+	(dir_cleanup_list): Renamed from cleanup_list.
+	(cleanup_action): Process the file_cleanup_list as well.
+	(do_init_clean_temp): New function.
+	(clean_temp_once): New variable.
+	(init_clean_temp): New function.
+	(create_temp_dir): Invoke it.
+	(register_temporary_file, unregister_temporary_file,
+	cleanup_temporary_file): New functions.
+	(do_unlink, do_rmdir): Remove 'dir' argument. Add 'cleanup_verbose'
+	argument.
+
+2020-07-04  Bruno Haible  <bruno@clisp.org>
+
 	clean-temp: Improve comments.
 	* lib/clean-temp.h (open_temp, fopen_temp, close_temp, fclose_temp,
 	fwriteerror_temp, close_stream_temp): Clarify intended use.
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index 75bd1ee..275dc78 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -84,6 +84,14 @@
    signal handler can rely on these field values to be up to date.  */
 
 
+/* Lock that protects the file_cleanup_list from concurrent modification in
+   different threads.  */
+gl_lock_define_initialized (static, file_cleanup_list_lock)
+
+/* List of all temporary files without temporary directories.  */
+static gl_list_t /* <char *> */ volatile file_cleanup_list;
+
+
 /* Registry for a single temporary directory.
    'struct temp_dir' from the public header file overlaps with this.  */
 struct tempdir
@@ -98,9 +106,9 @@ struct tempdir
   gl_list_t /* <char *> */ volatile files;
 };
 
-/* Lock that protects the cleanup_list from concurrent modification in
+/* Lock that protects the dir_cleanup_list from concurrent modification in
    different threads.  */
-gl_lock_define_initialized (static, cleanup_list_lock)
+gl_lock_define_initialized (static, dir_cleanup_list_lock)
 
 /* List of all temporary directories.  */
 static struct
@@ -108,7 +116,8 @@ static struct
   struct tempdir * volatile * volatile tempdir_list;
   size_t volatile tempdir_count;
   size_t tempdir_allocated;
-} cleanup_list /* = { NULL, 0, 0 } */;
+} dir_cleanup_list /* = { NULL, 0, 0 } */;
+
 
 /* A file descriptor to be closed.
    In multithreaded programs, it is forbidden to close the same fd twice,
@@ -313,9 +322,27 @@ cleanup_action (int sig _GL_UNUSED)
       }
   }
 
-  for (i = 0; i < cleanup_list.tempdir_count; i++)
+  {
+    gl_list_t files = file_cleanup_list;
+
+    if (files != NULL)
+      {
+        gl_list_iterator_t iter;
+        const void *element;
+
+        iter = gl_list_iterator (files);
+        while (gl_list_iterator_next (&iter, &element, NULL))
+          {
+            const char *file = (const char *) element;
+            unlink (file);
+          }
+        gl_list_iterator_free (&iter);
+      }
+  }
+
+  for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
     {
-      struct tempdir *dir = cleanup_list.tempdir_list[i];
+      struct tempdir *dir = dir_cleanup_list.tempdir_list[i];
 
       if (dir != NULL)
         {
@@ -346,6 +373,110 @@ cleanup_action (int sig _GL_UNUSED)
     }
 }
 
+
+/* Initializes this facility.  */
+static void
+do_init_clean_temp (void)
+{
+  /* Initialize the data used by the cleanup handler.  */
+  init_fatal_signal_set ();
+  /* Register the cleanup handler.  */
+  at_fatal_signal (&cleanup_action);
+}
+
+/* Ensure that do_init_clean_temp is called once only.  */
+gl_once_define(static, clean_temp_once)
+
+/* Initializes this facility upon first use.  */
+static void
+init_clean_temp (void)
+{
+  gl_once (clean_temp_once, do_init_clean_temp);
+}
+
+
+/* ============= Temporary files without temporary directories ============= */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+   removed.
+   Should be called before the file ABSOLUTE_FILE_NAME is created.  */
+void
+register_temporary_file (const char *absolute_file_name)
+{
+  gl_lock_lock (file_cleanup_list_lock);
+
+  /* Make sure that this facility and the file_cleanup_list are initialized.  */
+  if (file_cleanup_list == NULL)
+    {
+      init_clean_temp ();
+      file_cleanup_list =
+        gl_list_create_empty (GL_LINKEDHASH_LIST,
+                              string_equals, string_hash, NULL, false);
+    }
+
+  /* Add absolute_file_name to file_cleanup_list, without duplicates.  */
+  if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL)
+    gl_list_add_first (file_cleanup_list, xstrdup (absolute_file_name));
+
+  gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+   removed.
+   Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
+void
+unregister_temporary_file (const char *absolute_file_name)
+{
+  gl_lock_lock (file_cleanup_list_lock);
+
+  gl_list_t list = file_cleanup_list;
+  if (list != NULL)
+    {
+      gl_list_node_t node = gl_list_search (list, absolute_file_name);
+      if (node != NULL)
+        {
+          char *old_string = (char *) gl_list_node_value (list, node);
+
+          gl_list_remove_node (list, node);
+          free (old_string);
+        }
+    }
+
+  gl_lock_unlock (file_cleanup_list_lock);
+}
+
+/* Remove a file, with optional error message.
+   Return 0 upon success, or -1 if there was some problem.  */
+static int
+do_unlink (const char *absolute_file_name, bool cleanup_verbose)
+{
+  if (unlink (absolute_file_name) < 0 && cleanup_verbose
+      && errno != ENOENT)
+    {
+      error (0, errno,
+             _("cannot remove temporary file %s"), absolute_file_name);
+      return -1;
+    }
+  return 0;
+}
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+   CLEANUP_VERBOSE determines whether errors are reported to standard error.
+   Return 0 upon success, or -1 if there was some problem.  */
+int
+cleanup_temporary_file (const char *absolute_file_name, bool cleanup_verbose)
+{
+  int err;
+
+  err = do_unlink (absolute_file_name, cleanup_verbose);
+  unregister_temporary_file (absolute_file_name);
+
+  return err;
+}
+
+
+/* ========= Temporary directories and temporary files inside them ========= */
+
 /* Create a temporary directory.
    PREFIX is used as a prefix for the name of the temporary directory. It
    should be short and still give an indication about the program.
@@ -359,7 +490,7 @@ struct temp_dir *
 create_temp_dir (const char *prefix, const char *parentdir,
                  bool cleanup_verbose)
 {
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   struct tempdir * volatile *tmpdirp = NULL;
   struct tempdir *tmpdir;
@@ -369,30 +500,29 @@ create_temp_dir (const char *prefix, const char *parentdir,
 
   /* See whether it can take the slot of an earlier temporary directory
      already cleaned up.  */
-  for (i = 0; i < cleanup_list.tempdir_count; i++)
-    if (cleanup_list.tempdir_list[i] == NULL)
+  for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
+    if (dir_cleanup_list.tempdir_list[i] == NULL)
       {
-        tmpdirp = &cleanup_list.tempdir_list[i];
+        tmpdirp = &dir_cleanup_list.tempdir_list[i];
         break;
       }
   if (tmpdirp == NULL)
     {
       /* See whether the array needs to be extended.  */
-      if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated)
+      if (dir_cleanup_list.tempdir_count == dir_cleanup_list.tempdir_allocated)
         {
           /* Note that we cannot use xrealloc(), because then the cleanup()
              function could access an already deallocated array.  */
-          struct tempdir * volatile *old_array = cleanup_list.tempdir_list;
-          size_t old_allocated = cleanup_list.tempdir_allocated;
-          size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1;
+          struct tempdir * volatile *old_array = dir_cleanup_list.tempdir_list;
+          size_t old_allocated = dir_cleanup_list.tempdir_allocated;
+          size_t new_allocated = 2 * dir_cleanup_list.tempdir_allocated + 1;
           struct tempdir * volatile *new_array =
             XNMALLOC (new_allocated, struct tempdir * volatile);
 
           if (old_allocated == 0)
             {
-              /* First use of this facility.  Register the cleanup handler.  */
-              init_fatal_signal_set ();
-              at_fatal_signal (&cleanup_action);
+              /* First use of this facility.  */
+              init_clean_temp ();
             }
           else
             {
@@ -405,8 +535,8 @@ create_temp_dir (const char *prefix, const char *parentdir,
                 new_array[k] = old_array[k];
             }
 
-          cleanup_list.tempdir_list = new_array;
-          cleanup_list.tempdir_allocated = new_allocated;
+          dir_cleanup_list.tempdir_list = new_array;
+          dir_cleanup_list.tempdir_allocated = new_allocated;
 
           /* Now we can free the old array.  */
           /* No, we can't do that.  If cleanup_action is running in a different
@@ -420,11 +550,11 @@ create_temp_dir (const char *prefix, const char *parentdir,
           #endif
         }
 
-      tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
+      tmpdirp = &dir_cleanup_list.tempdir_list[dir_cleanup_list.tempdir_count];
       /* Initialize *tmpdirp before incrementing tempdir_count, so that
          cleanup() will skip this entry before it is fully initialized.  */
       *tmpdirp = NULL;
-      cleanup_list.tempdir_count++;
+      dir_cleanup_list.tempdir_count++;
     }
 
   /* Initialize a 'struct tempdir'.  */
@@ -467,12 +597,12 @@ create_temp_dir (const char *prefix, const char *parentdir,
      block because then the cleanup handler would not remove the directory
      if xstrdup fails.  */
   tmpdir->dirname = xstrdup (tmpdirname);
-  gl_lock_unlock (cleanup_list_lock);
+  gl_lock_unlock (dir_cleanup_list_lock);
   freea (xtemplate);
   return (struct temp_dir *) tmpdir;
 
  quit:
-  gl_lock_unlock (cleanup_list_lock);
+  gl_lock_unlock (dir_cleanup_list_lock);
   freea (xtemplate);
   return NULL;
 }
@@ -486,13 +616,13 @@ register_temp_file (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
 
-  gl_lock_unlock (cleanup_list_lock);
+  gl_lock_unlock (dir_cleanup_list_lock);
 }
 
 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
@@ -504,7 +634,7 @@ unregister_temp_file (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   gl_list_t list = tmpdir->files;
   gl_list_node_t node;
@@ -518,7 +648,7 @@ unregister_temp_file (struct temp_dir *dir,
       free (old_string);
     }
 
-  gl_lock_unlock (cleanup_list_lock);
+  gl_lock_unlock (dir_cleanup_list_lock);
 }
 
 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -530,13 +660,13 @@ register_temp_subdir (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
 
-  gl_lock_unlock (cleanup_list_lock);
+  gl_lock_unlock (dir_cleanup_list_lock);
 }
 
 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
@@ -549,7 +679,7 @@ unregister_temp_subdir (struct temp_dir *dir,
 {
   struct tempdir *tmpdir = (struct tempdir *)dir;
 
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   gl_list_t list = tmpdir->subdirs;
   gl_list_node_t node;
@@ -563,29 +693,15 @@ unregister_temp_subdir (struct temp_dir *dir,
       free (old_string);
     }
 
-  gl_lock_unlock (cleanup_list_lock);
-}
-
-/* Remove a file, with optional error message.
-   Return 0 upon success, or -1 if there was some problem.  */
-static int
-do_unlink (struct temp_dir *dir, const char *absolute_file_name)
-{
-  if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
-      && errno != ENOENT)
-    {
-      error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
-      return -1;
-    }
-  return 0;
+  gl_lock_unlock (dir_cleanup_list_lock);
 }
 
 /* Remove a directory, with optional error message.
    Return 0 upon success, or -1 if there was some problem.  */
 static int
-do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
+do_rmdir (const char *absolute_dir_name, bool cleanup_verbose)
 {
-  if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
+  if (rmdir (absolute_dir_name) < 0 && cleanup_verbose
       && errno != ENOENT)
     {
       error (0, errno,
@@ -603,7 +719,7 @@ cleanup_temp_file (struct temp_dir *dir,
 {
   int err;
 
-  err = do_unlink (dir, absolute_file_name);
+  err = do_unlink (absolute_file_name, dir->cleanup_verbose);
   unregister_temp_file (dir, absolute_file_name);
 
   return err;
@@ -617,14 +733,14 @@ cleanup_temp_subdir (struct temp_dir *dir,
 {
   int err;
 
-  err = do_rmdir (dir, absolute_dir_name);
+  err = do_rmdir (absolute_dir_name, dir->cleanup_verbose);
   unregister_temp_subdir (dir, absolute_dir_name);
 
   return err;
 }
 
 /* Remove all registered files and subdirectories inside DIR.
-   Only to be called with cleanup_list_lock locked.
+   Only to be called with dir_cleanup_list_lock locked.
    Return 0 upon success, or -1 if there was some problem.  */
 int
 cleanup_temp_dir_contents (struct temp_dir *dir)
@@ -643,7 +759,7 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
     {
       char *file = (char *) element;
 
-      err |= do_unlink (dir, file);
+      err |= do_unlink (file, dir->cleanup_verbose);
       gl_list_remove_node (list, node);
       /* Now only we can free file.  */
       free (file);
@@ -657,7 +773,7 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
     {
       char *subdir = (char *) element;
 
-      err |= do_rmdir (dir, subdir);
+      err |= do_rmdir (subdir, dir->cleanup_verbose);
       gl_list_remove_node (list, node);
       /* Now only we can free subdir.  */
       free (subdir);
@@ -673,34 +789,34 @@ cleanup_temp_dir_contents (struct temp_dir *dir)
 int
 cleanup_temp_dir (struct temp_dir *dir)
 {
-  gl_lock_lock (cleanup_list_lock);
+  gl_lock_lock (dir_cleanup_list_lock);
 
   struct tempdir *tmpdir = (struct tempdir *)dir;
   int err = 0;
   size_t i;
 
   err |= cleanup_temp_dir_contents (dir);
-  err |= do_rmdir (dir, tmpdir->dirname);
+  err |= do_rmdir (tmpdir->dirname, dir->cleanup_verbose);
 
-  for (i = 0; i < cleanup_list.tempdir_count; i++)
-    if (cleanup_list.tempdir_list[i] == tmpdir)
+  for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
+    if (dir_cleanup_list.tempdir_list[i] == tmpdir)
       {
-        /* Remove cleanup_list.tempdir_list[i].  */
-        if (i + 1 == cleanup_list.tempdir_count)
+        /* Remove dir_cleanup_list.tempdir_list[i].  */
+        if (i + 1 == dir_cleanup_list.tempdir_count)
           {
-            while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
+            while (i > 0 && dir_cleanup_list.tempdir_list[i - 1] == NULL)
               i--;
-            cleanup_list.tempdir_count = i;
+            dir_cleanup_list.tempdir_count = i;
           }
         else
-          cleanup_list.tempdir_list[i] = NULL;
+          dir_cleanup_list.tempdir_list[i] = NULL;
         /* Now only we can free the tmpdir->dirname, tmpdir->subdirs,
            tmpdir->files, and tmpdir itself.  */
         gl_list_free (tmpdir->files);
         gl_list_free (tmpdir->subdirs);
         free (tmpdir->dirname);
         free (tmpdir);
-        gl_lock_unlock (cleanup_list_lock);
+        gl_lock_unlock (dir_cleanup_list_lock);
         return err;
       }
 
@@ -709,6 +825,8 @@ cleanup_temp_dir (struct temp_dir *dir)
 }
 
 
+/* ================== Opening and closing temporary files ================== */
+
 #if defined _WIN32 && ! defined __CYGWIN__
 
 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
@@ -840,7 +958,7 @@ fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
   return fp;
 }
 
-/* Close a temporary file in a temporary directory.
+/* Close a temporary file.
    FD must have been returned by open_temp.
    Unregisters the previously registered file descriptor.  */
 int
@@ -968,7 +1086,7 @@ fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
   return result;
 }
 
-/* Close a temporary file in a temporary directory.
+/* Close a temporary file.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
    returned by open_temp.
    Unregisters the previously registered file descriptor.  */
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index 7848a15..c828d72 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -37,19 +37,41 @@ extern "C" {
    and the temporary directories can be removed, because only on Unix
    (excluding Cygwin) can one 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.  The temporary directories and files
-   are automatically cleaned up (at the latest) when the program exits or
-   dies from a fatal signal such as SIGINT, SIGTERM, SIGHUP, but not if it
-   dies from a fatal signal such as SIGQUIT, SIGKILL, or SIGABRT, SIGSEGV,
-   SIGBUS, SIGILL, SIGFPE.
+   This module provides support for
+     - temporary directories and temporary files inside these temporary
+       directories,
+     - temporary files without temporary directories.
+   The temporary directories and files are automatically cleaned up (at the
+   latest) when the program exits or dies from a fatal signal such as SIGINT,
+   SIGTERM, SIGHUP, but not if it dies from a fatal signal such as SIGQUIT,
+   SIGKILL, or SIGABRT, SIGSEGV, SIGBUS, SIGILL, SIGFPE.
 
    For the cleanup in the normal case, programs that use this module need to
    call 'cleanup_temp_dir' for each successful return of 'create_temp_dir'.
    The cleanup in the case of a fatal signal such as SIGINT, SIGTERM, SIGHUP,
    is done entirely automatically by the functions of this module.  */
 
+
+/* ============= Temporary files without temporary directories ============= */
+
+/* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
+   removed.
+   Should be called before the file ABSOLUTE_FILE_NAME is created.  */
+extern void register_temporary_file (const char *absolute_file_name);
+
+/* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
+   removed.
+   Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
+extern void unregister_temporary_file (const char *absolute_file_name);
+
+/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
+   CLEANUP_VERBOSE determines whether errors are reported to standard error.
+   Return 0 upon success, or -1 if there was some problem.  */
+extern int cleanup_temporary_file (const char *absolute_file_name,
+                                   bool cleanup_verbose);
+
+/* ========= Temporary directories and temporary files inside them ========= */
+
 struct temp_dir
 {
   /* The absolute pathname of the directory.  */
@@ -116,6 +138,8 @@ extern int cleanup_temp_dir_contents (struct temp_dir *dir);
    Return 0 upon success, or -1 if there was some problem.  */
 extern int cleanup_temp_dir (struct temp_dir *dir);
 
+/* ================== Opening and closing temporary files ================== */
+
 /* Open a temporary file in a temporary directory.
    FILE_NAME must already have been passed to register_temp_file.
    Registers the resulting file descriptor to be closed.
@@ -126,12 +150,12 @@ extern int open_temp (const char *file_name, int flags, mode_t mode,
 extern FILE * fopen_temp (const char *file_name, const char *mode,
                           bool delete_on_close);
 
-/* Close a temporary file in a temporary directory.
+/* Close a temporary file.
    FD must have been returned by open_temp.
    Unregisters the previously registered file descriptor.  */
 extern int close_temp (int fd);
 
-/* Close a temporary file in a temporary directory.
+/* Close a temporary file.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
    returned by open_temp.
    Unregisters the previously registered file descriptor.  */
-- 
2.7.4


[-- Attachment #3: 0002-clean-temp-Add-support-for-temporary-files-with-unpr.patch --]
[-- Type: text/x-patch, Size: 6245 bytes --]

From 7ee75d845dbbd2791c99668fef18438652113a00 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Sat, 4 Jul 2020 15:18:20 +0200
Subject: [PATCH 2/3] clean-temp: Add support for temporary files with
 unpredictable names.

* lib/clean-temp.h (gen_register_open_temp): New declaration.
* lib/clean-temp.c: Include tempname.h.
(gen_register_open_temp): New function.
* modules/tempname (configure.ac): Define a module indicator.
---
 ChangeLog        |  8 ++++++++
 lib/clean-temp.c | 37 +++++++++++++++++++++++++++++++++----
 lib/clean-temp.h | 17 +++++++++++++----
 modules/tempname |  1 +
 4 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4c6319f..0b48871 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,13 @@
 2020-07-04  Bruno Haible  <bruno@clisp.org>
 
+	clean-temp: Add support for temporary files with unpredictable names.
+	* lib/clean-temp.h (gen_register_open_temp): New declaration.
+	* lib/clean-temp.c: Include tempname.h.
+	(gen_register_open_temp): New function.
+	* modules/tempname (configure.ac): Define a module indicator.
+
+2020-07-04  Bruno Haible  <bruno@clisp.org>
+
 	clean-temp: Add support for temporary files anywhere in the file system.
 	* lib/clean-temp.h (register_temporary_file, unregister_temporary_file,
 	cleanup_temporary_file): New declarations.
diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index 275dc78..948a7ed 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -48,6 +48,9 @@
 #include "gl_linkedhash_list.h"
 #include "gl_linked_list.h"
 #include "gettext.h"
+#if GNULIB_TEMPNAME
+# include "tempname.h"
+#endif
 #if GNULIB_FWRITEERROR
 # include "fwriteerror.h"
 #endif
@@ -958,8 +961,34 @@ fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
   return fp;
 }
 
+#if GNULIB_TEMPNAME
+/* Open a temporary file, generating its name based on FILE_NAME_TMPL.
+   FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
+   possibly with a suffix).  The name constructed does not exist at the time
+   of the call.  FILE_NAME_TMPL is overwritten with the result.
+   Registers the file for deletion.
+   Opens the file, with the given FLAGS and mode 0600.
+   Registers the resulting file descriptor to be closed.  */
+int
+gen_register_open_temp (char *file_name_tmpl, int suffixlen, int flags)
+{
+  block_fatal_signals ();
+  int fd = gen_tempname (file_name_tmpl, suffixlen, flags, GT_FILE);
+  int saved_errno = errno;
+  if (fd >= 0)
+    {
+      init_clean_temp ();
+      register_fd (fd);
+      register_temporary_file (file_name_tmpl);
+    }
+  unblock_fatal_signals ();
+  errno = saved_errno;
+  return fd;
+}
+#endif
+
 /* Close a temporary file.
-   FD must have been returned by open_temp.
+   FD must have been returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 int
 close_temp (int fd)
@@ -1088,7 +1117,7 @@ fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
 
 /* Close a temporary file.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 int
 fclose_temp (FILE *fp)
@@ -1099,7 +1128,7 @@ fclose_temp (FILE *fp)
 #if GNULIB_FWRITEERROR
 /* Like fwriteerror.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 int
 fwriteerror_temp (FILE *fp)
@@ -1111,7 +1140,7 @@ fwriteerror_temp (FILE *fp)
 #if GNULIB_CLOSE_STREAM
 /* Like close_stream.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 int
 close_stream_temp (FILE *fp)
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index c828d72..087db45 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -150,26 +150,35 @@ extern int open_temp (const char *file_name, int flags, mode_t mode,
 extern FILE * fopen_temp (const char *file_name, const char *mode,
                           bool delete_on_close);
 
+/* Open a temporary file, generating its name based on FILE_NAME_TMPL.
+   FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
+   possibly with a suffix).  The name constructed does not exist at the time
+   of the call.  FILE_NAME_TMPL is overwritten with the result.
+   Registers the file for deletion.
+   Opens the file, with the given FLAGS and mode 0600.
+   Registers the resulting file descriptor to be closed.  */
+extern int gen_register_open_temp (char *file_name_tmpl, int suffixlen, int flags);
+
 /* Close a temporary file.
-   FD must have been returned by open_temp.
+   FD must have been returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 extern int close_temp (int fd);
 
 /* Close a temporary file.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 extern int fclose_temp (FILE *fp);
 
 /* Like fwriteerror.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 extern int fwriteerror_temp (FILE *fp);
 
 /* Like close_stream.
    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
-   returned by open_temp.
+   returned by open_temp or gen_register_open_temp.
    Unregisters the previously registered file descriptor.  */
 extern int close_stream_temp (FILE *fp);
 
diff --git a/modules/tempname b/modules/tempname
index ca88522..794a763 100644
--- a/modules/tempname
+++ b/modules/tempname
@@ -19,6 +19,7 @@ sys_stat
 
 configure.ac:
 gl_FUNC_GEN_TEMPNAME
+gl_MODULE_INDICATOR([tempname])
 
 Makefile.am:
 lib_SOURCES += tempname.c
-- 
2.7.4


[-- Attachment #4: 0003-clean-temp-Document-limitations.patch --]
[-- Type: text/x-patch, Size: 1975 bytes --]

From 564370e171d3e503d10463288bee2ff5273b2a33 Mon Sep 17 00:00:00 2001
From: Bruno Haible <bruno@clisp.org>
Date: Sat, 4 Jul 2020 15:22:33 +0200
Subject: [PATCH 3/3] clean-temp: Document limitations.

* lib/clean-temp.h: Document limitations.
---
 ChangeLog        |  5 +++++
 lib/clean-temp.h | 12 +++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 0b48871..8e0408c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,10 @@
 2020-07-04  Bruno Haible  <bruno@clisp.org>
 
+	clean-temp: Document limitations.
+	* lib/clean-temp.h: Document limitations.
+
+2020-07-04  Bruno Haible  <bruno@clisp.org>
+
 	clean-temp: Add support for temporary files with unpredictable names.
 	* lib/clean-temp.h (gen_register_open_temp): New declaration.
 	* lib/clean-temp.c: Include tempname.h.
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index 087db45..df7e744 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -49,7 +49,17 @@ extern "C" {
    For the cleanup in the normal case, programs that use this module need to
    call 'cleanup_temp_dir' for each successful return of 'create_temp_dir'.
    The cleanup in the case of a fatal signal such as SIGINT, SIGTERM, SIGHUP,
-   is done entirely automatically by the functions of this module.  */
+   is done entirely automatically by the functions of this module.
+
+   Limitations: Files or directories can still be left over if
+     - the program is dies from a fatal signal such as SIGQUIT, SIGKILL, or
+       SIGABRT, SIGSEGV, SIGBUS, SIGILL, SIGFPE, or
+     - in a multithreaded program, the fatal signal handler is already running
+       while another thread of the program creates a new temporary directory
+       or temporary file, or
+     - on native Windows, some temporary files are used by a subprocess while
+       the fatal signal interrupts the program.
+ */
 
 
 /* ============= Temporary files without temporary directories ============= */
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: clean-temp: add support for temporary files anywhere
  2020-07-04 13:30 clean-temp: add support for temporary files anywhere Bruno Haible
@ 2020-07-04 16:08 ` Bruno Haible
  0 siblings, 0 replies; 2+ messages in thread
From: Bruno Haible @ 2020-07-04 16:08 UTC (permalink / raw)
  To: bug-gnulib

> 2020-07-04  Bruno Haible  <bruno@clisp.org>
> 
> 	clean-temp: Add support for temporary files with unpredictable names.
> 	* lib/clean-temp.h (gen_register_open_temp): New declaration.

This can be made a bit more powerful, by adding a 'mode' argument.


2020-07-04  Bruno Haible  <bruno@clisp.org>

	clean-temp: Add support for temporary files with given mode.
	* lib/clean-temp.h (gen_register_open_temp): Add mode argument.
	* lib/clean-temp.c (struct try_create_file_params): New type.
	(try_create_file): New function.
	(gen_register_open_temp): Add mode argument. Use try_tempname instead of
	gen_tempname.

diff --git a/lib/clean-temp.c b/lib/clean-temp.c
index 948a7ed..34ebb9b 100644
--- a/lib/clean-temp.c
+++ b/lib/clean-temp.c
@@ -962,18 +962,42 @@ fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
 }
 
 #if GNULIB_TEMPNAME
+
+struct try_create_file_params
+{
+  int flags;
+  mode_t mode;
+};
+
+static int
+try_create_file (char *file_name_tmpl, void *params_)
+{
+  struct try_create_file_params *params = params_;
+  return open (file_name_tmpl,
+               (params->flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL,
+               params->mode);
+}
+
 /* Open a temporary file, generating its name based on FILE_NAME_TMPL.
    FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
    possibly with a suffix).  The name constructed does not exist at the time
    of the call.  FILE_NAME_TMPL is overwritten with the result.
+   A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
    Registers the file for deletion.
-   Opens the file, with the given FLAGS and mode 0600.
+   Opens the file, with the given FLAGS and mode MODE.
    Registers the resulting file descriptor to be closed.  */
 int
-gen_register_open_temp (char *file_name_tmpl, int suffixlen, int flags)
+gen_register_open_temp (char *file_name_tmpl, int suffixlen,
+                        int flags, mode_t mode)
 {
   block_fatal_signals ();
-  int fd = gen_tempname (file_name_tmpl, suffixlen, flags, GT_FILE);
+
+  struct try_create_file_params params;
+  params.flags = flags;
+  params.mode = mode;
+
+  int fd = try_tempname (file_name_tmpl, suffixlen, &params, try_create_file);
+
   int saved_errno = errno;
   if (fd >= 0)
     {
@@ -985,6 +1009,7 @@ gen_register_open_temp (char *file_name_tmpl, int suffixlen, int flags)
   errno = saved_errno;
   return fd;
 }
+
 #endif
 
 /* Close a temporary file.
diff --git a/lib/clean-temp.h b/lib/clean-temp.h
index df7e744..d660b18 100644
--- a/lib/clean-temp.h
+++ b/lib/clean-temp.h
@@ -164,10 +164,12 @@ extern FILE * fopen_temp (const char *file_name, const char *mode,
    FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
    possibly with a suffix).  The name constructed does not exist at the time
    of the call.  FILE_NAME_TMPL is overwritten with the result.
+   A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
    Registers the file for deletion.
-   Opens the file, with the given FLAGS and mode 0600.
+   Opens the file, with the given FLAGS and mode MODE.
    Registers the resulting file descriptor to be closed.  */
-extern int gen_register_open_temp (char *file_name_tmpl, int suffixlen, int flags);
+extern int gen_register_open_temp (char *file_name_tmpl, int suffixlen,
+                                   int flags, mode_t mode);
 
 /* Close a temporary file.
    FD must have been returned by open_temp or gen_register_open_temp.



^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-07-04 16:09 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-04 13:30 clean-temp: add support for temporary files anywhere Bruno Haible
2020-07-04 16:08 ` Bruno Haible

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).