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, ¶ms, 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