1 |
/* remove.c -- core functions for removing files and directories |
2 |
Copyright (C) 88, 90, 91, 1994-2005 Free Software Foundation, Inc. |
3 |
|
4 |
This program is free software; you can redistribute it and/or modify |
5 |
it under the terms of the GNU General Public License as published by |
6 |
the Free Software Foundation; either version 2, or (at your option) |
7 |
any later version. |
8 |
|
9 |
This program is distributed in the hope that it will be useful, |
10 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 |
GNU General Public License for more details. |
13 |
|
14 |
You should have received a copy of the GNU General Public License |
15 |
along with this program; if not, write to the Free Software Foundation, |
16 |
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ |
17 |
|
18 |
/* Extracted from rm.c and librarified, then rewritten by Jim Meyering. */ |
19 |
|
20 |
#include <config.h> |
21 |
#include <stdio.h> |
22 |
#include <sys/types.h> |
23 |
#include <setjmp.h> |
24 |
#include <assert.h> |
25 |
|
26 |
#include "save-cwd.h" |
27 |
#include "system.h" |
28 |
#include "cycle-check.h" |
29 |
#include "dirname.h" |
30 |
#include "error.h" |
31 |
#include "euidaccess.h" |
32 |
#include "file-type.h" |
33 |
#include "hash.h" |
34 |
#include "hash-pjw.h" |
35 |
#include "obstack.h" |
36 |
#include "quote.h" |
37 |
#include "remove.h" |
38 |
#include "root-dev-ino.h" |
39 |
#include "unlinkdir.h" |
40 |
#include "yesno.h" |
41 |
|
42 |
/* Avoid shadowing warnings because these are functions declared |
43 |
in dirname.h as well as locals used below. */ |
44 |
#define dir_name rm_dir_name |
45 |
#define dir_len rm_dir_len |
46 |
|
47 |
#define obstack_chunk_alloc malloc |
48 |
#define obstack_chunk_free free |
49 |
|
50 |
/* This is the maximum number of consecutive readdir/unlink calls that |
51 |
can be made (with no intervening rewinddir or closedir/opendir) |
52 |
before triggering a bug that makes readdir return NULL even though |
53 |
some directory entries have not been processed. The bug afflicts |
54 |
SunOS's readdir when applied to ufs file systems and Darwin 6.5's |
55 |
(and OSX v.10.3.8's) HFS+. This maximum is conservative in that |
56 |
demonstrating the problem seems to require a directory containing |
57 |
at least 254 deletable entries (which doesn't count . and ..), so |
58 |
we could conceivably increase the maximum value to 254. */ |
59 |
enum |
60 |
{ |
61 |
CONSECUTIVE_READDIR_UNLINK_THRESHOLD = 200 |
62 |
}; |
63 |
|
64 |
enum Ternary |
65 |
{ |
66 |
T_UNKNOWN = 2, |
67 |
T_NO, |
68 |
T_YES |
69 |
}; |
70 |
typedef enum Ternary Ternary; |
71 |
|
72 |
/* The prompt function may be called twice for a given directory. |
73 |
The first time, we ask whether to descend into it, and the |
74 |
second time, we ask whether to remove it. */ |
75 |
enum Prompt_action |
76 |
{ |
77 |
PA_DESCEND_INTO_DIR = 2, |
78 |
PA_REMOVE_DIR |
79 |
}; |
80 |
|
81 |
/* On systems with an lstat function that accepts the empty string, |
82 |
arrange to make lstat calls go through the wrapper function. */ |
83 |
#if HAVE_LSTAT_EMPTY_STRING_BUG |
84 |
int rpl_lstat (const char *, struct stat *); |
85 |
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf) |
86 |
#endif |
87 |
|
88 |
/* Initial capacity of per-directory hash table of entries that have |
89 |
been processed but not been deleted. */ |
90 |
#define HT_UNREMOVABLE_INITIAL_CAPACITY 13 |
91 |
|
92 |
/* An entry in the active directory stack. |
93 |
Each entry corresponds to an `active' directory. */ |
94 |
struct AD_ent |
95 |
{ |
96 |
/* For a given active directory, this is the set of names of |
97 |
entries in that directory that could/should not be removed. |
98 |
For example, `.' and `..', as well as files/dirs for which |
99 |
unlink/rmdir failed e.g., due to access restrictions. */ |
100 |
Hash_table *unremovable; |
101 |
|
102 |
/* Record the status for a given active directory; we need to know |
103 |
whether an entry was not removed, either because of an error or |
104 |
because the user declined. */ |
105 |
enum RM_status status; |
106 |
|
107 |
/* The directory's dev/ino. Used to ensure that `chdir some-subdir', then |
108 |
`chdir ..' takes us back to the same directory from which we started). |
109 |
(valid for all but the bottommost entry on the stack. */ |
110 |
struct dev_ino dev_ino; |
111 |
}; |
112 |
|
113 |
extern char *program_name; |
114 |
|
115 |
struct dirstack_state |
116 |
{ |
117 |
/* The name of the directory (starting with and relative to a command |
118 |
line argument) being processed. When a subdirectory is entered, a new |
119 |
component is appended (pushed). Remove (pop) the top component |
120 |
upon chdir'ing out of a directory. This is used to form the full |
121 |
name of the current directory or a file therein, when necessary. */ |
122 |
struct obstack dir_stack; |
123 |
|
124 |
/* Stack of lengths of directory names (including trailing slash) |
125 |
appended to dir_stack. We have to have a separate stack of lengths |
126 |
(rather than just popping back to previous slash) because the first |
127 |
element pushed onto the dir stack may contain slashes. */ |
128 |
struct obstack len_stack; |
129 |
|
130 |
/* Stack of active directory entries. |
131 |
The first `active' directory is the initial working directory. |
132 |
Additional active dirs are pushed onto the stack as we `chdir' |
133 |
into each directory to be processed. When finished with the |
134 |
hierarchy under a directory, pop the active dir stack. */ |
135 |
struct obstack Active_dir; |
136 |
|
137 |
/* Used to detect cycles. */ |
138 |
struct cycle_check_state cycle_check_state; |
139 |
|
140 |
/* Target of a longjmp in case rm has to stop processing the current |
141 |
command-line argument. This happens 1) when rm detects a directory |
142 |
cycle or 2) when it has processed one or more directories, but then |
143 |
is unable to return to the initial working directory to process |
144 |
additional `.'-relative command-line arguments. */ |
145 |
jmp_buf current_arg_jumpbuf; |
146 |
}; |
147 |
typedef struct dirstack_state Dirstack_state; |
148 |
|
149 |
struct cwd_state |
150 |
{ |
151 |
/* The value of errno after a failed save_cwd or restore_cwd. */ |
152 |
int saved_errno; |
153 |
|
154 |
/* Information (open file descriptor or absolute directory name) |
155 |
required in order to restore the initial working directory. */ |
156 |
struct saved_cwd saved_cwd; |
157 |
}; |
158 |
|
159 |
static Dirstack_state * |
160 |
ds_init (void) |
161 |
{ |
162 |
Dirstack_state *ds = xmalloc (sizeof *ds); |
163 |
obstack_init (&ds->dir_stack); |
164 |
obstack_init (&ds->len_stack); |
165 |
obstack_init (&ds->Active_dir); |
166 |
return ds; |
167 |
} |
168 |
|
169 |
static void |
170 |
ds_free (Dirstack_state *ds) |
171 |
{ |
172 |
obstack_free (&ds->dir_stack, NULL); |
173 |
obstack_free (&ds->len_stack, NULL); |
174 |
obstack_free (&ds->Active_dir, NULL); |
175 |
free (ds); |
176 |
} |
177 |
|
178 |
static void |
179 |
hash_freer (void *x) |
180 |
{ |
181 |
free (x); |
182 |
} |
183 |
|
184 |
static bool |
185 |
hash_compare_strings (void const *x, void const *y) |
186 |
{ |
187 |
return STREQ (x, y) ? true : false; |
188 |
} |
189 |
|
190 |
static inline void |
191 |
push_dir (Dirstack_state *ds, const char *dir_name) |
192 |
{ |
193 |
size_t len = strlen (dir_name); |
194 |
|
195 |
/* Append the string onto the stack. */ |
196 |
obstack_grow (&ds->dir_stack, dir_name, len); |
197 |
|
198 |
/* Append a trailing slash. */ |
199 |
obstack_1grow (&ds->dir_stack, '/'); |
200 |
|
201 |
/* Add one for the slash. */ |
202 |
++len; |
203 |
|
204 |
/* Push the length (including slash) onto its stack. */ |
205 |
obstack_grow (&ds->len_stack, &len, sizeof (len)); |
206 |
} |
207 |
|
208 |
/* Return the entry name of the directory on the top of the stack |
209 |
in malloc'd storage. */ |
210 |
static inline char * |
211 |
top_dir (Dirstack_state const *ds) |
212 |
{ |
213 |
size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t); |
214 |
size_t *length = obstack_base (&ds->len_stack); |
215 |
size_t top_len = length[n_lengths - 1]; |
216 |
char const *p = obstack_next_free (&ds->dir_stack) - top_len; |
217 |
char *q = xmalloc (top_len); |
218 |
memcpy (q, p, top_len - 1); |
219 |
q[top_len - 1] = 0; |
220 |
return q; |
221 |
} |
222 |
|
223 |
static inline void |
224 |
pop_dir (Dirstack_state *ds) |
225 |
{ |
226 |
size_t n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t); |
227 |
size_t *length = obstack_base (&ds->len_stack); |
228 |
size_t top_len; |
229 |
|
230 |
assert (n_lengths > 0); |
231 |
top_len = length[n_lengths - 1]; |
232 |
assert (top_len >= 2); |
233 |
|
234 |
/* Pop off the specified length of pathname. */ |
235 |
assert (obstack_object_size (&ds->dir_stack) >= top_len); |
236 |
obstack_blank (&ds->dir_stack, -top_len); |
237 |
|
238 |
/* Pop the length stack, too. */ |
239 |
assert (obstack_object_size (&ds->len_stack) >= sizeof (size_t)); |
240 |
obstack_blank (&ds->len_stack, -(int) sizeof (size_t)); |
241 |
} |
242 |
|
243 |
/* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte |
244 |
buffer, DST, so that the last source byte is at the end of the destination |
245 |
buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED. |
246 |
Set *RESULT to point to the beginning of (the portion of) the source data |
247 |
in DST. Return the number of bytes remaining in the destination buffer. */ |
248 |
|
249 |
static size_t |
250 |
right_justify (char *dst, size_t dst_len, const char *src, size_t src_len, |
251 |
char **result, bool *truncated) |
252 |
{ |
253 |
const char *sp; |
254 |
char *dp; |
255 |
|
256 |
if (src_len <= dst_len) |
257 |
{ |
258 |
sp = src; |
259 |
dp = dst + (dst_len - src_len); |
260 |
*truncated = false; |
261 |
} |
262 |
else |
263 |
{ |
264 |
sp = src + (src_len - dst_len); |
265 |
dp = dst; |
266 |
src_len = dst_len; |
267 |
*truncated = true; |
268 |
} |
269 |
|
270 |
*result = memcpy (dp, sp, src_len); |
271 |
return dst_len - src_len; |
272 |
} |
273 |
|
274 |
/* Using the global directory name obstack, create the full path to FILENAME. |
275 |
Return it in sometimes-realloc'd space that should not be freed by the |
276 |
caller. Realloc as necessary. If realloc fails, use a static buffer |
277 |
and put as long a suffix in that buffer as possible. */ |
278 |
|
279 |
#define full_filename(Filename) full_filename_ (ds, Filename) |
280 |
static char * |
281 |
full_filename_ (Dirstack_state const *ds, const char *filename) |
282 |
{ |
283 |
static char *buf = NULL; |
284 |
static size_t n_allocated = 0; |
285 |
|
286 |
size_t dir_len = obstack_object_size (&ds->dir_stack); |
287 |
char *dir_name = obstack_base (&ds->dir_stack); |
288 |
size_t n_bytes_needed; |
289 |
size_t filename_len; |
290 |
|
291 |
filename_len = strlen (filename); |
292 |
n_bytes_needed = dir_len + filename_len + 1; |
293 |
|
294 |
if (n_allocated < n_bytes_needed) |
295 |
{ |
296 |
/* This code requires that realloc accept NULL as the first arg. |
297 |
This function must not use xrealloc. Otherwise, an out-of-memory |
298 |
error involving a file name to be expanded here wouldn't ever |
299 |
be issued. Use realloc and fall back on using a static buffer |
300 |
if memory allocation fails. */ |
301 |
char *new_buf = realloc (buf, n_bytes_needed); |
302 |
n_allocated = n_bytes_needed; |
303 |
|
304 |
if (new_buf == NULL) |
305 |
{ |
306 |
#define SBUF_SIZE 512 |
307 |
#define ELLIPSES_PREFIX "[...]" |
308 |
static char static_buf[SBUF_SIZE]; |
309 |
bool truncated; |
310 |
size_t len; |
311 |
char *p; |
312 |
|
313 |
free (buf); |
314 |
len = right_justify (static_buf, SBUF_SIZE, filename, |
315 |
filename_len + 1, &p, &truncated); |
316 |
right_justify (static_buf, len, dir_name, dir_len, &p, &truncated); |
317 |
if (truncated) |
318 |
{ |
319 |
memcpy (static_buf, ELLIPSES_PREFIX, |
320 |
sizeof (ELLIPSES_PREFIX) - 1); |
321 |
} |
322 |
return p; |
323 |
} |
324 |
|
325 |
buf = new_buf; |
326 |
} |
327 |
|
328 |
if (filename_len == 1 && *filename == '.' && dir_len) |
329 |
{ |
330 |
/* FILENAME is just `.' and dir_len is nonzero. |
331 |
Copy the directory part, omitting the trailing slash, |
332 |
and append a trailing zero byte. */ |
333 |
char *p = mempcpy (buf, dir_name, dir_len - 1); |
334 |
*p = 0; |
335 |
} |
336 |
else |
337 |
{ |
338 |
/* Copy the directory part, including trailing slash, and then |
339 |
append the filename part, including a trailing zero byte. */ |
340 |
memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1); |
341 |
assert (strlen (buf) + 1 == n_bytes_needed); |
342 |
} |
343 |
|
344 |
return buf; |
345 |
} |
346 |
|
347 |
static size_t |
348 |
AD_stack_height (Dirstack_state const *ds) |
349 |
{ |
350 |
return obstack_object_size (&ds->Active_dir) / sizeof (struct AD_ent); |
351 |
} |
352 |
|
353 |
static struct AD_ent * |
354 |
AD_stack_top (Dirstack_state const *ds) |
355 |
{ |
356 |
return (struct AD_ent *) |
357 |
((char *) obstack_next_free (&ds->Active_dir) - sizeof (struct AD_ent)); |
358 |
} |
359 |
|
360 |
static void |
361 |
AD_stack_pop (Dirstack_state *ds) |
362 |
{ |
363 |
/* operate on Active_dir. pop and free top entry */ |
364 |
struct AD_ent *top = AD_stack_top (ds); |
365 |
if (top->unremovable) |
366 |
hash_free (top->unremovable); |
367 |
obstack_blank (&ds->Active_dir, -(int) sizeof (struct AD_ent)); |
368 |
pop_dir (ds); |
369 |
} |
370 |
|
371 |
/* chdir `up' one level. |
372 |
Whenever using chdir '..', verify that the post-chdir |
373 |
dev/ino numbers for `.' match the saved ones. |
374 |
If they don't match, exit nonzero. |
375 |
Set *PREV_DIR to the name (in malloc'd storage) of the |
376 |
directory (usually now empty) from which we're coming. |
377 |
If we're at the bottom of the AD stack (about to return |
378 |
to the initial working directory), then use CWD. |
379 |
If that restore_cwd fails, set CWD_STATE->saved_errno. */ |
380 |
static void |
381 |
AD_pop_and_chdir (Dirstack_state *ds, char **prev_dir, |
382 |
struct cwd_state *cwd_state) |
383 |
{ |
384 |
enum RM_status old_status = AD_stack_top(ds)->status; |
385 |
struct AD_ent *top; |
386 |
|
387 |
/* Get the name of the current (but soon to be `previous') directory |
388 |
from the top of the stack. */ |
389 |
*prev_dir = top_dir (ds); |
390 |
|
391 |
AD_stack_pop (ds); |
392 |
top = AD_stack_top (ds); |
393 |
|
394 |
/* Propagate any failure to parent. */ |
395 |
UPDATE_STATUS (top->status, old_status); |
396 |
|
397 |
assert (AD_stack_height (ds)); |
398 |
|
399 |
if (1 < AD_stack_height (ds)) |
400 |
{ |
401 |
struct stat sb; |
402 |
|
403 |
/* We can give a better diagnostic here, since the target is relative. */ |
404 |
if (chdir ("..") != 0) |
405 |
{ |
406 |
error (EXIT_FAILURE, errno, |
407 |
_("cannot chdir from %s to .."), |
408 |
quote (full_filename ("."))); |
409 |
} |
410 |
|
411 |
if (lstat (".", &sb)) |
412 |
error (EXIT_FAILURE, errno, |
413 |
_("cannot lstat `.' in %s"), quote (full_filename ("."))); |
414 |
|
415 |
/* Ensure that post-chdir dev/ino match the stored ones. */ |
416 |
if ( ! SAME_INODE (sb, top->dev_ino)) |
417 |
error (EXIT_FAILURE, 0, |
418 |
_("%s changed dev/ino"), quote (full_filename ("."))); |
419 |
} |
420 |
else |
421 |
{ |
422 |
if (restore_cwd (&cwd_state->saved_cwd) != 0) |
423 |
{ |
424 |
/* We've failed to return to the initial working directory. |
425 |
That failure may be harmless if x->require_restore_cwd is false, |
426 |
but we do have to remember that fact, including the errno value, |
427 |
so we can give an accurate diagnostic when reporting the failure |
428 |
to remove a subsequent relative-named command-line argument. */ |
429 |
cwd_state->saved_errno = errno; |
430 |
} |
431 |
} |
432 |
} |
433 |
|
434 |
/* Initialize *HT if it is NULL. |
435 |
Insert FILENAME into HT. */ |
436 |
static void |
437 |
AD_mark_helper (Hash_table **ht, char const *filename) |
438 |
{ |
439 |
if (*ht == NULL) |
440 |
{ |
441 |
*ht = hash_initialize (HT_UNREMOVABLE_INITIAL_CAPACITY, NULL, hash_pjw, |
442 |
hash_compare_strings, hash_freer); |
443 |
if (*ht == NULL) |
444 |
xalloc_die (); |
445 |
} |
446 |
if (! hash_insert (*ht, filename)) |
447 |
xalloc_die (); |
448 |
} |
449 |
|
450 |
/* Mark FILENAME (in current directory) as unremovable. */ |
451 |
static void |
452 |
AD_mark_as_unremovable (Dirstack_state *ds, char const *filename) |
453 |
{ |
454 |
AD_mark_helper (&AD_stack_top(ds)->unremovable, xstrdup (filename)); |
455 |
} |
456 |
|
457 |
/* Mark the current directory as unremovable. I.e., mark the entry |
458 |
in the parent directory corresponding to `.'. |
459 |
This happens e.g., when an opendir fails and the only name |
460 |
the caller has conveniently at hand is `.'. */ |
461 |
static void |
462 |
AD_mark_current_as_unremovable (Dirstack_state *ds) |
463 |
{ |
464 |
struct AD_ent *top = AD_stack_top (ds); |
465 |
const char *curr = top_dir (ds); |
466 |
|
467 |
assert (1 < AD_stack_height (ds)); |
468 |
|
469 |
--top; |
470 |
AD_mark_helper (&top->unremovable, curr); |
471 |
} |
472 |
|
473 |
/* Push an initial dummy entry onto the stack. |
474 |
This will always be the bottommost entry on the stack. */ |
475 |
static void |
476 |
AD_push_initial (Dirstack_state *ds) |
477 |
{ |
478 |
struct AD_ent *top; |
479 |
|
480 |
/* Extend the stack. */ |
481 |
obstack_blank (&ds->Active_dir, sizeof (struct AD_ent)); |
482 |
|
483 |
/* Fill in the new values. */ |
484 |
top = AD_stack_top (ds); |
485 |
top->unremovable = NULL; |
486 |
|
487 |
/* These should never be used. |
488 |
Give them values that might look suspicious |
489 |
in a debugger or in a diagnostic. */ |
490 |
top->dev_ino.st_dev = TYPE_MAXIMUM (dev_t); |
491 |
top->dev_ino.st_ino = TYPE_MAXIMUM (ino_t); |
492 |
} |
493 |
|
494 |
/* Push info about the current working directory (".") onto the |
495 |
active directory stack. DIR is the ./-relative name through |
496 |
which we've just `chdir'd to this directory. DIR_SB_FROM_PARENT |
497 |
is the result of calling lstat on DIR from the parent of DIR. */ |
498 |
static void |
499 |
AD_push (Dirstack_state *ds, char const *dir, |
500 |
struct stat const *dir_sb_from_parent) |
501 |
{ |
502 |
struct stat sb; |
503 |
struct AD_ent *top; |
504 |
|
505 |
push_dir (ds, dir); |
506 |
|
507 |
if (lstat (".", &sb)) |
508 |
error (EXIT_FAILURE, errno, |
509 |
_("cannot lstat `.' in %s"), quote (full_filename ("."))); |
510 |
|
511 |
if ( ! SAME_INODE (sb, *dir_sb_from_parent)) |
512 |
error (EXIT_FAILURE, 0, |
513 |
_("%s changed dev/ino"), quote (full_filename ("."))); |
514 |
|
515 |
/* Extend the stack. */ |
516 |
obstack_blank (&ds->Active_dir, sizeof (struct AD_ent)); |
517 |
|
518 |
/* Fill in the new values. */ |
519 |
top = AD_stack_top (ds); |
520 |
top->dev_ino.st_dev = sb.st_dev; |
521 |
top->dev_ino.st_ino = sb.st_ino; |
522 |
top->unremovable = NULL; |
523 |
} |
524 |
|
525 |
static bool |
526 |
AD_is_removable (Dirstack_state const *ds, char const *file) |
527 |
{ |
528 |
struct AD_ent *top = AD_stack_top (ds); |
529 |
return ! (top->unremovable && hash_lookup (top->unremovable, file)); |
530 |
} |
531 |
|
532 |
/* Return true if DIR is determined to be an empty directory |
533 |
or if opendir or readdir fails. */ |
534 |
static bool |
535 |
is_empty_dir (char const *dir) |
536 |
{ |
537 |
DIR *dirp = opendir (dir); |
538 |
struct dirent const *dp; |
539 |
int saved_errno; |
540 |
|
541 |
if (dirp == NULL) |
542 |
return false; |
543 |
|
544 |
errno = 0; |
545 |
dp = readdir_ignoring_dot_and_dotdot (dirp); |
546 |
saved_errno = errno; |
547 |
closedir (dirp); |
548 |
if (dp != NULL) |
549 |
return false; |
550 |
return saved_errno == 0 ? true : false; |
551 |
} |
552 |
|
553 |
/* Return true if FILE is not a symbolic link and it is not writable. |
554 |
Also return true if FILE cannot be lstat'ed. Otherwise, return false. |
555 |
If lstat succeeds, set *BUF_P to BUF. |
556 |
This is to avoid calling euidaccess when FILE is a symlink. */ |
557 |
static bool |
558 |
write_protected_non_symlink (char const *file, |
559 |
struct stat **buf_p, |
560 |
struct stat *buf) |
561 |
{ |
562 |
if (lstat (file, buf) != 0) |
563 |
return false; |
564 |
*buf_p = buf; |
565 |
if (S_ISLNK (buf->st_mode)) |
566 |
return false; |
567 |
return euidaccess (file, W_OK) == -1 && errno == EACCES; |
568 |
} |
569 |
|
570 |
/* Prompt whether to remove FILENAME, if required via a combination of |
571 |
the options specified by X and/or file attributes. If the file may |
572 |
be removed, return RM_OK. If the user declines to remove the file, |
573 |
return RM_USER_DECLINED. If not ignoring missing files and we |
574 |
cannot lstat FILENAME, then return RM_ERROR. |
575 |
|
576 |
Depending on MODE, ask whether to `descend into' or to `remove' the |
577 |
directory FILENAME. MODE is ignored when FILENAME is not a directory. |
578 |
Set *IS_EMPTY to T_YES if FILENAME is an empty directory, and it is |
579 |
appropriate to try to remove it with rmdir (e.g. recursive mode). |
580 |
Don't even try to set *IS_EMPTY when MODE == PA_REMOVE_DIR. |
581 |
Set *IS_DIR to T_YES or T_NO if we happen to determine whether |
582 |
FILENAME is a directory. */ |
583 |
static enum RM_status |
584 |
prompt (Dirstack_state const *ds, char const *filename, |
585 |
struct rm_options const *x, enum Prompt_action mode, |
586 |
Ternary *is_dir, Ternary *is_empty) |
587 |
{ |
588 |
bool write_protected = false; |
589 |
struct stat *sbuf = NULL; |
590 |
struct stat buf; |
591 |
|
592 |
*is_empty = T_UNKNOWN; |
593 |
*is_dir = T_UNKNOWN; |
594 |
|
595 |
if (((!x->ignore_missing_files & (x->interactive | x->stdin_tty)) |
596 |
&& (write_protected = write_protected_non_symlink (filename, |
597 |
&sbuf, &buf))) |
598 |
|| x->interactive) |
599 |
{ |
600 |
if (sbuf == NULL) |
601 |
{ |
602 |
sbuf = &buf; |
603 |
if (lstat (filename, sbuf)) |
604 |
{ |
605 |
/* lstat failed. This happens e.g., with `rm '''. */ |
606 |
error (0, errno, _("cannot lstat %s"), |
607 |
quote (full_filename (filename))); |
608 |
return RM_ERROR; |
609 |
} |
610 |
} |
611 |
|
612 |
if (S_ISDIR (sbuf->st_mode) && !x->recursive) |
613 |
{ |
614 |
error (0, EISDIR, _("cannot remove directory %s"), |
615 |
quote (full_filename (filename))); |
616 |
return RM_ERROR; |
617 |
} |
618 |
|
619 |
/* Using permissions doesn't make sense for symlinks. */ |
620 |
if (S_ISLNK (sbuf->st_mode)) |
621 |
{ |
622 |
if ( ! x->interactive) |
623 |
return RM_OK; |
624 |
write_protected = false; |
625 |
} |
626 |
|
627 |
/* Issue the prompt. */ |
628 |
{ |
629 |
char const *quoted_name = quote (full_filename (filename)); |
630 |
|
631 |
*is_dir = (S_ISDIR (sbuf->st_mode) ? T_YES : T_NO); |
632 |
|
633 |
/* FIXME: use a variant of error (instead of fprintf) that doesn't |
634 |
append a newline. Then we won't have to declare program_name in |
635 |
this file. */ |
636 |
if (S_ISDIR (sbuf->st_mode) |
637 |
&& x->recursive |
638 |
&& mode == PA_DESCEND_INTO_DIR |
639 |
&& ((*is_empty = (is_empty_dir (filename) ? T_YES : T_NO)) |
640 |
== T_NO)) |
641 |
fprintf (stderr, |
642 |
(write_protected |
643 |
? _("%s: descend into write-protected directory %s? ") |
644 |
: _("%s: descend into directory %s? ")), |
645 |
program_name, quoted_name); |
646 |
else |
647 |
{ |
648 |
/* TRANSLATORS: You may find it more convenient to translate |
649 |
the equivalent of _("%s: remove %s (write-protected) %s? "). |
650 |
It should avoid grammatical problems with the output |
651 |
of file_type. */ |
652 |
fprintf (stderr, |
653 |
(write_protected |
654 |
? _("%s: remove write-protected %s %s? ") |
655 |
: _("%s: remove %s %s? ")), |
656 |
program_name, file_type (sbuf), quoted_name); |
657 |
} |
658 |
|
659 |
if (!yesno ()) |
660 |
return RM_USER_DECLINED; |
661 |
} |
662 |
} |
663 |
return RM_OK; |
664 |
} |
665 |
|
666 |
#if HAVE_STRUCT_DIRENT_D_TYPE |
667 |
|
668 |
/* True if the type of the directory entry D is known. */ |
669 |
# define DT_IS_KNOWN(d) ((d)->d_type != DT_UNKNOWN) |
670 |
|
671 |
/* True if the type of the directory entry D must be T. */ |
672 |
# define DT_MUST_BE(d, t) ((d)->d_type == (t)) |
673 |
|
674 |
#else |
675 |
# define DT_IS_KNOWN(d) false |
676 |
# define DT_MUST_BE(d, t) false |
677 |
#endif |
678 |
|
679 |
#define DO_UNLINK(Filename, X) \ |
680 |
do \ |
681 |
{ \ |
682 |
if (unlink (Filename) == 0) \ |
683 |
{ \ |
684 |
if ((X)->verbose) \ |
685 |
printf (_("removed %s\n"), quote (full_filename (Filename))); \ |
686 |
return RM_OK; \ |
687 |
} \ |
688 |
\ |
689 |
if (errno == ENOENT && (X)->ignore_missing_files) \ |
690 |
return RM_OK; \ |
691 |
} \ |
692 |
while (0) |
693 |
|
694 |
#define DO_RMDIR(Filename, X) \ |
695 |
do \ |
696 |
{ \ |
697 |
if (rmdir (Filename) == 0) \ |
698 |
{ \ |
699 |
if ((X)->verbose) \ |
700 |
printf (_("removed directory: %s\n"), \ |
701 |
quote (full_filename (Filename))); \ |
702 |
return RM_OK; \ |
703 |
} \ |
704 |
\ |
705 |
if (errno == ENOENT && (X)->ignore_missing_files) \ |
706 |
return RM_OK; \ |
707 |
\ |
708 |
if (errno == ENOTEMPTY || errno == EEXIST) \ |
709 |
return RM_NONEMPTY_DIR; \ |
710 |
} \ |
711 |
while (0) |
712 |
|
713 |
/* Remove the file or directory specified by FILENAME. |
714 |
Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not. |
715 |
But if FILENAME specifies a non-empty directory, return RM_NONEMPTY_DIR. */ |
716 |
|
717 |
static enum RM_status |
718 |
remove_entry (Dirstack_state const *ds, char const *filename, |
719 |
struct rm_options const *x, struct dirent const *dp) |
720 |
{ |
721 |
Ternary is_dir; |
722 |
Ternary is_empty_directory; |
723 |
enum RM_status s = prompt (ds, filename, x, PA_DESCEND_INTO_DIR, |
724 |
&is_dir, &is_empty_directory); |
725 |
|
726 |
if (s != RM_OK) |
727 |
return s; |
728 |
|
729 |
/* Why bother with the following if/else block? Because on systems with |
730 |
an unlink function that *can* unlink directories, we must determine the |
731 |
type of each entry before removing it. Otherwise, we'd risk unlinking |
732 |
an entire directory tree simply by unlinking a single directory; then |
733 |
all the storage associated with that hierarchy would not be freed until |
734 |
the next fsck. Not nice. To avoid that, on such slightly losing |
735 |
systems, we need to call lstat to determine the type of each entry, |
736 |
and that represents extra overhead that -- it turns out -- we can |
737 |
avoid on non-losing systems, since there, unlink will never remove |
738 |
a directory. Also, on systems where unlink may unlink directories, |
739 |
we're forced to allow a race condition: we lstat a non-directory, then |
740 |
go to unlink it, but in the mean time, a malicious someone has replaced |
741 |
it with a directory. */ |
742 |
|
743 |
if (cannot_unlink_dir ()) |
744 |
{ |
745 |
if (is_dir == T_YES && ! x->recursive) |
746 |
{ |
747 |
error (0, EISDIR, _("cannot remove directory %s"), |
748 |
quote (full_filename (filename))); |
749 |
return RM_ERROR; |
750 |
} |
751 |
|
752 |
/* is_empty_directory is set iff it's ok to use rmdir. |
753 |
Note that it's set only in interactive mode -- in which case it's |
754 |
an optimization that arranges so that the user is asked just |
755 |
once whether to remove the directory. */ |
756 |
if (is_empty_directory == T_YES) |
757 |
DO_RMDIR (filename, x); |
758 |
|
759 |
/* If we happen to know that FILENAME is a directory, return now |
760 |
and let the caller remove it -- this saves the overhead of a failed |
761 |
unlink call. If FILENAME is a command-line argument, then dp is NULL, |
762 |
so we'll first try to unlink it. Using unlink here is ok, because it |
763 |
cannot remove a directory. */ |
764 |
if ((dp && DT_MUST_BE (dp, DT_DIR)) || is_dir == T_YES) |
765 |
return RM_NONEMPTY_DIR; |
766 |
|
767 |
DO_UNLINK (filename, x); |
768 |
|
769 |
if (! x->recursive |
770 |
|| errno == ENOENT || errno == ENOTDIR |
771 |
|| errno == ELOOP || errno == ENAMETOOLONG) |
772 |
{ |
773 |
/* Either --recursive is not in effect, or the file cannot be a |
774 |
directory. Report the unlink problem and fail. */ |
775 |
error (0, errno, _("cannot remove %s"), |
776 |
quote (full_filename (filename))); |
777 |
return RM_ERROR; |
778 |
} |
779 |
} |
780 |
else |
781 |
{ |
782 |
/* If we don't already know whether FILENAME is a directory, find out now. |
783 |
Then, if it's a non-directory, we can use unlink on it. */ |
784 |
if (is_dir == T_UNKNOWN) |
785 |
{ |
786 |
if (dp && DT_IS_KNOWN (dp)) |
787 |
is_dir = DT_MUST_BE (dp, DT_DIR) ? T_YES : T_NO; |
788 |
else |
789 |
{ |
790 |
struct stat sbuf; |
791 |
if (lstat (filename, &sbuf)) |
792 |
{ |
793 |
if (errno == ENOENT && x->ignore_missing_files) |
794 |
return RM_OK; |
795 |
|
796 |
error (0, errno, _("cannot lstat %s"), |
797 |
quote (full_filename (filename))); |
798 |
return RM_ERROR; |
799 |
} |
800 |
|
801 |
is_dir = S_ISDIR (sbuf.st_mode) ? T_YES : T_NO; |
802 |
} |
803 |
} |
804 |
|
805 |
if (is_dir == T_NO) |
806 |
{ |
807 |
/* At this point, barring race conditions, FILENAME is known |
808 |
to be a non-directory, so it's ok to try to unlink it. */ |
809 |
DO_UNLINK (filename, x); |
810 |
|
811 |
/* unlink failed with some other error code. report it. */ |
812 |
error (0, errno, _("cannot remove %s"), |
813 |
quote (full_filename (filename))); |
814 |
return RM_ERROR; |
815 |
} |
816 |
|
817 |
if (! x->recursive) |
818 |
{ |
819 |
error (0, EISDIR, _("cannot remove directory %s"), |
820 |
quote (full_filename (filename))); |
821 |
return RM_ERROR; |
822 |
} |
823 |
|
824 |
if (is_empty_directory == T_YES) |
825 |
{ |
826 |
DO_RMDIR (filename, x); |
827 |
/* Don't diagnose any failure here. |
828 |
It'll be detected when the caller tries another way. */ |
829 |
} |
830 |
} |
831 |
|
832 |
return RM_NONEMPTY_DIR; |
833 |
} |
834 |
|
835 |
/* Remove entries in `.', the current working directory (cwd). |
836 |
Upon finding a directory that is both non-empty and that can be chdir'd |
837 |
into, return RM_OK and set *SUBDIR and fill in SUBDIR_SB, where |
838 |
SUBDIR is the malloc'd name of the subdirectory if the chdir succeeded, |
839 |
NULL otherwise (e.g., if opendir failed or if there was no subdirectory). |
840 |
Likewise, SUBDIR_SB is the result of calling lstat on SUBDIR. |
841 |
Return RM_OK if all entries are removed. Return RM_ERROR if any |
842 |
entry cannot be removed. Otherwise, return RM_USER_DECLINED if |
843 |
the user declines to remove at least one entry. Remove as much as |
844 |
possible, continuing even if we fail to remove some entries. */ |
845 |
static enum RM_status |
846 |
remove_cwd_entries (Dirstack_state *ds, char **subdir, struct stat *subdir_sb, |
847 |
struct rm_options const *x) |
848 |
{ |
849 |
DIR *dirp = opendir ("."); |
850 |
struct AD_ent *top = AD_stack_top (ds); |
851 |
enum RM_status status = top->status; |
852 |
size_t n_unlinked_since_opendir_or_last_rewind = 0; |
853 |
|
854 |
assert (VALID_STATUS (status)); |
855 |
*subdir = NULL; |
856 |
|
857 |
if (dirp == NULL) |
858 |
{ |
859 |
if (errno != ENOENT || !x->ignore_missing_files) |
860 |
{ |
861 |
error (0, errno, _("cannot open directory %s"), |
862 |
quote (full_filename ("."))); |
863 |
return RM_ERROR; |
864 |
} |
865 |
} |
866 |
|
867 |
while (1) |
868 |
{ |
869 |
struct dirent const *dp; |
870 |
enum RM_status tmp_status; |
871 |
const char *f; |
872 |
|
873 |
/* Set errno to zero so we can distinguish between a readdir failure |
874 |
and when readdir simply finds that there are no more entries. */ |
875 |
errno = 0; |
876 |
if ((dp = readdir_ignoring_dot_and_dotdot (dirp)) == NULL) |
877 |
{ |
878 |
if (errno) |
879 |
{ |
880 |
/* Save/restore errno across closedir call. */ |
881 |
int e = errno; |
882 |
closedir (dirp); |
883 |
errno = e; |
884 |
|
885 |
/* Arrange to give a diagnostic after exiting this loop. */ |
886 |
dirp = NULL; |
887 |
} |
888 |
else if (CONSECUTIVE_READDIR_UNLINK_THRESHOLD |
889 |
< n_unlinked_since_opendir_or_last_rewind) |
890 |
{ |
891 |
/* Call rewinddir if we've called unlink or rmdir so many times |
892 |
(since the opendir or the previous rewinddir) that this |
893 |
NULL-return may be the symptom of a buggy readdir. */ |
894 |
rewinddir (dirp); |
895 |
n_unlinked_since_opendir_or_last_rewind = 0; |
896 |
continue; |
897 |
} |
898 |
break; |
899 |
} |
900 |
|
901 |
f = dp->d_name; |
902 |
|
903 |
/* Skip files we've already tried/failed to remove. */ |
904 |
if ( ! AD_is_removable (ds, f)) |
905 |
continue; |
906 |
|
907 |
/* Pass dp->d_type info to remove_entry so the non-glibc |
908 |
case can decide whether to use unlink or chdir. |
909 |
Systems without the d_type member will have to endure |
910 |
the performance hit of first calling lstat F. */ |
911 |
tmp_status = remove_entry (ds, f, x, dp); |
912 |
switch (tmp_status) |
913 |
{ |
914 |
case RM_OK: |
915 |
/* Count how many files we've unlinked since the initial |
916 |
opendir or the last rewinddir. On buggy systems, if you |
917 |
remove too many, readdir returns NULL even though there |
918 |
remain unprocessed directory entries. */ |
919 |
++n_unlinked_since_opendir_or_last_rewind; |
920 |
break; |
921 |
|
922 |
case RM_ERROR: |
923 |
case RM_USER_DECLINED: |
924 |
AD_mark_as_unremovable (ds, f); |
925 |
UPDATE_STATUS (status, tmp_status); |
926 |
break; |
927 |
|
928 |
case RM_NONEMPTY_DIR: |
929 |
{ |
930 |
/* Save a copy of errno, in case the preceding unlink (from |
931 |
remove_entry's DO_UNLINK) of a non-directory failed. */ |
932 |
int saved_errno = errno; |
933 |
|
934 |
/* Record dev/ino of F so that we can compare |
935 |
that with dev/ino of `.' after the chdir. |
936 |
This dev/ino pair is also used in cycle detection. */ |
937 |
if (lstat (f, subdir_sb)) |
938 |
error (EXIT_FAILURE, errno, _("cannot lstat %s"), |
939 |
quote (full_filename (f))); |
940 |
|
941 |
errno = ENOTDIR; |
942 |
if (! S_ISDIR (subdir_sb->st_mode) || chdir (f) != 0) |
943 |
{ |
944 |
/* It is much more common that we reach this point for an |
945 |
inaccessible directory. Hence the second diagnostic, below. |
946 |
However it is also possible that F is a non-directory. |
947 |
That can happen when we use the `! UNLINK_CAN_UNLINK_DIRS' |
948 |
block of code and when DO_UNLINK fails due to EPERM. |
949 |
In that case, give a better diagnostic. */ |
950 |
if (errno == ENOTDIR) |
951 |
error (0, saved_errno, _("cannot remove %s"), |
952 |
quote (full_filename (f))); |
953 |
else |
954 |
error (0, errno, _("cannot chdir from %s to %s"), |
955 |
quote_n (0, full_filename (".")), quote_n (1, f)); |
956 |
AD_mark_as_unremovable (ds, f); |
957 |
status = RM_ERROR; |
958 |
break; |
959 |
} |
960 |
if (cycle_check (&ds->cycle_check_state, subdir_sb)) |
961 |
{ |
962 |
error (0, 0, _("\ |
963 |
WARNING: Circular directory structure.\n\ |
964 |
This almost certainly means that you have a corrupted file system.\n\ |
965 |
NOTIFY YOUR SYSTEM MANAGER.\n\ |
966 |
The following directory is part of the cycle:\n %s\n"), |
967 |
quote (full_filename ("."))); |
968 |
longjmp (ds->current_arg_jumpbuf, 1); |
969 |
} |
970 |
|
971 |
*subdir = xstrdup (f); |
972 |
break; |
973 |
} |
974 |
} |
975 |
|
976 |
/* Record status for this directory. */ |
977 |
UPDATE_STATUS (top->status, status); |
978 |
|
979 |
if (*subdir) |
980 |
break; |
981 |
} |
982 |
|
983 |
if (dirp == NULL || CLOSEDIR (dirp) != 0) |
984 |
{ |
985 |
/* Note that this diagnostic serves for both readdir |
986 |
and closedir failures. */ |
987 |
error (0, errno, _("reading directory %s"), quote (full_filename ("."))); |
988 |
status = RM_ERROR; |
989 |
} |
990 |
|
991 |
return status; |
992 |
} |
993 |
|
994 |
/* Do this after each call to AD_push or AD_push_initial. |
995 |
Because the status = RM_OK bit is too remove-specific to |
996 |
go into the general-purpose AD_* package. */ |
997 |
#define AD_INIT_OTHER_MEMBERS() \ |
998 |
do \ |
999 |
{ \ |
1000 |
AD_stack_top(ds)->status = RM_OK; \ |
1001 |
} \ |
1002 |
while (0) |
1003 |
|
1004 |
/* Remove the hierarchy rooted at DIR. |
1005 |
Do that by changing into DIR, then removing its contents, then |
1006 |
returning to the original working directory and removing DIR itself. |
1007 |
Don't use recursion. Be careful when using chdir ".." that we |
1008 |
return to the same directory from which we came, if necessary. |
1009 |
Return 1 for success, 0 if some file cannot be removed or if |
1010 |
a chdir fails. |
1011 |
If the initial working directory cannot be saved or restored, |
1012 |
record the offending errno value in (*CWD_STATE)->saved_errno. */ |
1013 |
|
1014 |
static enum RM_status |
1015 |
remove_dir (Dirstack_state *ds, char const *dir, struct cwd_state **cwd_state, |
1016 |
struct rm_options const *x) |
1017 |
{ |
1018 |
enum RM_status status; |
1019 |
struct stat dir_sb; |
1020 |
|
1021 |
/* Save any errno (from caller's failed remove_entry call), in case DIR |
1022 |
is not a directory, so that we can give a reasonable diagnostic. */ |
1023 |
int saved_errno = errno; |
1024 |
|
1025 |
if (*cwd_state == NULL) |
1026 |
{ |
1027 |
*cwd_state = xmalloc (sizeof **cwd_state); |
1028 |
|
1029 |
if (save_cwd (&(*cwd_state)->saved_cwd) != 0) |
1030 |
{ |
1031 |
(*cwd_state)->saved_errno = errno; |
1032 |
assert (errno != 0); |
1033 |
|
1034 |
/* Pretend we started from "/". That is fine as long as there |
1035 |
is no requirement to return to the original working directory. |
1036 |
Use "/", not ".", so that we chdir out of a non-root target |
1037 |
directory before attempting to remove it: some hosts don't let |
1038 |
you remove a working directory. */ |
1039 |
(*cwd_state)->saved_cwd.name = xstrdup ("/"); |
1040 |
} |
1041 |
else |
1042 |
(*cwd_state)->saved_errno = 0; |
1043 |
|
1044 |
AD_push_initial (ds); |
1045 |
AD_INIT_OTHER_MEMBERS (); |
1046 |
} |
1047 |
|
1048 |
/* If we've failed to record and/or restore the initial working directory, |
1049 |
and we're now trying to access a `.'-relative file name, then give a |
1050 |
diagnostic, record the failure, and proceed with any subsequent |
1051 |
command-line arguments. */ |
1052 |
if ((*cwd_state)->saved_errno && IS_RELATIVE_FILE_NAME (dir)) |
1053 |
{ |
1054 |
error (0, (*cwd_state)->saved_errno, _("cannot remove directory %s"), |
1055 |
quote (full_filename (dir))); |
1056 |
longjmp (ds->current_arg_jumpbuf, 1); |
1057 |
} |
1058 |
|
1059 |
/* There is a race condition in that an attacker could replace the nonempty |
1060 |
directory, DIR, with a symlink between the preceding call to rmdir |
1061 |
(in our caller) and the chdir below. However, the following lstat, |
1062 |
along with the `stat (".",...' and dev/ino comparison in AD_push |
1063 |
ensure that we detect it and fail. */ |
1064 |
|
1065 |
if (lstat (dir, &dir_sb)) |
1066 |
{ |
1067 |
error (0, errno, |
1068 |
_("cannot lstat %s"), quote (full_filename (dir))); |
1069 |
return RM_ERROR; |
1070 |
} |
1071 |
|
1072 |
errno = ENOTDIR; |
1073 |
if (! S_ISDIR (dir_sb.st_mode) || chdir (dir) != 0) |
1074 |
{ |
1075 |
if (errno == ENOTDIR) |
1076 |
{ |
1077 |
error (0, saved_errno, |
1078 |
_("cannot remove %s"), quote (full_filename (dir))); |
1079 |
} |
1080 |
else |
1081 |
{ |
1082 |
error (0, errno, |
1083 |
_("cannot chdir from %s to %s"), |
1084 |
quote_n (0, full_filename (".")), quote_n (1, dir)); |
1085 |
} |
1086 |
return RM_ERROR; |
1087 |
} |
1088 |
|
1089 |
if (ROOT_DEV_INO_CHECK (x->root_dev_ino, &dir_sb)) |
1090 |
{ |
1091 |
ROOT_DEV_INO_WARN (full_filename (dir)); |
1092 |
return 1; |
1093 |
} |
1094 |
|
1095 |
AD_push (ds, dir, &dir_sb); |
1096 |
AD_INIT_OTHER_MEMBERS (); |
1097 |
|
1098 |
status = RM_OK; |
1099 |
|
1100 |
while (1) |
1101 |
{ |
1102 |
char *subdir = NULL; |
1103 |
struct stat subdir_sb; |
1104 |
enum RM_status tmp_status = remove_cwd_entries (ds, |
1105 |
&subdir, &subdir_sb, x); |
1106 |
if (tmp_status != RM_OK) |
1107 |
{ |
1108 |
UPDATE_STATUS (status, tmp_status); |
1109 |
AD_mark_current_as_unremovable (ds); |
1110 |
} |
1111 |
if (subdir) |
1112 |
{ |
1113 |
AD_push (ds, subdir, &subdir_sb); |
1114 |
AD_INIT_OTHER_MEMBERS (); |
1115 |
|
1116 |
free (subdir); |
1117 |
continue; |
1118 |
} |
1119 |
|
1120 |
/* Execution reaches this point when we've removed the last |
1121 |
removable entry from the current directory. */ |
1122 |
{ |
1123 |
/* This is the name of the directory that we have just |
1124 |
returned from, after nominally removing all of its contents. */ |
1125 |
char *empty_dir; |
1126 |
|
1127 |
AD_pop_and_chdir (ds, &empty_dir, *cwd_state); |
1128 |
|
1129 |
/* Try to remove D only if remove_cwd_entries succeeded. */ |
1130 |
if (tmp_status == RM_OK) |
1131 |
{ |
1132 |
/* This does a little more work than necessary when it actually |
1133 |
prompts the user. E.g., we already know that D is a directory |
1134 |
and that it's almost certainly empty, yet we lstat it. |
1135 |
But that's no big deal since we're interactive. */ |
1136 |
Ternary is_dir; |
1137 |
Ternary is_empty; |
1138 |
enum RM_status s = prompt (ds, empty_dir, x, PA_REMOVE_DIR, |
1139 |
&is_dir, &is_empty); |
1140 |
|
1141 |
if (s != RM_OK) |
1142 |
{ |
1143 |
free (empty_dir); |
1144 |
return s; |
1145 |
} |
1146 |
|
1147 |
if (rmdir (empty_dir) == 0) |
1148 |
{ |
1149 |
if (x->verbose) |
1150 |
printf (_("removed directory: %s\n"), |
1151 |
quote (full_filename (empty_dir))); |
1152 |
} |
1153 |
else |
1154 |
{ |
1155 |
error (0, errno, _("cannot remove directory %s"), |
1156 |
quote (full_filename (empty_dir))); |
1157 |
AD_mark_as_unremovable (ds, empty_dir); |
1158 |
status = RM_ERROR; |
1159 |
UPDATE_STATUS (AD_stack_top(ds)->status, status); |
1160 |
} |
1161 |
} |
1162 |
|
1163 |
free (empty_dir); |
1164 |
|
1165 |
if (AD_stack_height (ds) == 1) |
1166 |
break; |
1167 |
} |
1168 |
} |
1169 |
|
1170 |
return status; |
1171 |
} |
1172 |
|
1173 |
/* Remove the file or directory specified by FILENAME. |
1174 |
Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED if not. |
1175 |
On input, the first time this function is called, CWD_STATE should be |
1176 |
the address of a NULL pointer. Do not modify it for any subsequent calls. |
1177 |
On output, it is either that same NULL pointer or the address of |
1178 |
a malloc'd `struct saved_cwd' that may be freed. */ |
1179 |
|
1180 |
static enum RM_status |
1181 |
rm_1 (Dirstack_state *ds, char const *filename, |
1182 |
struct rm_options const *x, struct cwd_state **cwd_state) |
1183 |
{ |
1184 |
char *base = base_name (filename); |
1185 |
enum RM_status status; |
1186 |
|
1187 |
if (DOT_OR_DOTDOT (base)) |
1188 |
{ |
1189 |
error (0, 0, _("cannot remove `.' or `..'")); |
1190 |
return RM_ERROR; |
1191 |
} |
1192 |
|
1193 |
if (*cwd_state && (*cwd_state)->saved_errno |
1194 |
&& IS_RELATIVE_FILE_NAME (filename)) |
1195 |
{ |
1196 |
error (0, (*cwd_state)->saved_errno, |
1197 |
_("cannot remove %s"), quote (filename)); |
1198 |
return RM_ERROR; |
1199 |
} |
1200 |
|
1201 |
status = remove_entry (ds, filename, x, NULL); |
1202 |
if (status != RM_NONEMPTY_DIR) |
1203 |
return status; |
1204 |
|
1205 |
return remove_dir (ds, filename, cwd_state, x); |
1206 |
} |
1207 |
|
1208 |
/* Remove all files and/or directories specified by N_FILES and FILE. |
1209 |
Apply the options in X. If X->require_restore_cwd is false, then |
1210 |
this function may return RM_OK even though it is unable to restore |
1211 |
the initial working directory. */ |
1212 |
extern enum RM_status |
1213 |
rm (size_t n_files, char const *const *file, struct rm_options const *x) |
1214 |
{ |
1215 |
struct cwd_state *cwd_state = NULL; |
1216 |
Dirstack_state *ds; |
1217 |
|
1218 |
/* Put the following two variables in static storage, so they can't |
1219 |
be clobbered by the potential longjmp into this function. */ |
1220 |
static enum RM_status status = RM_OK; |
1221 |
static size_t i; |
1222 |
|
1223 |
ds = ds_init (); |
1224 |
|
1225 |
for (i = 0; i < n_files; i++) |
1226 |
{ |
1227 |
enum RM_status s; |
1228 |
cycle_check_init (&ds->cycle_check_state); |
1229 |
/* In the event that rm_1->remove_dir->remove_cwd_entries detects |
1230 |
a directory cycle, arrange to fail, give up on this FILE, but |
1231 |
continue on with any other arguments. */ |
1232 |
if (setjmp (ds->current_arg_jumpbuf)) |
1233 |
s = RM_ERROR; |
1234 |
else |
1235 |
s = rm_1 (ds, file[i], x, &cwd_state); |
1236 |
assert (VALID_STATUS (s)); |
1237 |
UPDATE_STATUS (status, s); |
1238 |
} |
1239 |
|
1240 |
if (x->require_restore_cwd && cwd_state && cwd_state->saved_errno != 0) |
1241 |
{ |
1242 |
error (0, cwd_state->saved_errno, |
1243 |
_("cannot restore current working directory")); |
1244 |
status = RM_ERROR; |
1245 |
} |
1246 |
|
1247 |
ds_free (ds); |
1248 |
|
1249 |
if (cwd_state && cwd_state->saved_errno == 0) |
1250 |
free_cwd (&cwd_state->saved_cwd); |
1251 |
|
1252 |
free (cwd_state); |
1253 |
|
1254 |
return status; |
1255 |
} |