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