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 844