[インデックス 16123] ファイルの概要
このコミットは、Go言語プロジェクトのsrc/lib9/tempdir_unix.c
ファイルに対する変更です。このファイルは、Unix系システムにおいて一時ディレクトリを作成するためのユーティリティ関数を提供しています。具体的には、mktempdir
関数が含まれており、これは環境変数TMPDIR
の値を参照して一時ディレクトリのパスを決定し、そのディレクトリを作成する役割を担っています。
コミット
このコミットは、環境変数TMPDIR
が空文字列に設定されている場合に発生する、一時ディレクトリ作成時のエラーを修正することを目的としています。以前のコードでは、TMPDIR
が設定されていない(nil
またはNULL
)場合にのみデフォルトのパス(/var/tmp
)を使用していましたが、空文字列が設定されているケースを考慮していませんでした。この修正により、TMPDIR
が空文字列の場合も適切にデフォルトパスが使用されるようになり、より堅牢な一時ディレクトリのハンドリングが実現されました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/dc7daa550af74277b10e8025d836fd5da48513f6
元コミット内容
commit dc7daa550af74277b10e8025d836fd5da48513f6
Author: Albert Strasheim <fullung@gmail.com>
Date: Sun Apr 7 11:16:15 2013 -0700
lib9: handle empty TMPDIR more gracefully.
Before, an empty TMPDIR would lead to:
cannot create <nil>/go.o: No such file or directory
R=golang-dev, iant, dave, bradfitz
CC=golang-dev
https://golang.org/cl/8355045
変更の背景
この変更の背景には、TMPDIR
環境変数の扱いに関する既存のバグがありました。Unix系システムでは、プログラムが一時ファイルを生成する際に、通常TMPDIR
環境変数の値を使用して一時ディレクトリの場所を決定します。しかし、この変数が設定されていない場合(nil
またはNULL
)だけでなく、空文字列に設定されている場合も考慮されていませんでした。
元の実装では、TMPDIR
がnil
の場合にのみ/var/tmp
というデフォルトのパスにフォールバックしていました。そのため、もしユーザーやシステムがTMPDIR=""
のように空文字列を設定していた場合、getenv("TMPDIR")
はnil
ではない(有効なポインタを返す)ものの、その指す文字列は長さが0であるため、後続のパス結合処理で<nil>/go.o
のような不正なパスが生成されていました。
この不正なパスは、go.o
のようなファイルを作成しようとした際に「No such file or directory
」というエラーを引き起こし、プログラムの実行が失敗する原因となっていました。このコミットは、このようなエッジケースを適切に処理し、プログラムの堅牢性を向上させるために導入されました。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
- 環境変数 (Environment Variables): オペレーティングシステムが提供する動的な名前付き値で、実行中のプロセスに影響を与えます。
TMPDIR
は一時ファイルの保存場所を指定するための標準的な環境変数です。 TMPDIR
: Unix系システムで一時ファイルやディレクトリを作成する際のデフォルトの場所を指定する環境変数です。この変数が設定されていない場合、通常は/tmp
や/var/tmp
が使用されます。getenv()
: C言語の標準ライブラリ関数で、指定された環境変数の値を取得します。環境変数が存在しない場合はNULL
(またはnil
)を返します。strlen()
: C言語の標準ライブラリ関数で、ヌル終端文字列の長さを返します。nil
(またはNULL
): C言語におけるヌルポインタを表すキーワードです。ポインタが何も指していないことを示します。Go言語の文脈ではnil
が使われますが、このコードはC言語で書かれているため、概念的にはNULL
と同じです。lib9
: Go言語の初期の段階で、Plan 9オペレーティングシステムからインスパイアされたユーティリティ関数群を提供するライブラリとして存在していました。Go言語のランタイムやツールチェーンの一部として、低レベルのシステム操作やユーティリティ機能を提供していました。tempdir_unix.c
はその一部であり、Unixシステムでの一時ディレクトリ操作を担当しています。smprint()
: Plan 9由来の関数で、sprintf
に似ていますが、動的にメモリを割り当てて結果の文字列を返します。mkdtemp()
: 一意な一時ディレクトリを作成するためのPOSIX関数です。テンプレート文字列(例:/tmp/XXXXXX
)を受け取り、XXXXXX
部分をランダムな文字列に置き換えてディレクトリを作成します。
技術的詳細
このコミットの技術的な核心は、TMPDIR
環境変数の値が「存在しない」状態と「空文字列」である状態を区別し、どちらの場合も適切にデフォルトの一時ディレクトリパスにフォールバックするようにした点です。
元のコードでは、getenv("TMPDIR")
の戻り値がnil
(ポインタがNULL
)であるかどうかのみをチェックしていました。
if(tmp == nil)
tmp = "/var/tmp";
しかし、getenv("TMPDIR")
は、TMPDIR
環境変数が設定されていても、その値が空文字列(例: export TMPDIR=""
)である場合には、nil
ではない有効なポインタを返します。このポインタは、長さが0の文字列を指しています。
この場合、tmp
はnil
ではないため、上記のif
文の条件は偽となり、tmp
は空文字列を指したままになります。その後のsmprint("%s/go-link-XXXXXX", tmp)
の呼び出しでは、tmp
が空文字列であるため、結果として"/go-link-XXXXXX"
のような相対パス、あるいはシステムによっては<nil>/go-link-XXXXXX
のような不正なパスが生成されてしまいます。このようなパスは、ファイルシステム操作(mkdtemp
など)において「No such file or directory
」エラーを引き起こします。
新しいコードでは、この問題を解決するために、if
文の条件にstrlen(tmp) == 0
というチェックを追加しました。
if(tmp == nil || strlen(tmp) == 0)
tmp = "/var/tmp";
これにより、tmp
がnil
であるか、またはtmp
が指す文字列の長さが0である(つまり空文字列である)かのいずれかの条件が真であれば、tmp
は/var/tmp
というデフォルトのパスに設定されます。これにより、TMPDIR
が空文字列の場合でも、常に有効な一時ディレクトリパスが使用されるようになり、関連するエラーが回避されます。
この変更は、環境変数の値が「未設定」と「空文字列」という異なる状態を持つことを考慮し、より堅牢なプログラミングを行うための典型的なパターンを示しています。
コアとなるコードの変更箇所
変更はsrc/lib9/tempdir_unix.c
ファイルのmktempdir
関数内で行われています。
--- a/src/lib9/tempdir_unix.c
+++ b/src/lib9/tempdir_unix.c
@@ -16,7 +16,7 @@ mktempdir(void)
char *tmp, *p;
tmp = getenv("TMPDIR");
- if(tmp == nil)
+ if(tmp == nil || strlen(tmp) == 0)
tmp = "/var/tmp";
p = smprint("%s/go-link-XXXXXX", tmp);
if(mkdtemp(p) == nil)
コアとなるコードの解説
変更された行は以下の部分です。
変更前:
if(tmp == nil)
この行は、getenv("TMPDIR")
から取得したtmp
ポインタがnil
(つまり、TMPDIR
環境変数が設定されていない)場合にのみ、tmp
をデフォルトの/var/tmp
に設定していました。
変更後:
if(tmp == nil || strlen(tmp) == 0)
この行では、論理OR演算子||
を使用して条件が拡張されています。
tmp == nil
: これは以前と同じで、TMPDIR
が設定されていない場合をカバーします。strlen(tmp) == 0
: これは新しく追加された条件で、tmp
ポインタがnil
ではないが、その指す文字列の長さが0である(つまり、TMPDIR=""
のように空文字列に設定されている)場合をカバーします。
この変更により、TMPDIR
が未設定の場合と空文字列の場合の両方で、tmp
が/var/tmp
に適切にフォールバックされるようになります。これにより、後続のsmprint
関数が有効なパスを生成できるようになり、一時ディレクトリの作成が失敗する問題が解決されます。
関連リンク
- Go CL 8355045: https://golang.org/cl/8355045
参考にした情報源リンク
- getenv(3) - Linux man page
- strlen(3) - Linux man page
- mkdtemp(3) - Linux man page
- TMPDIR - Wikipedia
- Plan 9 from Bell Labs (lib9の背景にあるオペレーティングシステム)
- Go言語の初期の歴史に関する情報源 (lib9の文脈を理解するため)