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

[インデックス 15397] ファイルの概要

このコミットは、Go言語の標準ライブラリ archive/tar パッケージ内のテストファイル tar_test.go における定数の使用を簡素化するものです。具体的には、テストケース内でローカルに定義されていた setsid という定数を、既に定義されている c_ISGID という定数に置き換えることで、コードの重複を排除し、可読性と保守性を向上させています。

コミット

commit 39c476cbf874c1936e82731abd4511bb8364c00c
Author: Robin Eklind <r.eklind.87@gmail.com>
Date:   Sat Feb 23 11:39:01 2013 -0800

    archive/tar: simplify use of constants in test case.
    
    Replace setsid with c_ISGID since the constant is already defined.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7403048

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/39c476cbf874c1936e82731abd4511bb8364c00c

元コミット内容

archive/tar: simplify use of constants in test case. Replace setsid with c_ISGID since the constant is already defined.

このコミットは、archive/tar パッケージのテストケースにおいて、定数の利用方法を簡素化することを目的としています。具体的には、setsid という定数を、既にシステム内で定義されている c_ISGID という定数に置き換える変更です。

変更の背景

この変更の背景には、Go言語の archive/tar パッケージがTARアーカイブのヘッダ情報を扱う際に、ファイルモード(パーミッション)の特定のビットフラグを適切に処理する必要があったことが挙げられます。

元のコードでは、TestFileInfoHeaderDir というテスト関数内で setsid という定数が 02000 という値でローカルに定義されていました。この 02000 は、UNIX系のファイルシステムにおけるSet-GID (Set Group ID) ビットを表す八進数表記です。Set-GIDビットが設定されたディレクトリ内で作成されるファイルは、そのディレクトリのグループIDを継承するという特殊な動作をします。

golang.org/issue/4867 で言及されているように、archive/tar パッケージがTARヘッダを生成する際に、このSet-GIDビットの扱いに関して特定の課題があったと考えられます。テストコードでは、このビットが正しく処理されているか、あるいは特定の状況下で無視されるべきかを検証するために、h.Mode&^setsid のようにビットマスク操作を行っていました。

しかし、Goの内部パッケージ(特に syscall パッケージや、それを通じて利用されるC言語のヘッダファイル由来の定数)には、既に c_ISGID という定数が存在し、これが 02000 と同じ値を表していました。テストコード内で同じ意味を持つ定数を重複して定義することは、コードの冗長性を生み、将来的なメンテナンスの際に混乱を招く可能性があります。

このコミットは、このような重複を解消し、既存の標準的な定数 c_ISGID を利用することで、コードの簡素化、可読性の向上、そして一貫性の確保を目指しています。これにより、テストコードがよりGoの慣習に沿った形になり、archive/tar パッケージの内部実装とテストの整合性が高まります。

前提知識の解説

TARアーカイブとファイルモード

TAR (Tape Archive) は、複数のファイルを一つのアーカイブファイルにまとめるためのファイル形式です。TARアーカイブは、各ファイルの内容だけでなく、ファイル名、パーミッション(ファイルモード)、所有者、グループ、タイムスタンプなどのメタデータも保存します。

ファイルモードは、UNIX系システムにおけるファイルのアクセス権限や種類を示すビットフラグの集合です。通常、八進数表記(例: 0755)で表されます。ファイルモードには、読み取り(r)、書き込み(w)、実行(x)の権限ビットの他に、特殊なパーミッションビットが含まれます。

特殊なパーミッションビット

UNIX系ファイルシステムには、通常の読み書き実行権限に加えて、以下の特殊なパーミッションビットがあります。

  1. Set-UID (Set User ID) ビット (04000): 実行可能ファイルに設定された場合、そのファイルを実行したユーザーのIDではなく、ファイルの所有者のIDでプロセスが実行されます。
  2. Set-GID (Set Group ID) ビット (02000):
    • 実行可能ファイルに設定された場合、そのファイルを実行したユーザーのプライマリグループIDではなく、ファイルのグループ所有者のIDでプロセスが実行されます。
    • ディレクトリに設定された場合、そのディレクトリ内で新しく作成されるファイルやサブディレクトリは、親ディレクトリのグループIDを継承します。これは、共有ディレクトリでの作業において、グループの整合性を保つために非常に重要です。
  3. Sticky (Restricted Deletion) ビット (01000): ディレクトリに設定された場合、そのディレクトリ内のファイルは、ファイルの所有者、ディレクトリの所有者、またはrootユーザーのみが削除または名前変更できます。これは、/tmp のような共有ディレクトリでよく使用され、他のユーザーが作成したファイルを勝手に削除するのを防ぎます。

これらのビットは、ファイルモードの最上位ビットとして表現され、八進数表記ではそれぞれ 421 に対応します。例えば、0755 のファイルモードにSet-GIDビットを追加すると 02755 となります。

Go言語における定数と syscall パッケージ

Go言語では、C言語のシステムコールや定数を扱うために syscall パッケージが提供されています。このパッケージは、オペレーティングシステム固有の定数や関数をGoのコードから利用できるようにするためのラッパーを提供します。

c_ISGID のような定数は、通常、C言語のヘッダファイル(例: <sys/stat.h>)で定義されている S_ISGID のようなマクロに対応し、syscall パッケージを通じてGoのコードからアクセス可能になります。これらの定数は、ファイルモードのビットフラグを操作する際に使用されます。

golang.org/issue/4867golang.org/cl/7403048

  • golang.org/issue/4867: これはGo言語のIssueトラッカーにおける特定のバグ報告や機能要望のIDです。このIssueは、archive/tar パッケージがSet-GIDビットを持つディレクトリを処理する際の挙動に関するものであったと推測されます。具体的には、tar コマンドがSet-GIDビットを持つディレクトリをアーカイブする際に、そのビットが正しく保存されない、あるいは復元されないといった問題が議論された可能性があります。テストコードの変更は、このIssueで指摘された問題の検証、またはその問題に対する修正が正しく機能することを確認するためのものです。
  • golang.org/cl/7403048: これはGoのコードレビューシステム (Gerrit) におけるChange List (CL) のIDです。Goプロジェクトでは、すべてのコード変更はCLとして提出され、レビューを経てマージされます。このCLは、今回のコミットに対応するコード変更の提案であり、レビュープロセスを経て最終的にコミットとして取り込まれました。

技術的詳細

このコミットは、archive/tar パッケージのテストファイル src/pkg/archive/tar/tar_test.go 内の TestFileInfoHeaderDir 関数に焦点を当てています。このテスト関数は、ディレクトリの tar.Headeros.FileInfo から正しく生成されることを検証しています。

変更前のコードでは、以下の行がありました。

const setsid = 02000 // see golang.org/issue/4867
if g, e := h.Mode&^setsid, int64(fi.Mode().Perm())|c_ISDIR; g != e {

ここで setsid02000 という八進数で定義されており、これはSet-GIDビットを表します。h.Mode&^setsid という式は、h.Mode (TARヘッダのファイルモード) から setsid ビットをクリア(0にする)する操作です。これは、テストの比較において、Set-GIDビットが特定の状況下で無視されるべきであることを示唆しています。golang.org/issue/4867 のコメントは、このSet-GIDビットの扱いが特定のGoのIssueに関連していることを示しています。

変更後のコードは以下のようになります。

// Ignoring c_ISGID for golang.org/issue/4867
if g, e := h.Mode&^c_ISGID, int64(fi.Mode().Perm())|c_ISDIR; g != e {

ここで、ローカルに定義されていた setsid 定数が削除され、代わりに c_ISGID という定数が直接使用されています。c_ISGID は、Goの syscall パッケージ、またはそれを通じて利用されるC言語のシステムヘッダファイルから提供される定数であり、02000 と同じ値を持ちます。

この変更の技術的なポイントは以下の通りです。

  1. 定数の統一: 02000 というマジックナンバー(直接的な数値)を setsid という名前付き定数に置き換えることで、コードの意図が明確になりました。さらに、その setsidc_ISGID という既存の標準的な定数に置き換えることで、Goエコシステム全体での定数の一貫性が保たれます。
  2. 冗長性の排除: 同じ意味を持つ定数を複数箇所で定義する冗長性が排除されました。これにより、コードベース全体のサイズがわずかに減少し、将来的に 02000 の意味が変わるようなことがあった場合でも、一箇所 (c_ISGID の定義元) を変更するだけで済むようになります。
  3. 可読性の向上: c_ISGID という名前は、その定数がSet-GIDビットを表すことをより明確に示しており、コードを読む人がその意味を理解しやすくなります。
  4. コメントの更新: コメントも // Ignoring c_ISGID for golang.org/issue/4867 と更新され、c_ISGID が無視されている理由が golang.org/issue/4867 に関連していることが明示されています。これは、このテストの特定の挙動がなぜ必要なのかを理解するための重要な手がかりとなります。

この変更は、機能的な振る舞いを変更するものではなく、あくまでコードの内部的な品質(可読性、保守性、一貫性)を向上させるためのリファクタリングです。

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

変更は src/pkg/archive/tar/tar_test.go ファイルの以下の部分です。

--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -48,8 +48,8 @@ func TestFileInfoHeaderDir(t *testing.T) {
  if g, e := h.Name, "testdata/"; g != e {
  t.Errorf("Name = %q; want %q", g, e)
  }
- const setsid = 02000 // see golang.org/issue/4867
- if g, e := h.Mode&^setsid, int64(fi.Mode().Perm())|c_ISDIR; g != e {
+ // Ignoring c_ISGID for golang.org/issue/4867
+ if g, e := h.Mode&^c_ISGID, 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 {

具体的には、以下の2行が変更されました。

  1. const setsid = 02000 // see golang.org/issue/4867 が削除されました。
  2. if g, e := h.Mode&^setsid, int64(fi.Mode().Perm())|c_ISDIR; g != e {setsidc_ISGID に置き換えられ、コメントも変更されました。

コアとなるコードの解説

変更されたコードは、TestFileInfoHeaderDir というテスト関数の一部です。この関数は、os.FileInfo オブジェクトから tar.Header を生成する際のディレクトリのモード(パーミッション)が正しく変換されるかを検証しています。

元のコードでは、h.Mode&^setsid という式がありました。

  • h.Mode: tar.Header に格納されているファイルモードです。
  • &^: ビットクリア演算子(AND NOT)。A &^ B は、A のビットのうち B でセットされているビットをクリアします。
  • setsid: ローカルに定義された 02000 (Set-GIDビット) を表す定数。

この行は、「TARヘッダのモードからSet-GIDビットをクリアした値が、元の os.FileInfo のパーミッションに c_ISDIR (ディレクトリを示すビット) を加えた値と等しいか」を検証しています。Set-GIDビットをクリアしているのは、golang.org/issue/4867 で議論された特定の挙動(例えば、TARアーカイブの作成・展開時にSet-GIDビットが常に保持されるわけではない、あるいは特定のプラットフォームで問題があるなど)に対応するため、テストの比較対象からこのビットを除外しているためと考えられます。

変更後も、h.Mode&^c_ISGID という式で同じビットクリア操作が行われています。機能的な変更はなく、setsid というローカル定数が、Goの標準ライブラリ内で既に定義されている c_ISGID という定数に置き換えられただけです。これにより、コードの重複が解消され、より標準的な定数名が使用されることで、コードの意図がより明確になりました。

c_ISGID は、Goの syscall パッケージを通じて利用可能な定数であり、UNIX系のファイルシステムにおけるSet-GIDビットを表す 02000 と同じ値を持っています。この変更は、テストコードの内部的な品質を向上させるためのクリーンアップ作業であり、archive/tar パッケージの機能的な振る舞いには影響を与えません。

関連リンク

参考にした情報源リンク