며칠 전에 서버에서 작업을 진행하다가 용량 문제 때문에 잘 안 쓰는 대량의 파일들을 zip으로 압축해둔적이 있습니다. 문제는 zip 포맷의 한계로 4GB 이상의 파일을 다룰수가 없다는 문제가 있는걸 모르고 그냥 압축을 진행한 뒤 생성된 압축파일을 검증하지도 않고 그냥 원본 파일들을 다 지워버렸던 거죠. 뒤늦게 알아차리고 zip파일 압축을 해제하려고 했으나 손상된 파일이라며 압축 해제를 계속 실패했습니다...
Archive: archive.zip
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the
latter case the central directory and zipfile comment will be found on
the last disk(s) of this archive.
note: archive.zip may be a plain executable, not an archive
unzip: cannot find zipfile directory in one of archive.zip or
archive.zip.zip, and cannot find archive.zip.ZIP, period.
(아 안돼...)
여러 압축 유틸을 받아서 열어보았지만, 복구되는 파일은 4개에 불과했습니다. (전체 압축한게 1만개가 넘었는데...) 도저히 자료를 그대로 날릴수가 없어서 ZIP 파일 포맷 명세를 확인해보았습니다.
https://en.wikipedia.org/wiki/Zip_(file_format)
정말 다행인게 ZIP 포맷은 오픈소스로 명세가 다 공개되어있고, 또한 압축이 파일 단위로 진행되기에 일부 손상된 파일이 있어도 나머지 파일들은 멀쩡하게 복구할 수 있다는 거였습니다.
ZIP파일의 구조를 간략하게 설명하자면, 먼저 N개의 File Entry가 차례대로 나열되고, 마지막에 Central Directory 정보가 덧붙습니다. 사실 실제 압축된 파일의 정보들은 File Entry에 들어가고, Central Directory 정보에는 기타 메타데이터 및 빠른 파일 색인을 위한 정보들이 있는것이라서 이 부분은 없어도 파일을 복구하는데에는 문제가 없습니다.
File Entry의 구조는 다음과 같습니다.
Offset | Bytes | Description |
---|---|---|
0 | 4 | 파일헤더 시그니처 ('PK\x03\x04'의 값) |
4 | 2 | 버전 |
6 | 2 | 범용 비트 플래그 |
8 | 2 | 압축 방법 |
10 | 2 | 마지막 수정 시간 |
12 | 2 | 마지막 수정 날짜 |
14 | 4 | CRC-32 |
18 | 4 | 압축된 크기 |
22 | 4 | 압축되기 전 크기 (r) |
26 | 2 | 파일 이름 길이 (n) (바이트 단위) |
28 | 2 | 기타 필드 길이 (m) (바이트 단위) |
30 | n | 파일 이름 |
30+n | m | 기타 필드 |
30+n+m | r | 압축된 데이터 (총 r바이트 길이) |
이 부분이 계속 반복되면서 총 N개의 파일을 표현하는 것이죠. 만약 정상적인 파일이라면 하나의 File Entry를 다 읽은 다음에는 바로 다음 File Entry가 등장해야합니다. 이건 파일 헤더 시그니처 값이 'PK\x03\x04'가 나오는지를 통해서 확인할 수 있습니다.
그럼 제 파일은 왜 압축 해제 유틸에서 제대로 열리지 않았을까요? 이는 앞의 4개까지는 File Entry가 제대로 나왔는데, 그 다음부터는 잘못된 값들이 덮어씌워져 파일 헤더가 제대로 나타나지 않았기 때문입니다.
PK.. ~~~~~~ PK.. ~~~~~~~~~~~ PK.. ~~~ PK.. ~~~~~
원래는 위와 같은 모양으로 데이터가 나와야하는데 중간에 잘못된 값이 덮어씌워져서
PK.. ~~~~~~ PK.. ~~~~~~~~~ABCBASBD~~ PK.. ~~~~~
PK..가 등장할것으로 예상되는 지점에 다른 값이 섞여버려 있더라구요. 이렇게 되면 두번째 파일과 세번째 파일은 사실 값이 깨졌기 때문에 복구할수가 없습니다. 그러나 네 번째 파일부터는 또 제대로 PK..라고 시그니처가 등장하기 때문에 이 지점부터는 또 복구가 가능합니다. 따라서 중간에 예상치 않은 잘못된 값이 들어왔을 경우 다음 헤더 시그니처인 PK\x03\x04의 위치를 찾아 점프하는 식으로 살릴 수 있는 모든 File Entry를 발견할 수 있습니다.
그래서 급하게 코딩을 했습니다.
코드를 실행할때 손상된 zip파일의 경로를 argument로 넘겨주면, 그 중에서 복구 가능한 File Entry만 추출하여 restored.zip으로 저장합니다. 이렇게 되면 restored.zip파일은 비록 Central Directory는 없지만 다른 압축 유틸에서 충분히 복구가 가능한 상태가 됩니다.
압축을 시도했던 원본 파일들이 약 1만개가 넘었는데, 이 방법을 통해 720개를 제외한 나머지 9천 여개의 파일들을 복구할 수 있었습니다. (십년 감수했지요) 혹시나 저와 같이 문제를 겪으시는 분들을 위해 코드를 공유하니 자유롭게 쓰시면 되겠습니다.
물론 제일 좋은건 손상된 zip파일을 만들지 않는 것이구요!
결론1: 파일을 지우기 전에 3번만 더 생각하자.
결론2: ZIP파일은 오래된 포맷이라 4GB 이상의 파일을 지원하지 않는다!
[c++] 빠른 log sigmoid 계산 (0) | 2019.01.02 |
---|---|
[Python] 임의의 웹 페이지에서 텍스트를 추출하기 (1) | 2018.11.04 |
[Python] 호환용 한자를 통합 한자로 변환하기 (2) | 2018.10.28 |
SIMD를 이용한 깁스 샘플링 연산 최적화(SSE2, AVX) (0) | 2018.05.06 |
동시출현빈도 제대로 계산하기! (10) | 2018.03.04 |
영어 동사 원형 복원기(English Verb Lemmatizer) (0) | 2017.09.26 |
댓글 영역