1/* Temporary directories and temporary files with automatic cleanup. 2 Copyright (C) 2001, 2003, 2006-2007 Free Software Foundation, Inc. 3 Written by Bruno Haible <bruno@clisp.org>, 2006. 4 5 This program is free software: you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published by 7 the Free Software Foundation; either version 3 of the License, or 8 (at your option) any later version. 9 10 This program 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 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 17 18 19#include <config.h> 20 21/* Specification. */ 22#include "clean-temp.h" 23 24#include <errno.h> 25#include <fcntl.h> 26#include <limits.h> 27#include <stdbool.h> 28#include <stdlib.h> 29#include <string.h> 30#include <unistd.h> 31 32#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 33# define WIN32_LEAN_AND_MEAN /* avoid including junk */ 34# include <windows.h> 35#endif 36 37#include "error.h" 38#include "fatal-signal.h" 39#include "pathmax.h" 40#include "tmpdir.h" 41#include "xalloc.h" 42#include "xmalloca.h" 43#include "gl_linkedhash_list.h" 44#include "gettext.h" 45#if GNULIB_FWRITEERROR 46# include "fwriteerror.h" 47#endif 48#if GNULIB_CLOSE_STREAM 49# include "close-stream.h" 50#endif 51#if GNULIB_FCNTL_SAFER 52# include "fcntl--.h" 53#endif 54#if GNULIB_FOPEN_SAFER 55# include "stdio--.h" 56#endif 57 58#define _(str) gettext (str) 59 60/* GNU Hurd doesn't have PATH_MAX. */ 61#ifndef PATH_MAX 62# ifdef MAXPATHLEN 63# define PATH_MAX MAXPATHLEN 64# else 65# define PATH_MAX 1024 66# endif 67#endif 68 69#ifndef uintptr_t 70# define uintptr_t unsigned long 71#endif 72 73#if !GNULIB_FCNTL_SAFER 74/* The results of open() in this file are not used with fchdir, 75 therefore save some unnecessary work in fchdir.c. */ 76# undef open 77# undef close 78#endif 79 80 81/* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5)) 82 ensure that while constructing or modifying the data structures, the field 83 values are written to memory in the order of the C statements. So the 84 signal handler can rely on these field values to be up to date. */ 85 86 87/* Registry for a single temporary directory. 88 'struct temp_dir' from the public header file overlaps with this. */ 89struct tempdir 90{ 91 /* The absolute pathname of the directory. */ 92 char * volatile dirname; 93 /* Whether errors during explicit cleanup are reported to standard error. */ 94 bool cleanup_verbose; 95 /* Absolute pathnames of subdirectories. */ 96 gl_list_t /* <char *> */ volatile subdirs; 97 /* Absolute pathnames of files. */ 98 gl_list_t /* <char *> */ volatile files; 99}; 100 101/* List of all temporary directories. */ 102static struct 103{ 104 struct tempdir * volatile * volatile tempdir_list; 105 size_t volatile tempdir_count; 106 size_t tempdir_allocated; 107} cleanup_list /* = { NULL, 0, 0 } */; 108 109/* List of all open file descriptors to temporary files. */ 110static gl_list_t /* <int> */ volatile descriptors; 111 112 113/* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH. 114 Why? We need a data structure that 115 116 1) Can contain an arbitrary number of 'char *' values. The strings 117 are compared via strcmp, not pointer comparison. 118 2) Has insertion and deletion operations that are fast: ideally O(1), 119 or possibly O(log n). This is important for GNU sort, which may 120 create a large number of temporary files. 121 3) Allows iteration through all elements from within a signal handler. 122 4) May or may not allow duplicates. It doesn't matter here, since 123 any file or subdir can only be removed once. 124 125 Criterion 1) would allow any gl_list_t or gl_oset_t implementation. 126 127 Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or 128 GL_TREE_OSET. 129 130 Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET. 131 Namely, iteration through the elements of a binary tree requires access 132 to many ->left, ->right, ->parent pointers. However, the rebalancing 133 code for insertion and deletion in an AVL or red-black tree is so 134 complicated that we cannot assume that >left, ->right, ->parent pointers 135 are in a consistent state throughout these operations. Therefore, to 136 avoid a crash in the signal handler, all destructive operations to the 137 lists would have to be protected by a 138 block_fatal_signals (); 139 ... 140 unblock_fatal_signals (); 141 pair. Which causes extra system calls. 142 143 Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST, 144 if they were not already excluded. Namely, these implementations use 145 xrealloc(), leaving a time window in which in the list->elements pointer 146 points to already deallocated memory. To avoid a crash in the signal 147 handler at such a moment, all destructive operations would have to 148 protected by block/unblock_fatal_signals (), in this case too. 149 150 A list of type GL_LINKEDHASH_LIST without duplicates fulfills all 151 requirements: 152 2) Insertion and deletion are O(1) on average. 153 3) The gl_list_iterator, gl_list_iterator_next implementations do 154 not trigger memory allocations, nor other system calls, and are 155 therefore safe to be called from a signal handler. 156 Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation 157 of the destructive functions ensures that the list structure is 158 safe to be traversed at any moment, even when interrupted by an 159 asynchronous signal. 160 */ 161 162/* String equality and hash code functions used by the lists. */ 163 164static bool 165string_equals (const void *x1, const void *x2) 166{ 167 const char *s1 = (const char *) x1; 168 const char *s2 = (const char *) x2; 169 return strcmp (s1, s2) == 0; 170} 171 172#define SIZE_BITS (sizeof (size_t) * CHAR_BIT) 173 174/* A hash function for NUL-terminated char* strings using 175 the method described by Bruno Haible. 176 See http://www.haible.de/bruno/hashfunc.html. */ 177static size_t 178string_hash (const void *x) 179{ 180 const char *s = (const char *) x; 181 size_t h = 0; 182 183 for (; *s; s++) 184 h = *s + ((h << 9) | (h >> (SIZE_BITS - 9))); 185 186 return h; 187} 188 189 190/* The signal handler. It gets called asynchronously. */ 191static void 192cleanup () 193{ 194 size_t i; 195 196 /* First close all file descriptors to temporary files. */ 197 { 198 gl_list_t fds = descriptors; 199 200 if (fds != NULL) 201 { 202 gl_list_iterator_t iter; 203 const void *element; 204 205 iter = gl_list_iterator (fds); 206 while (gl_list_iterator_next (&iter, &element, NULL)) 207 { 208 int fd = (int) (uintptr_t) element; 209 close (fd); 210 } 211 gl_list_iterator_free (&iter); 212 } 213 } 214 215 for (i = 0; i < cleanup_list.tempdir_count; i++) 216 { 217 struct tempdir *dir = cleanup_list.tempdir_list[i]; 218 219 if (dir != NULL) 220 { 221 gl_list_iterator_t iter; 222 const void *element; 223 224 /* First cleanup the files in the subdirectories. */ 225 iter = gl_list_iterator (dir->files); 226 while (gl_list_iterator_next (&iter, &element, NULL)) 227 { 228 const char *file = (const char *) element; 229 unlink (file); 230 } 231 gl_list_iterator_free (&iter); 232 233 /* Then cleanup the subdirectories. */ 234 iter = gl_list_iterator (dir->subdirs); 235 while (gl_list_iterator_next (&iter, &element, NULL)) 236 { 237 const char *subdir = (const char *) element; 238 rmdir (subdir); 239 } 240 gl_list_iterator_free (&iter); 241 242 /* Then cleanup the temporary directory itself. */ 243 rmdir (dir->dirname); 244 } 245 } 246} 247 248/* Create a temporary directory. 249 PREFIX is used as a prefix for the name of the temporary directory. It 250 should be short and still give an indication about the program. 251 PARENTDIR can be used to specify the parent directory; if NULL, a default 252 parent directory is used (either $TMPDIR or /tmp or similar). 253 CLEANUP_VERBOSE determines whether errors during explicit cleanup are 254 reported to standard error. 255 Return a fresh 'struct temp_dir' on success. Upon error, an error message 256 is shown and NULL is returned. */ 257struct temp_dir * 258create_temp_dir (const char *prefix, const char *parentdir, 259 bool cleanup_verbose) 260{ 261 struct tempdir * volatile *tmpdirp = NULL; 262 struct tempdir *tmpdir; 263 size_t i; 264 char *xtemplate; 265 char *tmpdirname; 266 267 /* See whether it can take the slot of an earlier temporary directory 268 already cleaned up. */ 269 for (i = 0; i < cleanup_list.tempdir_count; i++) 270 if (cleanup_list.tempdir_list[i] == NULL) 271 { 272 tmpdirp = &cleanup_list.tempdir_list[i]; 273 break; 274 } 275 if (tmpdirp == NULL) 276 { 277 /* See whether the array needs to be extended. */ 278 if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated) 279 { 280 /* Note that we cannot use xrealloc(), because then the cleanup() 281 function could access an already deallocated array. */ 282 struct tempdir * volatile *old_array = cleanup_list.tempdir_list; 283 size_t old_allocated = cleanup_list.tempdir_allocated; 284 size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1; 285 struct tempdir * volatile *new_array = 286 XNMALLOC (new_allocated, struct tempdir * volatile); 287 288 if (old_allocated == 0) 289 /* First use of this facility. Register the cleanup handler. */ 290 at_fatal_signal (&cleanup); 291 else 292 { 293 /* Don't use memcpy() here, because memcpy takes non-volatile 294 arguments and is therefore not guaranteed to complete all 295 memory stores before the next statement. */ 296 size_t k; 297 298 for (k = 0; k < old_allocated; k++) 299 new_array[k] = old_array[k]; 300 } 301 302 cleanup_list.tempdir_list = new_array; 303 cleanup_list.tempdir_allocated = new_allocated; 304 305 /* Now we can free the old array. */ 306 if (old_array != NULL) 307 free ((struct tempdir **) old_array); 308 } 309 310 tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count]; 311 /* Initialize *tmpdirp before incrementing tempdir_count, so that 312 cleanup() will skip this entry before it is fully initialized. */ 313 *tmpdirp = NULL; 314 cleanup_list.tempdir_count++; 315 } 316 317 /* Initialize a 'struct tempdir'. */ 318 tmpdir = XMALLOC (struct tempdir); 319 tmpdir->dirname = NULL; 320 tmpdir->cleanup_verbose = cleanup_verbose; 321 tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST, 322 string_equals, string_hash, NULL, 323 false); 324 tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST, 325 string_equals, string_hash, NULL, 326 false); 327 328 /* Create the temporary directory. */ 329 xtemplate = (char *) xmalloca (PATH_MAX); 330 if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL)) 331 { 332 error (0, errno, 333 _("cannot find a temporary directory, try setting $TMPDIR")); 334 goto quit; 335 } 336 block_fatal_signals (); 337 tmpdirname = mkdtemp (xtemplate); 338 if (tmpdirname != NULL) 339 { 340 tmpdir->dirname = tmpdirname; 341 *tmpdirp = tmpdir; 342 } 343 unblock_fatal_signals (); 344 if (tmpdirname == NULL) 345 { 346 error (0, errno, 347 _("cannot create a temporary directory using template \"%s\""), 348 xtemplate); 349 goto quit; 350 } 351 /* Replace tmpdir->dirname with a copy that has indefinite extent. 352 We cannot do this inside the block_fatal_signals/unblock_fatal_signals 353 block because then the cleanup handler would not remove the directory 354 if xstrdup fails. */ 355 tmpdir->dirname = xstrdup (tmpdirname); 356 freea (xtemplate); 357 return (struct temp_dir *) tmpdir; 358 359 quit: 360 freea (xtemplate); 361 return NULL; 362} 363 364/* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that 365 needs to be removed before DIR can be removed. 366 Should be called before the file ABSOLUTE_FILE_NAME is created. */ 367void 368register_temp_file (struct temp_dir *dir, 369 const char *absolute_file_name) 370{ 371 struct tempdir *tmpdir = (struct tempdir *)dir; 372 373 /* Add absolute_file_name to tmpdir->files, without duplicates. */ 374 if (gl_list_search (tmpdir->files, absolute_file_name) == NULL) 375 gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name)); 376} 377 378/* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that 379 needs to be removed before DIR can be removed. 380 Should be called when the file ABSOLUTE_FILE_NAME could not be created. */ 381void 382unregister_temp_file (struct temp_dir *dir, 383 const char *absolute_file_name) 384{ 385 struct tempdir *tmpdir = (struct tempdir *)dir; 386 gl_list_t list = tmpdir->files; 387 gl_list_node_t node; 388 389 node = gl_list_search (list, absolute_file_name); 390 if (node != NULL) 391 { 392 char *old_string = (char *) gl_list_node_value (list, node); 393 394 gl_list_remove_node (list, node); 395 free (old_string); 396 } 397} 398 399/* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, 400 that needs to be removed before DIR can be removed. 401 Should be called before the subdirectory ABSOLUTE_DIR_NAME is created. */ 402void 403register_temp_subdir (struct temp_dir *dir, 404 const char *absolute_dir_name) 405{ 406 struct tempdir *tmpdir = (struct tempdir *)dir; 407 408 /* Add absolute_dir_name to tmpdir->subdirs, without duplicates. */ 409 if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL) 410 gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name)); 411} 412 413/* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR, 414 that needs to be removed before DIR can be removed. 415 Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be 416 created. */ 417void 418unregister_temp_subdir (struct temp_dir *dir, 419 const char *absolute_dir_name) 420{ 421 struct tempdir *tmpdir = (struct tempdir *)dir; 422 gl_list_t list = tmpdir->subdirs; 423 gl_list_node_t node; 424 425 node = gl_list_search (list, absolute_dir_name); 426 if (node != NULL) 427 { 428 char *old_string = (char *) gl_list_node_value (list, node); 429 430 gl_list_remove_node (list, node); 431 free (old_string); 432 } 433} 434 435/* Remove a file, with optional error message. 436 Return 0 upon success, or -1 if there was some problem. */ 437static int 438do_unlink (struct temp_dir *dir, const char *absolute_file_name) 439{ 440 if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose 441 && errno != ENOENT) 442 { 443 error (0, errno, _("cannot remove temporary file %s"), absolute_file_name); 444 return -1; 445 } 446 return 0; 447} 448 449/* Remove a directory, with optional error message. 450 Return 0 upon success, or -1 if there was some problem. */ 451static int 452do_rmdir (struct temp_dir *dir, const char *absolute_dir_name) 453{ 454 if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose 455 && errno != ENOENT) 456 { 457 error (0, errno, 458 _("cannot remove temporary directory %s"), absolute_dir_name); 459 return -1; 460 } 461 return 0; 462} 463 464/* Remove the given ABSOLUTE_FILE_NAME and unregister it. 465 Return 0 upon success, or -1 if there was some problem. */ 466int 467cleanup_temp_file (struct temp_dir *dir, 468 const char *absolute_file_name) 469{ 470 int err; 471 472 err = do_unlink (dir, absolute_file_name); 473 unregister_temp_file (dir, absolute_file_name); 474 475 return err; 476} 477 478/* Remove the given ABSOLUTE_DIR_NAME and unregister it. 479 Return 0 upon success, or -1 if there was some problem. */ 480int 481cleanup_temp_subdir (struct temp_dir *dir, 482 const char *absolute_dir_name) 483{ 484 int err; 485 486 err = do_rmdir (dir, absolute_dir_name); 487 unregister_temp_subdir (dir, absolute_dir_name); 488 489 return err; 490} 491 492/* Remove all registered files and subdirectories inside DIR. 493 Return 0 upon success, or -1 if there was some problem. */ 494int 495cleanup_temp_dir_contents (struct temp_dir *dir) 496{ 497 struct tempdir *tmpdir = (struct tempdir *)dir; 498 int err = 0; 499 gl_list_t list; 500 gl_list_iterator_t iter; 501 const void *element; 502 gl_list_node_t node; 503 504 /* First cleanup the files in the subdirectories. */ 505 list = tmpdir->files; 506 iter = gl_list_iterator (list); 507 while (gl_list_iterator_next (&iter, &element, &node)) 508 { 509 char *file = (char *) element; 510 511 err |= do_unlink (dir, file); 512 gl_list_remove_node (list, node); 513 /* Now only we can free file. */ 514 free (file); 515 } 516 gl_list_iterator_free (&iter); 517 518 /* Then cleanup the subdirectories. */ 519 list = tmpdir->subdirs; 520 iter = gl_list_iterator (list); 521 while (gl_list_iterator_next (&iter, &element, &node)) 522 { 523 char *subdir = (char *) element; 524 525 err |= do_rmdir (dir, subdir); 526 gl_list_remove_node (list, node); 527 /* Now only we can free subdir. */ 528 free (subdir); 529 } 530 gl_list_iterator_free (&iter); 531 532 return err; 533} 534 535/* Remove all registered files and subdirectories inside DIR and DIR itself. 536 DIR cannot be used any more after this call. 537 Return 0 upon success, or -1 if there was some problem. */ 538int 539cleanup_temp_dir (struct temp_dir *dir) 540{ 541 struct tempdir *tmpdir = (struct tempdir *)dir; 542 int err = 0; 543 size_t i; 544 545 err |= cleanup_temp_dir_contents (dir); 546 err |= do_rmdir (dir, tmpdir->dirname); 547 548 for (i = 0; i < cleanup_list.tempdir_count; i++) 549 if (cleanup_list.tempdir_list[i] == tmpdir) 550 { 551 /* Remove cleanup_list.tempdir_list[i]. */ 552 if (i + 1 == cleanup_list.tempdir_count) 553 { 554 while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL) 555 i--; 556 cleanup_list.tempdir_count = i; 557 } 558 else 559 cleanup_list.tempdir_list[i] = NULL; 560 /* Now only we can free the tmpdir->dirname and tmpdir itself. */ 561 free (tmpdir->dirname); 562 free (tmpdir); 563 return err; 564 } 565 566 /* The user passed an invalid DIR argument. */ 567 abort (); 568} 569 570 571#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 572 573/* On Windows, opening a file with _O_TEMPORARY has the effect of passing 574 the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect 575 of deleting the file when it is closed - even when the program crashes. 576 But (according to the Cygwin sources) it works only on Windows NT or newer. 577 So we cache the info whether we are running on Windows NT or newer. */ 578 579static bool 580supports_delete_on_close () 581{ 582 static int known; /* 1 = yes, -1 = no, 0 = unknown */ 583 if (!known) 584 { 585 OSVERSIONINFO v; 586 587 if (GetVersionEx (&v)) 588 known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1); 589 else 590 known = -1; 591 } 592 return (known > 0); 593} 594 595#endif 596 597 598/* Register a file descriptor to be closed. */ 599static void 600register_fd (int fd) 601{ 602 if (descriptors == NULL) 603 descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, NULL, 604 false); 605 gl_list_add_first (descriptors, (void *) (uintptr_t) fd); 606} 607 608/* Unregister a file descriptor to be closed. */ 609static void 610unregister_fd (int fd) 611{ 612 gl_list_t fds = descriptors; 613 gl_list_node_t node; 614 615 if (fds == NULL) 616 /* descriptors should already contain fd. */ 617 abort (); 618 node = gl_list_search (fds, (void *) (uintptr_t) fd); 619 if (node == NULL) 620 /* descriptors should already contain fd. */ 621 abort (); 622 gl_list_remove_node (fds, node); 623} 624 625/* Open a temporary file in a temporary directory. 626 Registers the resulting file descriptor to be closed. */ 627int 628open_temp (const char *file_name, int flags, mode_t mode) 629{ 630 int fd; 631 int saved_errno; 632 633 block_fatal_signals (); 634 /* Note: 'open' here is actually open() or open_safer(). */ 635#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 636 /* Use _O_TEMPORARY when possible, to increase the chances that the 637 temporary file is removed when the process crashes. */ 638 if (supports_delete_on_close ()) 639 fd = open (file_name, flags | _O_TEMPORARY, mode); 640 else 641#endif 642 fd = open (file_name, flags, mode); 643 saved_errno = errno; 644 if (fd >= 0) 645 register_fd (fd); 646 unblock_fatal_signals (); 647 errno = saved_errno; 648 return fd; 649} 650 651/* Open a temporary file in a temporary directory. 652 Registers the resulting file descriptor to be closed. */ 653FILE * 654fopen_temp (const char *file_name, const char *mode) 655{ 656 FILE *fp; 657 int saved_errno; 658 659 block_fatal_signals (); 660 /* Note: 'fopen' here is actually fopen() or fopen_safer(). */ 661#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ 662 /* Use _O_TEMPORARY when possible, to increase the chances that the 663 temporary file is removed when the process crashes. */ 664 if (supports_delete_on_close ()) 665 { 666 size_t mode_len = strlen (mode); 667 char *augmented_mode = (char *) xmalloca (mode_len + 2); 668 memcpy (augmented_mode, mode, mode_len); 669 memcpy (augmented_mode + mode_len, "D", 2); 670 671 fp = fopen (file_name, augmented_mode); 672 saved_errno = errno; 673 674 freea (augmented_mode); 675 } 676 else 677#endif 678 { 679 fp = fopen (file_name, mode); 680 saved_errno = errno; 681 } 682 if (fp != NULL) 683 { 684 /* It is sufficient to register fileno (fp) instead of the entire fp, 685 because at cleanup time there is no need to do an fflush (fp); a 686 close (fileno (fp)) will be enough. */ 687 int fd = fileno (fp); 688 if (!(fd >= 0)) 689 abort (); 690 register_fd (fd); 691 } 692 unblock_fatal_signals (); 693 errno = saved_errno; 694 return fp; 695} 696 697/* Close a temporary file in a temporary directory. 698 Unregisters the previously registered file descriptor. */ 699int 700close_temp (int fd) 701{ 702 if (fd >= 0) 703 { 704 /* No blocking of signals is needed here, since a double close of a 705 file descriptor is harmless. */ 706 int result = close (fd); 707 int saved_errno = errno; 708 709 /* No race condition here: we assume a single-threaded program, hence 710 fd cannot be re-opened here. */ 711 712 unregister_fd (fd); 713 714 errno = saved_errno; 715 return result; 716 } 717 else 718 return close (fd); 719} 720 721/* Close a temporary file in a temporary directory. 722 Unregisters the previously registered file descriptor. */ 723int 724fclose_temp (FILE *fp) 725{ 726 int fd = fileno (fp); 727 /* No blocking of signals is needed here, since a double close of a 728 file descriptor is harmless. */ 729 int result = fclose (fp); 730 int saved_errno = errno; 731 732 /* No race condition here: we assume a single-threaded program, hence 733 fd cannot be re-opened here. */ 734 735 unregister_fd (fd); 736 737 errno = saved_errno; 738 return result; 739} 740 741#if GNULIB_FWRITEERROR 742/* Like fwriteerror. 743 Unregisters the previously registered file descriptor. */ 744int 745fwriteerror_temp (FILE *fp) 746{ 747 int fd = fileno (fp); 748 /* No blocking of signals is needed here, since a double close of a 749 file descriptor is harmless. */ 750 int result = fwriteerror (fp); 751 int saved_errno = errno; 752 753 /* No race condition here: we assume a single-threaded program, hence 754 fd cannot be re-opened here. */ 755 756 unregister_fd (fd); 757 758 errno = saved_errno; 759 return result; 760} 761#endif 762 763#if GNULIB_CLOSE_STREAM 764/* Like close_stream. 765 Unregisters the previously registered file descriptor. */ 766int 767close_stream_temp (FILE *fp) 768{ 769 int fd = fileno (fp); 770 /* No blocking of signals is needed here, since a double close of a 771 file descriptor is harmless. */ 772 int result = close_stream (fp); 773 int saved_errno = errno; 774 775 /* No race condition here: we assume a single-threaded program, hence 776 fd cannot be re-opened here. */ 777 778 unregister_fd (fd); 779 780 errno = saved_errno; 781 return result; 782} 783#endif 784