annotate toys/posix/rm.c @ 1374:739d3fc60f48 draft

Fix rm -rf of chmod 000 directories.
author Rob Landley <rob@landley.net>
date Thu, 03 Jul 2014 18:42:44 -0500
parents dc9e297ae13b
children ffc7f606ce5b
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
1 /* rm.c - remove files
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
2 *
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
3 * Copyright 2012 Rob Landley <rob@landley.net>
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
4 *
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
5 * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
6
748
8947c0d35e58 The Linux kernel "make clean" calls rm -f with no arguments, which apparently is not an error.
Rob Landley <rob@landley.net>
parents: 746
diff changeset
7 USE_RM(NEWTOY(rm, "fiRr[-fi]", TOYFLAG_BIN))
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
8
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
9 config RM
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
10 bool "rm"
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
11 default y
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
12 help
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
13 usage: rm [-fiRr] FILE...
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
14
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
15 Remove each argument from the filesystem.
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
16
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
17 -f force: remove without confirmation, no error if it doesn't exist
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
18 -i interactive: prompt for confirmation
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
19 -rR recursive: remove directory contents
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
20 */
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
21
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
22 #define FOR_rm
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
23 #include "toys.h"
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
24
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
25 static int do_rm(struct dirtree *try)
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
26 {
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
27 int fd = dirtree_parentfd(try), flags = toys.optflags;
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
28 int dir = S_ISDIR(try->st.st_mode), or = 0, using = 0;
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
29
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
30 // Skip . and .. (yes, even explicitly on the command line: posix says to)
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
31 if (!dirtree_notdotdot(try)) return 0;
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
32
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
33 // Intentionally fail non-recursive attempts to remove even an empty dir
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
34 // (via wrong flags to unlinkat) because POSIX says to.
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
35 if (dir && !(flags & (FLAG_r|FLAG_R))) goto skip;
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
36
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
37 // This is either the posix section 2(b) prompt or the section 3 prompt.
746
5caa4035c1c8 essat's flag not to follow symlinks isn't in the system call, and if libc is supposed to implement this as a wrapper uClibc gets it wrong. So use the stat info about symlinks instead. (Doesn't check the parent directory, but if that's read only we can't delete the file anyway so prompting is moot.)
Rob Landley <rob@landley.net>
parents: 739
diff changeset
38 if (!(flags & FLAG_f)
5caa4035c1c8 essat's flag not to follow symlinks isn't in the system call, and if libc is supposed to implement this as a wrapper uClibc gets it wrong. So use the stat info about symlinks instead. (Doesn't check the parent directory, but if that's read only we can't delete the file anyway so prompting is moot.)
Rob Landley <rob@landley.net>
parents: 739
diff changeset
39 && (!S_ISLNK(try->st.st_mode) && faccessat(fd, try->name, W_OK, 0))) or++;
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
40 if (!(dir && try->data == -1) && ((or && isatty(0)) || (flags & FLAG_i))) {
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
41 char *s = dirtree_path(try, 0);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
42 fprintf(stderr, "rm %s%s", or ? "ro " : "", dir ? "dir " : "");
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
43 or = yesno(s, 0);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
44 free(s);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
45 if (!or) goto nodelete;
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
46 }
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
47
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
48 // handle directory recursion
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
49 if (dir) {
1374
739d3fc60f48 Fix rm -rf of chmod 000 directories.
Rob Landley <rob@landley.net>
parents: 1052
diff changeset
50 // Handle chmod 000 directories when -f
739d3fc60f48 Fix rm -rf of chmod 000 directories.
Rob Landley <rob@landley.net>
parents: 1052
diff changeset
51 if (faccessat(fd, try->name, R_OK, AT_SYMLINK_NOFOLLOW)) {
739d3fc60f48 Fix rm -rf of chmod 000 directories.
Rob Landley <rob@landley.net>
parents: 1052
diff changeset
52 if (toys.optflags & FLAG_f) wfchmodat(fd, try->name, 0600);
739d3fc60f48 Fix rm -rf of chmod 000 directories.
Rob Landley <rob@landley.net>
parents: 1052
diff changeset
53 else goto skip;
739d3fc60f48 Fix rm -rf of chmod 000 directories.
Rob Landley <rob@landley.net>
parents: 1052
diff changeset
54 }
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
55 if (try->data != -1) return DIRTREE_COMEAGAIN;
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
56 using = AT_REMOVEDIR;
1046
673fe8f927b0 fix rm to handle "mkdir sub/sub && chmod 007 sub/sub && rm -rf sub".
Rob Landley <rob@landley.net>
parents: 780
diff changeset
57 if (try->symlink) goto skip;
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
58 if (flags & FLAG_i) {
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
59 char *s = dirtree_path(try, 0);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
60 // This is the section 2(d) prompt. (Yes, posix says to prompt twice.)
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
61 fprintf(stderr, "rmdir ");
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
62 or = yesno(s, 0);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
63 free(s);
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
64 if (!or) goto nodelete;
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
65 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
66 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
67
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
68 skip:
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
69 if (unlinkat(fd, try->name, using)) {
1052
dc9e297ae13b Missing typecast in recent rm fix. Oops.
Rob Landley <rob@landley.net>
parents: 1046
diff changeset
70 if (!dir || try->symlink != (char *)2) perror_msg("%s", try->name);
739
451d7e91232e Complicate the rm -i behavior to do what posix specifies.
Rob Landley <rob@landley.net>
parents: 737
diff changeset
71 nodelete:
1046
673fe8f927b0 fix rm to handle "mkdir sub/sub && chmod 007 sub/sub && rm -rf sub".
Rob Landley <rob@landley.net>
parents: 780
diff changeset
72 if (try->parent) try->parent->symlink = (char *)2;
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
73 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
74
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
75 return 0;
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
76 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
77
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
78 void rm_main(void)
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
79 {
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
80 char **s;
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
81
748
8947c0d35e58 The Linux kernel "make clean" calls rm -f with no arguments, which apparently is not an error.
Rob Landley <rob@landley.net>
parents: 746
diff changeset
82 // Can't use <1 in optstring because zero arguments with -f isn't an error
8947c0d35e58 The Linux kernel "make clean" calls rm -f with no arguments, which apparently is not an error.
Rob Landley <rob@landley.net>
parents: 746
diff changeset
83 if (!toys.optc && !(toys.optflags & FLAG_f)) error_exit("Needs 1 argument");
8947c0d35e58 The Linux kernel "make clean" calls rm -f with no arguments, which apparently is not an error.
Rob Landley <rob@landley.net>
parents: 746
diff changeset
84
737
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
85 for (s = toys.optargs; *s; s++) {
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
86 if (!strcmp(*s, "/")) {
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
87 error_msg("rm /. if you mean it");
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
88 continue;
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
89 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
90
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
91 // There's a race here where a file removed between this access and
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
92 // dirtree's stat would report the nonexistence as an error, but that's
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
93 // not a normal "it didn't exist" so I'm ok with it.
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
94 if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT))
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
95 continue;
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
96 dirtree_read(*s, do_rm);
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
97 }
dc4a38a13270 New rm command.
Rob Landley <rob@landley.net>
parents:
diff changeset
98 }