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