source: vendor/m4/1.4.8/lib/clean-temp.c@ 3090

Last change on this file since 3090 was 3090, checked in by bird, 19 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 <[email protected]>, 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