bokumin.org

RSS Sitemap Mail
ritual-clearcut

開けないファイルを拡張子から調査する(Linux)

Verify the file extension to make sure it is correct

はじめに

会社で共有されたZIPの書類一式の中に、開けないファイルがいくつかありました。
はじめはZIPが壊れているのかなと思い、色々原因を探ってみたのですが、結論としてはZIPは一切壊れておらず、犯人は拡張子でした。

同じようなことは今後も起きそうなので、調べた手順を一通りまとめておきます。誰かの参考になれば幸いです。

※Linux向けの内容です(検証環境はKernel 7.0系、Celeron N4100機)

ZIPファイルチェック

unzipコマンドに-tを付けて、解凍前に整合性だけをチェックできます。

unzip -t archive.zip
Archive:  archive.zip
    testing: 画像 001.jpg             OK
    testing: 画像 002.jpg             OK
    testing: 画像 008.jpg             OK
No errors detected in compressed data of archive.zip.

全エントリのCRCチェックが走り、「No errors detected」と出ればZIPコンテナ自体は健全です。
※7-Zipがあれば7z t archive.zipでも同じことができます

ここでエラーが出る場合は、ダウンロード・転送が途中で切れているなど実データ自体が欠損している可能性が高いので、その場合は素直に再送をお願いするしかありません。

今回はここで「No errors detected」が出てしまい、ZIP自体は正常と考えてすすめました。

文字コードチェック

ファイル名が日本語の場合、Shift-JISで作られたZIPだと文字コードが原因で文字化けすることがあります。

unzip -O sjis archive.zip   # 表示・展開時にShift-JISとして解釈

これでうまくいかない場合は、Pythonでcp437として読み直してからcp932(Shift-JIS)に変換すると確実です。

import zipfile
with zipfile.ZipFile("archive.zip") as z:
    for info in z.infolist():
        name = info.filename.encode('cp437').decode('cp932')
        print(name)
画像 001.jpg
画像 002.jpg
画像 003.jpg
画像 008.jpg
画像 025.LST

※ファイル名は無事に読めるようになりましたが、これはあくまで「名前が読めるようになった」だけで、ファイルが開ける・開けないとは関係ないです。
実際に拡張子のチェックを次にしていきます。

拡張子と実体が合っているか

Linuxのfileコマンドは拡張子を一切見ず、ファイルの先頭バイト(マジックバイト)だけで中身の種類を判定してくれます。

unzip archive.zip -d test/
file test/*

test/画像 001.jpg: data
test/画像 008.jpg: Composite Document File V2 Document, Cannot read section info
test/画像 009.jpg: JPEG image data, Exif standard: [TIFF image data, ...]
test/画像 025.LST: JPEG image data, Exif standard: [TIFF image data, ...]

画像009.jpgは正常なJPEGですが、画像001.jpgはfileに「data」(=既知の形式に当てはまらない)とまで言われてしまいました。画像008.jpgは「Composite Document File V2」、つまりWordやExcelの旧形式・一太郎(.jtd)などと同じOLE複合文書形式です。画像025.LSTは拡張子が.LSTなのに中身は普通のJPEGでした。

手動でバイトを見る場合は、先頭16バイトだけで十分判断できます。

od -A x -t x1z -N 16 "画像 001.jpg"

000000 60 0e 82 01 07 80 03 00 c0 13 83 04 01 0d 0a 01  >`...............<

このマジックバイト(60 0e 82 01)は、富士フィルムさんのDocuWorks(.xdw)形式のものです。つまり画像001.jpgは拡張子こそ.jpgですが、実体はDocuWorksファイルだったというわけです。写真として開かないのも当然でした。

よく出てくるファイル形式の先頭バイトをまとめましたので参考にしてみてください。

先頭バイト実体
50 4B 03 04 (PK..)ZIP系(docx/xlsx/pptxもこれ)
25 50 44 46 (%PDF)PDF
FF D8 FF (末尾FF D9)JPEG
60 0E 82 01DocuWorks(.xdw)
D0 CF 11 E0古いOLE複合文書形式(jtd/doc/xls/ppt等)

補足:docxとxlsxの区別方法

別のファイルで、拡張子が.xdwなのに先頭バイトがPK 03 04になっているケースがありました。表の通りこれはZIP系、つまりOffice Open XML(docx/xlsx/pptx)の目印です。ただしPK 03 04はdocx/xlsx/pptxで共通なので、先頭バイトだけではこの3つまでは絞れません。

この場合はファイル自体を一旦ZIPとして開き、中のフォルダ構成を見れば確定します。

unzip -l "test.xdw"
Archive:  test.xdw
  Length      Date    Time    Name
---------  ---------- -----   ----
     2147  1980-01-01 00:00   [Content_Types].xml
     737  1980-01-01 00:00   _rels/.rels
     1774  1980-01-01 00:00   word/_rels/document.xml.rels
     7317  1980-01-01 00:00   word/document.xml
     991  1980-01-01 00:00   word/footnotes.xml

word/が入っていればWord(docx)、xl/が入っていればExcel(xlsx)、ppt/が入っていればPowerPoint(pptx)です。OOXML仕様で必須のフォルダなので、拡張子を完全に無視してもこれで断定できます。ワンライナーにするとこうなります。

unzip -l ファイル名 | grep -qm1 '^word/' && echo "docx" \
  || unzip -l ファイル名 | grep -qm1 '^xl/' && echo "xlsx" \
  || unzip -l ファイル名 | grep -qm1 '^ppt/' && echo "pptx"

ちなみに最近のfileコマンド(libmagic)はこの判定を内部でやってくれるので、file ファイル名を実行するだけで「Microsoft Word 2007+」といったように直接教えてくれることも多いです。めちゃ便利ですね

まとめ

結局すべて拡張子のせいだったというオチです。fileやodコマンドなどを使えばファイルの実体をみることができるので便利でした。

なぜこんなズレが起きたのかは正直わかりませんが、たぶん電子納品用にファイル一式をまとめる際、写真管理ソフトやDocuWorks側で連番リネーム・形式変換をかけた時に、何らかの理由で変換に失敗したファイルだけ元の拡張子がそのまま残ってしまった、というあたりが濃いんじゃないかなと思っています。CD-R納品のような古い慣習が残っている業界だと、似たような事故は今後も起こりそうです。

Windows環境で同じことをやる場合は、WSLを入れて同じコマンドを使うのが一番楽ですが、ネイティブで揃えるなら7-Zip・TrID・PowerShellのFormat-Hexの組み合わせになりそうです(わからん)。これはまた機会があれば・・

おわり

参考にしたサイト

マジックナンバーの特定にWikipediaとfilesig.search.orgを使用しました。リンクは以下の通りです。

Wikipedia List of file signatures (https://en.wikipedia.org/wiki/List_of_file_signatures)
GCK File Signature Table Powered by SEARCH (https://filesig.search.org/)