Как восстановить файлы после удаления командой rm -rf
Случайно удалили данные через rm -rf? Немедленно прекратите запись на диск. В статье описаны методы восстановления файлов в ext4 и команды для безопасного размонтирования.
Что делать в первые 60 секунд
Два фактора определяют, сможете ли вы восстановить файлы, удаленные с помощью rm -rf, и оба они зависят от ваших действий до того, как вы откроете поисковик. Прекратите запись на эту файловую систему. Затем выведите её из эксплуатации, отмонтировав или перемонтировав в режиме только для чтения.
rm ничего не стирает. Команда удаляет запись в каталоге, а затем помечает inode и блоки данных файла как свободные. Байты остаются на устройстве. Они будут там находиться до тех пор, пока распределитель блоков не передаст эти блоки другому процессу, и тот не запишет поверх них новые данные. Каждую секунду, пока файловая система примонтирована и активна, демон записывает строку в лог или база данных сбрасывает страницу на диск, и любая из этих операций может затронуть блоки, которые вы хотите вернуть.
Поэтому первыми командами должны быть те, что останавливают запись, а не те, что восстанавливают файлы.
sudo systemctl stop nginx postgresql
sudo umount /mnt/dataЕсли umount отвечает umount: /mnt/data: target is busy., выясните, какой процесс удерживает файловую систему открытой.
sudo fuser -vm /mnt/data
sudo lsof +D /mnt/dataЕсли вы не можете освободить её, перемонтируйте файловую систему в режиме только для чтения. Монтирование в режиме read-only останавливает новые операции выделения блоков, что является основной задачей.
sudo mount -o remount,ro /mnt/dataЕсли удаленный путь находился на корневой файловой системе, это сложнее. sudo mount -o remount,ro / обычно завершается с ошибкой mount: /: cannot remount /dev/vda1 read-only., так как запущенные процессы удерживают файлы открытыми для записи, а ядро не принуждает их к закрытию. На VPS практическим решением будет использование режима восстановления (rescue mode) вашего провайдера: он загружает отдельную «живую» систему, в которой ваш диск подключен, но не примонтирован. В этом случае каждая команда ниже выполняется для устройства, на которое никто не ведет запись.
Одно правило применимо ко всему этому руководству. Никогда не записывайте восстановленные файлы, образ диска или только что установленные инструменты на ту файловую систему, с которой вы ведете восстановление. Подключите второй том или передайте вывод на другую машину по SSH.
Почему восстановление после rm -rf на ext4 практически невозможно
Сформируйте ожидания до того, как что-либо устанавливать. Уточните, с какой файловой системой вы работаете:
lsblk -fВ ext4, которая является стандартом почти для любого образа VPS, расположение данных файла хранится в его inode в виде дерева экстентов. Экстент — это запись, указывающая, что логический блок N файла начинается с физического блока M и занимает L блоков. Небольшие файлы хранят до четырех таких записей непосредственно внутри inode. Более крупные файлы ссылаются на дополнительные блоки, содержащие остальную часть дерева.
Когда последняя ссылка на файл удаляется, ext4 проходит по этому дереву, возвращает каждый экстент распределителю блоков и очищает дерево из inode. Затем inode помечается как свободный и получает метку времени удаления. Сами данные остаются нетронутыми. Единственная запись о том, где находились эти данные, была стерта.
В этом заключается отличие от ext3, где удаленный inode сохранял достаточно информации для работы инструментов вроде ext3grep. Вы все еще можете вывести список удаленных inode в ext4:
sudo debugfs -R lsdel /dev/vdb1debugfs открывает устройство в режиме только для чтения, если не передать флаг -w, поэтому это безопасно для размонтированного устройства и ничего не стоит попробовать. Inode будут выведены в список. На этом этапе все заканчивается, так как карта блоков, которую раньше содержал inode, была очищена, и dump не имеет данных для отслеживания.
Два инструмента пытаются обойти это ограничение путем чтения журнала. Журнал — это кольцевой буфер фиксированного размера, который ext4 использует для поддержания целостности метаданных при сбоях; он может содержать старую копию inode, созданную до удаления. extundelete и ext4magic выполняют поиск именно в нем. Проверьте размер журнала, с которым вы работаете:
sudo dumpe2fs -h /dev/vdb1 | grep -i journalЖурнал хранит только метаданные и имеет малый размер, поэтому обычные операции записи быстро перезаписывают его содержимое. На работающем сервере окно времени, в котором еще существует inode до удаления, измеряется минутами. Ни один из этих инструментов не поддерживается активно, и ни один из них не входит в пакеты всех дистрибутивов. Рассматривайте оба варианта как маловероятные, запускайте их только на размонтированном устройстве или образе диска и не удивляйтесь, если они ничего не найдут.
Если lsblk -f сообщает xfs, ситуация не лучше, так как для XFS также не существует поддерживаемых средств восстановления удаленных файлов. Порядок действий, приведенный ниже, от этого не меняется.
Открыт ли файл в запущенном процессе?
Это единственный способ восстановления на данной странице с высокими шансами на успех, и именно поэтому не следует перезапускать сервис, который использовал файл.
Файл считается окончательно удаленным только тогда, когда два счетчика достигают нуля: количество записей в каталогах, указывающих на его inode, и количество открытых файловых дескрипторов. rm обнуляет первый счетчик. Если процесс все еще удерживает файл открытым, второй счетчик не равен нулю, поэтому inode и его блоки остаются выделенными, а данные доступны для чтения.
Найдите открытые файлы, количество ссылок на которые упало до нуля:
sudo lsof +L1+L1 означает вывод списка открытых файлов с количеством ссылок менее 1. Каждое совпадение показывает процесс, номер файлового дескриптора, NLINK из 0 и путь, заканчивающийся на (deleted). Используйте PID и номер дескриптора для /proc:
sudo ls -l /proc/1234/fdЗапись выглядит как 3 -> /var/log/app/events.log (deleted). Эта ссылка все еще ведет к данным. Скопируйте их в другую файловую систему:
sudo cp /proc/1234/fd/3 /mnt/rescue/events.logИспользуйте cp, а не mv. Открытие /proc/1234/fd/3 дает вам новый дескриптор того же inode, начиная с нулевого смещения, поэтому вы получите файл целиком, а не ту часть, что идет после текущей позиции пишущего процесса.
Следует учитывать два ограничения. Удаленное дерево каталогов таким способом не восстановить, так как удерживаются только отдельные файлы, открытые процессом. Файл базы данных, скопированный во время записи движком, является crash-consistent копией, поэтому планируйте запуск встроенной процедуры восстановления движка, а не считайте файл корректным. Записи, которые lsof отображает с mem вместо номера дескриптора, являются отображенными в память (memory mapped), и для них нет записи /proc/<pid>/fd, из которой можно было бы выполнить копирование.
Есть ли у вас снапшот на btrfs, ZFS или LVM?
Если файловая система поддерживает снапшоты, удаленные файлы уже находятся внутри одного из них в неизменном виде. Это поможет только в том случае, если снапшот был создан до удаления. Все, что вы создадите сейчас, не позволит вернуть данные из прошлого.
btrfs хранит снапшоты как субтома (subvolumes):
sudo btrfs subvolume list /Просмотрите снапшот и скопируйте нужные пути с помощью cp -a. Лучше копировать отдельные пути, чем выполнять откат всего субтома, так как откат также удаляет все данные, записанные после создания снапшота.
ZFS предоставляет доступ к каждому снапшоту как к директории только для чтения:
zfs list -t snapshot
ls /tank/data/.zfs/snapshot/Директория .zfs скрыта и не отображается при обычном ls корня датасета, но вы можете войти в нее по имени. Скопируйте файлы оттуда. zfs rollback возвращает весь датасет к состоянию снапшота и уничтожает все снапшоты, созданные позже указанного, поэтому используйте это как крайнюю меру.
Снапшоты LVM — это тома с механизмом copy-on-write и фиксированным размером:
sudo lvs
sudo mount -o ro /dev/vg0/data-snap /mnt/snapПримонтируйте его в режиме только для чтения и скопируйте данные. Проверьте lvs, прежде чем доверять содержимому, так как если снапшот LVM заполняет выделенное ему пространство, ядро помечает его как недействительный, и после этого данные внутри него теряются.
Снапшот не является резервной копией. Он находится на том же диске или в том же пуле, что и оригинал, поэтому он подвержен тем же сбоям, что и исходные данные. Снапшоты отлично подходят для исправления ошибок, допущенных пару минут назад, что как раз является нашей текущей задачей.
Восстановление данных с помощью PhotoRec: работа с образом, а не с «живым» диском
Если ничего из вышеперечисленного не помогло, остается метод carving: сканирование «сырого» устройства на предмет поиска последовательностей байтов, обозначающих начало файла известного типа, с последующим извлечением всего, что идет следом. Carving считывает только данные файлов. Имена файлов, структура каталогов, метки времени и владельцы — это метаданные файловой системы, а именно они были уничтожены командой rm, поэтому восстановить их невозможно. Вы получите файлы с именами вида f0384512.jpg в пронумерованном выходном каталоге, которые придется сортировать вручную.
Успех этого метода зависит от двух правил.
Во-первых, создайте образ устройства, прежде чем применять к нему любые другие инструменты. В Debian и Ubuntu нужный пакет называется gddrescue, а устанавливаемая им утилита — ddrescue.
sudo apt update && sudo apt install -y gddrescue testdisk
sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map/mnt/rescue должен находиться на другом устройстве, где объем свободного места не меньше размера раздела. lsblk -b выводит точный размер в байтах. Файл карты (map file) позволяет возобновить прерванное копирование, а не начинать его заново. Получив образ, вы сможете позже попробовать другой инструмент для работы с теми же самыми байтами, что невозможно, если первый инструмент начал запись поверх диска.
Во-вторых, направляйте инструмент восстановления на файл образа.
sudo photorec /d /mnt/rescue/recup /mnt/rescue/vdb1.imgphotorec открывает текстовое меню. Выберите раздел, затем тип файловой системы, типы сигнатур файлов для поиска и целевой каталог. Перед запуском ограничьте список сигнатур только теми типами файлов, которые вы действительно потеряли: по умолчанию утилита ищет всё подряд, что приведет к появлению десятков тысяч фрагментов, которые придется разбирать.
testdisk из того же пакета имеет собственную функцию восстановления удаленных файлов, но она поддерживает только FAT, exFAT, NTFS и ext2. Для ext4 остается photorec.
Будьте готовы к тому, что фрагментированные файлы будут восстановлены с повреждениями. Метод carving предполагает, что блоки файла расположены последовательно, поэтому файл, который распределитель памяти разнес по разным частям диска, будет либо собран неверно, либо пропущен вовсе. Медиафайлы восстанавливаются относительно успешно, так как имеют четкие заголовки. Обычный текст, конфигурационные файлы и исходный код восстанавливаются плохо, так как не существует байтовой сигнатуры, отмечающей начало shell-скрипта.
Лишний пробел: как был удален неверный путь
Почти каждый инцидент с rm -rf связан с проблемами командной оболочки. rm получает список путей и удаляет каждый из них по очереди. Утилита не знает, что именно вы имели в виду.
Классический пример — лишний пробел:
rm -rf /home/deploy/app /old
rm -rf /home/deploy/app/oldПервая строка содержит два аргумента. Сначала удаляется приложение, затем удаляется /old. Если /old не существует, rm не выводит ничего, так как -f подавляет ошибку об отсутствии файла. Отсутствие вывода не является подтверждением успеха.
Второй вариант — не взятая в кавычки переменная, содержащая пробел:
dir="/srv/my app"
rm -rf $dirОболочка разделяет значение по пробелам, поэтому rm получает /srv/my и app как два отдельных пути. При записи в виде rm -rf "$dir" это будет один путь.
Третий вариант — пустая переменная, обычно из-за того, что команда, которая должна была её заполнить, завершилась с ошибкой:
rm -rf "$TARGET"/*Если TARGET не задана, выражение раскрывается в rm -rf /*. GNU rm отказывается выполнять команду в таком виде: rm -rf / выводит rm: it is dangerous to operate recursively on '/' и завершает работу. Форма с подстановочным знаком (glob) не имеет такой защиты, так как оболочка заменяет /* списком реальных путей верхнего уровня до того, как запустится rm, а / в этот список не входит, поэтому защитный механизм не срабатывает.
Привычки, предотвращающие повторение ошибок
- Заключайте в кавычки каждую переменную, используемую в качестве пути. Всегда пишите
"$dir", включая использование внутри тестов и циклов. - Завершайте работу при пустом значении.
rm -rf "${TARGET:?TARGET is not set}"/*заставляет оболочку остановиться с вашим сообщением перед запускомrm, еслиTARGETне задана или пуста. Размещайтеset -euo pipefailв начале любого скрипта, выполняющего удаление. - Добавьте
--one-file-system. Этот флаг указываетrmпропускать любые каталоги, находящиеся на другой файловой системе относительно переданного аргумента, чтобы рекурсивное удаление не затронуло смонтированный том резервных копий или bind-mount. - Не выполняйте удаление от имени root. Сервисная учетная запись может удалить только то, чем она владеет; в этом заключается основной аргумент в пользу запуска каждого сервиса от имени собственного непривилегированного пользователя. Если вы не уверены, к чему имеет доступ конкретная учетная запись, чтение битов прав в выводе ls даст ответ одной командой.
- Выводите список перед выполнением действий. В скрипте сформируйте пути, выполните их
printf '%s\n', изучите вывод, а затем удаляйте при втором проходе. - Держите под рукой команду для перемещения в корзину.
sudo apt install trash-cliпредоставляет вамtrash-put,trash-list,trash-restoreиtrash-empty. Удаленные файлы перемещаются в~/.local/share/Trash, аtrash-empty 30очищает всё, что старше тридцати дней.
Создание алиаса rm для trash-put кажется очевидным следующим шагом, но это ловушка. Алиас вырабатывает рефлекс, который подведет вас на следующем сервере, где его нет, а алиасы не работают внутри скриптов, где и происходят дорогостоящие ошибки. Вводите trash-put осознанно.
Единственный способ восстановления, который работает всегда
Всё, что описано выше — это лишь вероятность. Резервная копия — это не вероятность.
Две вещи делают резервную копию настоящей. Она выполняется по расписанию без вашего участия, и вы хотя бы раз восстанавливали из неё данные. Репозиторий, из которого никто никогда не восстанавливался — это лишь вера, потому что причины его бесполезности (неверный путь в списке включения или пароль от репозитория, который никто не записал) проявятся только в тот день, когда он вам понадобится.
В restic восстановление выполняется двумя командами.
restic -r /srv/restic-repo snapshots
restic -r /srv/restic-repo restore latest --target /mnt/rescue --include /srv/appdataВосстанавливайте данные в пустую директорию, а не поверх рабочих путей, чтобы вы могли сравнить их перед тем, как что-либо будет перезаписано. В настройке резервного копирования restic на VPS описана настройка репозитория и таймера systemd для его запуска.
В Borg:
borg list /srv/borg-repo
borg extract /srv/borg-repo::daily-2026-08-08 srv/appdataПути внутри архива Borg хранятся без ведущего слэша, поэтому srv/appdata совпадает, а /srv/appdata не совпадает ни с чем. borg extract записывает данные в текущую рабочую директорию, поэтому сначала перейдите в cd во временную папку.
Если вы ещё не сделали выбор, в сравнении restic и Borg рассматриваются дедупликация и репозитории только для добавления (append-only) — это свойство, которое не позволяет скомпрометированному серверу удалить свою собственную историю резервных копий. Любой из этих инструментов подходит. Неправильный ответ — не использовать ни один из них.
Новый сервер — самый подходящий момент для настройки, пока на нём нет ничего, что стоило бы потерять. Первые десять минут на новом VPS — это то место, где должна выполняться эта работа, наряду с настройкой SSH и межсетевого экрана.
Затем добавьте повторяющееся событие в календарь: каждый месяц восстанавливайте одну директорию из репозитория в /tmp и проверяйте файлы. Эта единственная привычка стоит больше, чем все инструменты на этой странице.
FAQ
Можно ли восстановить удаленный файл в ext4?
Обычно нет. Когда удаляется последняя ссылка на файл, ext4 очищает дерево экстентов из inode, поэтому на диске не остается записей о расположении данных. extundelete и ext4magic ищут в журнале ext4 старую копию этого inode, что помогает только в том случае, если удаление произошло несколько минут назад, а файловая система с тех пор была неактивна. Оба проекта не поддерживаются. Запускайте их только на размонтированном устройстве или образе диска, никогда не работайте с примонтированной файловой системой. Предварительно проверьте, с чем вы работаете, с помощью sudo dumpe2fs -h /dev/vdb1 | grep -i journal.
Сервис все еще держит удаленный файл открытым. Можно ли его вернуть?
Да, это лучший сценарий. Пока процесс удерживает файл открытым, его inode и блоки данных остаются выделенными, поэтому данные можно прочитать. Не перезапускайте сервис, так как закрытие последнего дескриптора завершит удаление. Запустите sudo lsof +L1, чтобы вывести список открытых файлов со счетчиком ссылок 0, запишите PID и номер файлового дескриптора, затем скопируйте данные через /proc с помощью sudo cp /proc/1234/fd/3 /mnt/rescue/events.log. Сохраняйте копию на другую файловую систему. Записи, отображаемые с mem вместо номера дескриптора, являются отображениями в память (memory mapped) и не имеют пути /proc/<pid>/fd для копирования.
Почему стоит создать образ диска, а не запускать инструмент восстановления прямо на нем?
Потому что любой инструмент должен куда-то записывать результат, и запись в восстанавливаемую файловую систему может попасть в свободные блоки, где все еще хранятся ваши данные. Сначала скопируйте раздел на другое устройство с помощью sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map, затем укажите photorec на файл образа. Образ также позволяет позже попробовать другой инструмент на тех же самых байтах, что невозможно, если что-то уже было записано поверх оригинала.
Уничтожает ли rm -rf / систему Linux до сих пор?
Сама по себе команда — нет. GNU rm блокирует ее и выводит rm: it is dangerous to operate recursively on '/'. Опасные варианты возникают другими путями. rm -rf "$TARGET"/* при не установленном TARGET раскрывается в rm -rf /*, и оболочка передает rm список реальных корневых директорий, ни одна из которых не является /, поэтому защита не срабатывает. Используйте "${TARGET:?TARGET is not set}", и оболочка остановится до запуска rm.
Является ли снапшот файловой системы резервной копией?
Нет. Снапшот btrfs или ZFS находится в том же пуле, что и защищаемые данные, поэтому выход из строя диска или разрушение пула уничтожит и то, и другое одновременно. Снапшот LVM имеет дополнительную проблему фиксированного размера: как только он заполняется, ядро делает его недействительным, и содержимое теряется. Снапшоты отлично подходят для отмены удаления, совершенного две минуты назад. Для всего остального храните репозиторий на отдельном оборудовании.