[インデックス 13161] ファイルの概要
このコミットは、Go言語の標準ライブラリ archive/tar
パッケージ内のテストファイル tar_test.go
における、Windows環境でのテスト失敗を修正するものです。具体的には、FileInfoHeader
関数のテストにおいて、ファイルモードの比較方法をプラットフォームに依存しない形に修正しています。
コミット
commit 68f42ea27eb7c399e8577d4b5f999cec4ac59e20
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu May 24 14:32:18 2012 -0700
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/68f42ea27eb7c399e8577d4b5f999cec4ac59e20
元コミット内容
archive/tar: fix windows test failure
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6249052
変更の背景
このコミットの背景には、Go言語のクロスプラットフォーム対応におけるファイルパーミッションの扱いの違いがあります。Unix系OS(Linux, macOSなど)では、ファイルのパーミッションはrwxrwxrwx
のような形式で表現され、数値(例: 0644
)で設定されます。しかし、Windows OSでは、このようなUnixライクなパーミッションモデルは直接存在しません。Windowsのファイルシステム(NTFSなど)は、アクセス制御リスト(ACL)に基づいてアクセス権を管理しており、Unixのパーミッションとは概念が異なります。
archive/tar
パッケージは、TARアーカイブの読み書きを行うためのものであり、TARフォーマット自体はUnixのファイルパーミッションを記録するフィールドを持っています。したがって、Goのos.FileInfo
インターフェースは、ファイルシステムから取得した情報を抽象化し、Mode()
メソッドを通じてos.FileMode
型のファイルモードを提供します。このos.FileMode
は、Unixのパーミッションビットと、ディレクトリやシンボリックリンクなどのファイルタイプビットを組み合わせたものです。
問題が発生したのは、TestFileInfoHeader
というテスト関数内で、TARヘッダから抽出したファイルモードが期待される値(0644
)と一致するかを検証していた点です。Unix環境では0644
という固定値で問題ありませんでしたが、Windows環境でテストを実行した場合、os.FileInfo.Mode()
が返すパーミッションビットがUnix系OSとは異なる挙動を示すことがありました。特に、Windowsでは実行可能ビット(0111
)が常に設定される、あるいは特定のパーミッションビットが期待通りにマッピングされないといった差異が生じることが知られています。
この差異により、Windows上でarchive/tar
パッケージのテストを実行すると、h.Mode
(TARヘッダから取得したモード)とint64(0644|c_ISREG)
(期待されるモード)の比較が失敗していました。このコミットは、このプラットフォーム間の差異を吸収し、テストがWindows上でも正しく動作するように修正することを目的としています。
前提知識の解説
Go言語の archive/tar
パッケージ
archive/tar
パッケージは、TARアーカイブ(.tar
ファイル)の読み書きを実装したGoの標準ライブラリです。TARファイルは、複数のファイルを一つのアーカイブにまとめるためのフォーマットで、Unix系システムで広く使われています。ファイルの内容だけでなく、ファイル名、サイズ、タイムスタンプ、そしてファイルパーミッションなどのメタデータも保存します。
os.FileInfo
と os.FileMode
Go言語では、ファイルに関するメタデータは os.FileInfo
インターフェースを通じて提供されます。このインターフェースは、ファイル名、サイズ、最終更新時刻、そしてファイルモードなどの情報にアクセスするためのメソッドを定義しています。
os.FileMode
は、os.FileInfo.Mode()
メソッドが返す型で、ファイルのパーミッションビットとファイルタイプビット(例: ディレクトリ、シンボリックリンク、通常ファイルなど)を組み合わせたものです。
ファイルパーミッション (Unix vs. Windows)
- Unix系OS:
rwx
(読み取り、書き込み、実行)の3つの権限が、所有者、グループ、その他のユーザーに対してそれぞれ設定されます。これらは通常、8進数(例:0644
は所有者が読み書き、グループとその他が読み取りのみ)で表現されます。 - Windows OS: NTFSファイルシステムでは、アクセス制御リスト(ACL)が使用され、より詳細な権限設定が可能です。Unixのパーミッションモデルとは直接的な互換性がありません。Goの
os
パッケージは、可能な限りUnixパーミッションにマッピングしようとしますが、完全な一致は保証されません。
os.FileMode.Perm()
メソッド
os.FileMode
型には Perm()
メソッドがあり、これはファイルモードからファイルパーミッションビットのみを抽出して返します。つまり、ファイルタイプビット(例: os.ModeDir
, os.ModeSymlink
など)を除外した、純粋なパーミッション部分(0o777
マスクされた部分)を返します。
c_ISREG
定数
c_ISREG
は、archive/tar
パッケージ内で定義されている定数で、TARヘッダのファイルモードにおいて、そのエントリが通常のファイル(regular file)であることを示すビットです。TARフォーマットでは、ファイルタイプを示すビットとパーミッションビットが同じフィールドに格納されるため、この定数を使って通常のファイルであることを明示的に示す必要があります。
技術的詳細
このコミットの核心は、tar_test.go
内のTestFileInfoHeader
関数におけるファイルモードの比較ロジックの変更です。
元のコードでは、以下のように固定の8進数0644
とc_ISREG
をOR演算した値と比較していました。
if g, e := h.Mode, int64(0644|c_ISREG); g != e {
t.Errorf("Mode = %#o; want %#o", g, e)
}
ここでh.Mode
は、TARヘッダから読み取られたファイルモードです。0644
はUnixにおける一般的なファイルパーミッション(所有者読み書き、グループとその他読み取り)を表します。
しかし、Windows環境では、os.FileInfo
から得られるファイルモードのパーミッション部分が、Unix系OSと同じ0644
になるとは限りません。特に、Goのos
パッケージがWindowsのACLをUnixパーミッションにマッピングする際に、実行可能ビット(0111
)が常に設定されるなどの挙動の違いが生じることがあります。
この問題を解決するため、コミットでは比較対象の期待値をint64(fi.Mode().Perm())|c_ISREG
に変更しました。
if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
t.Errorf("Mode = %#o; want %#o", g, e)
}
ここで、fi
はテスト対象のファイル(small.txt
)のos.FileInfo
インスタンスです。
fi.Mode().Perm()
は、現在のOS(この場合はWindows)がsmall.txt
に対して報告する実際のパーミッションビットを抽出します。これにより、テストはハードコードされたUnixパーミッション0644
と比較するのではなく、その環境で実際にファイルが持つパーミッションと比較するようになります。
c_ISREG
は、TARヘッダが通常のファイルであることを示すビットであり、これはプラットフォームに依存しないため、引き続きOR演算で結合されます。
この変更により、Windows環境でos.FileInfo.Mode().Perm()
が返す値が0644
と異なっていたとしても、テストはh.Mode
がその環境で期待されるパーミッションビットとc_ISREG
の組み合わせであることを検証できるようになり、テストの失敗が解消されました。これは、クロスプラットフォームなテストにおいて、環境固有の挙動を考慮した柔軟な比較を行うための典型的なアプローチです。
コアとなるコードの変更箇所
--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -22,7 +22,7 @@ func TestFileInfoHeader(t *testing.T) {
if g, e := h.Name, "small.txt"; g != e {
t.Errorf("Name = %q; want %q", g, e)
}
- if g, e := h.Mode, int64(0644|c_ISREG); g != e {
+ if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
t.Errorf("Mode = %#o; want %#o", g, e)
}
if g, e := h.Size, int64(5); g != e {
コアとなるコードの解説
変更された行は以下の部分です。
- if g, e := h.Mode, int64(0644|c_ISREG); g != e {
+ if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISREG; g != e {
-
変更前:
int64(0644|c_ISREG)
0644
はUnix系のファイルパーミッションを直接指定した8進数リテラルです。c_ISREG
は、TARエントリが通常のファイルであることを示すビットマスクです。- この行は、TARヘッダから読み取ったモード
h.Mode
が、ハードコードされた0644
パーミッションとc_ISREG
ビットの組み合わせと完全に一致することを期待していました。Windows環境では、ファイルシステムが報告するパーミッションが0644
と異なる場合があるため、この比較が失敗していました。
-
変更後:
int64(fi.Mode().Perm())|c_ISREG
fi
は、テスト対象のファイル(small.txt
)のos.FileInfo
インスタンスです。fi.Mode()
は、そのファイルに関するos.FileMode
を返します。Perm()
メソッドは、os.FileMode
からファイルタイプビットを除いた純粋なパーミッションビットのみを抽出します。- この変更により、期待されるモードは、テストが実行されている環境のファイルシステムが実際に報告するパーミッションと
c_ISREG
ビットの組み合わせになります。 - これにより、Windows環境で
os.FileInfo.Mode().Perm()
が返すパーミッションが0644
と異なっていたとしても、テストは環境に合わせた正しい期待値と比較されるため、テストの失敗が解消されます。
この修正は、クロスプラットフォームなソフトウェア開発において、ファイルシステムやOSの挙動の違いを吸収し、テストの信頼性を高めるための重要なプラクティスを示しています。
関連リンク
- Go CL 6249052: https://golang.org/cl/6249052
参考にした情報源リンク
- Go言語
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語
archive/tar
パッケージドキュメント: https://pkg.go.dev/archive/tar - Unixファイルパーミッションに関する一般的な情報 (例: Wikipedia): https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%91%E3%83%BC%E3%83%9F%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3
- Windowsのファイルアクセス権に関する一般的な情報 (例: Microsoft Learn): https://learn.microsoft.com/ja-jp/windows/win32/secauthz/access-control-lists
- Go言語におけるWindowsでのファイルパーミッションの扱いに関する議論 (Go issue trackerなど): (具体的なリンクはコミットメッセージからは特定できませんが、Goコミュニティ内で同様の問題が議論されることがあります。)