1 /* Temporary directories and temporary files with automatic cleanup.
2    Copyright (C) 2001, 2003, 2006-2007, 2009-2021 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 <https://www.gnu.org/licenses/>.  */
18 
19 #include <config.h>
20 
21 /* Specification.  */
22 #include "clean-temp.h"
23 
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #if defined _WIN32 && ! defined __CYGWIN__
34 # define WIN32_LEAN_AND_MEAN  /* avoid including junk */
35 # include <windows.h>
36 #endif
37 
38 #include "clean-temp-simple.h"
39 #include "clean-temp-private.h"
40 #include "error.h"
41 #include "fatal-signal.h"
42 #include "asyncsafe-spin.h"
43 #include "pathmax.h"
44 #include "tmpdir.h"
45 #include "xalloc.h"
46 #include "xmalloca.h"
47 #include "glthread/lock.h"
48 #include "thread-optim.h"
49 #include "gl_xlist.h"
50 #include "gl_linkedhash_list.h"
51 #include "gl_linked_list.h"
52 #include "gettext.h"
53 #if GNULIB_TEMPNAME
54 # include "tempname.h"
55 #endif
56 #if GNULIB_FWRITEERROR
57 # include "fwriteerror.h"
58 #endif
59 #if GNULIB_CLOSE_STREAM
60 # include "close-stream.h"
61 #endif
62 #if GNULIB_FCNTL_SAFER
63 # include "fcntl--.h"
64 #endif
65 #if GNULIB_FOPEN_SAFER
66 # include "stdio--.h"
67 #endif
68 
69 #define _(str) gettext (str)
70 
71 /* GNU Hurd doesn't have PATH_MAX.  Use a fallback.
72    Temporary directory names are usually not that long.  */
73 #ifndef PATH_MAX
74 # define PATH_MAX 1024
75 #endif
76 
77 #if defined _WIN32 && ! defined __CYGWIN__
78 /* Don't assume that UNICODE is not defined.  */
79 # undef OSVERSIONINFO
80 # define OSVERSIONINFO OSVERSIONINFOA
81 # undef GetVersionEx
82 # define GetVersionEx GetVersionExA
83 #endif
84 
85 
86 /* Lock that protects the dir_cleanup_list from concurrent modification in
87    different threads.  */
gl_lock_define_initialized(static,dir_cleanup_list_lock)88 gl_lock_define_initialized (static, dir_cleanup_list_lock)
89 
90 /* Lock that protects the descriptors list from concurrent modification in
91    different threads.  */
92 gl_lock_define_initialized (static, descriptors_lock)
93 
94 
95 /* Close a file descriptor and the stream that contains it.
96    Avoids race conditions with signal-handler code that might want to close the
97    same file descriptor.  */
98 static int
99 asyncsafe_fclose_variant (struct closeable_fd *element, FILE *fp,
100                           int (*fclose_variant) (FILE *))
101 {
102   if (fileno (fp) != element->fd)
103     abort ();
104 
105   /* Flush buffered data first, to minimize the duration of the spin lock.  */
106   fflush (fp);
107 
108   sigset_t saved_mask;
109   int ret;
110   int saved_errno;
111 
112   asyncsafe_spin_lock (&element->lock, get_fatal_signal_set (), &saved_mask);
113   if (!element->closed)
114     {
115       ret = fclose_variant (fp); /* invokes close (element->fd) */
116       saved_errno = errno;
117       element->closed = true;
118     }
119   else
120     {
121       ret = 0;
122       saved_errno = 0;
123     }
124   asyncsafe_spin_unlock (&element->lock, &saved_mask);
125   element->done = true;
126 
127   errno = saved_errno;
128   return ret;
129 }
130 
131 
132 /* ========= Temporary directories and temporary files inside them ========= */
133 
134 /* Create a temporary directory.
135    PREFIX is used as a prefix for the name of the temporary directory. It
136    should be short and still give an indication about the program.
137    PARENTDIR can be used to specify the parent directory; if NULL, a default
138    parent directory is used (either $TMPDIR or /tmp or similar).
139    CLEANUP_VERBOSE determines whether errors during explicit cleanup are
140    reported to standard error.
141    Return a fresh 'struct temp_dir' on success.  Upon error, an error message
142    is shown and NULL is returned.  */
143 struct temp_dir *
create_temp_dir(const char * prefix,const char * parentdir,bool cleanup_verbose)144 create_temp_dir (const char *prefix, const char *parentdir,
145                  bool cleanup_verbose)
146 {
147   bool mt = gl_multithreaded ();
148 
149   if (mt) gl_lock_lock (dir_cleanup_list_lock);
150 
151   struct tempdir * volatile *tmpdirp = NULL;
152   struct tempdir *tmpdir;
153   size_t i;
154   char *xtemplate;
155   char *tmpdirname;
156 
157   /* See whether it can take the slot of an earlier temporary directory
158      already cleaned up.  */
159   for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
160     if (dir_cleanup_list.tempdir_list[i] == NULL)
161       {
162         tmpdirp = &dir_cleanup_list.tempdir_list[i];
163         break;
164       }
165   if (tmpdirp == NULL)
166     {
167       /* See whether the array needs to be extended.  */
168       if (dir_cleanup_list.tempdir_count == dir_cleanup_list.tempdir_allocated)
169         {
170           /* Note that we cannot use xrealloc(), because then the cleanup()
171              function could access an already deallocated array.  */
172           struct tempdir * volatile *old_array = dir_cleanup_list.tempdir_list;
173           size_t old_allocated = dir_cleanup_list.tempdir_allocated;
174           size_t new_allocated = 2 * dir_cleanup_list.tempdir_allocated + 1;
175           struct tempdir * volatile *new_array =
176             XNMALLOC (new_allocated, struct tempdir * volatile);
177 
178           if (old_allocated == 0)
179             {
180               /* First use of this facility.  */
181               if (clean_temp_init () < 0)
182                 xalloc_die ();
183             }
184           else
185             {
186               /* Don't use memcpy() here, because memcpy takes non-volatile
187                  arguments and is therefore not guaranteed to complete all
188                  memory stores before the next statement.  */
189               size_t k;
190 
191               for (k = 0; k < old_allocated; k++)
192                 new_array[k] = old_array[k];
193             }
194 
195           dir_cleanup_list.tempdir_list = new_array;
196           dir_cleanup_list.tempdir_allocated = new_allocated;
197 
198           /* Now we can free the old array.  */
199           /* No, we can't do that.  If cleanup_action is running in a different
200              thread and has already fetched the tempdir_list pointer (getting
201              old_array) but not yet accessed its i-th element, that thread may
202              crash when accessing an element of the already freed old_array
203              array.  */
204           #if 0
205           if (old_array != NULL)
206             free ((struct tempdir **) old_array);
207           #endif
208         }
209 
210       tmpdirp = &dir_cleanup_list.tempdir_list[dir_cleanup_list.tempdir_count];
211       /* Initialize *tmpdirp before incrementing tempdir_count, so that
212          cleanup() will skip this entry before it is fully initialized.  */
213       *tmpdirp = NULL;
214       dir_cleanup_list.tempdir_count++;
215     }
216 
217   /* Initialize a 'struct tempdir'.  */
218   tmpdir = XMALLOC (struct tempdir);
219   tmpdir->dirname = NULL;
220   tmpdir->cleanup_verbose = cleanup_verbose;
221   tmpdir->subdirs =
222     gl_list_create_empty (GL_LINKEDHASH_LIST,
223                           clean_temp_string_equals, clean_temp_string_hash,
224                           NULL, false);
225   tmpdir->files =
226     gl_list_create_empty (GL_LINKEDHASH_LIST,
227                           clean_temp_string_equals, clean_temp_string_hash,
228                           NULL, false);
229 
230   /* Create the temporary directory.  */
231   xtemplate = (char *) xmalloca (PATH_MAX);
232   if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
233     {
234       error (0, errno,
235              _("cannot find a temporary directory, try setting $TMPDIR"));
236       goto quit;
237     }
238   block_fatal_signals ();
239   tmpdirname = mkdtemp (xtemplate);
240   int saved_errno = errno;
241   if (tmpdirname != NULL)
242     {
243       tmpdir->dirname = tmpdirname;
244       *tmpdirp = tmpdir;
245     }
246   unblock_fatal_signals ();
247   if (tmpdirname == NULL)
248     {
249       error (0, saved_errno,
250              _("cannot create a temporary directory using template \"%s\""),
251              xtemplate);
252       goto quit;
253     }
254   /* Replace tmpdir->dirname with a copy that has indefinite extent.
255      We cannot do this inside the block_fatal_signals/unblock_fatal_signals
256      block because then the cleanup handler would not remove the directory
257      if xstrdup fails.  */
258   tmpdir->dirname = xstrdup (tmpdirname);
259   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
260   freea (xtemplate);
261   return (struct temp_dir *) tmpdir;
262 
263  quit:
264   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
265   freea (xtemplate);
266   return NULL;
267 }
268 
269 /* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
270    needs to be removed before DIR can be removed.
271    Should be called before the file ABSOLUTE_FILE_NAME is created.  */
272 void
register_temp_file(struct temp_dir * dir,const char * absolute_file_name)273 register_temp_file (struct temp_dir *dir,
274                     const char *absolute_file_name)
275 {
276   struct tempdir *tmpdir = (struct tempdir *)dir;
277   bool mt = gl_multithreaded ();
278 
279   if (mt) gl_lock_lock (dir_cleanup_list_lock);
280 
281   /* Add absolute_file_name to tmpdir->files, without duplicates.  */
282   if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
283     gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
284 
285   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
286 }
287 
288 /* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
289    needs to be removed before DIR can be removed.
290    Should be called when the file ABSOLUTE_FILE_NAME could not be created.  */
291 void
unregister_temp_file(struct temp_dir * dir,const char * absolute_file_name)292 unregister_temp_file (struct temp_dir *dir,
293                       const char *absolute_file_name)
294 {
295   struct tempdir *tmpdir = (struct tempdir *)dir;
296   bool mt = gl_multithreaded ();
297 
298   if (mt) gl_lock_lock (dir_cleanup_list_lock);
299 
300   gl_list_t list = tmpdir->files;
301   gl_list_node_t node;
302 
303   node = gl_list_search (list, absolute_file_name);
304   if (node != NULL)
305     {
306       char *old_string = (char *) gl_list_node_value (list, node);
307 
308       gl_list_remove_node (list, node);
309       free (old_string);
310     }
311 
312   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
313 }
314 
315 /* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
316    that needs to be removed before DIR can be removed.
317    Should be called before the subdirectory ABSOLUTE_DIR_NAME is created.  */
318 void
register_temp_subdir(struct temp_dir * dir,const char * absolute_dir_name)319 register_temp_subdir (struct temp_dir *dir,
320                       const char *absolute_dir_name)
321 {
322   struct tempdir *tmpdir = (struct tempdir *)dir;
323   bool mt = gl_multithreaded ();
324 
325   if (mt) gl_lock_lock (dir_cleanup_list_lock);
326 
327   /* Add absolute_dir_name to tmpdir->subdirs, without duplicates.  */
328   if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
329     gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
330 
331   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
332 }
333 
334 /* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
335    that needs to be removed before DIR can be removed.
336    Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
337    created.  */
338 void
unregister_temp_subdir(struct temp_dir * dir,const char * absolute_dir_name)339 unregister_temp_subdir (struct temp_dir *dir,
340                         const char *absolute_dir_name)
341 {
342   struct tempdir *tmpdir = (struct tempdir *)dir;
343   bool mt = gl_multithreaded ();
344 
345   if (mt) gl_lock_lock (dir_cleanup_list_lock);
346 
347   gl_list_t list = tmpdir->subdirs;
348   gl_list_node_t node;
349 
350   node = gl_list_search (list, absolute_dir_name);
351   if (node != NULL)
352     {
353       char *old_string = (char *) gl_list_node_value (list, node);
354 
355       gl_list_remove_node (list, node);
356       free (old_string);
357     }
358 
359   if (mt) gl_lock_unlock (dir_cleanup_list_lock);
360 }
361 
362 /* Remove a directory, with optional error message.
363    Return 0 upon success, or -1 if there was some problem.  */
364 static int
do_rmdir(const char * absolute_dir_name,bool cleanup_verbose)365 do_rmdir (const char *absolute_dir_name, bool cleanup_verbose)
366 {
367   if (rmdir (absolute_dir_name) < 0 && cleanup_verbose
368       && errno != ENOENT)
369     {
370       error (0, errno,
371              _("cannot remove temporary directory %s"), absolute_dir_name);
372       return -1;
373     }
374   return 0;
375 }
376 
377 /* Remove the given ABSOLUTE_FILE_NAME and unregister it.
378    Return 0 upon success, or -1 if there was some problem.  */
379 int
cleanup_temp_file(struct temp_dir * dir,const char * absolute_file_name)380 cleanup_temp_file (struct temp_dir *dir,
381                    const char *absolute_file_name)
382 {
383   int err;
384 
385   err = clean_temp_unlink (absolute_file_name, dir->cleanup_verbose);
386   unregister_temp_file (dir, absolute_file_name);
387 
388   return err;
389 }
390 
391 /* Remove the given ABSOLUTE_DIR_NAME and unregister it.
392    Return 0 upon success, or -1 if there was some problem.  */
393 int
cleanup_temp_subdir(struct temp_dir * dir,const char * absolute_dir_name)394 cleanup_temp_subdir (struct temp_dir *dir,
395                      const char *absolute_dir_name)
396 {
397   int err;
398 
399   err = do_rmdir (absolute_dir_name, dir->cleanup_verbose);
400   unregister_temp_subdir (dir, absolute_dir_name);
401 
402   return err;
403 }
404 
405 /* Remove all registered files and subdirectories inside DIR.
406    Only to be called with dir_cleanup_list_lock locked.
407    Return 0 upon success, or -1 if there was some problem.  */
408 int
cleanup_temp_dir_contents(struct temp_dir * dir)409 cleanup_temp_dir_contents (struct temp_dir *dir)
410 {
411   struct tempdir *tmpdir = (struct tempdir *)dir;
412   int err = 0;
413   gl_list_t list;
414   gl_list_iterator_t iter;
415   const void *element;
416   gl_list_node_t node;
417 
418   /* First cleanup the files in the subdirectories.  */
419   list = tmpdir->files;
420   iter = gl_list_iterator (list);
421   while (gl_list_iterator_next (&iter, &element, &node))
422     {
423       char *file = (char *) element;
424 
425       err |= clean_temp_unlink (file, dir->cleanup_verbose);
426       gl_list_remove_node (list, node);
427       /* Now only we can free file.  */
428       free (file);
429     }
430   gl_list_iterator_free (&iter);
431 
432   /* Then cleanup the subdirectories.  */
433   list = tmpdir->subdirs;
434   iter = gl_list_iterator (list);
435   while (gl_list_iterator_next (&iter, &element, &node))
436     {
437       char *subdir = (char *) element;
438 
439       err |= do_rmdir (subdir, dir->cleanup_verbose);
440       gl_list_remove_node (list, node);
441       /* Now only we can free subdir.  */
442       free (subdir);
443     }
444   gl_list_iterator_free (&iter);
445 
446   return err;
447 }
448 
449 /* Remove all registered files and subdirectories inside DIR and DIR itself.
450    DIR cannot be used any more after this call.
451    Return 0 upon success, or -1 if there was some problem.  */
452 int
cleanup_temp_dir(struct temp_dir * dir)453 cleanup_temp_dir (struct temp_dir *dir)
454 {
455   bool mt = gl_multithreaded ();
456 
457   if (mt) gl_lock_lock (dir_cleanup_list_lock);
458 
459   struct tempdir *tmpdir = (struct tempdir *)dir;
460   int err = 0;
461   size_t i;
462 
463   err |= cleanup_temp_dir_contents (dir);
464   err |= do_rmdir (tmpdir->dirname, dir->cleanup_verbose);
465 
466   for (i = 0; i < dir_cleanup_list.tempdir_count; i++)
467     if (dir_cleanup_list.tempdir_list[i] == tmpdir)
468       {
469         /* Remove dir_cleanup_list.tempdir_list[i].  */
470         if (i + 1 == dir_cleanup_list.tempdir_count)
471           {
472             while (i > 0 && dir_cleanup_list.tempdir_list[i - 1] == NULL)
473               i--;
474             dir_cleanup_list.tempdir_count = i;
475           }
476         else
477           dir_cleanup_list.tempdir_list[i] = NULL;
478         /* Now only we can free the tmpdir->dirname, tmpdir->subdirs,
479            tmpdir->files, and tmpdir itself.  */
480         gl_list_free (tmpdir->files);
481         gl_list_free (tmpdir->subdirs);
482         free (tmpdir->dirname);
483         free (tmpdir);
484         if (mt) gl_lock_unlock (dir_cleanup_list_lock);
485         return err;
486       }
487 
488   /* The user passed an invalid DIR argument.  */
489   abort ();
490 }
491 
492 
493 /* ================== Opening and closing temporary files ================== */
494 
495 #if defined _WIN32 && ! defined __CYGWIN__
496 
497 /* On Windows, opening a file with _O_TEMPORARY has the effect of passing
498    the FILE_FLAG_DELETE_ON_CLOSE flag to CreateFile(), which has the effect
499    of deleting the file when it is closed - even when the program crashes.
500    But (according to the Cygwin sources) it works only on Windows NT or newer.
501    So we cache the info whether we are running on Windows NT or newer.  */
502 
503 static bool
supports_delete_on_close()504 supports_delete_on_close ()
505 {
506   static int known; /* 1 = yes, -1 = no, 0 = unknown */
507   if (!known)
508     {
509       OSVERSIONINFO v;
510 
511       /* According to
512          <https://docs.microsoft.com/en-us/windows/desktop/api/sysinfoapi/nf-sysinfoapi-getversionexa>
513          this structure must be initialized as follows:  */
514       v.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
515 
516       if (GetVersionEx (&v))
517         known = (v.dwPlatformId == VER_PLATFORM_WIN32_NT ? 1 : -1);
518       else
519         known = -1;
520     }
521   return (known > 0);
522 }
523 
524 #endif
525 
526 
527 /* Register a file descriptor to be closed.  */
528 static void
register_fd(int fd)529 register_fd (int fd)
530 {
531   bool mt = gl_multithreaded ();
532 
533   if (mt) gl_lock_lock (descriptors_lock);
534 
535   if (descriptors == NULL)
536     descriptors = gl_list_create_empty (GL_LINKED_LIST, NULL, NULL, NULL,
537                                         false);
538 
539   struct closeable_fd *element = XMALLOC (struct closeable_fd);
540   element->fd = fd;
541   element->closed = false;
542   asyncsafe_spin_init (&element->lock);
543   element->done = false;
544 
545   gl_list_add_first (descriptors, element);
546 
547   if (mt) gl_lock_unlock (descriptors_lock);
548 }
549 
550 /* Open a temporary file in a temporary directory.
551    FILE_NAME must already have been passed to register_temp_file.
552    Registers the resulting file descriptor to be closed.
553    DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
554    file descriptor or stream is closed.  */
555 int
open_temp(const char * file_name,int flags,mode_t mode,bool delete_on_close)556 open_temp (const char *file_name, int flags, mode_t mode, bool delete_on_close)
557 {
558   int fd;
559   int saved_errno;
560 
561   block_fatal_signals ();
562   /* Note: 'open' here is actually open() or open_safer().  */
563 #if defined _WIN32 && ! defined __CYGWIN__
564   /* Use _O_TEMPORARY when possible, to increase the chances that the
565      temporary file is removed when the process crashes.  */
566   if (delete_on_close && supports_delete_on_close ())
567     fd = open (file_name, flags | _O_TEMPORARY, mode);
568   else
569 #endif
570     fd = open (file_name, flags, mode);
571   saved_errno = errno;
572   if (fd >= 0)
573     register_fd (fd);
574   unblock_fatal_signals ();
575   errno = saved_errno;
576   return fd;
577 }
578 
579 /* Open a temporary file in a temporary directory.
580    FILE_NAME must already have been passed to register_temp_file.
581    Registers the resulting file descriptor to be closed.
582    DELETE_ON_CLOSE indicates whether the file can be deleted when the resulting
583    file descriptor or stream is closed.  */
584 FILE *
fopen_temp(const char * file_name,const char * mode,bool delete_on_close)585 fopen_temp (const char *file_name, const char *mode, bool delete_on_close)
586 {
587   FILE *fp;
588   int saved_errno;
589 
590   block_fatal_signals ();
591   /* Note: 'fopen' here is actually fopen() or fopen_safer().  */
592 #if defined _WIN32 && ! defined __CYGWIN__
593   /* Use _O_TEMPORARY when possible, to increase the chances that the
594      temporary file is removed when the process crashes.  */
595   if (delete_on_close && supports_delete_on_close ())
596     {
597       size_t mode_len = strlen (mode);
598       char *augmented_mode = (char *) xmalloca (mode_len + 2);
599       memcpy (augmented_mode, mode, mode_len);
600       memcpy (augmented_mode + mode_len, "D", 2);
601 
602       fp = fopen (file_name, augmented_mode);
603       saved_errno = errno;
604 
605       freea (augmented_mode);
606     }
607   else
608 #endif
609     {
610       fp = fopen (file_name, mode);
611       saved_errno = errno;
612     }
613   if (fp != NULL)
614     {
615       /* It is sufficient to register fileno (fp) instead of the entire fp,
616          because at cleanup time there is no need to do an fflush (fp); a
617          close (fileno (fp)) will be enough.  */
618       int fd = fileno (fp);
619       if (!(fd >= 0))
620         abort ();
621       register_fd (fd);
622     }
623   unblock_fatal_signals ();
624   errno = saved_errno;
625   return fp;
626 }
627 
628 #if GNULIB_TEMPNAME
629 
630 struct try_create_file_params
631 {
632   int flags;
633   mode_t mode;
634 };
635 
636 static int
try_create_file(char * file_name_tmpl,void * params_)637 try_create_file (char *file_name_tmpl, void *params_)
638 {
639   struct try_create_file_params *params = params_;
640   return open (file_name_tmpl,
641                (params->flags & ~O_ACCMODE) | O_RDWR | O_CREAT | O_EXCL,
642                params->mode);
643 }
644 
645 /* Open a temporary file, generating its name based on FILE_NAME_TMPL.
646    FILE_NAME_TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX",
647    possibly with a suffix).  The name constructed does not exist at the time
648    of the call.  FILE_NAME_TMPL is overwritten with the result.
649    A safe choice for MODE is S_IRUSR | S_IWUSR, a.k.a. 0600.
650    Registers the file for deletion.
651    Opens the file, with the given FLAGS and mode MODE.
652    Registers the resulting file descriptor to be closed.  */
653 int
gen_register_open_temp(char * file_name_tmpl,int suffixlen,int flags,mode_t mode)654 gen_register_open_temp (char *file_name_tmpl, int suffixlen,
655                         int flags, mode_t mode)
656 {
657   block_fatal_signals ();
658 
659   struct try_create_file_params params;
660   params.flags = flags;
661   params.mode = mode;
662 
663   int fd = try_tempname (file_name_tmpl, suffixlen, &params, try_create_file);
664 
665   int saved_errno = errno;
666   if (fd >= 0)
667     {
668       if (clean_temp_init () < 0)
669         xalloc_die ();
670       register_fd (fd);
671       if (register_temporary_file (file_name_tmpl) < 0)
672         xalloc_die ();
673     }
674   unblock_fatal_signals ();
675   errno = saved_errno;
676   return fd;
677 }
678 
679 #endif
680 
681 /* Close a temporary file.
682    FD must have been returned by open_temp or gen_register_open_temp.
683    Unregisters the previously registered file descriptor.  */
684 int
close_temp(int fd)685 close_temp (int fd)
686 {
687   if (fd < 0)
688     return close (fd);
689 
690   clean_temp_init_asyncsafe_close ();
691 
692   int result = 0;
693   int saved_errno = 0;
694 
695   bool mt = gl_multithreaded ();
696 
697   if (mt) gl_lock_lock (descriptors_lock);
698 
699   gl_list_t list = descriptors;
700   if (list == NULL)
701     /* descriptors should already contain fd.  */
702     abort ();
703 
704   /* Search through the list, and clean it up on the fly.  */
705   bool found = false;
706   gl_list_iterator_t iter = gl_list_iterator (list);
707   const void *elt;
708   gl_list_node_t node;
709   if (gl_list_iterator_next (&iter, &elt, &node))
710     for (;;)
711       {
712         struct closeable_fd *element = (struct closeable_fd *) elt;
713 
714         /* Close the file descriptor, avoiding races with the signal
715            handler.  */
716         if (element->fd == fd)
717           {
718             found = true;
719             result = clean_temp_asyncsafe_close (element);
720             saved_errno = errno;
721           }
722 
723         bool free_this_node = element->done;
724         struct closeable_fd *element_to_free = element;
725         gl_list_node_t node_to_free = node;
726 
727         bool have_next = gl_list_iterator_next (&iter, &elt, &node);
728 
729         if (free_this_node)
730           {
731             free (element_to_free);
732             gl_list_remove_node (list, node_to_free);
733           }
734 
735         if (!have_next)
736           break;
737       }
738   gl_list_iterator_free (&iter);
739   if (!found)
740     /* descriptors should already contain fd.  */
741     abort ();
742 
743   if (mt) gl_lock_unlock (descriptors_lock);
744 
745   errno = saved_errno;
746   return result;
747 }
748 
749 static int
fclose_variant_temp(FILE * fp,int (* fclose_variant)(FILE *))750 fclose_variant_temp (FILE *fp, int (*fclose_variant) (FILE *))
751 {
752   int fd = fileno (fp);
753 
754   int result = 0;
755   int saved_errno = 0;
756 
757   bool mt = gl_multithreaded ();
758 
759   if (mt) gl_lock_lock (descriptors_lock);
760 
761   gl_list_t list = descriptors;
762   if (list == NULL)
763     /* descriptors should already contain fd.  */
764     abort ();
765 
766   /* Search through the list, and clean it up on the fly.  */
767   bool found = false;
768   gl_list_iterator_t iter = gl_list_iterator (list);
769   const void *elt;
770   gl_list_node_t node;
771   if (gl_list_iterator_next (&iter, &elt, &node))
772     for (;;)
773       {
774         struct closeable_fd *element = (struct closeable_fd *) elt;
775 
776         /* Close the file descriptor and the stream, avoiding races with the
777            signal handler.  */
778         if (element->fd == fd)
779           {
780             found = true;
781             result = asyncsafe_fclose_variant (element, fp, fclose_variant);
782             saved_errno = errno;
783           }
784 
785         bool free_this_node = element->done;
786         struct closeable_fd *element_to_free = element;
787         gl_list_node_t node_to_free = node;
788 
789         bool have_next = gl_list_iterator_next (&iter, &elt, &node);
790 
791         if (free_this_node)
792           {
793             free (element_to_free);
794             gl_list_remove_node (list, node_to_free);
795           }
796 
797         if (!have_next)
798           break;
799       }
800   gl_list_iterator_free (&iter);
801   if (!found)
802     /* descriptors should have contained fd.  */
803     abort ();
804 
805   if (mt) gl_lock_unlock (descriptors_lock);
806 
807   errno = saved_errno;
808   return result;
809 }
810 
811 /* Close a temporary file.
812    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
813    returned by open_temp or gen_register_open_temp.
814    Unregisters the previously registered file descriptor.  */
815 int
fclose_temp(FILE * fp)816 fclose_temp (FILE *fp)
817 {
818   return fclose_variant_temp (fp, fclose);
819 }
820 
821 #if GNULIB_FWRITEERROR
822 /* Like fwriteerror.
823    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
824    returned by open_temp or gen_register_open_temp.
825    Unregisters the previously registered file descriptor.  */
826 int
fwriteerror_temp(FILE * fp)827 fwriteerror_temp (FILE *fp)
828 {
829   return fclose_variant_temp (fp, fwriteerror);
830 }
831 #endif
832 
833 #if GNULIB_CLOSE_STREAM
834 /* Like close_stream.
835    FP must have been returned by fopen_temp, or by fdopen on a file descriptor
836    returned by open_temp or gen_register_open_temp.
837    Unregisters the previously registered file descriptor.  */
838 int
close_stream_temp(FILE * fp)839 close_stream_temp (FILE *fp)
840 {
841   return fclose_variant_temp (fp, close_stream);
842 }
843 #endif
844