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. */
|
---|
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 = (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. */
|
---|
167 | static size_t
|
---|
168 | string_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. */
|
---|
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 *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. */
|
---|
355 | void
|
---|
356 | register_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. */
|
---|
369 | void
|
---|
370 | unregister_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. */
|
---|
390 | void
|
---|
391 | register_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. */
|
---|
405 | void
|
---|
406 | unregister_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. */
|
---|
425 | static int
|
---|
426 | do_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. */
|
---|
439 | static int
|
---|
440 | do_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. */
|
---|
454 | int
|
---|
455 | cleanup_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. */
|
---|
468 | int
|
---|
469 | cleanup_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. */
|
---|
482 | int
|
---|
483 | cleanup_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. */
|
---|
526 | int
|
---|
527 | cleanup_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. */
|
---|
560 | static void
|
---|
561 | register_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. */
|
---|
569 | static void
|
---|
570 | unregister_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. */
|
---|
587 | int
|
---|
588 | open_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. */
|
---|
605 | FILE *
|
---|
606 | fopen_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. */
|
---|
631 | int
|
---|
632 | close_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. */
|
---|
655 | int
|
---|
656 | fclose_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. */
|
---|
676 | int
|
---|
677 | fwriteerror_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. */
|
---|
698 | int
|
---|
699 | close_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
|
---|