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

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

このコミットは、Go言語の標準ライブラリ path/filepath パッケージ内のテストコード TestAbs における潜在的な問題を修正するものです。具体的には、テスト中に作成された一時ディレクトリを削除する前に、カレントディレクトリをその一時ディレクトリから移動させることで、ディレクトリ削除の失敗を防ぎます。

コミット

commit 4b943bded8b92296e50aaafe3e55fc4bc96b4f23
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Thu Apr 5 15:17:24 2012 +1000

    path/filepath: move out of temp directory before deleting it in TestAbs
    
    Fixes #3476.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/5986045

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

https://github.com/golang/go/commit/4b943bded8b9229296e50aaafe3e55fc4bc96b4f23

元コミット内容

src/pkg/path/filepath/path_test.go ファイルにおいて、TestAbs 関数内で一時ディレクトリを作成し、そのディレクトリにカレントディレクトリを変更した後、テスト終了時にその一時ディレクトリを削除しようとしていました。しかし、カレントディレクトリが削除対象のディレクトリ内にある場合、os.RemoveAll が失敗する可能性がありました。

変更の背景

このコミットは、Go言語のIssue #3476を修正するために行われました。具体的なIssueの内容は公開されている情報からは特定できませんでしたが、コミットメッセージから推測すると、path/filepath パッケージの TestAbs テストが、一時ディレクトリのクリーンアップに失敗するケースがあったと考えられます。

一般的なオペレーティングシステムでは、カレントワーキングディレクトリ(CWD)として使用されているディレクトリを削除しようとすると、エラーが発生するか、削除が許可されないことがあります。TestAbs 関数はテストの開始時に一時ディレクトリを作成し、os.Chdir を使ってそのディレクトリに移動します。テストが終了すると、defer os.RemoveAll(root) を使ってその一時ディレクトリを削除しようとします。このとき、もしカレントディレクトリが一時ディレクトリのままであれば、os.RemoveAll は失敗する可能性があります。この修正は、この問題を回避するためのものです。

前提知識の解説

  • os.Getwd(): Go言語の os パッケージに含まれる関数で、現在のワーキングディレクトリ(カレントディレクトリ)の絶対パスを返します。
  • os.Chdir(path string): Go言語の os パッケージに含まれる関数で、現在のワーキングディレクトリを指定された path に変更します。
  • os.RemoveAll(path string): Go言語の os パッケージに含まれる関数で、指定されたパスにあるファイルまたはディレクトリとその内容をすべて削除します。ディレクトリの場合、そのディレクトリがカレントディレクトリであると、削除に失敗することがあります。
  • defer ステートメント: Go言語のキーワードで、defer に続く関数呼び出しを、その関数がリターンする直前に実行するようにスケジュールします。これはリソースのクリーンアップ(ファイルのクローズ、ロックの解除、一時ディレクトリの削除など)によく使用されます。

技術的詳細

TestAbs 関数は、テストの実行中に一時的なファイルシステム構造を作成します。これは、Abs 関数の動作を様々なパスの組み合わせでテストするためです。テストのセットアップとして、os.MkdirAll(root, 0777) で一時ディレクトリ root を作成し、os.Chdir(root) でそのディレクトリに移動します。

問題は、テストが終了し、defer os.RemoveAll(root) が実行される際に発生します。もし os.Chdir(root) でカレントディレクトリが root に変更されたままであれば、os.RemoveAll(root)root ディレクトリを削除しようとしますが、root がカレントディレクトリであるため、オペレーティングシステムによっては削除が拒否されることがあります。これにより、テストのクリーンアップが失敗し、一時ファイルが残ってしまう可能性があります。

この修正は、os.RemoveAll(root) が実行される前に、カレントディレクトリをテスト開始時の元のディレクトリに戻すことで、この問題を解決します。これにより、root ディレクトリがカレントディレクトリではなくなり、os.RemoveAll が正常に実行できるようになります。

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

src/pkg/path/filepath/path_test.go ファイルの TestAbs 関数に以下の5行が追加されました。

--- a/src/pkg/path/filepath/path_test.go
+++ b/src/pkg/path/filepath/path_test.go
@@ -684,10 +684,15 @@ func TestAbs(t *testing.T) {
 	}\n \tdefer os.RemoveAll(root)\n \n+\twd, err := os.Getwd()\n+\tif err != nil {\n+\t\tt.Fatal(\"getwd failed: \", err)\n+\t}\n \terr = os.Chdir(root)\n \tif err != nil {\n \t\tt.Fatal(\"chdir failed: \", err)\n \t}\n+\tdefer os.Chdir(wd)\n \n \tfor _, dir := range absTestDirs {\n \t\terr = os.Mkdir(dir, 0777)\n```

## コアとなるコードの解説

1.  **`wd, err := os.Getwd()`**:
    *   `os.Getwd()` を呼び出して、`TestAbs` 関数が実行される前の現在のワーキングディレクトリ(カレントディレクトリ)のパスを取得し、`wd` 変数に格納します。
    *   このパスは、テスト終了時にカレントディレクトリを元に戻すために使用されます。
    *   エラーが発生した場合は、`t.Fatal` でテストを終了させます。

2.  **`defer os.Chdir(wd)`**:
    *   この `defer` ステートメントは、`TestAbs` 関数がリターンする直前に `os.Chdir(wd)` を実行するようにスケジュールします。
    *   これにより、`os.Chdir(root)` で一時ディレクトリに移動した後、テストが完了し、`os.RemoveAll(root)` が実行される前に、カレントディレクトリがテスト開始時の元のディレクトリ (`wd`) に戻されます。
    *   この変更により、`os.RemoveAll(root)` が実行される際には、`root` ディレクトリはもはやカレントディレクトリではなくなり、削除が成功する可能性が高まります。

## 関連リンク

*   GitHubコミットページ: [https://github.com/golang/go/commit/4b943bded8b92296e50aaafe3e55fc4bc96b4f23](https://github.com/golang/go/commit/4b943bded8b92296e50aaafe3e55fc4bc96b4f23)
*   Go CL (Code Review) ページ: [https://golang.org/cl/5986045](https://golang.org/cl/5986045)

## 参考にした情報源リンク

*   Go言語の `os` パッケージのドキュメント: [https://pkg.go.dev/os](https://pkg.go.dev/os)
*   Go言語の `path/filepath` パッケージのドキュメント: [https://pkg.go.dev/path/filepath](https://pkg.go.dev/path/filepath)
*   Go言語の `testing` パッケージのドキュメント: [https://pkg.go.dev/testing](https://pkg.go.dev/testing)
*   Go言語の `defer` ステートメントに関するドキュメントやチュートリアル (例: [https://go.dev/blog/defer-panic-recover](https://go.dev/blog/defer-panic-recover))