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