source: trunk/essentials/sys-devel/m4/lib/clean-temp.c

Last change on this file was 3090, checked in by bird, 18 years ago

m4 1.4.8

File size: 20.5 KB
Line 
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@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. */
79struct 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. */
92static 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. */
100static 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
154static bool
155string_equals (const void *x1, const void *x2)
156{
157 const char *s1 = (const char *) x1;
158 const char *s2 = (const char *) 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. */
167static size_t
168string_hash (const void *x)
169{
170 const char *s = (const char *) 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. */
181static void
182cleanup ()
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. */
247struct temp_dir *
248create_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 *xtemplate;
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 XNMALLOC (new_allocated, struct tempdir * volatile);
277
278 if (old_allocated == 0)
279 /* First use of this facility. Register the cleanup handler. */
280 at_fatal_signal (&cleanup);
281 else
282 {
283 /* Don't use memcpy() here, because memcpy takes non-volatile
284 arguments and is therefore not guaranteed to complete all
285 memory stores before the next statement. */
286 size_t k;
287
288 for (k = 0; k < old_allocated; k++)
289 new_array[k] = old_array[k];
290 }
291
292 cleanup_list.tempdir_list = new_array;
293 cleanup_list.tempdir_allocated = new_allocated;
294
295 /* Now we can free the old array. */
296 if (old_array != NULL)
297 free ((struct tempdir **) old_array);
298 }
299
300 tmpdirp = &cleanup_list.tempdir_list[cleanup_list.tempdir_count];
301 /* Initialize *tmpdirp before incrementing tempdir_count, so that
302 cleanup() will skip this entry before it is fully initialized. */
303 *tmpdirp = NULL;
304 cleanup_list.tempdir_count++;
305 }
306
307 /* Initialize a 'struct tempdir'. */
308 tmpdir = XMALLOC (struct tempdir);
309 tmpdir->dirname = NULL;
310 tmpdir->cleanup_verbose = cleanup_verbose;
311 tmpdir->subdirs = gl_list_create_empty (GL_LINKEDHASH_LIST,
312 string_equals, string_hash, false);
313 tmpdir->files = gl_list_create_empty (GL_LINKEDHASH_LIST,
314 string_equals, string_hash, false);
315
316 /* Create the temporary directory. */
317 xtemplate = (char *) xallocsa (PATH_MAX);
318 if (path_search (xtemplate, PATH_MAX, parentdir, prefix, parentdir == NULL))
319 {
320 error (0, errno,
321 _("cannot find a temporary directory, try setting $TMPDIR"));
322 goto quit;
323 }
324 block_fatal_signals ();
325 tmpdirname = mkdtemp (xtemplate);
326 if (tmpdirname != NULL)
327 {
328 tmpdir->dirname = tmpdirname;
329 *tmpdirp = tmpdir;
330 }
331 unblock_fatal_signals ();
332 if (tmpdirname == NULL)
333 {
334 error (0, errno,
335 _("cannot create a temporary directory using template \"%s\""),
336 xtemplate);
337 goto quit;
338 }
339 /* Replace tmpdir->dirname with a copy that has indefinite extent.
340 We cannot do this inside the block_fatal_signals/unblock_fatal_signals
341 block because then the cleanup handler would not remove the directory
342 if xstrdup fails. */
343 tmpdir->dirname = xstrdup (tmpdirname);
344 freesa (xtemplate);
345 return (struct temp_dir *) tmpdir;
346
347 quit:
348 freesa (xtemplate);
349 return NULL;
350}
351
352/* Register the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
353 needs to be removed before DIR can be removed.
354 Should be called before the file ABSOLUTE_FILE_NAME is created. */
355void
356register_temp_file (struct temp_dir *dir,
357 const char *absolute_file_name)
358{
359 struct tempdir *tmpdir = (struct tempdir *)dir;
360
361 /* Add absolute_file_name to tmpdir->files, without duplicates. */
362 if (gl_list_search (tmpdir->files, absolute_file_name) == NULL)
363 gl_list_add_first (tmpdir->files, xstrdup (absolute_file_name));
364}
365
366/* Unregister the given ABSOLUTE_FILE_NAME as being a file inside DIR, that
367 needs to be removed before DIR can be removed.
368 Should be called when the file ABSOLUTE_FILE_NAME could not be created. */
369void
370unregister_temp_file (struct temp_dir *dir,
371 const char *absolute_file_name)
372{
373 struct tempdir *tmpdir = (struct tempdir *)dir;
374 gl_list_t list = tmpdir->files;
375 gl_list_node_t node;
376
377 node = gl_list_search (list, absolute_file_name);
378 if (node != NULL)
379 {
380 char *old_string = (char *) gl_list_node_value (list, node);
381
382 gl_list_remove_node (list, node);
383 free (old_string);
384 }
385}
386
387/* Register the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
388 that needs to be removed before DIR can be removed.
389 Should be called before the subdirectory ABSOLUTE_DIR_NAME is created. */
390void
391register_temp_subdir (struct temp_dir *dir,
392 const char *absolute_dir_name)
393{
394 struct tempdir *tmpdir = (struct tempdir *)dir;
395
396 /* Add absolute_dir_name to tmpdir->subdirs, without duplicates. */
397 if (gl_list_search (tmpdir->subdirs, absolute_dir_name) == NULL)
398 gl_list_add_first (tmpdir->subdirs, xstrdup (absolute_dir_name));
399}
400
401/* Unregister the given ABSOLUTE_DIR_NAME as being a subdirectory inside DIR,
402 that needs to be removed before DIR can be removed.
403 Should be called when the subdirectory ABSOLUTE_DIR_NAME could not be
404 created. */
405void
406unregister_temp_subdir (struct temp_dir *dir,
407 const char *absolute_dir_name)
408{
409 struct tempdir *tmpdir = (struct tempdir *)dir;
410 gl_list_t list = tmpdir->subdirs;
411 gl_list_node_t node;
412
413 node = gl_list_search (list, absolute_dir_name);
414 if (node != NULL)
415 {
416 char *old_string = (char *) gl_list_node_value (list, node);
417
418 gl_list_remove_node (list, node);
419 free (old_string);
420 }
421}
422
423/* Remove a file, with optional error message.
424 Return 0 upon success, or -1 if there was some problem. */
425static int
426do_unlink (struct temp_dir *dir, const char *absolute_file_name)
427{
428 if (unlink (absolute_file_name) < 0 && dir->cleanup_verbose
429 && errno != ENOENT)
430 {
431 error (0, errno, _("cannot remove temporary file %s"), absolute_file_name);
432 return -1;
433 }
434 return 0;
435}
436
437/* Remove a directory, with optional error message.
438 Return 0 upon success, or -1 if there was some problem. */
439static int
440do_rmdir (struct temp_dir *dir, const char *absolute_dir_name)
441{
442 if (rmdir (absolute_dir_name) < 0 && dir->cleanup_verbose
443 && errno != ENOENT)
444 {
445 error (0, errno,
446 _("cannot remove temporary directory %s"), absolute_dir_name);
447 return -1;
448 }
449 return 0;
450}
451
452/* Remove the given ABSOLUTE_FILE_NAME and unregister it.
453 Return 0 upon success, or -1 if there was some problem. */
454int
455cleanup_temp_file (struct temp_dir *dir,
456 const char *absolute_file_name)
457{
458 int err;
459
460 err = do_unlink (dir, absolute_file_name);
461 unregister_temp_file (dir, absolute_file_name);
462
463 return err;
464}
465
466/* Remove the given ABSOLUTE_DIR_NAME and unregister it.
467 Return 0 upon success, or -1 if there was some problem. */
468int
469cleanup_temp_subdir (struct temp_dir *dir,
470 const char *absolute_dir_name)
471{
472 int err;
473
474 err = do_rmdir (dir, absolute_dir_name);
475 unregister_temp_subdir (dir, absolute_dir_name);
476
477 return err;
478}
479
480/* Remove all registered files and subdirectories inside DIR.
481 Return 0 upon success, or -1 if there was some problem. */
482int
483cleanup_temp_dir_contents (struct temp_dir *dir)
484{
485 struct tempdir *tmpdir = (struct tempdir *)dir;
486 int err = 0;
487 gl_list_t list;
488 gl_list_iterator_t iter;
489 const void *element;
490 gl_list_node_t node;
491
492 /* First cleanup the files in the subdirectories. */
493 list = tmpdir->files;
494 iter = gl_list_iterator (list);
495 while (gl_list_iterator_next (&iter, &element, &node))
496 {
497 char *file = (char *) element;
498
499 err |= do_unlink (dir, file);
500 gl_list_remove_node (list, node);
501 /* Now only we can free file. */
502 free (file);
503 }
504 gl_list_iterator_free (&iter);
505
506 /* Then cleanup the subdirectories. */
507 list = tmpdir->subdirs;
508 iter = gl_list_iterator (list);
509 while (gl_list_iterator_next (&iter, &element, &node))
510 {
511 char *subdir = (char *) element;
512
513 err |= do_rmdir (dir, subdir);
514 gl_list_remove_node (list, node);
515 /* Now only we can free subdir. */
516 free (subdir);
517 }
518 gl_list_iterator_free (&iter);
519
520 return err;
521}
522
523/* Remove all registered files and subdirectories inside DIR and DIR itself.
524 DIR cannot be used any more after this call.
525 Return 0 upon success, or -1 if there was some problem. */
526int
527cleanup_temp_dir (struct temp_dir *dir)
528{
529 struct tempdir *tmpdir = (struct tempdir *)dir;
530 int err = 0;
531 size_t i;
532
533 err |= cleanup_temp_dir_contents (dir);
534 err |= do_rmdir (dir, tmpdir->dirname);
535
536 for (i = 0; i < cleanup_list.tempdir_count; i++)
537 if (cleanup_list.tempdir_list[i] == tmpdir)
538 {
539 /* Remove cleanup_list.tempdir_list[i]. */
540 if (i + 1 == cleanup_list.tempdir_count)
541 {
542 while (i > 0 && cleanup_list.tempdir_list[i - 1] == NULL)
543 i--;
544 cleanup_list.tempdir_count = i;
545 }
546 else
547 cleanup_list.tempdir_list[i] = NULL;
548 /* Now only we can free the tmpdir->dirname and tmpdir itself. */
549 free (tmpdir->dirname);
550 free (tmpdir);
551 return err;
552 }
553
554 /* The user passed an invalid DIR argument. */
555 abort ();
556}
557
558
559/* Register a file descriptor to be closed. */
560static void
561register_fd (int fd)
562{
563 if (descriptors == NULL)
564 descriptors = gl_list_create_empty (GL_LINKEDHASH_LIST, NULL, NULL, false);
565 gl_list_add_first (descriptors, (void *) (uintptr_t) fd);
566}
567
568/* Unregister a file descriptor to be closed. */
569static void
570unregister_fd (int fd)
571{
572 gl_list_t fds = descriptors;
573 gl_list_node_t node;
574
575 if (fds == NULL)
576 /* descriptors should already contain fd. */
577 abort ();
578 node = gl_list_search (fds, (void *) (uintptr_t) fd);
579 if (node == NULL)
580 /* descriptors should already contain fd. */
581 abort ();
582 gl_list_remove_node (fds, node);
583}
584
585/* Open a temporary file in a temporary directory.
586 Registers the resulting file descriptor to be closed. */
587int
588open_temp (const char *file_name, int flags, mode_t mode)
589{
590 int fd;
591 int saved_errno;
592
593 block_fatal_signals ();
594 fd = open (file_name, flags, mode); /* actually open or open_safer */
595 saved_errno = errno;
596 if (fd >= 0)
597 register_fd (fd);
598 unblock_fatal_signals ();
599 errno = saved_errno;
600 return fd;
601}
602
603/* Open a temporary file in a temporary directory.
604 Registers the resulting file descriptor to be closed. */
605FILE *
606fopen_temp (const char *file_name, const char *mode)
607{
608 FILE *fp;
609 int saved_errno;
610
611 block_fatal_signals ();
612 fp = fopen (file_name, mode); /* actually fopen or fopen_safer */
613 saved_errno = errno;
614 if (fp != NULL)
615 {
616 /* It is sufficient to register fileno (fp) instead of the entire fp,
617 because at cleanup time there is no need to do an fflush (fp); a
618 close (fileno (fp)) will be enough. */
619 int fd = fileno (fp);
620 if (!(fd >= 0))
621 abort ();
622 register_fd (fd);
623 }
624 unblock_fatal_signals ();
625 errno = saved_errno;
626 return fp;
627}
628
629/* Close a temporary file in a temporary directory.
630 Unregisters the previously registered file descriptor. */
631int
632close_temp (int fd)
633{
634 if (fd >= 0)
635 {
636 /* No blocking of signals is needed here, since a double close of a
637 file descriptor is harmless. */
638 int result = close (fd);
639 int saved_errno = errno;
640
641 /* No race condition here: we assume a single-threaded program, hence
642 fd cannot be re-opened here. */
643
644 unregister_fd (fd);
645
646 errno = saved_errno;
647 return result;
648 }
649 else
650 return close (fd);
651}
652
653/* Close a temporary file in a temporary directory.
654 Unregisters the previously registered file descriptor. */
655int
656fclose_temp (FILE *fp)
657{
658 int fd = fileno (fp);
659 /* No blocking of signals is needed here, since a double close of a
660 file descriptor is harmless. */
661 int result = fclose (fp);
662 int saved_errno = errno;
663
664 /* No race condition here: we assume a single-threaded program, hence
665 fd cannot be re-opened here. */
666
667 unregister_fd (fd);
668
669 errno = saved_errno;
670 return result;
671}
672
673#if GNULIB_FWRITEERROR
674/* Like fwriteerror.
675 Unregisters the previously registered file descriptor. */
676int
677fwriteerror_temp (FILE *fp)
678{
679 int fd = fileno (fp);
680 /* No blocking of signals is needed here, since a double close of a
681 file descriptor is harmless. */
682 int result = fwriteerror (fp);
683 int saved_errno = errno;
684
685 /* No race condition here: we assume a single-threaded program, hence
686 fd cannot be re-opened here. */
687
688 unregister_fd (fd);
689
690 errno = saved_errno;
691 return result;
692}
693#endif
694
695#if GNULIB_CLOSE_STREAM
696/* Like close_stream.
697 Unregisters the previously registered file descriptor. */
698int
699close_stream_temp (FILE *fp)
700{
701 int fd = fileno (fp);
702 /* No blocking of signals is needed here, since a double close of a
703 file descriptor is harmless. */
704 int result = close_stream (fp);
705 int saved_errno = errno;
706
707 /* No race condition here: we assume a single-threaded program, hence
708 fd cannot be re-opened here. */
709
710 unregister_fd (fd);
711
712 errno = saved_errno;
713 return result;
714}
715#endif
Note: See TracBrowser for help on using the repository browser.