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