[インデックス 17316] ファイルの概要
このコミットは、Go言語のビルドツールである cmd/dist
における一時ディレクトリのパス生成に関する問題を修正するものです。具体的には、TMPDIR
環境変数の値がスラッシュで終わる場合に発生する可能性のある、パス内の二重スラッシュ(//
)の生成を防ぎ、よりクリーンなパス名を保証します。これにより、mkdtemp
のような一時ディレクトリ作成関数が正しく動作しない可能性を低減します。
コミット
commit 24f302a69d80f2e89db81fad600165b2f857c193
Author: David Symonds <dsymonds@golang.org>
Date: Mon Aug 19 11:11:27 2013 +1000
cmd/dist: join with TMPDIR more carefully to avoid // in path.
This might fix the mkdtemp problem on the darwin builders if they
have TMPDIR set to a path ending in a slash; at worse this will
result in cleaner path names.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13097043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24f302a69d80f2e89db81fad600165b2f857c193
元コミット内容
cmd/dist: join with TMPDIR more carefully to avoid // in path.
This might fix the mkdtemp problem on the darwin builders if they
have TMPDIR set to a path ending in a slash; at worse this will
result in cleaner path names.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13097043
変更の背景
この変更は、Go言語のビルドシステムが一時ディレクトリを作成する際に発生する可能性のある問題を解決するために導入されました。特に、macOS (darwin) 環境のビルドマシンにおいて、TMPDIR
環境変数が末尾にスラッシュを含むパス(例: /var/tmp/
)に設定されている場合、cmd/dist
が生成する一時ディレクトリのパスが //
のように二重スラッシュを含む形になることがありました。
このような不正なパスは、mkdtemp
のようなシステムコールが一時ディレクトリを作成する際に予期せぬエラーを引き起こす可能性がありました。たとえエラーが発生しなくても、パスが不必要に冗長になり、デバッグやログの可読性を損なう可能性があります。このコミットは、このようなパスの生成をより堅牢にし、ビルドプロセスの安定性と信頼性を向上させることを目的としています。
前提知識の解説
cmd/dist
cmd/dist
は、Go言語のソースコードからGoツールチェイン自体をビルドするために使用される内部ツールです。Goのコンパイラ、リンカ、アセンブラなどのバイナリを生成する複雑なプロセスを管理します。このツールは、クロスコンパイルや異なるプラットフォーム向けのビルドをサポートするために、一時ファイルやディレクトリを頻繁に利用します。
TMPDIR
環境変数
TMPDIR
は、Unix系システムで一時ファイルや一時ディレクトリを作成する際のデフォルトの場所を指定するために使用される環境変数です。多くのプログラムは、一時的なデータを保存するためにこの変数で指定されたディレクトリを利用します。もし TMPDIR
が設定されていない場合、通常は /tmp
や /var/tmp
のようなシステムデフォルトの場所が使用されます。
mkdtemp
関数
mkdtemp
は、Unix系システムで安全な一時ディレクトリを作成するためのC言語のライブラリ関数です。この関数は、指定されたテンプレート文字列(通常は末尾に XXXXXX
を含む)に基づいて一意のディレクトリ名を作成し、そのディレクトリを作成します。XXXXXX
の部分は、関数が呼び出されるたびにランダムな文字列に置き換えられ、他のプロセスとの名前の衝突を防ぎます。mkdtemp
は、作成されたディレクトリのパスを返します。もしディレクトリの作成に失敗した場合(例えば、不正なパスが指定された場合や、パーミッションの問題がある場合)、NULL
を返します。
パスの正規化と二重スラッシュ
Unix系ファイルシステムでは、パスの区切り文字としてスラッシュ(/
)が使用されます。通常、複数のスラッシュが連続して出現しても、それは単一のスラッシュとして解釈されます(例: /a//b
は /a/b
と同じ)。しかし、一部のシステムコールやアプリケーションでは、二重スラッシュが予期せぬ動作を引き起こす可能性があります。特に、パスの先頭が //
で始まる場合、それはネットワークパス(UNCパス)として解釈されることがあり、ローカルファイルシステム上のパスとは異なる意味を持つことがあります。このコミットの文脈では、TMPDIR
が /var/tmp/
のように末尾にスラッシュを持つ場合、cmd/dist
がこれに go-cbuild-XXXXXX
を結合すると /var/tmp//go-cbuild-XXXXXX
のようなパスが生成され、これが mkdtemp
に渡されると問題を引き起こす可能性がありました。
技術的詳細
このコミットの技術的な核心は、一時ディレクトリのパスを構築する際に、TMPDIR
の値が末尾にスラッシュを持つかどうかをチェックし、必要に応じてスラッシュを追加することで、パスが常に TMPDIR/go-cbuild-XXXXXX
の形式になるようにすることです。これにより、TMPDIR
が /var/tmp
であっても /var/tmp/
であっても、結果として生成されるパスは /var/tmp/go-cbuild-XXXXXX
のように正規化され、二重スラッシュが回避されます。
src/cmd/dist/unix.c
は、Unix系システムにおける cmd/dist
の一時ディレクトリ関連の処理を担うファイルです。このファイル内で、xworkdir
関数が一時ディレクトリのパスを構築しています。
変更前は、TMPDIR
の値を取得した後、無条件に /go-cbuild-XXXXXX
を結合していました。
xgetenv(&b, "TMPDIR");
if(b.len == 0)
bwritestr(&b, "/var/tmp");
bwritestr(&b, "/go-cbuild-XXXXXX");
もし TMPDIR
が /var/tmp/
であった場合、これは /var/tmp//go-cbuild-XXXXXX
となります。
変更後は、TMPDIR
の末尾がスラッシュでない場合にのみスラッシュを追加するロジックが挿入されました。
xgetenv(&b, "TMPDIR");
if(b.len == 0)
bwritestr(&b, "/var/tmp");
if(b.p[b.len-1] != '/') // ここでチェック
bwrite(&b, "/", 1); // 必要ならスラッシュを追加
bwritestr(&b, "go-cbuild-XXXXXX");
これにより、TMPDIR
が /var/tmp
であれば /var/tmp/go-cbuild-XXXXXX
となり、/var/tmp/
であれば /var/tmp/go-cbuild-XXXXXX
となり、いずれの場合もパスが適切に正規化されます。
コアとなるコードの変更箇所
変更は src/cmd/dist/unix.c
ファイルの xworkdir
関数内で行われています。
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -466,7 +466,9 @@ xworkdir(void)
xgetenv(&b, "TMPDIR");
if(b.len == 0)
bwritestr(&b, "/var/tmp");
- bwritestr(&b, "/go-cbuild-XXXXXX");
+ if(b.p[b.len-1] != '/')
+ bwrite(&b, "/", 1);
+ bwritestr(&b, "go-cbuild-XXXXXX");
p = bstr(&b);
if(mkdtemp(p) == nil)
fatal("mkdtemp(%s): %s", p, strerror(errno));
コアとなるコードの解説
xworkdir
関数は、Goのビルドプロセスで使用される一時作業ディレクトリのパスを決定し、そのディレクトリを作成する役割を担っています。
-
xgetenv(&b, "TMPDIR");
TMPDIR
環境変数の値を取得し、b
というバッファに格納します。b
はおそらく文字列を扱うための内部的なバッファ構造体です。
-
if(b.len == 0)
- もし
TMPDIR
環境変数が設定されていない(または空の文字列である)場合、デフォルトの一時ディレクトリとして/var/tmp
を使用します。
- もし
-
if(b.p[b.len-1] != '/')
- この行が追加された変更の核心です。
b.p
はバッファb
の内容へのポインタ(文字列)を指し、b.len-1
は文字列の最後の文字のインデックスを指します。- この条件式は、
TMPDIR
の値の最後の文字がスラッシュ(/
)でないかどうかをチェックしています。
-
bwrite(&b, "/", 1);
- 上記の
if
文の条件が真(つまり、TMPDIR
の末尾がスラッシュでない)の場合、バッファb
にスラッシュを1文字追加します。これにより、TMPDIR
の値が必ずスラッシュで終わるようになります。
- 上記の
-
bwritestr(&b, "go-cbuild-XXXXXX");
go-cbuild-XXXXXX
という文字列をバッファb
に追加します。XXXXXX
はmkdtemp
関数によって一意の文字列に置き換えられます。- このステップにより、最終的な一時ディレクトリのパスは
[TMPDIRの値]/go-cbuild-XXXXXX
の形式になります。
-
p = bstr(&b);
- バッファ
b
の内容をCスタイルの文字列(char*
)として取得し、p
に格納します。これがmkdtemp
に渡されるパスとなります。
- バッファ
-
if(mkdtemp(p) == nil)
mkdtemp
関数を呼び出して、p
で指定されたパスに基づいて一意の一時ディレクトリを作成します。mkdtemp
がnil
を返した場合、ディレクトリの作成に失敗したことを意味します。
-
fatal("mkdtemp(%s): %s", p, strerror(errno));
mkdtemp
が失敗した場合、エラーメッセージを出力してプログラムを終了します。
この変更により、TMPDIR
の設定に関わらず、mkdtemp
に渡されるパスが常に正しい形式(二重スラッシュを含まない)であることが保証され、一時ディレクトリの作成がより堅牢になります。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のIssueトラッカー: https://github.com/golang/go/issues
注記: コミットメッセージに記載されている https://golang.org/cl/13097043
は、Web検索の結果によると「runtime: fix race in TestStackGrowth」という別のコミット(CL)を指しているようです。本解説は、提供されたコミットハッシュ 24f302a69d80f2e89db81fad600165b2f857c193
に対応する実際の変更内容に基づいて作成されています。
参考にした情報源リンク
mkdtemp
manページ (Unix/Linuxシステム):man mkdtemp
- Go言語のソースコード (特に
src/cmd/dist/unix.c
): https://github.com/golang/go/blob/master/src/cmd/dist/unix.c - Go言語の環境変数に関するドキュメント (Goの公式ドキュメントや関連するブログ記事など)
- Unix系ファイルシステムのパスの扱いに関する一般的な知識