Вы удалили файл журнала, но запущенный процесс по-прежнему записывает в него, ls ничего не отображает, однако данные по-прежнему находятся там, и вы можете восстановить их до того, как процесс завершится.
Linux удаляет файлы так, но на самом деле команда rm удаляет запись в каталоге и уменьшает счетчик ссылок на файл, и если какой-либо процесс все еще имеет этот файл открытым, ядро сохраняет базовый инод до тех пор, пока не будет закрыт последний файловый дескриптор, указывающий на него.
Это окно между rm и завершением процесса — ваше окно восстановления, и на рабочей системе оно часто достаточно продолжительное, чтобы спасти вас.
Как на самом деле работает удаление файлов в Linux
Когда вы запускаете rm на файле, ядро удаляет его из дерева каталогов, поэтому ls перестает его показывать, но оно не освобождает блоки диска сразу, потому что inode все еще имеет счетчик ссылок больше нуля, пока какой-либо процесс держит открытый файловый дескриптор на него.
В тот момент, когда последний процесс закрывает этот дескриптор или завершается, счетчик ссылок падает до нуля, и ядро помечает эти блоки как свободные — именно тогда данные действительно исчезают.
Поэтому, если ваш веб-сервер, база данных или Сервер логов активно записывает в файл, который вы только что удалили, данные на диске остаются нетронутыми и доступны через специальный путь, который ядро предоставляет в /proc.
Найдите процесс, удерживающий файл в открытом состоянии
Вам понадобится команда lsof, которая означает list open files (список открытых файлов) и показывает все файловые дескрипторы, удерживаемые в данный момент запущенными процессами.
Если она не установлена, сначала установите ее.
sudo apt install lsof [On Debian, Ubuntu and Mint] sudo dnf install lsof [On RHEL/CentOS/Fedora and Rocky/AlmaLinux]
Префикс sudo запускает команду с правами root, что здесь необходимо, поскольку файловые дескрипторы, принадлежащие другим процессам, не видны пользователям без привилегий.
Теперь найдите удаленный файл по имени или по строке «deleted» в выводе:
sudo lsof | grep deleted
Вывод:
nginx 1423 www-data 4w REG 253,1 204800 131074 /var/log/nginx/access.log (deleted) rsyslogd 1201 root 7w REG 253,1 819200 131075 /var/log/syslog (deleted)
Вас интересуют второй столбец (PID), четвертый столбец (номер файлового дескриптора, здесь 4w и 7w) и последний столбец, который подтверждает, что файл помечен (deleted).
Если у удаленного файла нет слова deleted, запустите вместо этого lsof +L1, которая явно перечисляет все файлы с количеством ссылок меньше 1.
sudo lsof +L1
Вывод:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINKS NODE NAME nginx 1423 www-data 4w REG 253,1 204800 0 131074 /var/log/nginx/access.log (deleted)
Столбец NLINKS с значением 0 подтверждает, что запись в каталоге исчезла, но ядро по-прежнему хранит данные.
Восстановление файла через /proc/fd
Ядро предоставляет доступ к каждому открытому файловому дескриптору для каждого процесса в /proc/<PID>/fd/, где каждый дескриптор отображается в виде символьной ссылки, указывающей на исходный путь к файлу, даже после удаления.
Из приведенного выше вывода lsof видно, что процесс nginx имеет PID 1423 и файловый дескриптор 4, поэтому путь к сохранившимся данным — /proc/1423/fd/4.
Скопируйте их с помощью команды cp:
sudo cp /proc/1423/fd/4 /var/log/nginx/access.log.recovered
Если вывода нет, это означает успех.
Убедитесь, что восстановленный файл содержит данные:
ls -lh /var/log/nginx/access.log.recovered
Вывод:
-rw-r--r-- 1 root root 200K May 6 03:14 /var/log/nginx/access.log.recovered
Если вы видите размер файла, соответствующий ожидаемому, данные не повреждены. Теперь вы можете переместить его обратно в исходный путь или передать любому инструменту, которому он нужен.
Если запущенный процесс по-прежнему записывает в удаленный дескриптор, он будет продолжать записывать в /proc/<PID>/fd/4, но ничего не попадёт в восстановленный файл, который вы только что скопировали, поскольку это моментальный снимок на момент копирования.
Поэтому, если этот процесс — записывает в журнал, который вам нужен, вам также следует перезапустить его после восстановления файла, чтобы он снова открыл нужный связанный файл.
Если при доступе к /proc/<PID>/fd/4 вы видите Permission denied, либо запустите команду от имени root с sudo, либо проверьте, существует ли PID, с помощью:
ps aux | grep PID
Быстрое восстановление удаленных файлов с помощью скрипта оболочки
Когда вы в полночь смотрите на сломанную ранее рабочую систему, ввод нескольких команд подряд чреват ошибками, поэтому вот небольшая функция оболочки, которая объединяет весь поиск и копирование в один шаг.
Укажите ей имя удаленного файла, а она позаботится об остальном.
recover_deleted() {
local filename="$1"
local output="${2:-/tmp/recovered_file}"
local result
result=$(sudo lsof +L1 2>/dev/null | grep "$filename")
if [[ -z "$result" ]]; then
echo "No process holds $filename open. Data may already be gone."
return 1
fi
local pid fd
pid=$(echo "$result" | awk 'NR==1{print $2}')
fd=$(echo "$result" | awk 'NR==1{print $4}' | tr -d 'rwu')
echo "Found: PID=$pid FD=$fd"
sudo cp /proc/"$pid"/fd/"$fd" "$output" && echo "Recovered to $output"
}
Вставьте это в свой ~/.bashrc или общий файл системного администратора и запустите его.
Затем вызовите его так:
recover_deleted /var/log/nginx/access.log /var/log/nginx/access.log.recovered
Вывод:
Found: PID=1423 FD=4 Recovered to /var/log/nginx/access.log.recovered
awk NR==1 выбирает первый совпадающий результат, если несколько процессов открыли один и тот же файл, а tr -d 'rwu' удаляет суффикс чтения/записи/блокировки из поля FD, так что у вас остается чистое целое число для пути /proc
Совет: Если у вас есть несколько процессов, удерживающих один и тот же удаленный файл в открытом состоянии, восстанавливайте данные из того, у которого размер файла наибольший, так как в нем, скорее всего, содержатся наиболее полные данные. Проверьте размеры с помощью следующей команды для каждого кандидата.
sudo ls -lh /proc//fd/
Что делать, если процесс уже закрыл файл?
Как только все процессы, у которых файл был открыт, завершаются или закрывают дескриптор, ядро освобождает блоки диска, и данные исчезают также из /proc.
В этот момент вы попадаете в область аппаратного восстановления, используя такие инструменты, как extundelete для файловых систем ext4 или testdisk и photorec для более широкой поддержки файловых систем, но эти инструменты восстанавливают данные из необработанных блоков диска и не дают гарантии успеха после интенсивной записи.
Метод /proc быстр и надежен именно потому, что ядро возвращает вам «живые» данные, в то время как инструменты для криминалистической экспертизы собирают их из фрагментов.
Предупреждение: Никогда не запускайте extundelete или любой другой инструмент восстановления на смонтированной файловой системе с доступом для чтения и записи. Сначала размонтируйте раздел или загрузитесь с Live-USB, иначе вы рискуете, что файловая система перезапишет именно те блоки, которые вы пытаетесь восстановить.
Предотвращение случайного удаления с помощью жестких ссылок или привязок
Если вы управляете системой, в которой процесс записывает журналы в один путь, и вам нужно, чтобы эти журналы сохранились даже при случайном rm, самым чистым решением будет жесткая ссылка, которая сохраняет дополнительную запись в каталоге, указывающую на тот же инод, так что количество ссылок никогда не упадет до нуля из-за одного rm.
ln /var/log/nginx/access.log /var/log/nginx/access.log.hardlink
Убедитесь, что обе записи указывают на один и тот же инод:
ls -li /var/log/nginx/access.log /var/log/nginx/access.log.hardlink
Вывод:
131074 -rw-r--r-- 2 www-data www-data 204800 May 6 03:14 /var/log/nginx/access.log 131074 -rw-r--r-- 2 www-data www-data 204800 May 6 03:14 /var/log/nginx/access.log.hardlink
Обе записи показывают один и тот же номер инода 131074 и количество ссылок 2, что означает, что rm /var/log/nginx/access.log снизит счетчик до 1 и оставит данные полностью нетронутыми по пути жесткой ссылки.
Жесткие ссылки работают только в пределах одной файловой системы, поэтому, если вам нужна защита между файловыми системами, монтирование с помощью команды mount --bind дает аналогичный эффект.
Заключение
Путь /proc/<PID>/fd/ — это один из тех механизмов Linux, который при первом использовании кажется «чит-кодом», и как только вы спасете рабочую среду с его помощью, вы никогда больше не будете смотреть на rm прежним взглядом.
Теперь вы знаете, как найти нужный PID и дескриптор файла с помощью lsof +L1, скопировать актуальные данные до завершения процесса и обернуть всё это в функцию оболочки, чтобы это была одна команда, когда вы работаете на адреналине в 3 часа ночи.
Прямо сейчас откройте Терминал в вашей Linux-системе и попробуйте это сами: создайте тестовый файл, откройте его с помощью tail -f в одном терминале, удалите его с помощью rm в другом, затем выполните run lsof +L1 | grep test и найдите активный дескриптор.
Скопируйте его с помощью следующей команды и убедитесь, что содержимое соответствует тому, что вы поместили в исходный файл. Запустив это один раз в некритичной среде, вы обеспечите себе уверенность: когда вам понадобится это в реальности, ваши руки уже будут знать, что делать.



Комментарии (0)