rm.c revision 829497311b541b63e08aa17768c8e67e95b73638
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->data == -1) && ((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 if (try->data != -1) return DIRTREE_COMEAGAIN; 51 using = AT_REMOVEDIR; 52 if (try->symlink) goto skip; 53 if (flags & FLAG_i) { 54 char *s = dirtree_path(try, 0); 55 // This is the section 2(d) prompt. (Yes, posix says to prompt twice.) 56 fprintf(stderr, "rmdir "); 57 or = yesno(s, 0); 58 free(s); 59 if (!or) goto nodelete; 60 } 61 } 62 63skip: 64 if (unlinkat(fd, try->name, using)) { 65 if (!dir || try->symlink != 2) perror_msg("%s", try->name); 66nodelete: 67 if (try->parent) try->parent->symlink = (char *)2; 68 } 69 70 return 0; 71} 72 73void rm_main(void) 74{ 75 char **s; 76 77 // Can't use <1 in optstring because zero arguments with -f isn't an error 78 if (!toys.optc && !(toys.optflags & FLAG_f)) error_exit("Needs 1 argument"); 79 80 for (s = toys.optargs; *s; s++) { 81 if (!strcmp(*s, "/")) { 82 error_msg("rm /. if you mean it"); 83 continue; 84 } 85 86 // There's a race here where a file removed between this access and 87 // dirtree's stat would report the nonexistence as an error, but that's 88 // not a normal "it didn't exist" so I'm ok with it. 89 if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT)) 90 continue; 91 dirtree_read(*s, do_rm); 92 } 93} 94