Si un archivo se ha eliminado pero todavía está abierto, eso significa que el archivo todavía existe en el sistema de archivos (tiene un inodo ) pero tiene un recuento de enlaces duros de 0. Dado que no hay un enlace al archivo, no puede abrirlo por nombre . Tampoco hay posibilidad de abrir un archivo por inodo.
No hay forma de descubrir el archivo a través de su sistema de archivos, y especialmente no hay forma de buscar el archivo en el directorio donde estuvo por última vez. La entrada del directorio se ha ido. Todo lo que queda es el archivo en sí. Puede acceder al archivo con un depurador del sistema de archivos, pero eso requiere permisos de root y es difícil de usar y propenso a errores.
Linux expone archivos abiertos a través de enlaces simbólicos especiales en /proc
. Estos enlaces se llaman /proc/12345/fd/42
donde 12345 es el PID de un proceso y 42 es el número de un descriptor de archivo en ese proceso. Un programa que se ejecuta con el mismo usuario que ese proceso puede acceder al archivo (los permisos de lectura / escritura / ejecución son los mismos que tenía cuando se eliminó el archivo).
El nombre con el que se abrió el archivo todavía es visible en el destino del enlace simbólico: si el archivo estaba /var/log/apache/foo.log
, entonces el destino del enlace es /var/log/apache/foo.log (deleted)
. (Si se cambió el nombre del archivo después de abrirlo, el destino del enlace simbólico puede reflejar el cambio de nombre).
Por lo tanto, puede recuperar el contenido de un archivo eliminado abierto dado el PID de un proceso que lo tiene abierto y el descriptor en el que está abierto de esta manera:
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"
Si solo conoce la ID del proceso pero no el descriptor, puede recuperar todos los archivos con
for x in /proc/$pid/fd/*; do
recover_open_deleted_file "$x"
done
Si tampoco conoce la ID del proceso, puede buscar entre todos los procesos:
for x in /proc/[1-9]*/fd/*; do
case $(readlink "$x") in
/var/log/apache/*) recover_open_deleted_file "$x";;
esac
done
También puede obtener esta lista analizando la salida de lsof
, pero no es más simple ni más confiable ni más portátil (de todos modos, esto es específico de Linux).
lsof / | awk '(/deleted/||/abc.txt/) {print "FD :-",$4,"| File Name:-",$9}'