1  /* Temporary directories and temporary files with automatic cleanup.
       2     Copyright (C) 2001, 2003, 2006-2007, 2009-2021 Free Software Foundation,
       3     Inc.
       4     Written by Bruno Haible <bruno@clisp.org>, 2006.
       5  
       6     This program is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU General Public License as published by
       8     the Free Software Foundation; either version 3 of the License, or
       9     (at your option) any later version.
      10  
      11     This program is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU General Public License for more details.
      15  
      16     You should have received a copy of the GNU General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <config.h>
      20  
      21  /* Specification.  */
      22  #include "clean-temp.h"
      23  
      24  #include <errno.h>
      25  #include <fcntl.h>
      26  #include <signal.h>
      27  #include <stdbool.h>
      28  #include <stdio.h>
      29  #include <stdlib.h>
      30  #include <string.h>
      31  #include <unistd.h>
      32  
      33  #if defined _WIN32 && ! defined __CYGWIN__
      34  # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
      35  # include <windows.h>
      36  #endif
      37  
      38  #include "clean-temp-simple.h"
      39  #include "clean-temp-private.h"
      40  #include "error.h"
      41  #include "fatal-signal.h"
      42  #include "asyncsafe-spin.h"
      43  #include "pathmax.h"
      44  #include "tmpdir.h"
      45  #include "xalloc.h"
      46  #include "xmalloca.h"
      47  #include "glthread/lock.h"
      48  #include "thread-optim.h"
      49  #include "gl_xlist.h"
      50  #include "gl_linkedhash_list.h"
      51  #include "gl_linked_list.h"
      52  #include "gettext.h"
      53  #if GNULIB_TEMPNAME
      54  # include "tempname.h"
      55  #endif
      56  #if GNULIB_FWRITEERROR
      57  # include "fwriteerror.h"
      58  #endif
      59  #if GNULIB_CLOSE_STREAM
      60  # include "close-stream.h"
      61  #endif
      62  #if GNULIB_FCNTL_SAFER
      63  # include "fcntl--.h"
      64  #endif
      65  #if GNULIB_FOPEN_SAFER
      66  # include "stdio--.h"
      67  #endif
      68  
      69  #define _(str) gettext (str)
      70  
      71  /* GNU Hurd doesn't have PATH_MAX.  Use a fallback.
      72     Temporary directory names are usually not that long.  */
      73  #ifndef PATH_MAX
      74  # define PATH_MAX 1024
      75  #endif
      76  
      77  #if defined _WIN32 && ! defined __CYGWIN__
      78  /* Don't assume that UNICODE is not defined.  */
      79  # undef OSVERSIONINFO
      80  # define OSVERSIONINFO OSVERSIONINFOA
      81  # undef GetVersionEx
      82  # define GetVersionEx GetVersionExA
      83  #endif
      84  
      85  
      86  /* Lock that protects the dir_cleanup_list from concurrent modification in
      87     different threads.  */
      88  gl_lock_define_initialized (static, dir_cleanup_list_lock)
      89  
      90  /* Lock that protects the descriptors list from concurrent modification in
      91     different threads.  */
      92  gl_lock_define_initialized (static, descriptors_lock)
      93  
      94  
      95  /* Close a file descriptor and the stream that contains it.
      96     Avoids race conditions with signal-handler code that might want to close the
      97     same file descriptor.  */
      98  static int
      99  asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
     100                            int (*fclose_variant) (FILE *))
     101  {
     102    if (fileno (fp) != element->fd)
     103      abort ();
     104  
     105    /* Flush buffered data first, to minimize the duration of the spin lock.  */
     106    fflush (fp);
     107  
     108    sigset_t saved_mask;
     109    int ret;
     110    int saved_errno;
     111  
     112    asyncsafe_spin_lock (&element->lock, get_fatal_signal_set (), &saved_mask);
     113    if (!element->closed)
     114      {
     115        ret = fclose_variant (fp); /* invokes close (element->fd) */
     116        saved_errno = errno;
     117        element->closed = true;
     118      }
     119    else
     120      {
     121        ret = 0;
     122        saved_errno = 0;
     123      }
     124    asyncsafe_spin_unlock (&element->lock, &saved_mask);
     125    element->done = true;
     126  
     127    errno = saved_errno;
     128    return ret;
     129  }
     130  
     131  
     132  /* ========= Temporary directories and temporary files inside them ========= */
     133  
     134  /* Create a temporary directory.
     135     PREFIX is used as a prefix for the name of the temporary directory. It
     136     should be short and still give an indication about the program.
     137     PARENTDIR can be used to specify the parent directory; if NULL, a default
     138     parent directory is used (either $TMPDIR or /tmp or similar).
     139     CLEANUP_VERBOSE determines whether errors during explicit cleanup are
     140     reported to standard error.
     141     Return a fresh 'struct temp_dir' on success.  Upon error, an error message
     142     is shown and NULL is returned.  */
     143  struct temp_dir *
     144  create_temp_dir (const char *prefix, const char *parentdir,
     145                   bool cleanup_verbose)
     146  {
     147    bool mt = gl_multithreaded ();
     148  
     149    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     150  
     151    struct tempdir * volatile *tmpdirp = NULL;
     152    struct tempdir *tmpdir;
     153    size_t i;
     154    char *xtemplate;
     155    char *tmpdirname;
     156  
     157    /* See whether it can take the slot of an earlier temporary directory
     158       already cleaned up.  */
     159    for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
     160      if (dir_cleanup_list.tempdir_list[i] == NULL)
     161        {
     162          tmpdirp = &dir_cleanup_list.tempdir_list[i];
     163          break;
     164        }
     165    if (tmpdirp == NULL)
     166      {
     167        /* See whether the array needs to be extended.  */
     168        if (dir_cleanup_list.tempdir_count == dir_cleanup_list.tempdir_allocated)
     169          {
     170            /* Note that we cannot use xrealloc(), because then the cleanup()
     171               function could access an already deallocated array.  */
     172            struct tempdir * volatile *old_array = dir_cleanup_list.tempdir_list;
     173            size_t old_allocated = dir_cleanup_list.tempdir_allocated;
     174            size_t new_allocated = 2 * dir_cleanup_list.tempdir_allocated + 1;
     175            struct tempdir * volatile *new_array =
     176              XNMALLOC (new_allocated, struct tempdir * volatile);
     177  
     178            if (old_allocated == 0)
     179              {
     180                /* First use of this facility.  */
     181                if (clean_temp_init () < 0)
     182                  xalloc_die ();
     183              }
     184            else
     185              {
     186                /* Don't use memcpy() here, because memcpy takes non-volatile
     187                   arguments and is therefore not guaranteed to complete all
     188                   memory stores before the next statement.  */
     189                size_t k;
     190  
     191                for (k = 0; k < old_allocated; k++)
     192                  new_array[k] = old_array[k];
     193              }
     194  
     195            dir_cleanup_list.tempdir_list = new_array;
     196            dir_cleanup_list.tempdir_allocated = new_allocated;
     197  
     198            /* Now we can free the old array.  */
     199            /* No, we can't do that.  If cleanup_action is running in a different
     200               thread and has already fetched the tempdir_list pointer (getting
     201               old_array) but not yet accessed its i-th element, that thread may
     202               crash when accessing an element of the already freed old_array
     203               array.  */
     204            #if 0
     205            if (old_array != NULL)
     206              free ((struct tempdir **) old_array);
     207            #endif
     208          }
     209  
     210        tmpdirp = &dir_cleanup_list.tempdir_list[dir_cleanup_list.tempdir_count];
     211        /* Initialize *tmpdirp before incrementing tempdir_count, so that
     212           cleanup() will skip this entry before it is fully initialized.  */
     213        *tmpdirp = NULL;
     214        dir_cleanup_list.tempdir_count++;
     215      }
     216  
     217    /* Initialize a 'struct tempdir'.  */
     218    tmpdir = XMALLOC (struct tempdir);
     219    tmpdir->dirname = NULL;
     220    tmpdir->cleanup_verbose = cleanup_verbose;
     221    tmpdir->subdirs =
     222      gl_list_create_empty (GL_LINKEDHASH_LIST,
     223                            clean_temp_string_equals, clean_temp_string_hash,
     224                            NULL, false);
     225    tmpdir->files =
     226      gl_list_create_empty (GL_LINKEDHASH_LIST,
     227                            clean_temp_string_equals, clean_temp_string_hash,
     228                            NULL, false);
     229  
     230    /* Create the temporary directory.  */
     231    xtemplate = (char *) xmalloca (PATH_MAX);
     232    if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
     233      {
     234        error (0, errno,
     235               _("cannot find a temporary directory, try setting $TMPDIR"));
     236        goto quit;
     237      }
     238    block_fatal_signals ();
     239    tmpdirname = mkdtemp (xtemplate);
     240    int saved_errno = errno;
     241    if (tmpdirname != NULL)
     242      {
     243        tmpdir->dirname = tmpdirname;
     244        *tmpdirp = tmpdir;
     245      }
     246    unblock_fatal_signals ();
     247    if (tmpdirname == NULL)
     248      {
     249        error (0, saved_errno,
     250               _("cannot create a temporary directory using template \"%s\""),
     251               xtemplate);
     252        goto quit;
     253      }
     254    /* Replace tmpdir->dirname with a copy that has indefinite extent.
     255       We cannot do this inside the block_fatal_signals/unblock_fatal_signals
     256       block because then the cleanup handler would not remove the directory
     257       if xstrdup fails.  */
     258    tmpdir->dirname = xstrdup (tmpdirname);
     259    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     260    freea (xtemplate);
     261    return (struct temp_dir *) tmpdir;
     262  
     263   quit:
     264    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     265    freea (xtemplate);
     266    return NULL;
     267  }
     268  
     269  /* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
     270     needs to be removed before DIR can be removed.
     271     Should be called before the file ABSOLUTE_FILE_NAME is created.  */
     272  void
     273  register_temp_file (struct temp_dir *dir,
     274                      const char *absolute_file_name)
     275  {
     276    struct tempdir *tmpdir = (struct tempdir *)dir;
     277    bool mt = gl_multithreaded ();
     278  
     279    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     280  
     281    /* Add absolute_file_name to tmpdir->files, without duplicates.  */
     282    if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
     283      gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
     284  
     285    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     286  }
     287  
     288  /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
     289     needs to be removed before DIR can be removed.
     290     Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
     291  void
     292  unregister_temp_file (struct temp_dir *dir,
     293                        const char *absolute_file_name)
     294  {
     295    struct tempdir *tmpdir = (struct tempdir *)dir;
     296    bool mt = gl_multithreaded ();
     297  
     298    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     299  
     300    gl_list_t list = tmpdir->files;
     301    gl_list_node_t node;
     302  
     303    node = gl_list_search (list, absolute_file_name);
     304    if (node != NULL)
     305      {
     306        char *old_string = (char *) gl_list_node_value (list, node);
     307  
     308        gl_list_remove_node (list, node);
     309        free (old_string);
     310      }
     311  
     312    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     313  }
     314  
     315  /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
     316     that needs to be removed before DIR can be removed.
     317     Should be called before the subdirectory ABSOLUTE_DIR_NAME is created.  */
     318  void
     319  register_temp_subdir (struct temp_dir *dir,
     320                        const char *absolute_dir_name)
     321  {
     322    struct tempdir *tmpdir = (struct tempdir *)dir;
     323    bool mt = gl_multithreaded ();
     324  
     325    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     326  
     327    /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
     328    if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
     329      gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
     330  
     331    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     332  }
     333  
     334  /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
     335     that needs to be removed before DIR can be removed.
     336     Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
     337     created.  */
     338  void
     339  unregister_temp_subdir (struct temp_dir *dir,
     340                          const char *absolute_dir_name)
     341  {
     342    struct tempdir *tmpdir = (struct tempdir *)dir;
     343    bool mt = gl_multithreaded ();
     344  
     345    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     346  
     347    gl_list_t list = tmpdir->subdirs;
     348    gl_list_node_t node;
     349  
     350    node = gl_list_search (list, absolute_dir_name);
     351    if (node != NULL)
     352      {
     353        char *old_string = (char *) gl_list_node_value (list, node);
     354  
     355        gl_list_remove_node (list, node);
     356        free (old_string);
     357      }
     358  
     359    if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     360  }
     361  
     362  /* Remove a directory, with optional error message.
     363     Return 0 upon success, or -1 if there was some problem.  */
     364  static int
     365  do_rmdir (const char *absolute_dir_name, bool cleanup_verbose)
     366  {
     367    if (rmdir (absolute_dir_name) < 0 && cleanup_verbose
     368        && errno != ENOENT)
     369      {
     370        error (0, errno,
     371               _("cannot remove temporary directory %s"), absolute_dir_name);
     372        return -1;
     373      }
     374    return 0;
     375  }
     376  
     377  /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
     378     Return 0 upon success, or -1 if there was some problem.  */
     379  int
     380  cleanup_temp_file (struct temp_dir *dir,
     381                     const char *absolute_file_name)
     382  {
     383    int err;
     384  
     385    err = clean_temp_unlink (absolute_file_name, dir->cleanup_verbose);
     386    unregister_temp_file (dir, absolute_file_name);
     387  
     388    return err;
     389  }
     390  
     391  /* Remove the given ABSOLUTE_DIR_NAME and unregister it.
     392     Return 0 upon success, or -1 if there was some problem.  */
     393  int
     394  cleanup_temp_subdir (struct temp_dir *dir,
     395                       const char *absolute_dir_name)
     396  {
     397    int err;
     398  
     399    err = do_rmdir (absolute_dir_name, dir->cleanup_verbose);
     400    unregister_temp_subdir (dir, absolute_dir_name);
     401  
     402    return err;
     403  }
     404  
     405  /* Remove all registered files and subdirectories inside DIR.
     406     Only to be called with dir_cleanup_list_lock locked.
     407     Return 0 upon success, or -1 if there was some problem.  */
     408  int
     409  cleanup_temp_dir_contents (struct temp_dir *dir)
     410  {
     411    struct tempdir *tmpdir = (struct tempdir *)dir;
     412    int err = 0;
     413    gl_list_t list;
     414    gl_list_iterator_t iter;
     415    const void *element;
     416    gl_list_node_t node;
     417  
     418    /* First cleanup the files in the subdirectories.  */
     419    list = tmpdir->files;
     420    iter = gl_list_iterator (list);
     421    while (gl_list_iterator_next (&iter, &element, &node))
     422      {
     423        char *file = (char *) element;
     424  
     425        err |= clean_temp_unlink (file, dir->cleanup_verbose);
     426        gl_list_remove_node (list, node);
     427        /* Now only we can free file.  */
     428        free (file);
     429      }
     430    gl_list_iterator_free (&iter);
     431  
     432    /* Then cleanup the subdirectories.  */
     433    list = tmpdir->subdirs;
     434    iter = gl_list_iterator (list);
     435    while (gl_list_iterator_next (&iter, &element, &node))
     436      {
     437        char *subdir = (char *) element;
     438  
     439        err |= do_rmdir (subdir, dir->cleanup_verbose);
     440        gl_list_remove_node (list, node);
     441        /* Now only we can free subdir.  */
     442        free (subdir);
     443      }
     444    gl_list_iterator_free (&iter);
     445  
     446    return err;
     447  }
     448  
     449  /* Remove all registered files and subdirectories inside DIR and DIR itself.
     450     DIR cannot be used any more after this call.
     451     Return 0 upon success, or -1 if there was some problem.  */
     452  int
     453  cleanup_temp_dir (struct temp_dir *dir)
     454  {
     455    bool mt = gl_multithreaded ();
     456  
     457    if (mt) gl_lock_lock (dir_cleanup_list_lock);
     458  
     459    struct tempdir *tmpdir = (struct tempdir *)dir;
     460    int err = 0;
     461    size_t i;
     462  
     463    err |= cleanup_temp_dir_contents (dir);
     464    err |= do_rmdir (tmpdir->dirname, dir->cleanup_verbose);
     465  
     466    for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
     467      if (dir_cleanup_list.tempdir_list[i] == tmpdir)
     468        {
     469          /* Remove dir_cleanup_list.tempdir_list[i].  */
     470          if (i + 1 == dir_cleanup_list.tempdir_count)
     471            {
     472              while (i > 0 && dir_cleanup_list.tempdir_list[i - 1] == NULL)
     473                i--;
     474              dir_cleanup_list.tempdir_count = i;
     475            }
     476          else
     477            dir_cleanup_list.tempdir_list[i] = NULL;
     478          /* Now only we can free the tmpdir->dirname, tmpdir->subdirs,
     479             tmpdir->files, and tmpdir itself.  */
     480          gl_list_free (tmpdir->files);
     481          gl_list_free (tmpdir->subdirs);
     482          free (tmpdir->dirname);
     483          free (tmpdir);
     484          if (mt) gl_lock_unlock (dir_cleanup_list_lock);
     485          return err;
     486        }
     487  
     488    /* The user passed an invalid DIR argument.  */
     489    abort ();
     490  }
     491  
     492  
     493  /* ================== Opening and closing temporary files ================== */
     494  
     495  #if defined _WIN32 && ! defined __CYGWIN__
     496  
     497  /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
     498     the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
     499     of deleting the file when it is closed - even when the program crashes.
     500     But (according to the Cygwin sources) it works only on Windows NT or newer.
     501     So we cache the info whether we are running on Windows NT or newer.  */
     502  
     503  static bool
     504  supports_delete_on_close ()
     505  {
     506    static int known; /* 1 = yes, -1 = no, 0 = unknown */
     507    if (!known)
     508      {
     509        OSVERSIONINFO v;
     510  
     511        /* According to
     512           <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getversionexa>
     513           this structure must be initialized as follows:  */
     514        v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
     515  
     516        if (GetVersionEx (&v))
     517          known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
     518        else
     519          known = -1;
     520      }
     521    return (known > 0);
     522  }
     523  
     524  #endif
     525  
     526  
     527  /* Register a file descriptor to be closed.  */
     528  static void
     529  register_fd (int fd)
     530  {
     531    bool mt = gl_multithreaded ();
     532  
     533    if (mt) gl_lock_lock (descriptors_lock);
     534  
     535    if (descriptors == NULL)
     536      descriptors = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL,
     537                                          false);
     538  
     539    struct closeable_fd *element = XMALLOC (struct closeable_fd);
     540    element->fd = fd;
     541    element->closed = false;
     542    asyncsafe_spin_init (&element->lock);
     543    element->done = false;
     544  
     545    gl_list_add_first (descriptors, element);
     546  
     547    if (mt) gl_lock_unlock (descriptors_lock);
     548  }
     549  
     550  /* Open a temporary file in a temporary directory.
     551     FILE_NAME must already have been passed to register_temp_file.
     552     Registers the resulting file descriptor to be closed.
     553     DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
     554     file descriptor or stream is closed.  */
     555  int
     556  open_temp (const char *file_name, int flags, mode_t mode, bool delete_on_close)
     557  {
     558    int fd;
     559    int saved_errno;
     560  
     561    block_fatal_signals ();
     562    /* Note: 'open' here is actually open() or open_safer().  */
     563  #if defined _WIN32 && ! defined __CYGWIN__
     564    /* Use _O_TEMPORARY when possible, to increase the chances that the
     565       temporary file is removed when the process crashes.  */
     566    if (delete_on_close && supports_delete_on_close ())
     567      fd = open (file_name, flags | _O_TEMPORARY, mode);
     568    else
     569  #endif
     570      fd = open (file_name, flags, mode);
     571    saved_errno = errno;
     572    if (fd >= 0)
     573      register_fd (fd);
     574    unblock_fatal_signals ();
     575    errno = saved_errno;
     576    return fd;
     577  }
     578  
     579  /* Open a temporary file in a temporary directory.
     580     FILE_NAME must already have been passed to register_temp_file.
     581     Registers the resulting file descriptor to be closed.
     582     DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
     583     file descriptor or stream is closed.  */
     584  FILE *
     585  fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
     586  {
     587    FILE *fp;
     588    int saved_errno;
     589  
     590    block_fatal_signals ();
     591    /* Note: 'fopen' here is actually fopen() or fopen_safer().  */
     592  #if defined _WIN32 && ! defined __CYGWIN__
     593    /* Use _O_TEMPORARY when possible, to increase the chances that the
     594       temporary file is removed when the process crashes.  */
     595    if (delete_on_close && supports_delete_on_close ())
     596      {
     597        size_t mode_len = strlen (mode);
     598        char *augmented_mode = (char *) xmalloca (mode_len + 2);
     599        memcpy (augmented_mode, mode, mode_len);
     600        memcpy (augmented_mode + mode_len, "D", 2);
     601  
     602        fp = fopen (file_name, augmented_mode);
     603        saved_errno = errno;
     604  
     605        freea (augmented_mode);
     606      }
     607    else
     608  #endif
     609      {
     610        fp = fopen (file_name, mode);
     611        saved_errno = errno;
     612      }
     613    if (fp != NULL)
     614      {
     615        /* It is sufficient to register fileno (fp) instead of the entire fp,
     616           because at cleanup time there is no need to do an fflush (fp); a
     617           close (fileno (fp)) will be enough.  */
     618        int fd = fileno (fp);
     619        if (!(fd >= 0))
     620          abort ();
     621        register_fd (fd);
     622      }
     623    unblock_fatal_signals ();
     624    errno = saved_errno;
     625    return fp;
     626  }
     627  
     628  #if GNULIB_TEMPNAME
     629  
     630  struct try_create_file_params
     631  {
     632    int flags;
     633    mode_t mode;
     634  };
     635  
     636  static int
     637  try_create_file (char *file_name_tmpl, void *params_)
     638  {
     639    struct try_create_file_params *params = params_;
     640    return open (file_name_tmpl,
     641                 (params->flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL,
     642                 params->mode);
     643  }
     644  
     645  /* Open a temporary file, generating its name based on FILE_NAME_TMPL.
     646     FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
     647     possibly with a suffix).  The name constructed does not exist at the time
     648     of the call.  FILE_NAME_TMPL is overwritten with the result.
     649     A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
     650     Registers the file for deletion.
     651     Opens the file, with the given FLAGS and mode MODE.
     652     Registers the resulting file descriptor to be closed.  */
     653  int
     654  gen_register_open_temp (char *file_name_tmpl, int suffixlen,
     655                          int flags, mode_t mode)
     656  {
     657    block_fatal_signals ();
     658  
     659    struct try_create_file_params params;
     660    params.flags = flags;
     661    params.mode = mode;
     662  
     663    int fd = try_tempname (file_name_tmpl, suffixlen, ¶ms, try_create_file);
     664  
     665    int saved_errno = errno;
     666    if (fd >= 0)
     667      {
     668        if (clean_temp_init () < 0)
     669          xalloc_die ();
     670        register_fd (fd);
     671        if (register_temporary_file (file_name_tmpl) < 0)
     672          xalloc_die ();
     673      }
     674    unblock_fatal_signals ();
     675    errno = saved_errno;
     676    return fd;
     677  }
     678  
     679  #endif
     680  
     681  /* Close a temporary file.
     682     FD must have been returned by open_temp or gen_register_open_temp.
     683     Unregisters the previously registered file descriptor.  */
     684  int
     685  close_temp (int fd)
     686  {
     687    if (fd < 0)
     688      return close (fd);
     689  
     690    clean_temp_init_asyncsafe_close ();
     691  
     692    int result = 0;
     693    int saved_errno = 0;
     694  
     695    bool mt = gl_multithreaded ();
     696  
     697    if (mt) gl_lock_lock (descriptors_lock);
     698  
     699    gl_list_t list = descriptors;
     700    if (list == NULL)
     701      /* descriptors should already contain fd.  */
     702      abort ();
     703  
     704    /* Search through the list, and clean it up on the fly.  */
     705    bool found = false;
     706    gl_list_iterator_t iter = gl_list_iterator (list);
     707    const void *elt;
     708    gl_list_node_t node;
     709    if (gl_list_iterator_next (&iter, &elt, &node))
     710      for (;;)
     711        {
     712          struct closeable_fd *element = (struct closeable_fd *) elt;
     713  
     714          /* Close the file descriptor, avoiding races with the signal
     715             handler.  */
     716          if (element->fd == fd)
     717            {
     718              found = true;
     719              result = clean_temp_asyncsafe_close (element);
     720              saved_errno = errno;
     721            }
     722  
     723          bool free_this_node = element->done;
     724          struct closeable_fd *element_to_free = element;
     725          gl_list_node_t node_to_free = node;
     726  
     727          bool have_next = gl_list_iterator_next (&iter, &elt, &node);
     728  
     729          if (free_this_node)
     730            {
     731              free (element_to_free);
     732              gl_list_remove_node (list, node_to_free);
     733            }
     734  
     735          if (!have_next)
     736            break;
     737        }
     738    gl_list_iterator_free (&iter);
     739    if (!found)
     740      /* descriptors should already contain fd.  */
     741      abort ();
     742  
     743    if (mt) gl_lock_unlock (descriptors_lock);
     744  
     745    errno = saved_errno;
     746    return result;
     747  }
     748  
     749  static int
     750  fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
     751  {
     752    int fd = fileno (fp);
     753  
     754    int result = 0;
     755    int saved_errno = 0;
     756  
     757    bool mt = gl_multithreaded ();
     758  
     759    if (mt) gl_lock_lock (descriptors_lock);
     760  
     761    gl_list_t list = descriptors;
     762    if (list == NULL)
     763      /* descriptors should already contain fd.  */
     764      abort ();
     765  
     766    /* Search through the list, and clean it up on the fly.  */
     767    bool found = false;
     768    gl_list_iterator_t iter = gl_list_iterator (list);
     769    const void *elt;
     770    gl_list_node_t node;
     771    if (gl_list_iterator_next (&iter, &elt, &node))
     772      for (;;)
     773        {
     774          struct closeable_fd *element = (struct closeable_fd *) elt;
     775  
     776          /* Close the file descriptor and the stream, avoiding races with the
     777             signal handler.  */
     778          if (element->fd == fd)
     779            {
     780              found = true;
     781              result = asyncsafe_fclose_variant (element, fp, fclose_variant);
     782              saved_errno = errno;
     783            }
     784  
     785          bool free_this_node = element->done;
     786          struct closeable_fd *element_to_free = element;
     787          gl_list_node_t node_to_free = node;
     788  
     789          bool have_next = gl_list_iterator_next (&iter, &elt, &node);
     790  
     791          if (free_this_node)
     792            {
     793              free (element_to_free);
     794              gl_list_remove_node (list, node_to_free);
     795            }
     796  
     797          if (!have_next)
     798            break;
     799        }
     800    gl_list_iterator_free (&iter);
     801    if (!found)
     802      /* descriptors should have contained fd.  */
     803      abort ();
     804  
     805    if (mt) gl_lock_unlock (descriptors_lock);
     806  
     807    errno = saved_errno;
     808    return result;
     809  }
     810  
     811  /* Close a temporary file.
     812     FP must have been returned by fopen_temp, or by fdopen on a file descriptor
     813     returned by open_temp or gen_register_open_temp.
     814     Unregisters the previously registered file descriptor.  */
     815  int
     816  fclose_temp (FILE *fp)
     817  {
     818    return fclose_variant_temp (fp, fclose);
     819  }
     820  
     821  #if GNULIB_FWRITEERROR
     822  /* Like fwriteerror.
     823     FP must have been returned by fopen_temp, or by fdopen on a file descriptor
     824     returned by open_temp or gen_register_open_temp.
     825     Unregisters the previously registered file descriptor.  */
     826  int
     827  fwriteerror_temp (FILE *fp)
     828  {
     829    return fclose_variant_temp (fp, fwriteerror);
     830  }
     831  #endif
     832  
     833  #if GNULIB_CLOSE_STREAM
     834  /* Like close_stream.
     835     FP must have been returned by fopen_temp, or by fdopen on a file descriptor
     836     returned by open_temp or gen_register_open_temp.
     837     Unregisters the previously registered file descriptor.  */
     838  int
     839  close_stream_temp (FILE *fp)
     840  {
     841    return fclose_variant_temp (fp, close_stream);
     842  }
     843  #endif