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