Nếu một tập tin đã bị xóa nhưng vẫn còn mở, có nghĩa là các tập tin vẫn còn tồn tại trong hệ thống tập tin (nó có một inode ) nhưng có một liên kết cứng đếm 0. Vì không có liên kết đến tập tin, bạn không thể mở nó bằng tên . Không có cơ sở để mở một tập tin bằng inode.
Không có cách nào để khám phá tệp thông qua hệ thống tệp của nó và đặc biệt là không có cách nào để tìm tệp trong thư mục chứa nó. Các mục nhập thư mục đã biến mất. Tất cả những gì còn lại là tập tin chính nó. Bạn có thể truy cập tệp bằng trình gỡ lỗi hệ thống tệp, nhưng yêu cầu quyền root và khó sử dụng và dễ bị lỗi.
Linux hiển thị các tệp đang mở thông qua các liên kết tượng trưng đặc biệt dưới /proc
. Các liên kết này được gọi /proc/12345/fd/42
trong đó 12345 là PID của một quy trình và 42 là số lượng mô tả tệp trong quy trình đó. Một chương trình chạy cùng một người dùng với quá trình đó có thể truy cập tệp (các quyền đọc / ghi / thực thi giống như khi bạn xóa tệp).
Tên mà tệp được mở vẫn hiển thị trong mục tiêu của liên kết tượng trưng: nếu tệp là /var/log/apache/foo.log
, thì mục tiêu của liên kết là /var/log/apache/foo.log (deleted)
. (Nếu tệp được đổi tên sau khi được mở, mục tiêu của liên kết tượng trưng có thể phản ánh việc đổi tên.)
Do đó, bạn có thể khôi phục nội dung của một tệp đã bị xóa được cung cấp cho PID của một quá trình mở nó và mô tả mà nó đã mở như thế này:
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"
Nếu bạn chỉ biết ID tiến trình chứ không biết mô tả, bạn có thể khôi phục tất cả các tệp bằng
for x in /proc/$pid/fd/*; do
recover_open_deleted_file "$x"
done
Nếu bạn không biết ID tiến trình, bạn có thể tìm kiếm trong số tất cả các quy trình:
for x in /proc/[1-9]*/fd/*; do
case $(readlink "$x") in
/var/log/apache/*) recover_open_deleted_file "$x";;
esac
done
Bạn cũng có thể có được danh sách này bằng cách phân tích cú pháp đầu ra lsof
, nhưng nó không đơn giản hơn cũng không đáng tin cậy hơn hay dễ mang theo hơn (dù sao đây cũng là đặc thù của Linux).
lsof / | awk '(/deleted/||/abc.txt/) {print "FD :-",$4,"| File Name:-",$9}'