Jeśli plik został usunięty, ale nadal jest otwarty, oznacza to, że plik nadal istnieje w systemie plików (ma i- węzeł ), ale ma twardą liczbę linków równą 0. Ponieważ nie ma łącza do pliku, nie można go otworzyć według nazwy . Nie ma też możliwości otwarcia pliku przez i-węzeł.
Nie ma sposobu, aby odkryć plik poprzez jego system plików, a zwłaszcza nie ma sposobu, aby wyszukać plik w katalogu, w którym był ostatnio. Wpis w katalogu zniknął. Pozostaje tylko sam plik. Do pliku można dostać się za pomocą debugera systemu plików, ale wymaga to uprawnień administratora i jest trudny w użyciu i podatny na błędy.
Linux udostępnia otwarte pliki za pomocą specjalnych dowiązań symbolicznych pod /proc
. Te łącza są wywoływane, gdy /proc/12345/fd/42
12345 jest PID procesu, a 42 to liczba deskryptorów plików w tym procesie. Program działający jako ten sam użytkownik co ten proces może uzyskać dostęp do pliku (uprawnienia do odczytu / zapisu / wykonania są takie same, jak w przypadku usunięcia pliku).
Nazwa, pod którą plik został otwarty, jest nadal widoczna w celu dowiązania symbolicznego: jeśli plik był /var/log/apache/foo.log
, to celem dowiązania jest /var/log/apache/foo.log (deleted)
. (Jeśli nazwa pliku została zmieniona po jego otwarciu, cel dowiązania symbolicznego może odzwierciedlać zmianę nazwy).
W ten sposób możesz odzyskać zawartość otwartego usuniętego pliku, biorąc pod uwagę PID procesu, który go otworzył i deskryptor, na którym jest otwarty w następujący sposób:
recover_open_deleted_file () {
old_name=$(readlink "$1")
case "$old_name" in
*' (deleted)')
old_name=${old_name%' (deleted)'}
if [ -e "$old_name" ]; then
new_name=$(TMPDIR=${old_name%/*} mktemp)
echo "$oldname has been replaced, recovering content to $new_name"
else
new_name="$old_name"
fi
cat <"$1" >"$new_name";;
*) echo "File is not deleted, doing nothing";;
esac
}
recover_open_deleted_file "/proc/$pid/fd/$fd"
Jeśli znasz tylko identyfikator procesu, ale nie deskryptor, możesz odzyskać wszystkie pliki za pomocą
for x in /proc/$pid/fd/*; do
recover_open_deleted_file "$x"
done
Jeśli nie znasz identyfikatora procesu, możesz przeszukać wszystkie procesy:
for x in /proc/[1-9]*/fd/*; do
case $(readlink "$x") in
/var/log/apache/*) recover_open_deleted_file "$x";;
esac
done
Możesz także uzyskać tę listę, analizując dane wyjściowe lsof
, ale nie jest to prostsze, bardziej niezawodne ani przenośne (w każdym razie jest to specyficzne dla Linuksa).
lsof / | awk '(/deleted/||/abc.txt/) {print "FD :-",$4,"| File Name:-",$9}'