参考: zipの仕様書
zipはリトルエンディアンであるため先に出現するデータが下位バイトである。
なんの話
うちのjs製zip生成が吐いたzipをunzipするとutf8のファイル名が文字化けした。
具体的にはGeneral Purpose Bit Flag
のutf8のビットは既に立ててあるにもかかわらず、asciiとしてパースされてしまった。
比較
a/あああ
を作成し、js製zip生成でzipを作成、比較対象としてzip -r0DX
(再帰、圧縮0、ディレクトリエントリ抜き、拡張フィールド抜き)でzipを作成した。
diffをとったら二箇所違いがあった
Version Made By
作成した環境を記録する2Bのデータである。
下位バイトがPKZIPのバージョン、上位ビットが作成したOSを表す。
js製zip生成では0x000a
、zip -r0DXは0x031e
であった。
作成OSについては仕様書4.4.2.2に記載があり、0x00
はMS-DOS、0x03
はUNIXである
後方互換性について考えると、ここで下位バイトがあまり意味を持たないことは容易に想像できる。
試しに0x03
を0x00
に変えたところ、文字化けが再現した。
作成OSのデータが解凍時の挙動に影響を与えていたことがわかる。
解凍できたは良いものの、解凍したファイルから再びzip -r0DX
しようとしたところPermission Deniedで止まった。
どうやら解凍時に正しい権限設定がなされていないようである。
External File Attributes
作成OSによりプロトコルの異なる4Bのデータである。
js製zip生成では0x00000000
、zip -r0DXは0x81a40000
であった。
調べたところ以下が当たった。
どうやら上位16bitは"ls -la
で見る権限のやつ"、"chmodでよく入れるあの数字"らしい。
フォーマットはglibcのbits/stat.h
に記載されているとのこと。
要点を抜粋する
/* Encoding of the file mode. */
#define __S_IFMT 0170000 /* These bits determine file type. */
/* File types. */
#define __S_IFDIR 0040000 /* Directory. */
#define __S_IFCHR 0020000 /* Character device. */
#define __S_IFBLK 0060000 /* Block device. */
#define __S_IFREG 0100000 /* Regular file. */
#define __S_IFIFO 0010000 /* FIFO. */
#define __S_IFLNK 0120000 /* Symbolic link. */
#define __S_IFSOCK 0140000 /* Socket. */
/* POSIX.1b objects. Note that these macros always evaluate to zero. But
they do it by enforcing the correct use of the macros. */
#define __S_TYPEISMQ(buf) ((buf)->st_mode - (buf)->st_mode)
#define __S_TYPEISSEM(buf) ((buf)->st_mode - (buf)->st_mode)
#define __S_TYPEISSHM(buf) ((buf)->st_mode - (buf)->st_mode)
/* Protection bits. */
#define __S_ISUID 04000 /* Set user ID on execution. */
#define __S_ISGID 02000 /* Set group ID on execution. */
#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */
#define __S_IREAD 0400 /* Read by owner. */
#define __S_IWRITE 0200 /* Write by owner. */
#define __S_IEXEC 0100 /* Execute by owner. */
頭の0は8進数を表す0なのでここで使われているビットはちょうど16bitである。
また、sys/stat.hには以下の記載もある
#define S_IRUSR __S_IREAD /* Read by owner. */
#define S_IWUSR __S_IWRITE /* Write by owner. */
#define S_IXUSR __S_IEXEC /* Execute by owner. */
/* Read, write, and execute by owner. */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)
#ifdef __USE_MISC
# define S_IREAD S_IRUSR
# define S_IWRITE S_IWUSR
# define S_IEXEC S_IXUSR
#endif
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
/* Read, write, and execute by group. */
#define S_IRWXG (S_IRWXU >> 3)
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
/* Read, write, and execute by others. */
#define S_IRWXO (S_IRWXG >> 3)
下位6bitについても確認が取れた。
0x81a40000
を読んでみる。
上位16bitを8進数に下すと0o100644
である。
上記を参照するとRegular File、権限が644ということになる。
知見
General Purpose Flag Bitを立てて満足してはいけない