Secure your rm command

May 1996

 

This is a shell script for Bash, called ‘rm_secure’. I use it as frontal for the rm command. It stores the deleted files in an archive in the user’s directory. A command-line option allows the user to view the content of this archive, and another option permits the restoration of the deleted files.

For example :

$ ls -l
-rw-r--r-- 1 ccb users 22 May 26 10:33 important_file
-rw-r--r-- 1 ccb users 23 May 26 10:34 not_important
$ rm * (OOPS!)
$ ls -l
$ rm --viewtrash
-rw-r--r-- 1 ccb users 22 May 26 10:35 1996 important_file
-rw-r--r-- 1 ccb users 23 May 26 10:35 1996 not_important
$ rm --restore important_file
$ ls -l
-rw-r--r-- 1 ccb users 22 May 26 10:35 important_file
$ rm --viewtrash
-rw-r--r-- 1 ccb users 23 May 26 10:35 1996 not_important
$ rm --emptytrash
$ rm --viewtrash
$

Okay, it slows down a few the rm command. But it may also save hours of work lost due to a keystroke error…

There is the script ‘rm_secure’ :

#!/bin/bash

# Configuration
# the real 'rm' command
bin_rm=/bin/rm
# where archiving the files
Archive=~/.rm_saved.tar
# you may prefer something like :
# Archive=/var/trash/$USER/saved_file.tar
# (with write access permission on the directory)

# global variables for the options
Opt_recursive=0
Opt_no_secure=0
Opt_restore=0
Opt_rm=""

# function for archiving a file or a directory
save_file() {
  if [ $Opt_no_secure -ne 1 ] ; then
  # set date/time of deletion
  touch "$1" > /dev/null 2>&1
    if [ -f $Archive ] ; then
      tar --delete -f "$Archive" "$1" > /dev/null 2>&1
      tar -rf "$Archive" "$1" > /dev/null 2>&1
    else
      tar -cf "$Archive" "$1" > /dev/null 2>&1
      # r/w access only for the user
      chmod 600 "$Archive"
    fi
  fi
}

# function for restoring file or directory
restore_file () {
  if [ -f $Archive ] ; then
    tar -xf "$Archive" "$1" > /dev/null 2>&1
    tar --delete -f "$Archive" "$1" > /dev/null 2>&1
  fi
}

# reading the command-line args
while getopts "dfirRvns-:" opt ; do
  case $opt in
    d ) Opt_rm="$Opt_rm -d" ;;
    f ) Opt_rm="$Opt_rm -f" ;;
    i ) Opt_rm="$Opt_rm -i" ;;
    r | R ) Opt_recursive=1
            Opt_rm="$Opt_rm -r" ;;
    v ) Opt_rm="$Opt_rm -v" ;;
    n ) Opt_no_secure=1 ;;
    s ) Option_restore=1 ;;
    - ) case $OPTARG in
      directory ) Opt_rm="$Opt_rm -d" ;;
      force ) Opt_rm="$Opt_rm -f" ;;
      interactive ) Opt_rm="$Opt_rm -i" ;;
      recursive ) Opt_recursive=1
                  Opt_rm="$Opt_rm -r" ;;
     help ) $bin_rm --help
             echo "(rm_secure)"
             echo " -n, --nosecure delete without backup"
             echo " --viewtrash list the saved files"
             echo " --emptytrash erase the saved files"
             echo " -s, --restore restore the specified files"
             exit 0 ;;
      version ) $bin_rm --version
                 echo "(rm_secure 1.0)"
                 exit 0 ;;
      verbose ) Opt_rm="$Opt_rm -v" ;;
      viewtrash ) if [ -f $Archive.gz ] ; then
                    tar -tvzf $Archive.gz
                  fi
                  exit 0 ;;
      nosecure ) Opt_no_secure=1 ;;
      emptytrash ) if [ -f $Archive.gz ] ; then
                     $bin_rm $Archive.gz
                   fi
                   exit 0 ;;
      restore ) Opt_restore=1 ;;
      * )  ;;
    esac ;;
    ? )  ;;
  esac
done

shift $(($OPTIND - 1))
gunzip $Archive.gz >; /dev/null 2>&1

# restoration ?
if [ $Opt_restore -ne 0 ] ; then
  while [ -n "$1" ] ; do
    restore_file "$1"
    shift
  done
  exit 0
else
  while [ -n "$1" ] ; do
    if [ -d "$1" ] ; then
      # the directories are archived only with
      # the -r option
      if [ $Opt_recursive -ne 0 ] ; then
        save_file "$1"
      fi
      $bin_rm $Opt_rm $1
    elif [ -e "$1" ] ; then
      # existing file
      save_file "$1"
      $bin_rm $Opt_rm $1
    else
      # let 'rm' give his error message
      $bin_rm $1
    fi
  shift
done
fi

nice gzip $Archive > /dev/null 2>&1 &
# -- end of script --

Place it in /usr/bin or /usr/local/bin then insert a ligne :

alias rm='/usr/local/bin/rm_secure'

in /etc/profile, so this script will be called by Bash in the place of the true rm command.

You can use the ‘–nosecure’ or ‘-n’ option to delete a file without archiving it. This is useful when you decide to erase huge amount of files in recursive directories (for example a package you have tested but find uninteresting).

I use a cron job to deleted the archived files every day (running as root job).

# crontab -l
[ …]
00 04 * * * /usr/local/bin/empty_trash
#

Here is the ‘empty_trash’ script :

#! /bin/bash
  for user in /home/* ; do
    /bin/rm $user/.rm_saved.tar.gz
  done
  /bin/rm /root/.rm_saved.tar.gz
# -- end of script --

Maybe you can prefer something like :

trap '/bin/rm ~/.rm_saved.tar.gz EXIT

in /etc/profile, which erase the archive each time the user exits the shell. (I’ve not fully tested this)

Obviously this tips doesn’t secure the deletion of files or directories by a file-manager, but I find it quite usefull, especially when doing administrative jobs as root (‘rm tmp/ *’ in place of ‘rm tmp/*’ …)

URL de trackback pour cette page