Use spaces for indentation, not tabs.
[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 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.  */
89 struct 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.  */
102 static 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.  */
110 static 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
164 static bool
165 string_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.  */
177 static size_t
178 string_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.  */
191 static void
192 cleanup ()
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.  */
257 struct temp_dir *
258 create_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.  */
367 void
368 register_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.  */
381 void
382 unregister_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.  */
402 void
403 register_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.  */
417 void
418 unregister_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.  */
437 static int
438 do_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.  */
451 static int
452 do_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.  */
466 int
467 cleanup_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.  */
480 int
481 cleanup_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.  */
494 int
495 cleanup_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.  */
538 int
539 cleanup_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
579 static bool
580 supports_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.  */
599 static void
600 register_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.  */
609 static void
610 unregister_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.  */
627 int
628 open_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.  */
653 FILE *
654 fopen_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.  */
699 int
700 close_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.  */
723 int
724 fclose_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.  */
744 int
745 fwriteerror_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.  */
766 int
767 close_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