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

[インデックス 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のビルドプロセスで使用される一時作業ディレクトリのパスを決定し、そのディレクトリを作成する役割を担っています。

  1. xgetenv(&b, "TMPDIR");

    • TMPDIR 環境変数の値を取得し、b というバッファに格納します。b はおそらく文字列を扱うための内部的なバッファ構造体です。
  2. if(b.len == 0)

    • もし TMPDIR 環境変数が設定されていない(または空の文字列である)場合、デフォルトの一時ディレクトリとして /var/tmp を使用します。
  3. if(b.p[b.len-1] != '/')

    • この行が追加された変更の核心です。
    • b.p はバッファ b の内容へのポインタ(文字列)を指し、b.len-1 は文字列の最後の文字のインデックスを指します。
    • この条件式は、TMPDIR の値の最後の文字がスラッシュ(/)でないかどうかをチェックしています。
  4. bwrite(&b, "/", 1);

    • 上記の if 文の条件が真(つまり、TMPDIR の末尾がスラッシュでない)の場合、バッファ b にスラッシュを1文字追加します。これにより、TMPDIR の値が必ずスラッシュで終わるようになります。
  5. bwritestr(&b, "go-cbuild-XXXXXX");

    • go-cbuild-XXXXXX という文字列をバッファ b に追加します。XXXXXXmkdtemp 関数によって一意の文字列に置き換えられます。
    • このステップにより、最終的な一時ディレクトリのパスは [TMPDIRの値]/go-cbuild-XXXXXX の形式になります。
  6. p = bstr(&b);

    • バッファ b の内容をCスタイルの文字列(char*)として取得し、p に格納します。これが mkdtemp に渡されるパスとなります。
  7. if(mkdtemp(p) == nil)

    • mkdtemp 関数を呼び出して、p で指定されたパスに基づいて一意の一時ディレクトリを作成します。
    • mkdtempnil を返した場合、ディレクトリの作成に失敗したことを意味します。
  8. fatal("mkdtemp(%s): %s", p, strerror(errno));

    • mkdtemp が失敗した場合、エラーメッセージを出力してプログラムを終了します。

この変更により、TMPDIR の設定に関わらず、mkdtemp に渡されるパスが常に正しい形式(二重スラッシュを含まない)であることが保証され、一時ディレクトリの作成がより堅牢になります。

関連リンク

注記: コミットメッセージに記載されている 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系ファイルシステムのパスの扱いに関する一般的な知識