9 my $homedir = $ENV{HOME
} || q{};
10 my $LEGACY_CONFIG_FILE = "$homedir/.safe-rm";
11 my $USER_CONFIG_FILE = ($ENV{XDG_CONFIG_HOME
} || "$homedir/.config") . "/safe-rm";
12 my $GLOBAL_CONFIG_FILE = '/etc/safe-rm.conf';
14 my %default_protected_dirs = (
33 '/usr/local/bin' => 1,
34 '/usr/local/include' => 1,
35 '/usr/local/sbin' => 1,
36 '/usr/local/share' => 1,
43 my %protected_dirs = ();
45 sub read_config_file
{
49 if ( open my $fh, '<', $filename ) {
52 foreach my $file (glob) {
53 $protected_dirs{$file} = 1;
56 close $fh; # deliberatly ignore errors
59 print {*STDERR
} "Could not open configuration file: $filename\n";
66 read_config_file
($GLOBAL_CONFIG_FILE);
67 read_config_file
($LEGACY_CONFIG_FILE);
68 read_config_file
($USER_CONFIG_FILE);
70 if ( 0 == scalar keys %protected_dirs ) {
71 %protected_dirs = %default_protected_dirs;
74 my @allowed_args = ();
78 # Normalize the pathname
79 my $normalized_pathname = $pathname;
80 if ( $normalized_pathname =~ m{/}xms or -e
"$normalized_pathname" ) {
82 # Convert to an absolute path (e.g. remove "..")
83 $normalized_pathname = realpath
($normalized_pathname);
84 if ( !$normalized_pathname ) {
85 $normalized_pathname = $pathname;
88 if ( $normalized_pathname =~ m{^(.+?)/+$}xms ) {
90 # Trim trailing slashes
91 $normalized_pathname = $1;
94 # Check against the blacklist
95 if ( exists $protected_dirs{$normalized_pathname} and not -l
$pathname ) {
96 print {*STDERR
} "safe-rm: skipping $pathname\n" || 0;
98 elsif ( $pathname =~ /(.*)/xms ) { # pointless untainting
99 push @allowed_args, $1;
103 # Prepare for actually deleting the file
104 local $ENV{PATH
} = q{}; # pointless untainting
105 local $ENV{CDPATH
} = q{}; # pointless untainting
106 local $ENV{IFS
} = " \t\n"; # pointless untainting
107 my $real_rm = '/bin/rm';
109 # Make sure we're not calling ourselves recursively
110 if ( realpath
($real_rm) eq realpath
($0) ) {
111 die 'safe-rm cannot find the real "rm" binary' . "\n";
114 # Run the real rm command, returning with the same error code
115 my $status = system $real_rm, @allowed_args;
116 my $errcode = $status >> 8;
123 safe-rm - wrapper around the rm command to prevent accidental deletions
128 (same arguments as rm)
132 safe-rm prevents the accidental deletion of important files by
133 replacing rm with a wrapper which checks the given arguments against a
134 configurable blacklist of files and directories which should never be
137 Users who attempt to delete one of these protected files or
138 directories will not be able to do so and will be shown a warning
141 safe-rm is meant to replace the rm command so you can achieve this by
142 putting a symbolic link with the name "rm" in a directory which sits
143 at the front of your path. For example, given this path:
145 PATH=/usr/local/bin:/bin:/usr/bin
147 You could create the following symbolic link:
149 ln -s /usr/local/bin/safe-rm /usr/local/bin/rm
153 Protected paths can be set both at the site and user levels.
155 Both of these configuration files can contain a list of important files
156 or directories (one per line):
161 If both of these are empty, a default list of important paths will be
164 =for stopword Wildcards
165 Wildcards are allowed in the configuration files, but be careful
169 will protect all of the files inside the /usr/lib directory if they are referred to directly, but it will not protect your system against:
173 For a full protection, you should include both of these lines:
180 Same exit status as the real rm command.
182 Note that if all file arguments are skipped by safe-rm then the exit status
183 will be the same as the exit status of the real rm when no files arguments
186 =head1 BUGS AND LIMITATIONS
188 Note that if you put the following in your protected paths list:
190 $ cat /etc/safe-rm.conf
193 Then safe-rm will prevent you from deleting the directory:
197 /bin/rm: missing operand
198 Try `/bin/rm --help' for more information.
200 However it cannot protect you from the following:
207 Francois Marier <francois@fmarier.org>
213 =head1 LICENSE AND COPYRIGHT
215 Copyright (C) 2008-2014 Francois Marier
217 This program is free software: you can redistribute it and/or modify
218 it under the terms of the GNU General Public License as published by
219 the Free Software Foundation, either version 3 of the License, or
220 (at your option) any later version.
222 This program is distributed in the hope that it will be useful,
223 but WITHOUT ANY WARRANTY; without even the implied warranty of
224 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
225 GNU General Public License for more details.
227 You should have received a copy of the GNU General Public License
228 along with this program. If not, see <http://www.gnu.org/licenses/>.