Bump to 1.14.1
[platform/upstream/augeas.git] / lib / clean-temp.c
1 /* Temporary directories and temporary files with automatic cleanup.
2    Copyright (C) 2001, 2003, 2006-2007, 2009-2016 Free Software Foundation,
3    Inc.
4    Written by Bruno Haible <bruno@clisp.org>, 2006.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
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 "xmalloca.h"
44 #include "gl_xlist.h"
45 #include "gl_linkedhash_list.h"
46 #include "gettext.h"
47 #if GNULIB_FWRITEERROR
48 # include "fwriteerror.h"
49 #endif
50 #if GNULIB_CLOSE_STREAM
51 # include "close-stream.h"
52 #endif
53 #if GNULIB_FCNTL_SAFER
54 # include "fcntl--.h"
55 #endif
56 #if GNULIB_FOPEN_SAFER
57 # include "stdio--.h"
58 #endif
59
60 #define _(str) gettext (str)
61
62 /* GNU Hurd doesn't have PATH_MAX.  Use a fallback.
63    Temporary directory names are usually not that long.  */
64 #ifndef PATH_MAX
65 # define PATH_MAX 1024
66 #endif
67
68 #ifndef uintptr_t
69 # define uintptr_t unsigned long
70 #endif
71
72
73 /* The use of 'volatile' in the types below (and ISO C 99 section 5.1.2.3.(5))
74    ensure that while constructing or modifying the data structures, the field
75    values are written to memory in the order of the C statements.  So the
76    signal handler can rely on these field values to be up to date.  */
77
78
79 /* Registry for a single temporary directory.
80    'struct temp_dir' from the public header file overlaps with this.  */
81 struct tempdir
82 {
83   /* The absolute pathname of the directory.  */
84   char * volatile dirname;
85   /* Whether errors during explicit cleanup are reported to standard error.  */
86   bool cleanup_verbose;
87   /* Absolute pathnames of subdirectories.  */
88   gl_list_t /* <char *> */ volatile subdirs;
89   /* Absolute pathnames of files.  */
90   gl_list_t /* <char *> */ volatile files;
91 };
92
93 /* List of all temporary directories.  */
94 static struct
95 {
96   struct tempdir * volatile * volatile tempdir_list;
97   size_t volatile tempdir_count;
98   size_t tempdir_allocated;
99 } cleanup_list /* = { NULL, 0, 0 } */;
100
101 /* List of all open file descriptors to temporary files.  */
102 static gl_list_t /* <int> */ volatile descriptors;
103
104
105 /* For the subdirs and for the files, we use a gl_list_t of type LINKEDHASH.
106    Why?  We need a data structure that
107
108      1) Can contain an arbitrary number of 'char *' values.  The strings
109         are compared via strcmp, not pointer comparison.
110      2) Has insertion and deletion operations that are fast: ideally O(1),
111         or possibly O(log n).  This is important for GNU sort, which may
112         create a large number of temporary files.
113      3) Allows iteration through all elements from within a signal handler.
114      4) May or may not allow duplicates.  It doesn't matter here, since
115         any file or subdir can only be removed once.
116
117    Criterion 1) would allow any gl_list_t or gl_oset_t implementation.
118
119    Criterion 2) leaves only GL_LINKEDHASH_LIST, GL_TREEHASH_LIST, or
120    GL_TREE_OSET.
121
122    Criterion 3) puts at disadvantage GL_TREEHASH_LIST and GL_TREE_OSET.
123    Namely, iteration through the elements of a binary tree requires access
124    to many ->left, ->right, ->parent pointers. However, the rebalancing
125    code for insertion and deletion in an AVL or red-black tree is so
126    complicated that we cannot assume that >left, ->right, ->parent pointers
127    are in a consistent state throughout these operations.  Therefore, to
128    avoid a crash in the signal handler, all destructive operations to the
129    lists would have to be protected by a
130        block_fatal_signals ();
131        ...
132        unblock_fatal_signals ();
133    pair.  Which causes extra system calls.
134
135    Criterion 3) would also discourage GL_ARRAY_LIST and GL_CARRAY_LIST,
136    if they were not already excluded.  Namely, these implementations use
137    xrealloc(), leaving a time window in which in the list->elements pointer
138    points to already deallocated memory.  To avoid a crash in the signal
139    handler at such a moment, all destructive operations would have to
140    protected by block/unblock_fatal_signals (), in this case too.
141
142    A list of type GL_LINKEDHASH_LIST without duplicates fulfills all
143    requirements:
144      2) Insertion and deletion are O(1) on average.
145      3) The gl_list_iterator, gl_list_iterator_next implementations do
146         not trigger memory allocations, nor other system calls, and are
147         therefore safe to be called from a signal handler.
148         Furthermore, since SIGNAL_SAFE_LIST is defined, the implementation
149         of the destructive functions ensures that the list structure is
150         safe to be traversed at any moment, even when interrupted by an
151         asynchronous signal.
152  */
153
154 /* String equality and hash code functions used by the lists.  */
155
156 static bool
157 string_equals (const void *x1, const void *x2)
158 {
159   const char *s1 = (const char *) x1;
160   const char *s2 = (const char *) x2;
161   return strcmp (s1, s2) == 0;
162 }
163
164 #define SIZE_BITS (sizeof (size_t) * CHAR_BIT)
165
166 /* A hash function for NUL-terminated char* strings using
167    the method described by Bruno Haible.
168    See http://www.haible.de/bruno/hashfunc.html.  */
169 static size_t
170 string_hash (const void *x)
171 {
172   const char *s = (const char *) x;
173   size_t h = 0;
174
175   for (; *s; s++)
176     h = *s + ((h << 9) | (h >> (SIZE_BITS - 9)));
177
178   return h;
179 }
180
181
182 /* The signal handler.  It gets called asynchronously.  */
183 static void
184 cleanup ()
185 {
186   size_t i;
187
188   /* First close all file descriptors to temporary files.  */
189   {
190     gl_list_t fds = descriptors;
191
192     if (fds != NULL)
193       {
194         gl_list_iterator_t iter;
195         const void *element;
196
197         iter = gl_list_iterator (fds);
198         while (gl_list_iterator_next (&iter, &element, NULL))
199           {
200             int fd = (int) (uintptr_t) element;
201             close (fd);
202           }
203         gl_list_iterator_free (&iter);
204       }
205   }
206
207   for (i = 0; i < cleanup_list.tempdir_count; i++)
208     {
209       struct tempdir *dir = cleanup_list.tempdir_list[i];
210
211       if (dir != NULL)
212         {
213           gl_list_iterator_t iter;
214           const void *element;
215
216           /* First cleanup the files in the subdirectories.  */
217           iter = gl_list_iterator (dir->files);
218           while (gl_list_iterator_next (&iter, &element, NULL))
219             {
220               const char *file = (const char *) element;
221               unlink (file);
222             }
223           gl_list_iterator_free (&iter);
224
225           /* Then cleanup the subdirectories.  */
226           iter = gl_list_iterator (dir->subdirs);
227           while (gl_list_iterator_next (&iter, &element, NULL))
228             {
229               const char *subdir = (const char *) element;
230               rmdir (subdir);
231             }
232           gl_list_iterator_free (&iter);
233
234           /* Then cleanup the temporary directory itself.  */
235           rmdir (dir->dirname);
236         }
237     }
238 }
239
240 /* Create a temporary directory.
241    PREFIX is used as a prefix for the name of the temporary directory. It
242    should be short and still give an indication about the program.
243    PARENTDIR can be used to specify the parent directory; if NULL, a default
244    parent directory is used (either $TMPDIR or /tmp or similar).
245    CLEANUP_VERBOSE determines whether errors during explicit cleanup are
246    reported to standard error.
247    Return a fresh 'struct temp_dir' on success.  Upon error, an error message
248    is shown and NULL is returned.  */
249 struct temp_dir *
250 create_temp_dir (const char *prefix, const char *parentdir,
251                  bool cleanup_verbose)
252 {
253   struct tempdir * volatile *tmpdirp = NULL;
254   struct tempdir *tmpdir;
255   size_t i;
256   char *xtemplate;
257   char *tmpdirname;
258
259   /* See whether it can take the slot of an earlier temporary directory
260      already cleaned up.  */
261   for (i = 0; i < cleanup_list.tempdir_count; i++)
262     if (cleanup_list.tempdir_list[i] == NULL)
263       {
264         tmpdirp = &cleanup_list.tempdir_list[i];
265         break;
266       }
267   if (tmpdirp == NULL)
268     {
269       /* See whether the array needs to be extended.  */
270       if (cleanup_list.tempdir_count == cleanup_list.tempdir_allocated)
271         {
272           /* Note that we cannot use xrealloc(), because then the cleanup()
273              function could access an already deallocated array.  */
274           struct tempdir * volatile *old_array = cleanup_list.tempdir_list;
275           size_t old_allocated = cleanup_list.tempdir_allocated;
276           size_t new_allocated = 2 * cleanup_list.tempdir_allocated + 1;
277           struct tempdir * volatile *new_array =
278             XNMALLOC (new_allocated, struct tempdir * volatile);
279
280           if (old_allocated == 0)
281             /* First use of this facility.  Register the cleanup handler.  */
282             at_fatal_signal (&cleanup);
283           else
284             {
285               /* Don't use memcpy() here, because memcpy takes non-volatile
286                  arguments and is therefore not guaranteed to complete all
287                  memory stores before the next statement.  */
288               size_t k;
289
290               for (k = 0; k < old_allocated; k++)
291                 new_array[k] = old_array[k];
292             }
293
294           cleanup_list.tempdir_list = new_array;
295           cleanup_list.tempdir_allocated = new_allocated;
296
297           /* Now we can free the old array.  */
298           if (old_array != NULL)
299             free ((struct tempdir **) old_array);
300         }
301
302       tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
303       /* Initialize *tmpdirp before incrementing tempdir_count, so that
304          cleanup() will skip this entry before it is fully initialized.  */
305       *tmpdirp = NULL;
306       cleanup_list.tempdir_count++;
307     }
308
309   /* Initialize a 'struct tempdir'.  */
310   tmpdir = XMALLOC (struct tempdir);
311   tmpdir->dirname = NULL;
312   tmpdir->cleanup_verbose = cleanup_verbose;
313   tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST,
314                                           string_equals, string_hash, NULL,
315                                           false);
316   tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST,
317                                         string_equals, string_hash, NULL,
318                                         false);
319
320   /* Create the temporary directory.  */
321   xtemplate = (char *) xmalloca (PATH_MAX);
322   if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
323     {
324       error (0, errno,
325              _("cannot find a temporary directory, try setting $TMPDIR"));
326       goto quit;
327     }
328   block_fatal_signals ();
329   tmpdirname = mkdtemp (xtemplate);
330   if (tmpdirname != NULL)
331     {
332       tmpdir->dirname = tmpdirname;
333       *tmpdirp = tmpdir;
334     }
335   unblock_fatal_signals ();
336   if (tmpdirname == NULL)
337     {
338       error (0, errno,
339              _("cannot create a temporary directory using template \"%s\""),
340              xtemplate);
341       goto quit;
342     }
343   /* Replace tmpdir->dirname with a copy that has indefinite extent.
344      We cannot do this inside the block_fatal_signals/unblock_fatal_signals
345      block because then the cleanup handler would not remove the directory
346      if xstrdup fails.  */
347   tmpdir->dirname = xstrdup (tmpdirname);
348   freea (xtemplate);
349   return (struct temp_dir *) tmpdir;
350
351  quit:
352   freea (xtemplate);
353   return NULL;
354 }
355
356 /* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
357    needs to be removed before DIR can be removed.
358    Should be called before the file ABSOLUTE_FILE_NAME is created.  */
359 void
360 register_temp_file (struct temp_dir *dir,
361                     const char *absolute_file_name)
362 {
363   struct tempdir *tmpdir = (struct tempdir *)dir;
364
365   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
366   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
367     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
368 }
369
370 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
371    needs to be removed before DIR can be removed.
372    Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
373 void
374 unregister_temp_file (struct temp_dir *dir,
375                       const char *absolute_file_name)
376 {
377   struct tempdir *tmpdir = (struct tempdir *)dir;
378   gl_list_t list = tmpdir->files;
379   gl_list_node_t node;
380
381   node = gl_list_search (list, absolute_file_name);
382   if (node != NULL)
383     {
384       char *old_string = (char *) gl_list_node_value (list, node);
385
386       gl_list_remove_node (list, node);
387       free (old_string);
388     }
389 }
390
391 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
392    that needs to be removed before DIR can be removed.
393    Should be called before the subdirectory ABSOLUTE_DIR_NAME is created.  */
394 void
395 register_temp_subdir (struct temp_dir *dir,
396                       const char *absolute_dir_name)
397 {
398   struct tempdir *tmpdir = (struct tempdir *)dir;
399
400   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
401   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
402     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
403 }
404
405 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
406    that needs to be removed before DIR can be removed.
407    Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
408    created.  */
409 void
410 unregister_temp_subdir (struct temp_dir *dir,
411                         const char *absolute_dir_name)
412 {
413   struct tempdir *tmpdir = (struct tempdir *)dir;
414   gl_list_t list = tmpdir->subdirs;
415   gl_list_node_t node;
416
417   node = gl_list_search (list, absolute_dir_name);
418   if (node != NULL)
419     {
420       char *old_string = (char *) gl_list_node_value (list, node);
421
422       gl_list_remove_node (list, node);
423       free (old_string);
424     }
425 }
426
427 /* Remove a file, with optional error message.
428    Return 0 upon success, or -1 if there was some problem.  */
429 static int
430 do_unlink (struct temp_dir *dir, const char *absolute_file_name)
431 {
432   if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
433       && errno != ENOENT)
434     {
435       error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
436       return -1;
437     }
438   return 0;
439 }
440
441 /* Remove a directory, with optional error message.
442    Return 0 upon success, or -1 if there was some problem.  */
443 static int
444 do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
445 {
446   if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
447       && errno != ENOENT)
448     {
449       error (0, errno,
450              _("cannot remove temporary directory %s"), absolute_dir_name);
451       return -1;
452     }
453   return 0;
454 }
455
456 /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
457    Return 0 upon success, or -1 if there was some problem.  */
458 int
459 cleanup_temp_file (struct temp_dir *dir,
460                    const char *absolute_file_name)
461 {
462   int err;
463
464   err = do_unlink (dir, absolute_file_name);
465   unregister_temp_file (dir, absolute_file_name);
466
467   return err;
468 }
469
470 /* Remove the given ABSOLUTE_DIR_NAME and unregister it.
471    Return 0 upon success, or -1 if there was some problem.  */
472 int
473 cleanup_temp_subdir (struct temp_dir *dir,
474                      const char *absolute_dir_name)
475 {
476   int err;
477
478   err = do_rmdir (dir, absolute_dir_name);
479   unregister_temp_subdir (dir, absolute_dir_name);
480
481   return err;
482 }
483
484 /* Remove all registered files and subdirectories inside DIR.
485    Return 0 upon success, or -1 if there was some problem.  */
486 int
487 cleanup_temp_dir_contents (struct temp_dir *dir)
488 {
489   struct tempdir *tmpdir = (struct tempdir *)dir;
490   int err = 0;
491   gl_list_t list;
492   gl_list_iterator_t iter;
493   const void *element;
494   gl_list_node_t node;
495
496   /* First cleanup the files in the subdirectories.  */
497   list = tmpdir->files;
498   iter = gl_list_iterator (list);
499   while (gl_list_iterator_next (&iter, &element, &node))
500     {
501       char *file = (char *) element;
502
503       err |= do_unlink (dir, file);
504       gl_list_remove_node (list, node);
505       /* Now only we can free file.  */
506       free (file);
507     }
508   gl_list_iterator_free (&iter);
509
510   /* Then cleanup the subdirectories.  */
511   list = tmpdir->subdirs;
512   iter = gl_list_iterator (list);
513   while (gl_list_iterator_next (&iter, &element, &node))
514     {
515       char *subdir = (char *) element;
516
517       err |= do_rmdir (dir, subdir);
518       gl_list_remove_node (list, node);
519       /* Now only we can free subdir.  */
520       free (subdir);
521     }
522   gl_list_iterator_free (&iter);
523
524   return err;
525 }
526
527 /* Remove all registered files and subdirectories inside DIR and DIR itself.
528    DIR cannot be used any more after this call.
529    Return 0 upon success, or -1 if there was some problem.  */
530 int
531 cleanup_temp_dir (struct temp_dir *dir)
532 {
533   struct tempdir *tmpdir = (struct tempdir *)dir;
534   int err = 0;
535   size_t i;
536
537   err |= cleanup_temp_dir_contents (dir);
538   err |= do_rmdir (dir, tmpdir->dirname);
539
540   for (i = 0; i < cleanup_list.tempdir_count; i++)
541     if (cleanup_list.tempdir_list[i] == tmpdir)
542       {
543         /* Remove cleanup_list.tempdir_list[i].  */
544         if (i + 1 == cleanup_list.tempdir_count)
545           {
546             while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
547               i--;
548             cleanup_list.tempdir_count = i;
549           }
550         else
551           cleanup_list.tempdir_list[i] = NULL;
552         /* Now only we can free the tmpdir->dirname, tmpdir->subdirs,
553            tmpdir->files, and tmpdir itself.  */
554         gl_list_free (tmpdir->files);
555         gl_list_free (tmpdir->subdirs);
556         free (tmpdir->dirname);
557         free (tmpdir);
558         return err;
559       }
560
561   /* The user passed an invalid DIR argument.  */
562   abort ();
563 }
564
565
566 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
567
568 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
569    the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
570    of deleting the file when it is closed - even when the program crashes.
571    But (according to the Cygwin sources) it works only on Windows NT or newer.
572    So we cache the info whether we are running on Windows NT or newer.  */
573
574 static bool
575 supports_delete_on_close ()
576 {
577   static int known; /* 1 = yes, -1 = no, 0 = unknown */
578   if (!known)
579     {
580       OSVERSIONINFO v;
581
582       /* According to
583          <http://msdn.microsoft.com/en-us/library/windows/desktop/ms724451(v=vs.85).aspx>
584          this structure must be initialized as follows:  */
585       v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
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