(root)/
gettext-0.22.4/
gettext-tools/
gnulib-lib/
clean-temp-simple.c
       1  /* Temporary files with automatic cleanup.
       2     Copyright (C) 2006-2023 Free Software Foundation, Inc.
       3     Written by Bruno Haible <bruno@clisp.org>, 2006.
       4  
       5     This file is free software: you can redistribute it and/or modify
       6     it under the terms of the GNU Lesser General Public License as
       7     published by the Free Software Foundation; either version 2.1 of the
       8     License, or (at your option) any later version.
       9  
      10     This file is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13     GNU Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public License
      16     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <config.h>
      19  
      20  /* Specification.  */
      21  #include "clean-temp-simple.h"
      22  #include "clean-temp-private.h"
      23  
      24  #include <errno.h>
      25  #include <limits.h>
      26  #include <signal.h>
      27  #include <stdlib.h>
      28  #include <string.h>
      29  #include <unistd.h>
      30  
      31  #include "error.h"
      32  #include "fatal-signal.h"
      33  #include "asyncsafe-spin.h"
      34  #include "glthread/lock.h"
      35  #include "thread-optim.h"
      36  #include "gl_list.h"
      37  #include "gl_linkedhash_list.h"
      38  #include "gettext.h"
      39  
      40  #define _(str) gettext (str)
      41  
      42  
      43  /* Lock that protects the file_cleanup_list from concurrent modification in
      44     different threads.  */
      45  gl_lock_define_initialized (static, file_cleanup_list_lock)
      46  
      47  /* List of all temporary files without temporary directories.  */
      48  static gl_list_t /* <char *> */ volatile file_cleanup_list;
      49  
      50  
      51  /* List of all temporary directories.  */
      52  struct all_tempdirs dir_cleanup_list /* = { NULL, 0, 0 } */;
      53  
      54  
      55  /* List of all open file descriptors to temporary files.  */
      56  gl_list_t /* <closeable_fd *> */ volatile descriptors;
      57  
      58  
      59  /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
      60     Why?  We need a data structure that
      61  
      62       1) Can contain an arbitrary number of 'char *' values.  The strings
      63          are compared via strcmp, not pointer comparison.
      64       2) Has insertion and deletion operations that are fast: ideally O(1),
      65          or possibly O(log n).  This is important for GNU sort, which may
      66          create a large number of temporary files.
      67       3) Allows iteration through all elements from within a signal handler.
      68       4) May or may not allow duplicates.  It doesn't matter here, since
      69          any file or subdir can only be removed once.
      70  
      71     Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
      72  
      73     Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
      74     GL_TREE_OSET.
      75  
      76     Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
      77     Namely, iteration through the elements of a binary tree requires access
      78     to many ->left, ->right, ->parent pointers. However, the rebalancing
      79     code for insertion and deletion in an AVL or red-black tree is so
      80     complicated that we cannot assume that >left, ->right, ->parent pointers
      81     are in a consistent state throughout these operations.  Therefore, to
      82     avoid a crash in the signal handler, all destructive operations to the
      83     lists would have to be protected by a
      84         block_fatal_signals ();
      85         ...
      86         unblock_fatal_signals ();
      87     pair.  Which causes extra system calls.
      88  
      89     Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
      90     if they were not already excluded.  Namely, these implementations use
      91     xrealloc(), leaving a time window in which in the list->elements pointer
      92     points to already deallocated memory.  To avoid a crash in the signal
      93     handler at such a moment, all destructive operations would have to
      94     protected by block/unblock_fatal_signals (), in this case too.
      95  
      96     A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
      97     requirements:
      98       2) Insertion and deletion are O(1) on average.
      99       3) The gl_list_iterator, gl_list_iterator_next implementations do
     100          not trigger memory allocations, nor other system calls, and are
     101          therefore safe to be called from a signal handler.
     102          Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
     103          of the destructive functions ensures that the list structure is
     104          safe to be traversed at any moment, even when interrupted by an
     105          asynchronous signal.
     106   */
     107  
     108  /* String equality and hash code functions used by the lists.  */
     109  
     110  bool
     111  clean_temp_string_equals (const void *x1, const void *x2)
     112  {
     113    const char *s1 = (const char *) x1;
     114    const char *s2 = (const char *) x2;
     115    return strcmp (s1, s2) == 0;
     116  }
     117  
     118  #define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
     119  
     120  /* A hash function for NUL-terminated char* strings using
     121     the method described by Bruno Haible.
     122     See https://www.haible.de/bruno/hashfunc.html.  */
     123  size_t
     124  clean_temp_string_hash (const void *x)
     125  {
     126    const char *s = (const char *) x;
     127    size_t h = 0;
     128  
     129    for (; *s; s++)
     130      h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
     131  
     132    return h;
     133  }
     134  
     135  
     136  /* The set of fatal signal handlers.
     137     Cached here because we are not allowed to call get_fatal_signal_set ()
     138     from a signal handler.  */
     139  static const sigset_t *fatal_signal_set /* = NULL */;
     140  
     141  static void
     142  init_fatal_signal_set (void)
     143  {
     144    if (fatal_signal_set == NULL)
     145      fatal_signal_set = get_fatal_signal_set ();
     146  }
     147  
     148  
     149  /* Close a file descriptor.
     150     Avoids race conditions with normal thread code or signal-handler code that
     151     might want to close the same file descriptor.  */
     152  _GL_ASYNC_SAFE int
     153  clean_temp_asyncsafe_close (struct closeable_fd *element)
     154  {
     155    sigset_t saved_mask;
     156    int ret;
     157    int saved_errno;
     158  
     159    asyncsafe_spin_lock (&element->lock, fatal_signal_set, &saved_mask);
     160    if (!element->closed)
     161      {
     162        ret = close (element->fd);
     163        saved_errno = errno;
     164        element->closed = true;
     165      }
     166    else
     167      {
     168        ret = 0;
     169        saved_errno = 0;
     170      }
     171    asyncsafe_spin_unlock (&element->lock, &saved_mask);
     172    element->done = true;
     173  
     174    errno = saved_errno;
     175    return ret;
     176  }
     177  /* Initializations for use of this function.  */
     178  void
     179  clean_temp_init_asyncsafe_close (void)
     180  {
     181    init_fatal_signal_set ();
     182  }
     183  
     184  /* The signal handler.  It gets called asynchronously.  */
     185  static _GL_ASYNC_SAFE void
     186  cleanup_action (_GL_UNUSED int sig)
     187  {
     188    size_t i;
     189  
     190    /* First close all file descriptors to temporary files.  */
     191    {
     192      gl_list_t fds = descriptors;
     193  
     194      if (fds != NULL)
     195        {
     196          gl_list_iterator_t iter;
     197          const void *element;
     198  
     199          iter = gl_list_iterator (fds);
     200          while (gl_list_iterator_next (&iter, &element, NULL))
     201            {
     202              clean_temp_asyncsafe_close ((struct closeable_fd *) element);
     203            }
     204          gl_list_iterator_free (&iter);
     205        }
     206    }
     207  
     208    {
     209      gl_list_t files = file_cleanup_list;
     210  
     211      if (files != NULL)
     212        {
     213          gl_list_iterator_t iter;
     214          const void *element;
     215  
     216          iter = gl_list_iterator (files);
     217          while (gl_list_iterator_next (&iter, &element, NULL))
     218            {
     219              const char *file = (const char *) element;
     220              unlink (file);
     221            }
     222          gl_list_iterator_free (&iter);
     223        }
     224    }
     225  
     226    for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
     227      {
     228        struct tempdir *dir = dir_cleanup_list.tempdir_list[i];
     229  
     230        if (dir != NULL)
     231          {
     232            gl_list_iterator_t iter;
     233            const void *element;
     234  
     235            /* First cleanup the files in the subdirectories.  */
     236            iter = gl_list_iterator (dir->files);
     237            while (gl_list_iterator_next (&iter, &element, NULL))
     238              {
     239                const char *file = (const char *) element;
     240                unlink (file);
     241              }
     242            gl_list_iterator_free (&iter);
     243  
     244            /* Then cleanup the subdirectories.  */
     245            iter = gl_list_iterator (dir->subdirs);
     246            while (gl_list_iterator_next (&iter, &element, NULL))
     247              {
     248                const char *subdir = (const char *) element;
     249                rmdir (subdir);
     250              }
     251            gl_list_iterator_free (&iter);
     252  
     253            /* Then cleanup the temporary directory itself.  */
     254            rmdir (dir->dirname);
     255          }
     256      }
     257  }
     258  
     259  
     260  /* Set to -1 if initialization of this facility failed.  */
     261  static int volatile init_failed /* = 0 */;
     262  
     263  /* Initializes this facility.  */
     264  static void
     265  do_clean_temp_init (void)
     266  {
     267    /* Initialize the data used by the cleanup handler.  */
     268    init_fatal_signal_set ();
     269    /* Register the cleanup handler.  */
     270    if (at_fatal_signal (&cleanup_action) < 0)
     271      init_failed = -1;
     272  }
     273  
     274  /* Ensure that do_clean_temp_init is called once only.  */
     275  gl_once_define(static, clean_temp_once)
     276  
     277  /* Initializes this facility upon first use.
     278     Return 0 upon success, or -1 if there was a memory allocation problem.  */
     279  int
     280  clean_temp_init (void)
     281  {
     282    gl_once (clean_temp_once, do_clean_temp_init);
     283    return init_failed;
     284  }
     285  
     286  
     287  /* Remove a file, with optional error message.
     288     Return 0 upon success, or -1 if there was some problem.  */
     289  int
     290  clean_temp_unlink (const char *absolute_file_name, bool cleanup_verbose)
     291  {
     292    if (unlink (absolute_file_name) < 0 && cleanup_verbose
     293        && errno != ENOENT)
     294      {
     295        error (0, errno,
     296               _("cannot remove temporary file %s"), absolute_file_name);
     297        return -1;
     298      }
     299    return 0;
     300  }
     301  
     302  
     303  /* ============= Temporary files without temporary directories ============= */
     304  
     305  /* Register the given ABSOLUTE_FILE_NAME as being a file that needs to be
     306     removed.
     307     Should be called before the file ABSOLUTE_FILE_NAME is created.
     308     Return 0 upon success, or -1 if there was a memory allocation problem.  */
     309  int
     310  register_temporary_file (const char *absolute_file_name)
     311  {
     312    bool mt = gl_multithreaded ();
     313  
     314    if (mt) gl_lock_lock (file_cleanup_list_lock);
     315  
     316    int ret = 0;
     317  
     318    /* Make sure that this facility and the file_cleanup_list are initialized.  */
     319    if (file_cleanup_list == NULL)
     320      {
     321        if (clean_temp_init () < 0)
     322          {
     323            ret = -1;
     324            goto done;
     325          }
     326        file_cleanup_list =
     327          gl_list_nx_create_empty (GL_LINKEDHASH_LIST,
     328                                   clean_temp_string_equals,
     329                                   clean_temp_string_hash,
     330                                   NULL, false);
     331        if (file_cleanup_list == NULL)
     332          {
     333            ret = -1;
     334            goto done;
     335          }
     336      }
     337  
     338    /* Add absolute_file_name to file_cleanup_list, without duplicates.  */
     339    if (gl_list_search (file_cleanup_list, absolute_file_name) == NULL)
     340      {
     341        char *absolute_file_name_copy = strdup (absolute_file_name);
     342        if (absolute_file_name_copy == NULL)
     343          {
     344            ret = -1;
     345            goto done;
     346          }
     347        if (gl_list_nx_add_first (file_cleanup_list, absolute_file_name_copy)
     348            == NULL)
     349          {
     350            free (absolute_file_name_copy);
     351            ret = -1;
     352            goto done;
     353          }
     354      }
     355  
     356   done:
     357    if (mt) gl_lock_unlock (file_cleanup_list_lock);
     358  
     359    return ret;
     360  }
     361  
     362  /* Unregister the given ABSOLUTE_FILE_NAME as being a file that needs to be
     363     removed.
     364     Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
     365  void
     366  unregister_temporary_file (const char *absolute_file_name)
     367  {
     368    bool mt = gl_multithreaded ();
     369  
     370    if (mt) gl_lock_lock (file_cleanup_list_lock);
     371  
     372    gl_list_t list = file_cleanup_list;
     373    if (list != NULL)
     374      {
     375        gl_list_node_t node = gl_list_search (list, absolute_file_name);
     376        if (node != NULL)
     377          {
     378            char *old_string = (char *) gl_list_node_value (list, node);
     379  
     380            gl_list_remove_node (list, node);
     381            free (old_string);
     382          }
     383      }
     384  
     385    if (mt) gl_lock_unlock (file_cleanup_list_lock);
     386  }
     387  
     388  /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
     389     CLEANUP_VERBOSE determines whether errors are reported to standard error.
     390     Return 0 upon success, or -1 if there was some problem.  */
     391  int
     392  cleanup_temporary_file (const char *absolute_file_name, bool cleanup_verbose)
     393  {
     394    int err;
     395  
     396    err = clean_temp_unlink (absolute_file_name, cleanup_verbose);
     397    unregister_temporary_file (absolute_file_name);
     398  
     399    return err;
     400  }