• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* rm.c - remove files
2  *
3  * Copyright 2012 Rob Landley <rob@landley.net>
4  *
5  * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html
6 
7 USE_RM(NEWTOY(rm, "fiRrv[-fi]", TOYFLAG_BIN))
8 
9 config RM
10   bool "rm"
11   default y
12   help
13     usage: rm [-fiRrv] FILE...
14 
15     Remove each argument from the filesystem.
16 
17     -f	Force: remove without confirmation, no error if it doesn't exist
18     -i	Interactive: prompt for confirmation
19     -rR	Recursive: remove directory contents
20     -v	Verbose
21 */
22 
23 #define FOR_rm
24 #include "toys.h"
25 
do_rm(struct dirtree * try)26 static int do_rm(struct dirtree *try)
27 {
28   int fd=dirtree_parentfd(try), dir=S_ISDIR(try->st.st_mode), or=0, using=0;
29 
30   // Skip . and .. (yes, even explicitly on the command line: posix says to)
31   if (isdotdot(try->name)) return 0;
32 
33   // Intentionally fail non-recursive attempts to remove even an empty dir
34   // (via wrong flags to unlinkat) because POSIX says to.
35   if (dir && !(toys.optflags & (FLAG_r|FLAG_R))) goto skip;
36 
37   // This is either the posix section 2(b) prompt or the section 3 prompt.
38   if (!FLAG(f)
39     && (!S_ISLNK(try->st.st_mode) && faccessat(fd, try->name, W_OK, 0))) or++;
40   if (!(dir && try->again) && ((or && isatty(0)) || FLAG(i))) {
41     char *s = dirtree_path(try, 0);
42 
43     fprintf(stderr, "rm %s%s%s", or ? "ro " : "", dir ? "dir " : "", s);
44     free(s);
45     or = yesno(0);
46     if (!or) goto nodelete;
47   }
48 
49   // handle directory recursion
50   if (dir) {
51     using = AT_REMOVEDIR;
52     // Handle chmod 000 directories when -f
53     if (faccessat(fd, try->name, R_OK, 0)) {
54       if (FLAG(f)) wfchmodat(fd, try->name, 0700);
55       else goto skip;
56     }
57     if (!try->again) return DIRTREE_COMEAGAIN;
58     if (try->symlink) goto skip;
59     if (FLAG(i)) {
60       char *s = dirtree_path(try, 0);
61 
62       // This is the section 2(d) prompt. (Yes, posix says to prompt twice.)
63       fprintf(stderr, "rmdir %s", s);
64       free(s);
65       or = yesno(0);
66       if (!or) goto nodelete;
67     }
68   }
69 
70 skip:
71   if (!unlinkat(fd, try->name, using)) {
72     if (FLAG(v)) {
73       char *s = dirtree_path(try, 0);
74       printf("%s%s '%s'\n", toys.which->name, dir ? "dir" : "", s);
75       free(s);
76     }
77   } else {
78     if (!dir || try->symlink != (char *)2) perror_msg_raw(try->name);
79 nodelete:
80     if (try->parent) try->parent->symlink = (char *)2;
81   }
82 
83   return 0;
84 }
85 
rm_main(void)86 void rm_main(void)
87 {
88   char **s;
89 
90   // Can't use <1 in optstring because zero arguments with -f isn't an error
91   if (!toys.optc && !FLAG(f)) help_exit("Needs 1 argument");
92 
93   for (s = toys.optargs; *s; s++) {
94     if (!strcmp(*s, "/")) {
95       error_msg("rm /. if you mean it");
96       continue;
97     }
98 
99     // Files that already don't exist aren't errors for -f, so try a quick
100     // unlink now to see if it succeeds or reports that it didn't exist.
101     if (FLAG(f) && (!unlink(*s) || errno == ENOENT)) continue;
102 
103     // There's a race here where a file removed between the above check and
104     // dirtree's stat would report the nonexistence as an error, but that's
105     // not a normal "it didn't exist" so I'm ok with it.
106 
107     dirtree_read(*s, do_rm);
108   }
109 }
110