Mercurial > hg > toybox
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 |
rev | line source |
---|---|
737 | 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 | |
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 | 8 |
9 config 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 | |
25 static int do_rm(struct dirtree *try) | |
26 { | |
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 | 29 |
30 // Skip . and .. (yes, even explicitly on the command line: posix says to) | |
31 if (!dirtree_notdotdot(try)) return 0; | |
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 | 65 } |
66 } | |
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 | 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 | 73 } |
74 | |
75 return 0; | |
76 } | |
77 | |
78 void rm_main(void) | |
79 { | |
80 char **s; | |
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 | 85 for (s = toys.optargs; *s; s++) { |
86 if (!strcmp(*s, "/")) { | |
87 error_msg("rm /. if you mean it"); | |
88 continue; | |
89 } | |
90 | |
91 // There's a race here where a file removed between this access and | |
92 // dirtree's stat would report the nonexistence as an error, but that's | |
93 // not a normal "it didn't exist" so I'm ok with it. | |
94 if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT)) | |
95 continue; | |
96 dirtree_read(*s, do_rm); | |
97 } | |
98 } |