Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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ファイル内に格納される場合、その情報が適切に扱われていませんでした。

コミットメッセージによると、以下の点が変更の動機となっています。

  1. シンボリックリンクのサポート: ZIPファイルはシンボリックリンクを格納できます。これはUnixの S_IFLNK フラグで表現され、ファイルデータにはシンボリックリンクのターゲットパスが格納されます。これまでの実装では、この情報が適切に解釈・保存されていませんでした。
  2. os パッケージの進化: Goの os パッケージが、システムに依存しない形でファイルモードの全範囲(シンボリックリンク、デバイスファイル、FIFOなど)をサポートするようになったため、archive/zip パッケージでもこれらの情報を破棄する理由がなくなりました。
  3. 情報の一貫性と完全性: 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): キャラクターデバイス

これらのフラグは、ファイルモードの最上位ビットに設定されます。

シンボリックリンク(またはソフトリンク)は、ファイルシステム内の別のファイルやディレクトリへの参照(ポインタ)として機能する特殊な種類のファイルです。シンボリックリンクを開くと、参照先のファイルやディレクトリにリダイレクトされます。

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.gosymlink.zip という新しいテストケースが追加されました。このテストケースは、シンボリックリンクを含むZIPファイルを読み込み、そのファイルモードが os.ModeSymlink であること、および内容がシンボリックリンクのターゲットパスであることを検証します。

writer_test.go では、シンボリックリンクを書き込むテストケースが追加され、os.ModeSymlink フラグが正しく処理されることを確認しています。

これらの変更により、archive/zip パッケージは、ZIPファイル内のファイルモード情報をより完全に、かつシステムに依存しない形で扱うことができるようになり、Goの os パッケージとの連携が強化されました。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更は、主に src/pkg/archive/zip/struct.go ファイルに集中しています。

  1. 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 への変換で、より多くのファイルタイプを正確にマッピングするように修正。
  2. src/pkg/archive/zip/reader_test.go:

    • symlink.zip を使用した新しいテストケースの追加。シンボリックリンクの読み込みと検証を行う。
  3. src/pkg/archive/zip/writer_test.go:

    • シンボリックリンクを書き込むテストケースの修正。os.ModeSymlink を使用してシンボリックリンクが正しく書き込まれることを確認。
  4. 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 との間で正確な変換を行うことができるようになりました。

関連リンク

参考にした情報源リンク