mkdtemp.h is replaced with <stdlib.h>.
[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, false);
324   tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST,
325                                         string_equals, string_hash, false);
326
327   /* Create the temporary directory.  */
328   xtemplate = (char *) xallocsa (PATH_MAX);
329   if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
330     {
331       error (0, errno,
332              _("cannot find a temporary directory, try setting $TMPDIR"));
333       goto quit;
334     }
335   block_fatal_signals ();
336   tmpdirname = mkdtemp (xtemplate);
337   if (tmpdirname != NULL)
338     {
339       tmpdir->dirname = tmpdirname;
340       *tmpdirp = tmpdir;
341     }
342   unblock_fatal_signals ();
343   if (tmpdirname == NULL)
344     {
345       error (0, errno,
346              _("cannot create a temporary directory using template \"%s\""),
347              xtemplate);
348       goto quit;
349     }
350   /* Replace tmpdir->dirname with a copy that has indefinite extent.
351      We cannot do this inside the block_fatal_signals/unblock_fatal_signals
352      block because then the cleanup handler would not remove the directory
353      if xstrdup fails.  */
354   tmpdir->dirname = xstrdup (tmpdirname);
355   freesa (xtemplate);
356   return (struct temp_dir *) tmpdir;
357
358  quit:
359   freesa (xtemplate);
360   return NULL;
361 }
362
363 /* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
364    needs to be removed before DIR can be removed.
365    Should be called before the file ABSOLUTE_FILE_NAME is created.  */
366 void
367 register_temp_file (struct temp_dir *dir,
368                     const char *absolute_file_name)
369 {
370   struct tempdir *tmpdir = (struct tempdir *)dir;
371
372   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
373   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
374     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
375 }
376
377 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
378    needs to be removed before DIR can be removed.
379    Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
380 void
381 unregister_temp_file (struct temp_dir *dir,
382                       const char *absolute_file_name)
383 {
384   struct tempdir *tmpdir = (struct tempdir *)dir;
385   gl_list_t list = tmpdir->files;
386   gl_list_node_t node;
387
388   node = gl_list_search (list, absolute_file_name);
389   if (node != NULL)
390     {
391       char *old_string = (char *) gl_list_node_value (list, node);
392
393       gl_list_remove_node (list, node);
394       free (old_string);
395     }
396 }
397
398 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
399    that needs to be removed before DIR can be removed.
400    Should be called before the subdirectory ABSOLUTE_DIR_NAME is created.  */
401 void
402 register_temp_subdir (struct temp_dir *dir,
403                       const char *absolute_dir_name)
404 {
405   struct tempdir *tmpdir = (struct tempdir *)dir;
406
407   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
408   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
409     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
410 }
411
412 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
413    that needs to be removed before DIR can be removed.
414    Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
415    created.  */
416 void
417 unregister_temp_subdir (struct temp_dir *dir,
418                         const char *absolute_dir_name)
419 {
420   struct tempdir *tmpdir = (struct tempdir *)dir;
421   gl_list_t list = tmpdir->subdirs;
422   gl_list_node_t node;
423
424   node = gl_list_search (list, absolute_dir_name);
425   if (node != NULL)
426     {
427       char *old_string = (char *) gl_list_node_value (list, node);
428
429       gl_list_remove_node (list, node);
430       free (old_string);
431     }
432 }
433
434 /* Remove a file, with optional error message.
435    Return 0 upon success, or -1 if there was some problem.  */
436 static int
437 do_unlink (struct temp_dir *dir, const char *absolute_file_name)
438 {
439   if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
440       && errno != ENOENT)
441     {
442       error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
443       return -1;
444     }
445   return 0;
446 }
447
448 /* Remove a directory, with optional error message.
449    Return 0 upon success, or -1 if there was some problem.  */
450 static int
451 do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
452 {
453   if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
454       && errno != ENOENT)
455     {
456       error (0, errno,
457              _("cannot remove temporary directory %s"), absolute_dir_name);
458       return -1;
459     }
460   return 0;
461 }
462
463 /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
464    Return 0 upon success, or -1 if there was some problem.  */
465 int
466 cleanup_temp_file (struct temp_dir *dir,
467                    const char *absolute_file_name)
468 {
469   int err;
470
471   err = do_unlink (dir, absolute_file_name);
472   unregister_temp_file (dir, absolute_file_name);
473
474   return err;
475 }
476
477 /* Remove the given ABSOLUTE_DIR_NAME and unregister it.
478    Return 0 upon success, or -1 if there was some problem.  */
479 int
480 cleanup_temp_subdir (struct temp_dir *dir,
481                      const char *absolute_dir_name)
482 {
483   int err;
484
485   err = do_rmdir (dir, absolute_dir_name);
486   unregister_temp_subdir (dir, absolute_dir_name);
487
488   return err;
489 }
490
491 /* Remove all registered files and subdirectories inside DIR.
492    Return 0 upon success, or -1 if there was some problem.  */
493 int
494 cleanup_temp_dir_contents (struct temp_dir *dir)
495 {
496   struct tempdir *tmpdir = (struct tempdir *)dir;
497   int err = 0;
498   gl_list_t list;
499   gl_list_iterator_t iter;
500   const void *element;
501   gl_list_node_t node;
502
503   /* First cleanup the files in the subdirectories.  */
504   list = tmpdir->files;
505   iter = gl_list_iterator (list);
506   while (gl_list_iterator_next (&iter, &element, &node))
507     {
508       char *file = (char *) element;
509
510       err |= do_unlink (dir, file);
511       gl_list_remove_node (list, node);
512       /* Now only we can free file.  */
513       free (file);
514     }
515   gl_list_iterator_free (&iter);
516
517   /* Then cleanup the subdirectories.  */
518   list = tmpdir->subdirs;
519   iter = gl_list_iterator (list);
520   while (gl_list_iterator_next (&iter, &element, &node))
521     {
522       char *subdir = (char *) element;
523
524       err |= do_rmdir (dir, subdir);
525       gl_list_remove_node (list, node);
526       /* Now only we can free subdir.  */
527       free (subdir);
528     }
529   gl_list_iterator_free (&iter);
530
531   return err;
532 }
533
534 /* Remove all registered files and subdirectories inside DIR and DIR itself.
535    DIR cannot be used any more after this call.
536    Return 0 upon success, or -1 if there was some problem.  */
537 int
538 cleanup_temp_dir (struct temp_dir *dir)
539 {
540   struct tempdir *tmpdir = (struct tempdir *)dir;
541   int err = 0;
542   size_t i;
543
544   err |= cleanup_temp_dir_contents (dir);
545   err |= do_rmdir (dir, tmpdir->dirname);
546
547   for (i = 0; i < cleanup_list.tempdir_count; i++)
548     if (cleanup_list.tempdir_list[i] == tmpdir)
549       {
550         /* Remove cleanup_list.tempdir_list[i].  */
551         if (i + 1 == cleanup_list.tempdir_count)
552           {
553             while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
554               i--;
555             cleanup_list.tempdir_count = i;
556           }
557         else
558           cleanup_list.tempdir_list[i] = NULL;
559         /* Now only we can free the tmpdir->dirname and tmpdir itself.  */
560         free (tmpdir->dirname);
561         free (tmpdir);
562         return err;
563       }
564
565   /* The user passed an invalid DIR argument.  */
566   abort ();
567 }
568
569
570 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
571
572 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
573    the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
574    of deleting the file when it is closed - even when the program crashes.
575    But (according to the Cygwin sources) it works only on Windows NT or newer.
576    So we cache the info whether we are running on Windows NT or newer.  */
577
578 static bool
579 supports_delete_on_close ()
580 {
581   static int known; /* 1 = yes, -1 = no, 0 = unknown */
582   if (!known)
583     {
584       OSVERSIONINFO v;
585
586       if (GetVersionEx (&v))
587         known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
588       else
589         known = -1;
590     }
591   return (known > 0);
592 }
593
594 #endif
595
596
597 /* Register a file descriptor to be closed.  */
598 static void
599 register_fd (int fd)
600 {
601   if (descriptors == NULL)
602     descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, false);
603   gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
604 }
605
606 /* Unregister a file descriptor to be closed.  */
607 static void
608 unregister_fd (int fd)
609 {
610   gl_list_t fds = descriptors;
611   gl_list_node_t node;
612
613   if (fds == NULL)
614     /* descriptors should already contain fd.  */
615     abort ();
616   node = gl_list_search (fds, (void *) (uintptr_t) fd);
617   if (node == NULL)
618     /* descriptors should already contain fd.  */
619     abort ();
620   gl_list_remove_node (fds, node);
621 }
622
623 /* Open a temporary file in a temporary directory.
624    Registers the resulting file descriptor to be closed.  */
625 int
626 open_temp (const char *file_name, int flags, mode_t mode)
627 {
628   int fd;
629   int saved_errno;
630
631   block_fatal_signals ();
632   /* Note: 'open' here is actually open() or open_safer().  */
633 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
634   /* Use _O_TEMPORARY when possible, to increase the chances that the
635      temporary file is removed when the process crashes.  */
636   if (supports_delete_on_close ())
637     fd = open (file_name, flags | _O_TEMPORARY, mode);
638   else
639 #endif
640     fd = open (file_name, flags, mode);
641   saved_errno = errno;
642   if (fd >= 0)
643     register_fd (fd);
644   unblock_fatal_signals ();
645   errno = saved_errno;
646   return fd;
647 }
648
649 /* Open a temporary file in a temporary directory.
650    Registers the resulting file descriptor to be closed.  */
651 FILE *
652 fopen_temp (const char *file_name, const char *mode)
653 {
654   FILE *fp;
655   int saved_errno;
656
657   block_fatal_signals ();
658   /* Note: 'fopen' here is actually fopen() or fopen_safer().  */
659 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
660   /* Use _O_TEMPORARY when possible, to increase the chances that the
661      temporary file is removed when the process crashes.  */
662   if (supports_delete_on_close ())
663     {
664       size_t mode_len = strlen (mode);
665       char *augmented_mode = (char *) xallocsa (mode_len + 2);
666       memcpy (augmented_mode, mode, mode_len);
667       memcpy (augmented_mode + mode_len, "D", 2);
668
669       fp = fopen (file_name, augmented_mode);
670       saved_errno = errno;
671
672       freesa (augmented_mode);
673     }
674   else
675 #endif
676     {
677       fp = fopen (file_name, mode);
678       saved_errno = errno;
679     }
680   if (fp != NULL)
681     {
682       /* It is sufficient to register fileno (fp) instead of the entire fp,
683          because at cleanup time there is no need to do an fflush (fp); a
684          close (fileno (fp)) will be enough.  */
685       int fd = fileno (fp);
686       if (!(fd >= 0))
687         abort ();
688       register_fd (fd);
689     }
690   unblock_fatal_signals ();
691   errno = saved_errno;
692   return fp;
693 }
694
695 /* Close a temporary file in a temporary directory.
696    Unregisters the previously registered file descriptor.  */
697 int
698 close_temp (int fd)
699 {
700   if (fd >= 0)
701     {
702       /* No blocking of signals is needed here, since a double close of a
703          file descriptor is harmless.  */
704       int result = close (fd);
705       int saved_errno = errno;
706
707       /* No race condition here: we assume a single-threaded program, hence
708          fd cannot be re-opened here.  */
709
710       unregister_fd (fd);
711
712       errno = saved_errno;
713       return result;
714     }
715   else
716     return close (fd);
717 }
718
719 /* Close a temporary file in a temporary directory.
720    Unregisters the previously registered file descriptor.  */
721 int
722 fclose_temp (FILE *fp)
723 {
724   int fd = fileno (fp);
725   /* No blocking of signals is needed here, since a double close of a
726      file descriptor is harmless.  */
727   int result = fclose (fp);
728   int saved_errno = errno;
729
730   /* No race condition here: we assume a single-threaded program, hence
731      fd cannot be re-opened here.  */
732
733   unregister_fd (fd);
734
735   errno = saved_errno;
736   return result;
737 }
738
739 #if GNULIB_FWRITEERROR
740 /* Like fwriteerror.
741    Unregisters the previously registered file descriptor.  */
742 int
743 fwriteerror_temp (FILE *fp)
744 {
745   int fd = fileno (fp);
746   /* No blocking of signals is needed here, since a double close of a
747      file descriptor is harmless.  */
748   int result = fwriteerror (fp);
749   int saved_errno = errno;
750
751   /* No race condition here: we assume a single-threaded program, hence
752      fd cannot be re-opened here.  */
753
754   unregister_fd (fd);
755
756   errno = saved_errno;
757   return result;
758 }
759 #endif
760
761 #if GNULIB_CLOSE_STREAM
762 /* Like close_stream.
763    Unregisters the previously registered file descriptor.  */
764 int
765 close_stream_temp (FILE *fp)
766 {
767   int fd = fileno (fp);
768   /* No blocking of signals is needed here, since a double close of a
769      file descriptor is harmless.  */
770   int result = close_stream (fp);
771   int saved_errno = errno;
772
773   /* No race condition here: we assume a single-threaded program, hence
774      fd cannot be re-opened here.  */
775
776   unregister_fd (fd);
777
778   errno = saved_errno;
779   return result;
780 }
781 #endif