1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 1988-2023 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 3 of the License, or
7 (at your option) 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, see <https://www.gnu.org/licenses/>. */
16
17 /* Extracted from rm.c, librarified, then rewritten twice by Jim Meyering. */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22
23 #include "system.h"
24 #include "assure.h"
25 #include "file-type.h"
26 #include "filenamecat.h"
27 #include "ignore-value.h"
28 #include "remove.h"
29 #include "root-dev-ino.h"
30 #include "stat-time.h"
31 #include "write-any-file.h"
32 #include "xfts.h"
33 #include "yesno.h"
34
35 /* The prompt function may be called twice for a given directory.
36 The first time, we ask whether to descend into it, and the
37 second time, we ask whether to remove it. */
38 enum Prompt_action
39 {
40 PA_DESCEND_INTO_DIR = 2,
41 PA_REMOVE_DIR
42 };
43
44 /* D_TYPE(D) is the type of directory entry D if known, DT_UNKNOWN
45 otherwise. */
46 #if ! HAVE_STRUCT_DIRENT_D_TYPE
47 /* Any int values will do here, so long as they're distinct.
48 Undef any existing macros out of the way. */
49 # undef DT_UNKNOWN
50 # undef DT_DIR
51 # undef DT_LNK
52 # define DT_UNKNOWN 0
53 # define DT_DIR 1
54 # define DT_LNK 2
55 #endif
56
57 /* Like fstatat, but cache on POSIX-compatible systems. */
58 static int
cache_fstatat(int fd,char const * file,struct stat * st,int flag)59 cache_fstatat (int fd, char const *file, struct stat *st, int flag)
60 {
61 #if HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
62 /* If ST->st_atim.tv_nsec is -1, the status has not been gotten yet.
63 If less than -1, fstatat failed with errno == ST->st_ino.
64 Otherwise, the status has already been gotten, so return 0. */
65 if (0 <= st->st_atim.tv_nsec)
66 return 0;
67 if (st->st_atim.tv_nsec == -1)
68 {
69 if (fstatat (fd, file, st, flag) == 0)
70 return 0;
71 st->st_atim.tv_nsec = -2;
72 st->st_ino = errno;
73 }
74 errno = st->st_ino;
75 return -1;
76 #else
77 return fstatat (fd, file, st, flag);
78 #endif
79 }
80
81 /* Initialize a fstatat cache *ST. Return ST for convenience. */
82 static inline struct stat *
cache_stat_init(struct stat * st)83 cache_stat_init (struct stat *st)
84 {
85 #if HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC
86 st->st_atim.tv_nsec = -1;
87 #endif
88 return st;
89 }
90
91 /* Return 1 if FILE is an unwritable non-symlink,
92 0 if it is writable or some other type of file,
93 -1 and set errno if there is some problem in determining the answer.
94 Set *BUF to the file status. */
95 static int
write_protected_non_symlink(int fd_cwd,char const * file,struct stat * buf)96 write_protected_non_symlink (int fd_cwd,
97 char const *file,
98 struct stat *buf)
99 {
100 if (can_write_any_file ())
101 return 0;
102 if (cache_fstatat (fd_cwd, file, buf, AT_SYMLINK_NOFOLLOW) != 0)
103 return -1;
104 if (S_ISLNK (buf->st_mode))
105 return 0;
106 /* Here, we know FILE is not a symbolic link. */
107
108 /* In order to be reentrant -- i.e., to avoid changing the working
109 directory, and at the same time to be able to deal with alternate
110 access control mechanisms (ACLs, xattr-style attributes) and
111 arbitrarily deep trees -- we need a function like eaccessat, i.e.,
112 like Solaris' eaccess, but fd-relative, in the spirit of openat. */
113
114 /* In the absence of a native eaccessat function, here are some of
115 the implementation choices [#4 and #5 were suggested by Paul Eggert]:
116 1) call openat with O_WRONLY|O_NOCTTY
117 Disadvantage: may create the file and doesn't work for directory,
118 may mistakenly report 'unwritable' for EROFS or ACLs even though
119 perm bits say the file is writable.
120
121 2) fake eaccessat (save_cwd, fchdir, call euidaccess, restore_cwd)
122 Disadvantage: changes working directory (not reentrant) and can't
123 work if save_cwd fails.
124
125 3) if (euidaccess (full_name, W_OK) == 0)
126 Disadvantage: doesn't work if full_name is too long.
127 Inefficient for very deep trees (O(Depth^2)).
128
129 4) If the full pathname is sufficiently short (say, less than
130 PATH_MAX or 8192 bytes, whichever is shorter):
131 use method (3) (i.e., euidaccess (full_name, W_OK));
132 Otherwise: vfork, fchdir in the child, run euidaccess in the
133 child, then the child exits with a status that tells the parent
134 whether euidaccess succeeded.
135
136 This avoids the O(N**2) algorithm of method (3), and it also avoids
137 the failure-due-to-too-long-file-names of method (3), but it's fast
138 in the normal shallow case. It also avoids the lack-of-reentrancy
139 and the save_cwd problems.
140 Disadvantage; it uses a process slot for very-long file names,
141 and would be very slow for hierarchies with many such files.
142
143 5) If the full file name is sufficiently short (say, less than
144 PATH_MAX or 8192 bytes, whichever is shorter):
145 use method (3) (i.e., euidaccess (full_name, W_OK));
146 Otherwise: look just at the file bits. Perhaps issue a warning
147 the first time this occurs.
148
149 This is like (4), except for the "Otherwise" case where it isn't as
150 "perfect" as (4) but is considerably faster. It conforms to current
151 POSIX, and is uniformly better than what Solaris and FreeBSD do (they
152 mess up with long file names). */
153
154 {
155 if (faccessat (fd_cwd, file, W_OK, AT_EACCESS) == 0)
156 return 0;
157
158 return errno == EACCES ? 1 : -1;
159 }
160 }
161
162 /* Return the status of the directory identified by FTS and ENT.
163 This is -1 if the directory is empty, 0 if it is nonempty,
164 and a positive error number if there was trouble determining the status,
165 e.g., it is not a directory, or permissions problems, or I/O errors.
166 Use *DIR_STATUS as a cache for the status. */
167 static int
get_dir_status(FTS const * fts,FTSENT const * ent,int * dir_status)168 get_dir_status (FTS const *fts, FTSENT const *ent, int *dir_status)
169 {
170 if (*dir_status == DS_UNKNOWN)
171 *dir_status = directory_status (fts->fts_cwd_fd, ent->fts_accpath);
172 return *dir_status;
173 }
174
175 /* Prompt whether to remove FILENAME, if required via a combination of
176 the options specified by X and/or file attributes. If the file may
177 be removed, return RM_OK or RM_USER_ACCEPTED, the latter if the user
178 was prompted and accepted. If the user declines to remove the file,
179 return RM_USER_DECLINED. If not ignoring missing files and we
180 cannot lstat FILENAME, then return RM_ERROR.
181
182 IS_DIR is true if ENT designates a directory, false otherwise.
183
184 Depending on MODE, ask whether to 'descend into' or to 'remove' the
185 directory FILENAME. MODE is ignored when FILENAME is not a directory.
186 Use and update *DIR_STATUS as needed, via the conventions of
187 get_dir_status. */
188 static enum RM_status
prompt(FTS const * fts,FTSENT const * ent,bool is_dir,struct rm_options const * x,enum Prompt_action mode,int * dir_status)189 prompt (FTS const *fts, FTSENT const *ent, bool is_dir,
190 struct rm_options const *x, enum Prompt_action mode,
191 int *dir_status)
192 {
193 int fd_cwd = fts->fts_cwd_fd;
194 char const *full_name = ent->fts_path;
195 char const *filename = ent->fts_accpath;
196 struct stat st;
197 struct stat *sbuf = &st;
198 cache_stat_init (sbuf);
199
200 int dirent_type = is_dir ? DT_DIR : DT_UNKNOWN;
201 int write_protected = 0;
202
203 /* When nonzero, this indicates that we failed to remove a child entry,
204 either because the user declined an interactive prompt, or due to
205 some other failure, like permissions. */
206 if (ent->fts_number)
207 return RM_USER_DECLINED;
208
209 if (x->interactive == RMI_NEVER)
210 return RM_OK;
211
212 int wp_errno = 0;
213 if (!x->ignore_missing_files
214 && (x->interactive == RMI_ALWAYS || x->stdin_tty)
215 && dirent_type != DT_LNK)
216 {
217 write_protected = write_protected_non_symlink (fd_cwd, filename, sbuf);
218 wp_errno = errno;
219 }
220
221 if (write_protected || x->interactive == RMI_ALWAYS)
222 {
223 if (0 <= write_protected && dirent_type == DT_UNKNOWN)
224 {
225 if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) == 0)
226 {
227 if (S_ISLNK (sbuf->st_mode))
228 dirent_type = DT_LNK;
229 else if (S_ISDIR (sbuf->st_mode))
230 dirent_type = DT_DIR;
231 /* Otherwise it doesn't matter, so leave it DT_UNKNOWN. */
232 }
233 else
234 {
235 /* This happens, e.g., with 'rm '''. */
236 write_protected = -1;
237 wp_errno = errno;
238 }
239 }
240
241 if (0 <= write_protected)
242 switch (dirent_type)
243 {
244 case DT_LNK:
245 /* Using permissions doesn't make sense for symlinks. */
246 if (x->interactive != RMI_ALWAYS)
247 return RM_OK;
248 break;
249
250 case DT_DIR:
251 /* Unless we're either deleting directories or deleting
252 recursively, we want to raise an EISDIR error rather than
253 prompting the user */
254 if ( ! (x->recursive
255 || (x->remove_empty_directories
256 && get_dir_status (fts, ent, dir_status) != 0)))
257 {
258 write_protected = -1;
259 wp_errno = *dir_status <= 0 ? EISDIR : *dir_status;
260 }
261 break;
262 }
263
264 char const *quoted_name = quoteaf (full_name);
265
266 if (write_protected < 0)
267 {
268 error (0, wp_errno, _("cannot remove %s"), quoted_name);
269 return RM_ERROR;
270 }
271
272 /* Issue the prompt. */
273 if (dirent_type == DT_DIR
274 && mode == PA_DESCEND_INTO_DIR
275 && get_dir_status (fts, ent, dir_status) == DS_NONEMPTY)
276 fprintf (stderr,
277 (write_protected
278 ? _("%s: descend into write-protected directory %s? ")
279 : _("%s: descend into directory %s? ")),
280 program_name, quoted_name);
281 else if (0 < *dir_status)
282 {
283 if ( ! (x->remove_empty_directories && *dir_status == EACCES))
284 {
285 error (0, *dir_status, _("cannot remove %s"), quoted_name);
286 return RM_ERROR;
287 }
288
289 /* The following code can lead to a successful deletion only with
290 the --dir (-d) option (remove_empty_directories) and an empty
291 inaccessible directory. In the first prompt call for a directory,
292 we'd normally ask whether to descend into it, but in this case
293 (it's inaccessible), that is not possible, so don't prompt. */
294 if (mode == PA_DESCEND_INTO_DIR)
295 return RM_OK;
296
297 fprintf (stderr,
298 _("%s: attempt removal of inaccessible directory %s? "),
299 program_name, quoted_name);
300 }
301 else
302 {
303 if (cache_fstatat (fd_cwd, filename, sbuf, AT_SYMLINK_NOFOLLOW) != 0)
304 {
305 error (0, errno, _("cannot remove %s"), quoted_name);
306 return RM_ERROR;
307 }
308
309 fprintf (stderr,
310 (write_protected
311 /* TRANSLATORS: In the next two strings the second %s is
312 replaced by the type of the file. To avoid grammatical
313 problems, it may be more convenient to translate these
314 strings instead as: "%1$s: %3$s is write-protected and
315 is of type '%2$s' -- remove it? ". */
316 ? _("%s: remove write-protected %s %s? ")
317 : _("%s: remove %s %s? ")),
318 program_name, file_type (sbuf), quoted_name);
319 }
320
321 return yesno () ? RM_USER_ACCEPTED : RM_USER_DECLINED;
322 }
323 return RM_OK;
324 }
325
326 /* When a function like unlink, rmdir, or fstatat fails with an errno
327 value of ERRNUM, return true if the specified file system object
328 is guaranteed not to exist; otherwise, return false. */
329 static inline bool
nonexistent_file_errno(int errnum)330 nonexistent_file_errno (int errnum)
331 {
332 /* Do not include ELOOP here, since the specified file may indeed
333 exist, but be (in)accessible only via too long a symlink chain.
334 Likewise for ENAMETOOLONG, since rm -f ./././.../foo may fail
335 if the "..." part expands to a long enough sequence of "./"s,
336 even though ./foo does indeed exist.
337
338 Another case to consider is when a particular name is invalid for
339 a given file system. In 2011, smbfs returns EINVAL, but the next
340 revision of POSIX will require EILSEQ for that situation:
341 https://austingroupbugs.net/view.php?id=293
342 */
343
344 switch (errnum)
345 {
346 case EILSEQ:
347 case EINVAL:
348 case ENOENT:
349 case ENOTDIR:
350 return true;
351 default:
352 return false;
353 }
354 }
355
356 /* Encapsulate the test for whether the errno value, ERRNUM, is ignorable. */
357 static inline bool
ignorable_missing(struct rm_options const * x,int errnum)358 ignorable_missing (struct rm_options const *x, int errnum)
359 {
360 return x->ignore_missing_files && nonexistent_file_errno (errnum);
361 }
362
363 /* Tell fts not to traverse into the hierarchy at ENT. */
364 static void
fts_skip_tree(FTS * fts,FTSENT * ent)365 fts_skip_tree (FTS *fts, FTSENT *ent)
366 {
367 fts_set (fts, ent, FTS_SKIP);
368 /* Ensure that we do not process ENT a second time. */
369 ignore_value (fts_read (fts));
370 }
371
372 /* Upon unlink failure, or when the user declines to remove ENT, mark
373 each of its ancestor directories, so that we know not to prompt for
374 its removal. */
375 static void
mark_ancestor_dirs(FTSENT * ent)376 mark_ancestor_dirs (FTSENT *ent)
377 {
378 FTSENT *p;
379 for (p = ent->fts_parent; FTS_ROOTLEVEL <= p->fts_level; p = p->fts_parent)
380 {
381 if (p->fts_number)
382 break;
383 p->fts_number = 1;
384 }
385 }
386
387 /* Remove the file system object specified by ENT. IS_DIR specifies
388 whether it is expected to be a directory or non-directory.
389 Return RM_OK upon success, else RM_ERROR. */
390 static enum RM_status
excise(FTS * fts,FTSENT * ent,struct rm_options const * x,bool is_dir)391 excise (FTS *fts, FTSENT *ent, struct rm_options const *x, bool is_dir)
392 {
393 int flag = is_dir ? AT_REMOVEDIR : 0;
394 if (unlinkat (fts->fts_cwd_fd, ent->fts_accpath, flag) == 0)
395 {
396 if (x->verbose)
397 {
398 printf ((is_dir
399 ? _("removed directory %s\n")
400 : _("removed %s\n")), quoteaf (ent->fts_path));
401 }
402 return RM_OK;
403 }
404
405 /* The unlinkat from kernels like linux-2.6.32 reports EROFS even for
406 nonexistent files. When the file is indeed missing, map that to ENOENT,
407 so that rm -f ignores it, as required. Even without -f, this is useful
408 because it makes rm print the more precise diagnostic. */
409 if (errno == EROFS)
410 {
411 struct stat st;
412 if ( ! (fstatat (fts->fts_cwd_fd, ent->fts_accpath, &st,
413 AT_SYMLINK_NOFOLLOW)
414 && errno == ENOENT))
415 errno = EROFS;
416 }
417
418 if (ignorable_missing (x, errno))
419 return RM_OK;
420
421 /* When failing to rmdir an unreadable directory, we see errno values
422 like EISDIR or ENOTDIR (or, on Solaris 10, EEXIST), but they would be
423 meaningless in a diagnostic. When that happens, use the earlier, more
424 descriptive errno value. */
425 if (ent->fts_info == FTS_DNR
426 && (errno == ENOTEMPTY || errno == EISDIR || errno == ENOTDIR
427 || errno == EEXIST)
428 && ent->fts_errno != 0)
429 errno = ent->fts_errno;
430 error (0, errno, _("cannot remove %s"), quoteaf (ent->fts_path));
431 mark_ancestor_dirs (ent);
432 return RM_ERROR;
433 }
434
435 /* This function is called once for every file system object that fts
436 encounters. fts performs a depth-first traversal.
437 A directory is usually processed twice, first with fts_info == FTS_D,
438 and later, after all of its entries have been processed, with FTS_DP.
439 Return RM_ERROR upon error, RM_USER_DECLINED for a negative response
440 to an interactive prompt, and otherwise, RM_OK. */
441 static enum RM_status
rm_fts(FTS * fts,FTSENT * ent,struct rm_options const * x)442 rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
443 {
444 int dir_status = DS_UNKNOWN;
445
446 switch (ent->fts_info)
447 {
448 case FTS_D: /* preorder directory */
449 if (! x->recursive
450 && !(x->remove_empty_directories
451 && get_dir_status (fts, ent, &dir_status) != 0))
452 {
453 /* This is the first (pre-order) encounter with a directory
454 that we cannot delete.
455 Not recursive, and it's not an empty directory (if we're removing
456 them) so arrange to skip contents. */
457 int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR;
458 error (0, err, _("cannot remove %s"), quoteaf (ent->fts_path));
459 mark_ancestor_dirs (ent);
460 fts_skip_tree (fts, ent);
461 return RM_ERROR;
462 }
463
464 /* Perform checks that can apply only for command-line arguments. */
465 if (ent->fts_level == FTS_ROOTLEVEL)
466 {
467 /* POSIX says:
468 If the basename of a command line argument is "." or "..",
469 diagnose it and do nothing more with that argument. */
470 if (dot_or_dotdot (last_component (ent->fts_accpath)))
471 {
472 error (0, 0,
473 _("refusing to remove %s or %s directory: skipping %s"),
474 quoteaf_n (0, "."), quoteaf_n (1, ".."),
475 quoteaf_n (2, ent->fts_path));
476 fts_skip_tree (fts, ent);
477 return RM_ERROR;
478 }
479
480 /* POSIX also says:
481 If a command line argument resolves to "/" (and --preserve-root
482 is in effect -- default) diagnose and skip it. */
483 if (ROOT_DEV_INO_CHECK (x->root_dev_ino, ent->fts_statp))
484 {
485 ROOT_DEV_INO_WARN (ent->fts_path);
486 fts_skip_tree (fts, ent);
487 return RM_ERROR;
488 }
489
490 /* If a command line argument is a mount point and
491 --preserve-root=all is in effect, diagnose and skip it.
492 This doesn't handle "/", but that's handled above. */
493 if (x->preserve_all_root)
494 {
495 bool failed = false;
496 char *parent = file_name_concat (ent->fts_accpath, "..", nullptr);
497 struct stat statbuf;
498
499 if (!parent || lstat (parent, &statbuf))
500 {
501 error (0, 0,
502 _("failed to stat %s: skipping %s"),
503 quoteaf_n (0, parent),
504 quoteaf_n (1, ent->fts_accpath));
505 failed = true;
506 }
507
508 free (parent);
509
510 if (failed || fts->fts_dev != statbuf.st_dev)
511 {
512 if (! failed)
513 {
514 error (0, 0,
515 _("skipping %s, since it's on a different device"),
516 quoteaf (ent->fts_path));
517 error (0, 0, _("and --preserve-root=all is in effect"));
518 }
519 fts_skip_tree (fts, ent);
520 return RM_ERROR;
521 }
522 }
523 }
524
525 {
526 enum RM_status s = prompt (fts, ent, true /*is_dir*/, x,
527 PA_DESCEND_INTO_DIR, &dir_status);
528
529 if (s == RM_USER_ACCEPTED && dir_status == DS_EMPTY)
530 {
531 /* When we know (from prompt when in interactive mode)
532 that this is an empty directory, don't prompt twice. */
533 s = excise (fts, ent, x, true);
534 if (s == RM_OK)
535 fts_skip_tree (fts, ent);
536 }
537
538 if (! (s == RM_OK || s == RM_USER_ACCEPTED))
539 {
540 mark_ancestor_dirs (ent);
541 fts_skip_tree (fts, ent);
542 }
543
544 return s;
545 }
546
547 case FTS_F: /* regular file */
548 case FTS_NS: /* stat(2) failed */
549 case FTS_SL: /* symbolic link */
550 case FTS_SLNONE: /* symbolic link without target */
551 case FTS_DP: /* postorder directory */
552 case FTS_DNR: /* unreadable directory */
553 case FTS_NSOK: /* e.g., dangling symlink */
554 case FTS_DEFAULT: /* none of the above */
555 {
556 /* With --one-file-system, do not attempt to remove a mount point.
557 fts' FTS_XDEV ensures that we don't process any entries under
558 the mount point. */
559 if (ent->fts_info == FTS_DP
560 && x->one_file_system
561 && FTS_ROOTLEVEL < ent->fts_level
562 && ent->fts_statp->st_dev != fts->fts_dev)
563 {
564 mark_ancestor_dirs (ent);
565 error (0, 0, _("skipping %s, since it's on a different device"),
566 quoteaf (ent->fts_path));
567 return RM_ERROR;
568 }
569
570 bool is_dir = ent->fts_info == FTS_DP || ent->fts_info == FTS_DNR;
571 enum RM_status s = prompt (fts, ent, is_dir, x, PA_REMOVE_DIR,
572 &dir_status);
573 if (! (s == RM_OK || s == RM_USER_ACCEPTED))
574 return s;
575 return excise (fts, ent, x, is_dir);
576 }
577
578 case FTS_DC: /* directory that causes cycles */
579 emit_cycle_warning (ent->fts_path);
580 fts_skip_tree (fts, ent);
581 return RM_ERROR;
582
583 case FTS_ERR:
584 /* Various failures, from opendir to ENOMEM, to failure to "return"
585 to preceding directory, can provoke this. */
586 error (0, ent->fts_errno, _("traversal failed: %s"),
587 quotef (ent->fts_path));
588 fts_skip_tree (fts, ent);
589 return RM_ERROR;
590
591 default:
592 error (0, 0, _("unexpected failure: fts_info=%d: %s\n"
593 "please report to %s"),
594 ent->fts_info,
595 quotef (ent->fts_path),
596 PACKAGE_BUGREPORT);
597 abort ();
598 }
599 }
600
601 /* Remove FILEs, honoring options specified via X.
602 Return RM_OK if successful. */
603 enum RM_status
rm(char * const * file,struct rm_options const * x)604 rm (char *const *file, struct rm_options const *x)
605 {
606 enum RM_status rm_status = RM_OK;
607
608 if (*file)
609 {
610 int bit_flags = (FTS_CWDFD
611 | FTS_NOSTAT
612 | FTS_PHYSICAL);
613
614 if (x->one_file_system)
615 bit_flags |= FTS_XDEV;
616
617 FTS *fts = xfts_open (file, bit_flags, nullptr);
618
619 while (true)
620 {
621 FTSENT *ent;
622
623 ent = fts_read (fts);
624 if (ent == nullptr)
625 {
626 if (errno != 0)
627 {
628 error (0, errno, _("fts_read failed"));
629 rm_status = RM_ERROR;
630 }
631 break;
632 }
633
634 enum RM_status s = rm_fts (fts, ent, x);
635
636 affirm (VALID_STATUS (s));
637 UPDATE_STATUS (rm_status, s);
638 }
639
640 if (fts_close (fts) != 0)
641 {
642 error (0, errno, _("fts_close failed"));
643 rm_status = RM_ERROR;
644 }
645 }
646
647 return rm_status;
648 }
649