Lambdaカクテル

京都在住Webエンジニアの日記です

Invite link for Scalaわいわいランド

TARにアーカイブされたファイル名のエンコーディングはどうなっているのか

ふと思って調べたところ色々と面白いことが分かったのでメモ。

TARという形式がある。Tape ARchiveの略で、元々はテープドライブにファイルシステムを記録するためのフォーマットだが、ファイルシステムを1つのファイルに結合できるという利便性から、現代ではアーカイブ目的やファイルの配布目的として広く使われている。これを読んでいるあなたも、多分一度は使ったことがあるはず。

さて、 ファイルシステムをそのままテープに記録するからには、ファイルの内容はもちろん、ファイル・パーミッションも保存されるし、オーナー情報も保存される。そして、ファイル名も保存されるのだが、ファイル名はテープドライブにどのようなエンコーディングで保存されるのだろうか?UTF-8?それとも別の形式だろうか?

ちなみに、GNUのtar(1)は日本語をファイル名に含むアーカイブを作成できる:

windymelt% tar cvf cat.tar 猫
猫/
猫/猫.txt

僕の実行環境では$LANG=ja_JP.UTF-8で、UTF-8ではe7 8c abに該当する。

windymelt% hexyl cat.tar
(snip)
│00000a00│ e7 8c ab 2f e7 8c ab 2e ┊ 74 78 74 00 00 00 00 00 │×××/×××.┊txt00000│
│00000a10│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│*       │                         ┊                         │        ┊        │
│00000a60│ 00 00 00 00 30 30 30 30 ┊ 36 34 34 00 30 30 30 31 │00000000┊64400001│
│00000a70│ 37 35 30 00 30 30 30 30 ┊ 31 34 34 00 30 30 30 30 │75000000┊14400000│
│00000a80│ 30 30 30 30 30 31 37 00 ┊ 31 34 33 32 31 35 33 32 │00000170┊14321532│
│00000a90│ 32 35 30 00 30 31 35 32 ┊ 37 37 00 20 30 00 00 00 │25000152┊770 0000│
│00000aa0│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│*       │                         ┊                         │        ┊        │
│00000b00│ 00 75 73 74 61 72 00 30 ┊ 30 77 69 6e 64 79 6d 65 │0ustar00┊0windyme│
│00000b10│ 6c 74 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │lt000000┊00000000│
│00000b20│ 00 00 00 00 00 00 00 00 ┊ 00 75 73 65 72 73 00 00 │00000000┊0users00│
│00000b30│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000b40│ 00 00 00 00 00 00 00 00 ┊ 00 30 30 30 30 30 30 30 │00000000┊00000000│
│00000b50│ 00 30 30 30 30 30 30 30 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│00000b60│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│*       │                         ┊                         │        ┊        │
│00000c00│ 6d 65 6f 77 20 6d 65 6f ┊ 77 20 6d 65 6f 77 0a 00 │meow meo┊w meow_0│
│00000c10│ 00 00 00 00 00 00 00 00 ┊ 00 00 00 00 00 00 00 00 │00000000┊00000000│
│*       │                         ┊                         │        ┊        │
│00002800│                         ┊                         │        ┊        │
└────────┴─────────────────────────┴─────────────────────────┴────────┴────────┘

そっくりそのまま、e7 8c abが登場した。TARはUTF-8でファイル名をエンコードするのだろうか?それとも、ファイルシステムが返すバイト列をそのまま格納するのだろうか?

ちなみにバイナリビューワにはHexylを使っている。便利なのでおすすめ。

github.com

tarはファイル名を関知しない

結論から言えば、tarはファイルシステムが返すファイル名のバイト列をそのまま記録するようだ。これは挙動から推測したもので、決定的なドキュメントを見付けたわけではない。255文字を超えないNULL終端文字列であればなんでも良いようだ。

その一方で、非-ASCIIなファイル名はtar の構造によれば規格外になるようだ。特に、POSIXが定めるFilenaming conventionsを遵守することが望ましいようだ。

osr5doc.xinuos.com

要するに、規格外だがGNU Tarではなぜか動く、といった状態なのだが、今や誰もがGNU Tarを使っていて、非-ASCIIファイル名を付ける人間が少ないのであまり困ったことにはなっていない、という状況のようだ。

PAX(1)コマンド

ちなみに、tar(1)はPOSIX標準ではない(マジかよ!)らしいので、POSIXに揃えるならば、きちんと仕様が定義されたファイルを作成できるpax(1)を使うべきだろう(生成されるファイルはtar同等で、普通にtarで解凍できる)。昔の人は、tarファイルの相互運用で相当苦労したことが伺える。paxは原則としてPOSIXに書かれていない機能は提供しない。

windymelt% pax -w  猫 > cat2.tar
windymelt% tar xvf cat2.tar
猫/
猫/猫.txt

qiita.com

Paxというネーミングは、Pax Romana同様、平和を意味しているとのこと。ファイルフォーマットをめぐる争いに平和をもたらしたかったらしい。

じゃあファイルシステムのファイル名のエンコーディングはどうなっているのか

ではファイルシステム自体はどのようにファイル名をエンコーディングするのか?という疑問が当然浮かぶのだが、以下の記事によれば、大抵のLinuxにおけるファイルシステムはエンコーディングを関知しない(つまり、許可された任意のバイト列を格納してよい)ということらしい。

superuser.com

unix.stackexchange.com

例えばbtrfsではNULと '/' を除くすべての文字を許容している。したがってBtrfsを使っているなら、NUL/を除くすべてのバイトからなる255文字を超えないバイト列をファイル名としてtarに格納可能だ。

NTFS

ところで、特筆すべき例外として、NTFSはファイル名のエンコーディングを指定している。NTFSでは、ファイル名としてUTF-16エンコーディングを使う。より厳密には、UTF-16LE(Little Endian)を使用し、BOMは付与されない(BOMが付いていたらそれは単にUTF-16と呼ぶ)。

learn.microsoft.com

そして、UTF-16でエンコードされた文字列にはnull byteが含まれるおそれがあり(UTF-8ではそうはならない)、ここれによりtarの作成や解凍に失敗するかもしれない(例えば、WはUTF-16LEにおいて0x57 0x00にエンコードされる)。これは実験していないので実際のところどうなるのかは分からない。

これには先行研究が存在する:

wanderingengineer.hatenablog.com

UTF-16のBOMについてはこちら:

www.wdic.org

tar vs cpio物語:

qiita.com

★記事をRTしてもらえると喜びます
Webアプリケーション開発関連の記事を投稿しています.読者になってみませんか?