view rm-limit.pl @ 4:21f705b2b8c9

Fixed a couple of comments.
author Eris Caffee <discordia@eldalin.com>
date Wed, 18 May 2011 02:22:19 -0500
parents 58e218e2b4ac
children e17c757389d7
line source
1 #!/usr/bin/env perl
3 ################################################################################
4 #
5 # Copyright (C) 2011 Sarah Eris Horsley Caffee
6 #
7 # This is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #
20 ################################################################################
21 #
22 # A limited rm wrapper.
23 #
24 # This scripts has 3 lists:
25 # A blacklist of directories from which this script will absolutely refuse
26 # to delete anything. Individual files may be listed too.
27 # A whitelist of directories from which deletions are always allowed.
28 # Individual files may be listed too.
29 # A whitelist of directories from which deletions are always allowed only if
30 # they occur in subdirectories of the listed main directory. Only
31 # directories may be listed. No individual files allowed here.
32 #
33 # Any file not specified as whitelisted or blacklisted will generate a
34 # warning prompt and offer the user a chance to cancel the deletion.
35 #
36 # The purpose is to help prevent accidental deletion of important system files.
37 #
38 # To use this, install this script somewhere in your path and add something
39 # like the following to your default login scripts, such as the .bash_profile
40 # file of the root user.
41 #
42 # alias | grep -q "alias rm="
43 # if [ $? -eq 0 ] ; then
44 # RM_Opts=$(alias | awk '/alias rm=/ { sub(/^rm /, "", $2); print $2}' FS="'")
45 # fi
46 #
47 # unalias rm 2> /dev/null
48 # alias rm="rm-limit.pl ${RM_Opts}"
49 #
50 # By installing this as an alias for rm that is set up in .bash_profile, it will
51 # only be active during interactive logins, and not when scripts are running.
52 #
54 # Last update: 2011/05/18 02:21:42
56 use strict;
57 use warnings;
59 use Cwd 'realpath';
60 use File::Basename;
63 ################################################################################
64 #
65 # Note: / itself is protected by default. You are not allowed to delete the
66 # entire filesystem using this script no matter what.
67 #
68 # Protecting or exposing a directory affects all subdirectories underneath it.
69 #
70 # The whitelist consists of directories from which we may always delete.
72 my @whitelist = (
73 '/tmp',
74 );
76 # whitelist_subdirs lists directories from which it is safe to delete only if
77 # we are deleting from a subdirectory of the listed directory.
78 # The purpose of this is to let us delete with impunity from user home directories
79 # but guard against accidental deletion of multiple home directories in their
80 # entirety.
82 my @whitelist_subdirs = (
83 '/home',
84 );
86 # The blacklist consists of directories that we must never delete from
87 # under any circumstances. To delete from these directories the user must
88 # invoke the /bin/rm command directly.
90 my @blacklist = (
91 '/bin/',
92 '/boot',
93 '/etc',
94 '/lib/',
95 '/lib64',
96 '/sbin',
97 );
99 ################################################################################
101 my $debug = 0;
103 my $rm="/bin/rm";
104 my $echo="/bin/echo";
107 my $proceed = "yes";
108 my $fail = 0;
109 my $file = 0;
110 my $opts = "";
113 setup_lists();
116 my $path = "";
117 foreach (@ARGV) {
118 $path = $_;
119 if ($path !~ /^-/) {
120 if (check_whitelist($path) ) {
121 next;
122 }
124 if (check_whitelist_subdirs($path) ) {
125 next;
126 }
128 $proceed = "no";
130 if (check_blacklist($path)) {
131 $fail = 1;
132 }
133 }
134 }
136 if ($fail) {
137 print("File deletion aborted because blacklisted files were detected in the file list.
138 If you truly need to delete the files, please call /bin/rm directly.
140 ");
141 exit 1;
142 }
146 if ($proceed ne "yes") {
147 print("
149 ============ WARNING! ========== WARNING! ========== WARNING! =================
151 You are about to delete files or directories that are not in the whitelist of
152 safe locations from which to delete. Please review the rm command for any
153 typos before proceeding.
155 Type \"yes\" to continue.
158 ");
159 $proceed = <STDIN>;
160 }
162 chomp($proceed);
163 if ($proceed eq "yes"){
164 if ($debug) {
165 exec($echo, ($rm, @ARGV));
166 } else {
167 exec($rm, @ARGV);
168 }
169 }
171 ################################################################################
172 # Expand to full paths, append / to ends of directories.
173 # Returns empty string if the specified file does not exist.
175 sub normalize_name {
176 my $path = $_;
177 my $normal_path = "";
179 if (-l $path) {
180 $path = readlink($_);
181 }
183 $normal_path = realpath($path);
184 if (!defined $normal_path) {
185 return "";
186 }
188 if ((-d $normal_path) and ($normal_path !~ m{/$} )) {
189 $normal_path = $normal_path."/";
190 }
192 return $normal_path;
193 }
195 ################################################################################
196 # Return true if on whitelist
198 sub check_whitelist {
199 my $regex = undef;
200 my $path = normalize_name($_);
201 for (my $i = 0; $i <= $#whitelist; $i += 1) {
202 $regex = "^".quotemeta($whitelist[$i]);
203 if ($whitelist[$i] !~ m{/$}) {
204 $regex = $regex."$$";
205 }
206 ($debug) and print("regex is $regex\n");
207 if (($path =~ $regex) and ($path ne $whitelist[$i])) {
208 $debug and print("Whitelisted for being in $whitelist[$i]: $_\n");
209 return 1;
210 }
211 }
213 return 0;
214 }
216 ################################################################################
217 # Return true if on whitelist_subdirs
219 sub check_whitelist_subdirs {
220 my $regex = undef;
221 my $path = normalize_name($_);
222 for (my $i = 0; $i <= $#whitelist_subdirs; $i += 1) {
223 $regex = "^".quotemeta($whitelist_subdirs[$i]);
224 ($debug) and print("regex is $regex\n");
225 if (($path =~ $regex) and ($path ne $whitelist_subdirs[$i])) {
226 $regex = $regex.".*/.+";
227 ($debug) and print("new regex is $regex\n");
228 if ($path =~ $regex) {
229 $debug and print("Whitelisted for being subdir of $whitelist_subdirs[$i]: $_\n");
230 return 1;
231 }
232 }
233 }
235 return 0;
236 }
238 ################################################################################
239 # Return true if on blacklist
241 sub check_blacklist {
242 my $regex = undef;
243 my $path = normalize_name($_);
245 # Always blacklist /
246 if ($path eq "/\n") {
247 print("Blacklisted for being the entire system: /");
248 return 1;
249 }
251 for (my $i = 0; $i <= $#blacklist; $i += 1) {
252 $regex = "^".quotemeta($blacklist[$i]);
253 if ($blacklist[$i] !~ m{/$}) {
254 $regex = $regex."$$";
255 }
256 ($debug) and print("regex is $regex\n");
257 if ($path =~ $regex) {
258 print("Blacklisted for being in $blacklist[$i]: $_\n");
259 return 1;
260 }
261 }
263 return 0;
264 }
266 ################################################################################
267 # At the moment all this does is make sure that directories listed in the
268 # lists all have a / at the end.
270 sub setup_lists {
271 for (my $i = 0; $i <= $#whitelist; $i += 1) {
272 if (-d $whitelist[$i] and $whitelist[$i] !~ m{/$}) {
273 $whitelist[$i] = $whitelist[$i]."/";
274 }
275 }
277 # All entries on the whitelist_subdirs list _must_ be directories.
278 for (my $i = 0; $i <= $#whitelist_subdirs; $i += 1) {
279 if ($whitelist_subdirs[$i] !~ m{/$}) {
280 $whitelist_subdirs[$i] = $whitelist_subdirs[$i]."/";
281 }
282 }
283 for (my $i = 0; $i <= $#blacklist; $i += 1) {
284 if (-d $blacklist[$i] and $blacklist[$i] !~ m{/$}) {
285 $blacklist[$i] = $blacklist[$i]."/";
286 }
287 }
289 if ($debug) {
290 print("whitelist:\n");
291 for (my $i = 0; $i <= $#whitelist; $i += 1) {
292 print($whitelist[$i]."\n");
293 }
294 print("whitelist_subdirs:\n");
295 for (my $i = 0; $i <= $#whitelist_subdirs; $i += 1) {
296 print($whitelist_subdirs[$i]."\n");
297 }
298 print("blacklist:\n");
299 for (my $i = 0; $i <= $#blacklist; $i += 1) {
300 print($blacklist[$i]."\n");
301 }
302 }
303 }