root/maint/gnulib/lib/clean-temp.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. gl_lock_define_initialized
  2. create_temp_dir
  3. register_temp_file
  4. unregister_temp_file
  5. register_temp_subdir
  6. unregister_temp_subdir
  7. do_rmdir
  8. cleanup_temp_file
  9. cleanup_temp_subdir
  10. cleanup_temp_dir_contents
  11. cleanup_temp_dir
  12. supports_delete_on_close
  13. register_fd
  14. open_temp
  15. fopen_temp
  16. try_create_file
  17. gen_register_open_temp
  18. close_temp
  19. fclose_variant_temp
  20. fclose_temp
  21. fwriteerror_temp
  22. close_stream_temp

   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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 ()
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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_)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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, &params, 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 *))
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 840 {
 841   return fclose_variant_temp (fp, close_stream);
 842 }
 843 #endif

/* [previous][next][first][last][top][bottom][index][help] */