written on Thursday, March 21, 2013
Вчера применил адский уровень красноглазости - доставал убитый файл через отладчик. Дело было так: качаю себе большой длинный файл через торренты на фиговом интернете (от отсутствия оптики на острове я страдаю и ностальгирую). Скачал. Тут рука промахивается в истории команд и вместо открытия файла в мплеере я резко грохаю его через rm. Упс.
Можно конечно перескачать, но это долго, я уже настроился посмотреть эту серию. И обидно. Но я-то в курсе, как в юниксах работают файловые системы и знаю, что пока торрент-клиент открыт, файл еще никуда не делся.
Был бы линукс - скопировал бы через /proc/$pid/fd/$n. Но увы, это злобная макось и /proc тут нету. Зато есть lsof и gdb.
Цепляюсь к процессу торрент-клиента через gdb -p pid, смотрю номер дескриптора, соответствующего удаленному файлу черещ lsof:
Transmiss 45382 muromec 28r REG 1,2 635760707 66811886 блаблабла.mkv
Ну окей, файл еще живой, надо всего-то сделать хардлинк. Идут в гуглу и стековерфлоу искать, как делаются хардлинки на файловый дескриптор.
А никак. Ну или я тупой и не нашел. link() хочет два пути и все.
Параллельно узнал, что в линуксе больше нет рута, все на capabilities переделали.
Через gdb можно нагло залезть в память процесса и подергать функции. Поскольку выполнение программы остановлено, то она даже не упадет до тех пор, пока отладчик ее не отпустит.
И так, у меня есть номер дескриптора и возможность дергать функции. Дальше все нудно. Делаем копию файлового дескриптора, ставим позицию в файле на 0 и начинаем read-write loop (лог gdb записан приблизительно и по памяти):
(gdb) print (int)dup(28) $1 = 30 (gdb) print lseek(30, 0,) $2 = 0 (gdb) print (long)malloc(0x3200000) $3 = 4389474304 (gdb) print (int)open("copy.mkv", 0x202) $4 = 31 (gdb) print (int)read(30, 4389474304, 0x3200000) $5 = 52428800 (gdb) print (int)write(31, 4389474304, 0x3200000) $6 = 52428800
Ну и так далее. Иногда оно почему-то ловило SIGPIPE и приходилось ручками ставить правильный offset через lseek(), потомучто я не понимал, в какой момент и зачем этот сигнал прилетел.
В конце этого действа я дернул close() и пошел смотреть восстановленный файл. Только выставил на него нормальные пермишшены и все.