한 UNIX 시스템의 루트 파일시스템이 실수로 삭제된 뒤, 남아 있는 도구만으로 시스템을 복구해 낸 극적인 실화.
1986년 10월 1일 수요일 오후는 조용했다. 정확히는 BST 기준 10월 1일 수요일 15시 15분, 내 사무실 동료인 Peter가 터미널에서 몸을 돌리며 내게 말했다. "Mario, 메일을 보내는 데 조금 문제가 있어." msg가 아주 유능한 사람조차도 혼란스럽게 만들 수 있다는 것을 알고 있었기에, 나는 무슨 문제인지 보려고 그의 터미널로 느긋하게 걸어갔다. msg는 이상한 형태의 오류 메시지(정확한 내용은 기억나지 않지만)인 "cannot access /foo/bar for userid 147"를 내보내고 있었다. 내 첫 생각은 "userid 147이 누구지? 메시지 발신자? 수신자? 아니면 뭐지?"였다. 그래서 나는 이미 로그인되어 있던 다른 터미널로 몸을 기울여 다음과 같이 입력했다.
grep 147 /etc/passwd
그러자 돌아온 응답은
/etc/passwd: No such file or directory.
순간적으로 뭔가 심상치 않다는 것을 알아차렸다. 그리고 다음 명령에 대한 응답으로 그것은 확인되었다.
ls /etc
내가 받은 것은
ls: not found.
나는 Peter에게 당분간은 아무것도 시도하지 않는 것이 좋겠다고 말하고, 시스템 관리자을 찾으러 갔다.
그의 사무실에 도착했을 때 문은 반쯤 열려 있었고, 10초도 지나지 않아 문제가 무엇인지 깨달았다. 우리 관리자 James는 세상의 끝을 방금 맞이한 사람처럼 앉아 머리를 손에 묻고, 손은 무릎 사이에 둔 채 있었다. 새로 임명된 시스템 프로그래머 Neil은 그 옆에서 터미널 화면을 멍하니 바라보고 있었다. 그리고 화면 맨 위에서 나는 다음 줄들을 보았다.
# cd
# rm -rf *
아, 젠장, 나는 생각했다. 그거면 설명이 딱 된다.
그 뒤 몇 분 동안 무슨 일이 있었는지 기억나지 않는다. 기억은 그저 흐릿하다. ls(또다시), ps, who, 그리고 아마 몇몇 다른 명령도 시도했지만 모두 소용없었다는 것만은 기억난다. 그다음으로 기억나는 것은 내가 다시 내 터미널(멀티윈도 그래픽 터미널)에 앉아 다음을 입력하고 있었다는 점이다.
cd / echo *
echo를 자신의 셸 내장 명령으로 만들어 준 David Korn에게 감사의 빚을 지고 있다. 말할 것도 없이 /bin은 /bin/echo와 함께 삭제되어 있었다. 그다음 몇 분 동안 드러난 사실은 /dev, /etc, /lib 역시 통째로 사라졌다는 것이었다. 다행히 Neil이 rm이 /news 아래 어딘가를 지우고 있을 때 그것을 중단시켰고, /tmp, /usr, /users는 모두 손상되지 않았다.
그 사이 James는 테이프 보관함으로 달려가 4주 전에 떠 놓았다고 적힌 루트 파일시스템의 덤프 테이프를 가져왔다. 시급한 질문은 "테이프 내용을 어떻게 복구하지?"였다. /etc/restore를 잃어버렸을 뿐 아니라, 테이프 드라이브용 디바이스 엔트리도 전부 사라졌다. 그리고 mknod는 어디에 있을까? 맞다, /etc다. 다른 VAX에서 Ethernet을 통해 이것들을 복구하는 것은 어떨까? 글쎄, /bin/tar는 사라졌고, Berkeley 사람들은 친절하게도 4.3 배포판에서 rcp를 /bin에 넣어 두었다. 게다가 최소한 /etc/hosts 없이는 어떤 Ether 관련 것도 제대로 동작하려 하지 않았다. 우리는 /usr/local에서 cpio 한 버전을 찾았지만, 테이프 드라이브가 없으면 그것도 별 도움이 되지 않을 것 같았다.
다른 방법으로는 부트 테이프를 꺼내 루트 파일시스템을 다시 구축할 수 있었지만, James도 Neil도 이전에 그렇게 해 본 적이 없었고, 첫 단계에서 디스크 전체가 재포맷되어 사용자 파일을 전부 잃게 되지 않을까 확신할 수 없었다. (사용자 파일 덤프는 매주 목요일에 뜬다. Murphy의 법칙에 따르면 이런 일은 반드시 수요일에 일어나야 한다.) 또 다른 해결책은 다른 VAX에서 디스크를 빌려 그것으로 부팅한 뒤 나중에 정리하는 것이었겠지만, 적어도 DEC 엔지니어를 불러내야 했을 것이다. 우리에게는 박사학위 논문을 거의 마무리하고 있는 사용자들이 여럿 있었고, 어쩌면 일주일치 작업을 잃는 일(기계 다운타임은 말할 것도 없고)은 상상조차 할 수 없었다.
그렇다면 무엇을 해야 할까? 다음 아이디어는 테이프 드라이브용 디바이스 디스크립터를 만드는 프로그램을 작성하는 것이었지만, cc, as, ld가 어디에 있는지는 우리 모두 알고 있다. 아니면 /etc/passwd, /etc/hosts 같은 것들의 뼈대 엔트리라도 만들어서 /usr/bin/ftp가 동작하게 할 수도 있었다. 정말 순전히 운 좋게도, 내 창 하나에는 아직 gnuemacs가 실행 중이었고, 그것으로 passwd 등을 만들 수 있었다. 하지만 첫 단계는 그것들을 넣을 디렉터리를 만드는 것이었다. 물론 /bin/mkdir는 사라졌고 /bin/mv도 없었으니 /tmp를 /etc로 이름 바꿀 수도 없었다. 그래도 이것은 꽤 그럴듯한 공략 방향으로 보였다.
그 무렵 우리 상주 UNIX 전문가 Alasdair도 합류했고, 운 좋게도 그는 VAX 어셈블리를 아는 사람이었다. 그래서 계획은 이렇게 되었다. /tmp를 /etc로 이름 바꾸거나, 혹은 /etc를 만드는 어셈블리 프로그램을 작성한다. 그것을 다른 VAX에서 어셈블하고, uuencode한 다음, uuencoded 파일을 내 gnu로 직접 입력하고, uudecode하고(uudecode를 /usr/bin에 넣어 둔 어떤 기특한 사람이 있었다), 실행한다. 그러면 짠, 그다음부터는 순탄하게 풀릴 터였다. 또 하나의 기적 같은 행운으로, 피해가 발생한 터미널은 아직 root로 su된 상태였다(su는 /bin에 있다는 것을 기억하라). 그러니 적어도 이 모든 것이 작동할 가능성은 있었다.
그리하여 우리는 씩씩하게 일을 시작했고, 불과 한 시간 만에 /etc를 만들 수 있는 열두 줄 남짓한 어셈블리 코드를 짜내는 데 성공했다. 스트립된 바이너리는 겨우 76바이트였기에, 우리는 그것을 16진수로 변환했다(uuencode 출력보다 조금 더 읽기 쉬웠다). 그리고 내 편집기로 그것을 입력했다. 여러분 중 누군가가 언젠가 같은 문제를 겪게 된다면, 나중을 위해 여기 그 16진수가 있다.
070100002c000000000000000000000000000000000000000000000000000000
0000dd8fff010000dd8f27000000fb02ef07000000fb01ef070000000000bc8f
8800040000bc012f65746300
마침 내가 ASCII 16진수를 바이너리로 바꾸는 편리한 프로그램을 하나 가지고 있었고(다들 그런 것 하나쯤은 있지 않은가?), /usr/bin/sum의 출력도 원래 바이너리와 일치했다. 하지만 잠깐, /bin/chmod 없이 실행 권한은 어떻게 설정하지? 몇 초간의 생각(늘 그렇듯 실제로는 몇 분이었다) 끝에, 이미 존재하는 어떤 바이너리, 그것도 내 소유인 것 위에 이 바이너리를 덮어쓰면 되겠다는 결론이 나왔다... 문제 해결.
그래서 우리는 root 로그인된 터미널로 가서, 조심스럽게 umask를 0으로 설정하는 것을 잊지 않고(그래야 내 gnu로 그 안에 파일을 만들 수 있었다), 바이너리를 실행했다. 그렇게 해서 모두가 쓸 수 있는 /etc가 생겼다. 거기서부터는 passwd, hosts, services, protocols(기타 등등)을 만들고, 그러자 ftp가 말을 잘 듣게 되는 몇 단계의 쉬운 작업만 남았다. 그다음 우리는 Ethernet을 통해 /bin의 내용을 복구했고(몇 시간만 지나도 ls가 얼마나 그리워지는지 놀라울 정도다), /etc의 일부 파일도 가져왔다. 핵심 파일은 /etc/rrestore였고, 그것으로 덤프 테이프에서 /dev를 복구했다. 그리고 나머지는 역사가 되었다.
이제 여러분은 스스로에게(나도 그렇듯이) 묻고 있을 것이다. 이 이야기의 교훈은 무엇일까? 우선 한 가지, 불멸의 말을 언제나 기억해야 한다. DON'T PANIC. 우리의 첫 반응은 머신을 재부팅하고 단일 사용자 모드에서 이것저것 시도해 보는 것이었지만, /etc/init와 /bin/sh 없이 시스템이 제대로 올라왔을 가능성은 거의 없었다. 이 경우 우리를 구한 것은 냉정한 사고였다.
다음으로 기억할 것은 UNIX 도구가 정말로 예상 밖의 용도로도 쓰일 수 있다는 점이다. 내 gnuemacs가 없었더라도, 예를 들어 /usr/bin/grep을 /bin/cat의 대용품으로 써서 버틸 수 있었을 것이다.
그리고 마지막으로, 시스템이 완전히 무너지지 않은 채로도 얼마나 많은 부분을 삭제할 수 있는지는 놀라울 정도다. 물론 아무도 로그인할 수는 없었고(bin/login?), 쓸모 있는 명령의 대부분도 사라졌지만, 그 외의 모든 것은 겉보기에는 정상처럼 보였다. 물론 /etc/termcap이나 /dev/kmem, /etc/utmp 없이 버티지 못하는 것들도 있지만, 대체로 전체는 그럭저럭 맞물려 돌아간다.
이 질문을 남기며 끝내고자 한다. 만약 여러분이 같은 상황에 놓였고, 뒤늦은 깨달음과 함께 오는 그 침착함을 당시에도 가지고 있었다면, 더 단순하거나 더 쉬운 방법으로 빠져나올 수 있었을까? 답장은 우표 붙인 엽서에 적어 다음으로 보내 주시기 바란다.
UNIX 복구 전설 / Hacker's Wisdom | 원문: USENET c.1986 | 마지막 수정: Sun Oct 13 23:58:51 2024