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

[インデックス 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)だけでなく、空文字列に設定されている場合も考慮されていませんでした。

元の実装では、TMPDIRnilの場合にのみ/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の文字列を指しています。

この場合、tmpnilではないため、上記の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";

これにより、tmpnilであるか、または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演算子||を使用して条件が拡張されています。

  1. tmp == nil: これは以前と同じで、TMPDIRが設定されていない場合をカバーします。
  2. strlen(tmp) == 0: これは新しく追加された条件で、tmpポインタがnilではないが、その指す文字列の長さが0である(つまり、TMPDIR=""のように空文字列に設定されている)場合をカバーします。

この変更により、TMPDIRが未設定の場合と空文字列の場合の両方で、tmp/var/tmpに適切にフォールバックされるようになります。これにより、後続のsmprint関数が有効なパスを生成できるようになり、一時ディレクトリの作成が失敗する問題が解決されます。

関連リンク

参考にした情報源リンク