[インデックス 15362] ファイルの概要
このコミットは、Go言語の標準ライブラリarchive/tar
パッケージにおけるテストの修正に関するものです。具体的には、setgid
ビットが設定されたディレクトリを扱う際に、tar
アーカイブのヘッダー情報が正しく検証されるようにテストコードが調整されました。これにより、特定のファイルパーミッションを持つディレクトリが原因で発生していたテストの失敗が解消されました。
コミット
- コミットハッシュ:
4335e69af64b96cd69fa876c5157d6caebde23a6
- 作者: Brad Fitzpatrick bradfitz@golang.org
- コミット日時: 2013年2月21日 木曜日 14:00:03 -0800
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4335e69af64b96cd69fa876c5157d6caebde23a6
元コミット内容
archive/tar: make test pass on setgid dirs
Fixes #4867
R=golang-dev, dsymonds
CC=golang-dev
https://golang.org/cl/7382045
変更の背景
このコミットは、Go言語のIssue #4867を修正するために行われました。Issue #4867は、archive/tar
パッケージがsetgid
ビットが設定されたディレクトリを処理する際に、テストが失敗するという問題に関連しています。
Unix/Linuxシステムにおいて、ディレクトリにsetgid
ビットが設定されている場合、そのディレクトリ内に新しく作成されるファイルやサブディレクトリは、親ディレクトリのグループIDを継承します。archive/tar
パッケージは、ファイルシステム上のファイルやディレクトリのメタデータ(パーミッションなど)をtar
アーカイブのヘッダー情報に変換する役割を担っています。
問題は、FileInfoHeaderDir
テスト関数が、ディレクトリのモード(パーミッション)を検証する際に、setgid
ビットの存在を適切に考慮していなかったことにありました。os.FileInfo
から取得したモード情報にはsetgid
ビットが含まれる可能性がありますが、tar
ヘッダーのモードには通常、このビットは含まれないか、異なる扱いがされることがあります。この不一致がテストの失敗を引き起こしていました。
このコミットの目的は、setgid
ディレクトリのモードを比較する際に、setgid
ビットを無視することで、テストが正しくパスするようにすることです。
前提知識の解説
archive/tar
パッケージ
Go言語の標準ライブラリの一部であり、tar
アーカイブ(テープアーカイブ)の読み書きをサポートします。tar
は、複数のファイルを一つのアーカイブファイルにまとめるための一般的なフォーマットです。このパッケージは、ファイルのメタデータ(パーミッション、所有者、タイムスタンプなど)を保持しながら、ファイルのアーカイブ化と展開を可能にします。
Unix/Linuxファイルパーミッションとsetgid
ビット
Unix/Linuxシステムでは、ファイルやディレクトリには所有者、グループ、その他のユーザーに対する読み取り(r)、書き込み(w)、実行(x)のパーミッションが設定されます。これらは通常、3桁の8進数(例: 755
)で表現されます。
さらに、特殊なパーミッションビットとしてsetuid
、setgid
、sticky
ビットが存在します。
setgid
(Set Group ID) ビット:- ファイルに設定された場合: その実行可能ファイルが実行される際に、実行者の実効グループIDではなく、ファイルのグループIDで実行されます。
- ディレクトリに設定された場合: そのディレクトリ内に新しく作成されるファイルやサブディレクトリは、親ディレクトリのグループIDを継承します。これは、複数のユーザーが共有ディレクトリで作業する際に、新しく作成されたファイルが常に正しいグループに属するようにするために非常に便利です。
- 8進数表記では、パーミッションの先頭に
2
を追加して表現されます(例:2755
)。
c_ISDIR
定数
archive/tar
パッケージ内で定義されている定数で、tar
ヘッダーのモードフィールドにおいて、エントリがディレクトリであることを示すビットマスクです。
Go言語のテストフレームワーク
Go言語には、標準でtesting
パッケージが提供されており、ユニットテストやベンチマークテストを記述できます。テスト関数はTest
で始まり、*testing.T
型の引数を取ります。t.Errorf
などのメソッドを使ってテストの失敗を報告します。
技術的詳細
archive/tar
パッケージのFileInfoHeaderDir
関数は、os.FileInfo
インターフェースからディレクトリのメタデータを読み取り、それをtar.Header
構造体に変換します。この変換プロセスにおいて、ファイルパーミッション(モード)も適切にマッピングされる必要があります。
問題の核心は、os.FileInfo.Mode()
が返すファイルモードにはsetgid
ビットが含まれる可能性があるのに対し、tar.Header.Mode
に格納されるモードは、tar
フォーマットの仕様や慣習により、このsetgid
ビットが考慮されない場合がある点です。
元のコードでは、h.Mode
(tar
ヘッダーのモード)とint64(fi.Mode().Perm())|c_ISDIR
(os.FileInfo
から取得したパーミッションにディレクトリビットをORしたもの)を直接比較していました。fi.Mode().Perm()
は、os.FileMode
からパーミッションビットのみを抽出するメソッドですが、setgid
ビットも含まれる可能性があります。
このコミットでは、setgid
ビット(8進数で02000
)をsetsid
という定数として定義し、h.Mode
からこのsetsid
ビットをマスクアウト(クリア)してから比較を行うように変更しました。
h.Mode &^ setsid
という式は、Go言語におけるビットクリア操作です。
&
はビットごとのAND演算子です。^
はビットごとのXOR演算子ですが、&^
と組み合わせることで「AND NOT」として機能し、特定のビットをクリアする(0にする)ために使用されます。 つまり、h.Mode
からsetsid
で指定されたビットが立っている部分を0にし、それ以外のビットはそのまま保持します。これにより、setgid
ビットの有無に関わらず、純粋なパーミッションとディレクトリフラグのみを比較できるようになり、テストがsetgid
ディレクトリに対しても正しく動作するようになりました。
コアとなるコードの変更箇所
変更はsrc/pkg/archive/tar/tar_test.go
ファイルにあります。
--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -48,7 +48,8 @@ func TestFileInfoHeaderDir(t *testing.T) {
if g, e := h.Name, "testdata/"; g != e {
t.Errorf("Name = %q; want %q", g, e)
}
- if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISDIR; g != e {
+ const setsid = 02000 // see golang.org/issue/4867
+ if g, e := h.Mode&^setsid, int64(fi.Mode().Perm())|c_ISDIR; g != e {
t.Errorf("Mode = %#o; want %#o", g, e)
}
if g, e := h.Size, int64(0); g != e {
コアとなるコードの解説
変更された行は以下の部分です。
- if g, e := h.Mode, int64(fi.Mode().Perm())|c_ISDIR; g != e {
+ const setsid = 02000 // see golang.org/issue/4867
+ if g, e := h.Mode&^setsid, int64(fi.Mode().Perm())|c_ISDIR; g != e {
const setsid = 02000
:setgid
ビットを表す8進数02000
をsetsid
という定数として定義しています。h.Mode&^setsid
: ここが変更の核心です。tar
ヘッダーのモードh.Mode
からsetsid
ビットをクリアしています。これにより、h.Mode
がsetgid
ビットを含んでいるかどうかにかかわらず、比較対象となる値からsetgid
ビットが取り除かれます。int64(fi.Mode().Perm())|c_ISDIR
:os.FileInfo
から取得したパーミッションにc_ISDIR
(ディレクトリを示すビット)をOR演算で結合しています。Perm()
メソッドは、setuid
,setgid
,sticky
ビットを含む特殊なパーミッションビットを返します。
この変更により、h.Mode
とfi.Mode().Perm()
の比較において、setgid
ビットの有無による不一致が解消され、テストがsetgid
ディレクトリに対しても期待通りに動作するようになりました。
関連リンク
- Go Issue #4867: https://golang.org/issue/4867 (直接のリンクは現在見つかりませんが、コミットメッセージで参照されています)
- Go Code Review: https://golang.org/cl/7382045
参考にした情報源リンク
- Go言語の
archive/tar
パッケージに関するドキュメント - Unix/Linuxファイルパーミッション、特に
setgid
ビットに関する一般的な情報 - Go言語のビット演算子に関する情報
- Go言語の
testing
パッケージに関するドキュメント - Go言語のIssueトラッカーの慣習とコミットメッセージの形式