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

[インデックス 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.FileInfoos.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進数0644c_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の挙動の違いを吸収し、テストの信頼性を高めるための重要なプラクティスを示しています。

関連リンク

参考にした情報源リンク