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 7USE_RM(NEWTOY(rm, "fiRr[-fi]", TOYFLAG_BIN)) 8 9config RM 10 bool "rm" 11 default y 12 help 13 usage: rm [-fiRr] 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*/ 21 22#define FOR_rm 23#include "toys.h" 24 25static int do_rm(struct dirtree *try) 26{ 27 int fd = dirtree_parentfd(try), flags = toys.optflags; 28 int 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 (!dirtree_notdotdot(try)) 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 && !(flags & (FLAG_r|FLAG_R))) goto skip; 36 37 // This is either the posix section 2(b) prompt or the section 3 prompt. 38 if (!(flags & 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)) || (flags & FLAG_i))) { 41 char *s = dirtree_path(try, 0); 42 fprintf(stderr, "rm %s%s", or ? "ro " : "", dir ? "dir " : ""); 43 or = yesno(s, 0); 44 free(s); 45 if (!or) goto nodelete; 46 } 47 48 // handle directory recursion 49 if (dir) { 50 using = AT_REMOVEDIR; 51 // Handle chmod 000 directories when -f 52 if (faccessat(fd, try->name, R_OK, 0)) { 53 if (toys.optflags & FLAG_f) wfchmodat(fd, try->name, 0700); 54 else goto skip; 55 } 56 if (!try->again) return DIRTREE_COMEAGAIN; 57 if (try->symlink) goto skip; 58 if (flags & FLAG_i) { 59 char *s = dirtree_path(try, 0); 60 // This is the section 2(d) prompt. (Yes, posix says to prompt twice.) 61 fprintf(stderr, "rmdir "); 62 or = yesno(s, 0); 63 free(s); 64 if (!or) goto nodelete; 65 } 66 } 67 68skip: 69 if (unlinkat(fd, try->name, using)) { 70 if (!dir || try->symlink != (char *)2) perror_msg("%s", try->name); 71nodelete: 72 if (try->parent) try->parent->symlink = (char *)2; 73 } 74 75 return 0; 76} 77 78void rm_main(void) 79{ 80 char **s; 81 82 // Can't use <1 in optstring because zero arguments with -f isn't an error 83 if (!toys.optc && !(toys.optflags & FLAG_f)) error_exit("Needs 1 argument"); 84 85 for (s = toys.optargs; *s; s++) { 86 if (!strcmp(*s, "/")) { 87 error_msg("rm /. if you mean it"); 88 continue; 89 } 90 91 // Files that already don't exist aren't errors for -f, so try a quick 92 // unlink now to see if it succeeds or reports that it didn't exist. 93 if ((toys.optflags & FLAG_f) && (!unlink(*s) || errno == ENOENT)) 94 continue; 95 96 // There's a race here where a file removed between the above check and 97 // dirtree's stat would report the nonexistence as an error, but that's 98 // not a normal "it didn't exist" so I'm ok with it. 99 100 dirtree_read(*s, do_rm); 101 } 102} 103