[インデックス 11644] ファイルの概要
このコミットは、Go言語の archive/zip
パッケージにおいて、ZIPファイルがサポートする FileMode
フラグの範囲を拡張し、特にシンボリックリンク(symlink)の適切な処理を可能にするものです。これにより、ZIPファイル内のエントリが持つUnixのファイルモード情報(ファイルタイプやパーミッションなど)をより正確に表現し、システムに依存しない形で扱うことができるようになりました。
コミット
commit 02fb021161cf36cf7326380fb1ff9239cd195067
Author: Gustavo Niemeyer <gustavo@niemeyer.net>
Date: Mon Feb 6 11:58:32 2012 -0200
archive/zip: support full range of FileMode flags
Zip files may actually store symlinks, and that's represented
as a file with unix flag S_IFLNK and with its data containing
the symlink target name.
The other flags are being supported too. Now that the os package
has the full range of flags in a system agnostic manner, there's
no reason to discard that information.
R=golang-dev, adg, rogpeppe
CC=golang-dev
https://golang.org/cl/5624048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/02fb021161cf36cf7326380fb1ff9239cd195067
元コミット内容
archive/zip: support full range of FileMode flags
Zip files may actually store symlinks, and that's represented
as a file with unix flag S_IFLNK and with its data containing
the symlink target name.
The other flags are being supported too. Now that the os package
has the full range of flags in a system agnostic manner, there's
no reason to discard that information.
R=golang-dev, adg, rogpeppe
CC=golang-dev
https://golang.org/cl/5624048
変更の背景
この変更の主な背景は、Go言語の archive/zip
パッケージがZIPファイル内のエントリのファイルモード(パーミッションやファイルタイプなど)を完全にサポートしていなかった点にあります。特に、シンボリックリンクがZIPファイル内に格納される場合、その情報が適切に扱われていませんでした。
コミットメッセージによると、以下の点が変更の動機となっています。
- シンボリックリンクのサポート: ZIPファイルはシンボリックリンクを格納できます。これはUnixの
S_IFLNK
フラグで表現され、ファイルデータにはシンボリックリンクのターゲットパスが格納されます。これまでの実装では、この情報が適切に解釈・保存されていませんでした。 os
パッケージの進化: Goのos
パッケージが、システムに依存しない形でファイルモードの全範囲(シンボリックリンク、デバイスファイル、FIFOなど)をサポートするようになったため、archive/zip
パッケージでもこれらの情報を破棄する理由がなくなりました。- 情報の一貫性と完全性: ZIPファイルが持つファイルモード情報を完全に利用することで、より堅牢で互換性のあるアーカイブ処理が可能になります。
この変更により、archive/zip
パッケージは、ZIPファイル内のエントリが持つファイルシステム上の特性(ディレクトリ、通常ファイル、シンボリックリンク、デバイスファイルなど)をより正確に反映できるようになり、異なるOS間でのZIPファイルの互換性と機能性が向上しました。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
1. ZIPファイルフォーマット
ZIPは、ファイルを圧縮・アーカイブするための一般的なファイルフォーマットです。単一のファイルに複数のファイルやディレクトリをまとめて格納できます。ZIPファイル内の各エントリ(ファイルやディレクトリ)は、その名前、圧縮データ、およびメタデータ(最終更新日時、圧縮方法、元のファイルサイズなど)を持っています。
ZIPファイルフォーマットには、ファイルモード(パーミッションやファイルタイプ)を格納するためのフィールドが存在します。これは通常、Unixのファイルモードビットに対応する形で格納されます。
2. Unixファイルモード (FileMode)
Unix系OSでは、ファイルやディレクトリには「ファイルモード」と呼ばれる属性が関連付けられています。これは、ファイルのタイプ(通常ファイル、ディレクトリ、シンボリックリンクなど)と、そのファイルに対するアクセス権限(読み取り、書き込み、実行)を定義します。
ファイルモードは通常、8進数で表現され、例えば 0755
は所有者に読み書き実行権限、グループとその他に読み取りと実行権限を与えることを意味します。
ファイルタイプを示すフラグも存在し、主なものは以下の通りです。
S_IFREG
(0x8000): 通常ファイルS_IFDIR
(0x4000): ディレクトリS_IFLNK
(0xA000): シンボリックリンクS_IFSOCK
(0xC000): ソケットS_IFIFO
(0x1000): 名前付きパイプ (FIFO)S_IFBLK
(0x6000): ブロックデバイスS_IFCHR
(0x2000): キャラクターデバイス
これらのフラグは、ファイルモードの最上位ビットに設定されます。
3. シンボリックリンク (Symlink)
シンボリックリンク(またはソフトリンク)は、ファイルシステム内の別のファイルやディレクトリへの参照(ポインタ)として機能する特殊な種類のファイルです。シンボリックリンクを開くと、参照先のファイルやディレクトリにリダイレクトされます。
Unix系OSでは、シンボリックリンクは S_IFLNK
フラグを持つファイルとして扱われ、そのファイルの内容はリンク先のパス名になります。
4. Go言語の os
パッケージと os.FileMode
Go言語の標準ライブラリ os
パッケージは、オペレーティングシステムとのインタラクションを提供します。これには、ファイルシステム操作も含まれます。
os.FileMode
型は、Goにおいてファイルモードを表現するための型です。これは、Unixのファイルモードビットを抽象化したもので、os.ModeDir
(ディレクトリ), os.ModeSymlink
(シンボリックリンク), os.ModeDevice
(デバイスファイル) などの定数とビット演算子を使って、ファイルのタイプやパーミッションを表現します。
このコミット以前は、archive/zip
パッケージが os.FileMode
の全ての情報をZIPファイルとの間で適切に変換できていませんでした。特に、os.ModeSymlink
のような特殊なファイルタイプが失われる可能性がありました。
技術的詳細
このコミットの技術的詳細は、主に archive/zip
パッケージがZIPファイル内のファイルモード情報をどのように解釈し、Goの os.FileMode
との間で変換するかという点に集約されます。
1. ZIPファイルにおけるUnixファイルモードの格納
ZIPファイルフォーマットの仕様(特に「Extra Field」セクション)には、Unixのファイルモードを格納するための拡張フィールド(例えば UNIX extra field
)が存在します。このフィールドは、ファイルのパーミッションビットだけでなく、ファイルタイプ(通常ファイル、ディレクトリ、シンボリックリンクなど)も格納できます。
ZIPファイル内のエントリのヘッダには、通常、外部ファイル属性(External File Attributes)というフィールドがあり、ここにUnixのファイルモードがエンコードされて格納されることが一般的です。このフィールドは、OS固有のファイル属性を保持するために使用されます。
2. s_IFMT
定数の拡張
src/pkg/archive/zip/struct.go
内で定義されているUnix定数 s_IFMT
(ファイルタイプマスク) および個々のファイルタイプを示す定数(s_IFDIR
, s_IFREG
など)が拡張されました。
変更前は、s_IFMT
, s_IFDIR
, s_IFREG
, s_ISUID
, s_ISGID
のみが定義されていました。
変更後には、以下の定数が追加されました。
s_IFSOCK
(ソケット)s_IFLNK
(シンボリックリンク)s_IFBLK
(ブロックデバイス)s_IFCHR
(キャラクターデバイス)s_IFIFO
(名前付きパイプ)s_ISVTX
(スティッキービット)
これらの追加により、ZIPファイルから読み取られるUnixファイルモードの情報をより詳細に解釈できるようになりました。
3. fileModeToUnixMode
関数の改善
この関数は、Goの os.FileMode
をZIPファイルに書き込むためのUnixファイルモード(uint32
)に変換します。
変更前は、os.ModeDir
かどうかでディレクトリか通常ファイルかを判断する単純なロジックでした。
変更後には、switch mode & os.ModeType
を使用して、os.FileMode
のタイプビットを詳細にチェックし、対応するUnixファイルタイプフラグ(s_IFDIR
, s_IFLNK
, s_IFIFO
, s_IFSOCK
, s_IFBLK
, s_IFCHR
)を設定するように改善されました。これにより、シンボリックリンクやデバイスファイルなどの特殊なファイルタイプも正しく変換されるようになりました。
また、os.ModeSticky
に対応する s_ISVTX
フラグの変換も追加されました。
4. unixModeToFileMode
関数の改善
この関数は、ZIPファイルから読み取られたUnixファイルモード(uint32
)をGoの os.FileMode
に変換します。
変更前は、s_IFDIR
かどうかでディレクトリを判断する単純なロジックでした。
変更後には、switch m & s_IFMT
を使用して、Unixファイルモードのタイプビットを詳細にチェックし、対応する os.FileMode
のタイプフラグ(os.ModeDevice
, os.ModeCharDevice
, os.ModeDir
, os.ModeNamedPipe
, os.ModeSymlink
, os.ModeSocket
)を設定するように改善されました。これにより、ZIPファイルから読み取られたシンボリックリンクやデバイスファイルなどの情報が、Goの os.FileMode
に正確にマッピングされるようになりました。
同様に、s_ISVTX
フラグに対応する os.ModeSticky
の変換も追加されました。
5. テストケースの追加と修正
reader_test.go
に symlink.zip
という新しいテストケースが追加されました。このテストケースは、シンボリックリンクを含むZIPファイルを読み込み、そのファイルモードが os.ModeSymlink
であること、および内容がシンボリックリンクのターゲットパスであることを検証します。
writer_test.go
では、シンボリックリンクを書き込むテストケースが追加され、os.ModeSymlink
フラグが正しく処理されることを確認しています。
これらの変更により、archive/zip
パッケージは、ZIPファイル内のファイルモード情報をより完全に、かつシステムに依存しない形で扱うことができるようになり、Goの os
パッケージとの連携が強化されました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に src/pkg/archive/zip/struct.go
ファイルに集中しています。
-
src/pkg/archive/zip/struct.go
:- Unixファイルモード定数(
s_IFSOCK
,s_IFLNK
,s_IFBLK
,s_IFCHR
,s_IFIFO
,s_ISVTX
)の追加。 fileModeToUnixMode
関数のロジック変更。os.FileMode
からUnixモードへの変換で、より多くのファイルタイプ(シンボリックリンク、デバイス、FIFO、ソケットなど)を考慮するように修正。unixModeToFileMode
関数のロジック変更。Unixモードからos.FileMode
への変換で、より多くのファイルタイプを正確にマッピングするように修正。
- Unixファイルモード定数(
-
src/pkg/archive/zip/reader_test.go
:symlink.zip
を使用した新しいテストケースの追加。シンボリックリンクの読み込みと検証を行う。
-
src/pkg/archive/zip/writer_test.go
:- シンボリックリンクを書き込むテストケースの修正。
os.ModeSymlink
を使用してシンボリックリンクが正しく書き込まれることを確認。
- シンボリックリンクを書き込むテストケースの修正。
-
src/pkg/archive/zip/testdata/symlink.zip
:- 新しいバイナリテストデータファイル。シンボリックリンクを含むZIPファイル。
コアとなるコードの解説
src/pkg/archive/zip/struct.go
の変更点
このファイルは、ZIPアーカイブの構造体と、ファイルモードの変換ロジックを定義しています。
1. Unix定数の追加
// traditional names for Unix constants
const (
- s_IFMT = 0xf000
- s_IFDIR = 0x4000
- s_IFREG = 0x8000
- s_ISUID = 0x800
- s_ISGID = 0x400
+ // Unix constants. The specification doesn't mention them,
+ // but these seem to be the values agreed on by tools.
+ s_IFMT = 0xf000
+ s_IFSOCK = 0xc000
+ s_IFLNK = 0xa000
+ s_IFREG = 0x8000
+ s_IFBLK = 0x6000
+ s_IFDIR = 0x4000
+ s_IFCHR = 0x2000
+ s_IFIFO = 0x1000
+ s_ISUID = 0x800
+ s_ISGID = 0x400
+ s_ISVTX = 0x200
)
s_IFMT
はファイルタイプを識別するためのマスクです。これに加えて、s_IFSOCK
(ソケット)、s_IFLNK
(シンボリックリンク)、s_IFBLK
(ブロックデバイス)、s_IFCHR
(キャラクターデバイス)、s_IFIFO
(名前付きパイプ) といった新しいファイルタイプ定数が追加されました。また、s_ISVTX
(スティッキービット) も追加され、より多くのUnixファイル属性を表現できるようになりました。
2. fileModeToUnixMode
関数の変更
この関数は、Goの os.FileMode
をZIPヘッダに格納されるUnixファイルモード(uint32
)に変換します。
func fileModeToUnixMode(mode os.FileMode) uint32 {
var m uint32
- if mode&os.ModeDir != 0 {
- m = s_IFDIR
- } else {
- m = s_IFREG
+ switch mode & os.ModeType { // os.ModeType はファイルタイプを示すビットを抽出するマスク
+ default:
+ m = s_IFREG // デフォルトは通常ファイル
+ case os.ModeDir:
+ m = s_IFDIR // ディレクトリ
+ case os.ModeSymlink:
+ m = s_IFLNK // シンボリックリンク
+ case os.ModeNamedPipe:
+ m = s_IFIFO // 名前付きパイプ
+ case os.ModeSocket:
+ m = s_IFSOCK // ソケット
+ case os.ModeDevice: // デバイスファイルの場合
+ if mode&os.ModeCharDevice != 0 {
+ m = s_IFCHR // キャラクターデバイス
+ } else {
+ m = s_IFBLK // ブロックデバイス
+ }
}
if mode&os.ModeSetuid != 0 {
m |= s_ISUID
}
if mode&os.ModeSetgid != 0 {
m |= s_ISGID
}
+ if mode&os.ModeSticky != 0 { // スティッキービットの追加
+ m |= s_ISVTX
+ }
return m | uint32(mode&0777) // パーミッションビットを結合
}
変更前は、os.ModeDir
の有無のみでディレクトリか通常ファイルかを判断していました。変更後は、switch mode & os.ModeType
を用いて、os.FileMode
が持つより詳細なファイルタイプ(os.ModeSymlink
, os.ModeNamedPipe
, os.ModeSocket
, os.ModeDevice
など)を識別し、対応するUnixファイルタイプフラグを設定するように改善されました。これにより、Goの os.FileMode
の情報がZIPファイルに正確に反映されるようになりました。また、os.ModeSticky
に対応する s_ISVTX
フラグの変換も追加されています。
3. unixModeToFileMode
関数の変更
この関数は、ZIPヘッダから読み取られたUnixファイルモード(uint32
)をGoの os.FileMode
に変換します。
func unixModeToFileMode(m uint32) os.FileMode {
- var mode os.FileMode
- if m&s_IFMT == s_IFDIR {
- mode |= os.ModeDir
+ mode := os.FileMode(m & 0777) // パーミッションビットを初期化
+ switch m & s_IFMT { // ファイルタイプマスクでタイプを識別
+ case s_IFBLK:
+ mode |= os.ModeDevice // ブロックデバイス
+ case s_IFCHR:
+ mode |= os.ModeDevice | os.ModeCharDevice // キャラクターデバイス
+ case s_IFDIR:
+ mode |= os.ModeDir // ディレクトリ
+ case s_IFIFO:
+ mode |= os.ModeNamedPipe // 名前付きパイプ
+ case s_IFLNK:
+ mode |= os.ModeSymlink // シンボリックリンク
+ case s_IFREG:
+ // nothing to do (通常ファイルは追加のフラグ不要)
+ case s_IFSOCK:
+ mode |= os.ModeSocket // ソケット
}
if m&s_ISGID != 0 {
mode |= os.ModeSetgid
}
if m&s_ISUID != 0 {
mode |= os.ModeSetuid
}
- return mode | os.FileMode(m&0777)
+ if m&s_ISVTX != 0 { // スティッキービットの追加
+ mode |= os.ModeSticky
+ }
+ return mode
}
変更前は、s_IFDIR
の有無のみでディレクトリを判断していました。変更後は、switch m & s_IFMT
を用いて、Unixファイルモードが持つより詳細なファイルタイプ(s_IFBLK
, s_IFCHR
, s_IFDIR
, s_IFIFO
, s_IFLNK
, s_IFREG
, s_IFSOCK
)を識別し、対応する os.FileMode
のタイプフラグを設定するように改善されました。これにより、ZIPファイルから読み取られたファイルモード情報がGoの os.FileMode
に正確にマッピングされるようになりました。また、s_ISVTX
フラグに対応する os.ModeSticky
の変換も追加されています。
src/pkg/archive/zip/reader_test.go
の変更点
シンボリックリンクを含むZIPファイルを読み込む新しいテストケースが追加されました。
{
Name: "symlink.zip",
File: []ZipTestFile{
{
Name: "symlink",
Content: []byte("../target"), // シンボリックリンクのターゲットパス
Mode: 0777 | os.ModeSymlink, // シンボリックリンクのファイルモード
},
},
},
このテストは、symlink.zip
というテストデータファイル(このコミットで追加されたバイナリファイル)を読み込み、その中の symlink
というエントリが os.ModeSymlink
フラグを持ち、内容が ../target
であることを検証します。これにより、シンボリックリンクの読み込みが正しく行われることが保証されます。
src/pkg/archive/zip/writer_test.go
の変更点
シンボリックリンクを書き込むテストケースが修正されました。
{
- Name: "setgid",
- Data: []byte("setgid file"),
+ Name: "symlink",
+ Data: []byte("../link/target"), // シンボリックリンクのターゲットパス
Method: Deflate,
- Mode: 0755 | os.ModeSetgid,
+ Mode: 0755 | os.ModeSymlink, // シンボリックリンクのファイルモード
},
既存の setgid
テストケースが symlink
テストケースに置き換えられました。これにより、os.ModeSymlink
フラグを持つファイルがZIPファイルに正しく書き込まれることが検証されます。Data
フィールドにはシンボリックリンクのターゲットパスが格納されます。
これらの変更により、Goの archive/zip
パッケージは、ZIPファイル内のシンボリックリンクやその他の特殊なファイルタイプを完全にサポートし、Goの os.FileMode
との間で正確な変換を行うことができるようになりました。
関連リンク
- Go CL 5624048: https://golang.org/cl/5624048
参考にした情報源リンク
- GoDoc: archive/zip package
- GoDoc: os package
- GoDoc: os.FileMode type
- Wikipedia: ZIP (file format)
- Wikipedia: Symbolic link
- Unix file types and permissions (General concept)
- The .ZIP File Format Specification (PKWARE's official specification, relevant for "External File Attributes" and "Extra Field" sections)