Recovering Accidentally Deleted Files or Directories on Linux

Linux does not have a recycle bin as conspicuous as Windows, so you cannot simply restore things. Restoring deleted files on Linux can be divided into two cases: one where after deletion there is still process information holding the file, and one where after deletion no process can be found at all and you have to rely on tools to restore it. Let’s look at each of them in turn.

  1. The case where the process that accidentally deleted the file is still running.

This is usually a case where an active process keeps standard input or output open, so even after the file is deleted the process PID still exists. This is also the reason why deleting some files on a server does not free up disk space. For example, here is a demonstration: using a shell terminal to append to a test file with cat:

[root@21yunwei_backup ~]# echo  "hello  py" > testdelete.py

[root@21yunwei_backup ~]# cat  >> testdelete.py 
hello delete

Checking the file from another terminal, the contents are clearly visible:

[root@21yunwei_backup ~]# cat testdelete.py 

hello  py

hello delete

At this point, delete the file on the current server with rm -f ./testdelete.py

Looking at the directory with the command, the file no longer exists, so now let us recover it.

Use lsof to check whether the process for the deleted file still exists.
Here we use the lsof command; if it is not installed, install it yourself with yum or apt-get. In a case like this, we can first use lsof to check whether the deleted file is still there:

[root@21yunwei_backup ~]# lsof | grep deleted
mysqld     1512   mysql    5u      REG              252,3          0    6312397 /tmp/ibzW3Lot (deleted)
cat       20464    root    1w      REG              252,3         23    1310722 /root/testdelete.py (deleted)

Fortunately in this case the process still exists, so let’s begin the recovery operation.

Recovery.
Recovery command:

cp /proc/pid/fd/1 /target directory/file name

Enter the process directory, usually /proc/pid/fd/; for the current case:

[root@21yunwei_backup ~]# cd   /proc/20464/fd
[root@21yunwei_backup fd]# ll
total 0
lrwx------ 1 root root 64 Nov 15 18:12 0 > /dev/pts/1
l-wx------ 1 root root 64 Nov 15 18:12 1 > /root/testdelete.py (deleted)
lrwx------ 1 root root 64 Nov 15 18:12 2 > /dev/pts/1

The recovery operation:

cp 1 /tmp/testdelete.py

View the file:

[root@21yunwei_backup fd]# cat  /tmp/testdelete.py
hello  py
hello delete

Recovery complete.

  1. The accidentally deleted file’s process no longer exists — restore with the help of a tool.

Create the directory to be deleted and echo a file with contents into it:

[root@21yunwei_backup 21yunwei]# tree
.
├── deletetest
│   └── mail
│       └── test.py
├── lost+found
└── passwd
3 directories, 2 files
[root@21yunwei_backup 21yunwei]# cat /21yunwei/deletetest/mail/test.py 
hello Dj
[root@21yunwei_backup 21yunwei]# tail  -2  passwd 
haproxy:x:500:502::/home/haproxy:/bin/bash
tcpdump:x:72:72::/:/sbin/nologin

Perform the deletion:

[root@21yunwei_backup 21yunwei]# rm  -rf    ./*
[root@21yunwei_backup 21yunwei]# ll
total 0

Now let’s begin recovering the accidentally deleted files. In this case there is generally no daemon or background process continuously feeding the file, so once deleted it is gone, and lsof will not show it either. We have to rely on tools. The tool we use here is the third-party tool extundelete. The recovery steps are as follows:

  1. Stop all operations on the current partition to prevent the inode from being overwritten. Once the inode is overwritten, recovery is basically out of the question. For example, stop the services on that partition, unmount the device where the directory resides, and if necessary you can even disconnect from the network.

  2. Back up the current partition with the dd command, to prevent data loss if the third-party software fails to restore it. This suits cases where the data is very important; here it is just a test, so there is no backup. If you do back up, you can consider the following approach:

dd if=/path/filename of=/dev/vdc1

  1. Unmount the current device partition with the umount command. Or the fuser command.

umount /dev/vdb1 or umount /21yunwei

If it reports that the device is busy, you can force the unmount with the fuser command:

fuser -m -v -i -k /21yunwei

  1. Download and install the third-party tool extundelete, then search for the accidentally deleted files and restore them.

    wget http://nchc.dl.sourceforge.net/project/extundelete/extundelete/0.2.4/extundelete-0.2.4.tar.bz2
    tar jxvf extundelete-0.2.4.tar.bz2
    cd extundelete-0.2.4
    ./configure
    make
    make install
    Scan for the accidentally deleted files:

    [root@21yunwei_backup extundelete-0.2.4]# extundelete —inode 2 /dev/vdb1
    NOTICE: Extended attributes are not restored.
    Loading filesystem metadata … 8 groups loaded.
    Group: 0
    Contents of inode 2:
    .
    .N lines omitted
    File name | Inode number | Deleted status
    . 2
    .. 2
    lost+found 11 Deleted
    deletetest 12 Deleted
    passwd 14 Deleted

The scan found the folders we deleted, so now let’s carry out the recovery.

  1. Recover the single file passwd

    [root@21yunwei_backup /]# extundelete /dev/vdb1 —restore-file passwd
    NOTICE: Extended attributes are not restored.
    Loading filesystem metadata … 8 groups loaded.
    Loading journal descriptors … 46 descriptors loaded.
    Successfully restored file passwd

The restored file is placed in the RECOVERED_FILES directory of the current directory. View the restored file:

[root@21yunwei_backup /]# tail  -5  RECOVERED_FILES/passwd 
mysql:x:497:500::/home/mysql:/bin/false
nginx:x:496:501::/home/nginx:/sbin/nologin
zabbix:x:495:497:Zabbix Monitoring System:/var/lib/zabbix:/sbin/nologin
haproxy:x:500:502::/home/haproxy:/bin/bash
tcpdump:x:72:72::/:/sbin/nologin
  1. Recover the directory deletetest

    [root@21yunwei_backup /]# extundelete /dev/vdb1 —restore-directory deletetest
    NOTICE: Extended attributes are not restored.
    Loading filesystem metadata … 8 groups loaded.
    Loading journal descriptors … 46 descriptors loaded.
    Searching for recoverable inodes in directory deletetest …
    5 recoverable inodes found.
    Looking through the directory structure for deleted files …
    [root@21yunwei_backup /]# cat RECOVERED_FILES/deletetest/mail/test.py
    hello Dj

  2. Recover everything

    [root@21yunwei_backup /]# extundelete /dev/vdb1 —restore-all
    NOTICE: Extended attributes are not restored.
    Loading filesystem metadata … 8 groups loaded.
    Loading journal descriptors … 46 descriptors loaded.
    Searching for recoverable inodes in directory / …
    5 recoverable inodes found.
    Looking through the directory structure for deleted files …
    0 recoverable inodes still lost.
    [root@21yunwei_backup /]# cd RECOVERED_FILES/
    [root@21yunwei_backup RECOVERED_FILES]# tree
    .
    ├── deletetest
    │ └── mail
    │ └── test.py
    └── passwd
    2 directories, 2 files

  3. Recover a specified inode

    [root@21yunwei_backup /]# extundelete /dev/vdb1 —restore-inode 14
    NOTICE: Extended attributes are not restored.
    Loading filesystem metadata … 8 groups loaded.
    Loading journal descriptors … 46 descriptors loaded.
    [root@21yunwei_backup /]# tail -5 /RECOVERED_FILES/file.14
    mysql:x:497:500::/home/mysql:/bin/false
    nginx:x:496:501::/home/nginx:/sbin/nologin
    zabbix:x:495:497:Zabbix Monitoring System:/var/lib/zabbix:/sbin/nologin
    haproxy:x:500:502::/home/haproxy:/bin/bash
    tcpdump:x:72:72::/:/sbin/nologin

Note that when recovering an inode, the recovered file name is different from before, so you need to rename it separately. The contents are fine.

For more extundelete usage, please refer to the extundelete —help option descriptions. The current restore-everything operation is complete.