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

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

このコミットは、Go言語の標準ライブラリ path/filepath パッケージ内の Walk 関数に関するドキュメントの更新です。具体的には、Walk 関数がシンボリックリンクを辿らないという重要な挙動を明示的にドキュメントに追加しています。これにより、ユーザーが Walk 関数の動作について誤解するのを防ぎ、より正確な情報を提供することを目的としています。

コミット

commit a60ffed9e73a5956b6400ae8863856967982c779
Author: Russ Cox <rsc@golang.org>
Date:   Mon Feb 4 22:59:30 2013 -0500

    path/filepath: document that Walk does not follow symlinks
    
    Fixes #4759.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7304043

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

https://github.com/golang/go/commit/a60ffed9e73a5956b6400ae8863856967982c779

元コミット内容

このコミットの目的は、「path/filepath パッケージの Walk 関数がシンボリックリンクを辿らないことをドキュメントに明記する」ことです。これは、GoのIssue #4759を解決するための変更であり、コードレビュー担当者として golang-devbradfitz が指定され、golang-dev にCCされています。また、この変更はGoのコードレビューシステムであるGerritのチェンジリスト https://golang.org/cl/7304043 に対応しています。

変更の背景

この変更の背景には、GoのIssue #4759が存在します。このIssueでは、path/filepath.Walk 関数がシンボリックリンクを辿らないという挙動が、ドキュメントに明記されていないためにユーザーが混乱するという問題が報告されていました。

ファイルシステムを走査する際、シンボリックリンクの扱いは非常に重要な考慮事項です。シンボリックリンクを辿るか辿らないかによって、走査されるファイルの範囲や、無限ループの発生可能性などが大きく変わってきます。filepath.Walk は元々シンボリックリンクを辿らない設計でしたが、そのことがドキュメントに明確に記載されていなかったため、ユーザーが期待する動作と実際の動作が異なり、バグや誤解の原因となる可能性がありました。

このコミットは、このような混乱を解消し、Walk 関数の正確な挙動をユーザーに伝えるために、ドキュメントにその旨を追記することを目的としています。

前提知識の解説

path/filepath.Walk 関数

path/filepath.Walk は、Go言語の標準ライブラリ path/filepath パッケージに含まれる関数で、指定されたルートディレクトリからファイルシステムツリーを再帰的に走査するために使用されます。この関数は、走査中に見つかった各ファイルやディレクトリに対して、ユーザーが定義した WalkFunc を呼び出します。

Walk 関数のシグネチャは以下の通りです。

func Walk(root string, fn WalkFunc) error
  • root: 走査を開始するルートディレクトリのパスです。
  • fn: WalkFunc 型の関数で、走査中に見つかった各ファイルやディレクトリに対して呼び出されます。

WalkFunc のシグネチャは以下の通りです。

type WalkFunc func(path string, info os.FileInfo, err error) error
  • path: 現在走査しているファイルまたはディレクトリの絶対パスです。
  • info: os.FileInfo インターフェースで、ファイルまたはディレクトリに関する情報(名前、サイズ、パーミッション、変更時刻など)を提供します。
  • err: path へのアクセス中に発生したエラーです。エラーがない場合は nil です。

WalkFunc が返すエラーによって、Walk 関数の後続の動作を制御できます。

  • nil を返すと、走査は通常通り続行されます。
  • filepath.SkipDir を返すと、現在の path がディレクトリの場合、そのディレクトリの内容はスキップされ、次の兄弟要素の走査に移ります。
  • その他の非 nil エラーを返すと、走査は直ちに停止し、そのエラーが Walk 関数の戻り値として返されます。

シンボリックリンク(またはソフトリンク、symlink)は、ファイルシステム内の別のファイルやディレクトリへの参照(ポインタ)として機能する特殊な種類のファイルです。これは、WindowsのショートカットやmacOSのエイリアスに似ていますが、ファイルシステムレベルで動作します。

シンボリックリンクは、元のファイルやディレクトリのパスを格納しており、シンボリックリンクにアクセスすると、オペレーティングシステムは自動的にその参照先のファイルやディレクトリにリダイレクトします。

シンボリックリンクの主な用途は以下の通りです。

  • パスの短縮: 長いパスを持つファイルやディレクトリへのアクセスを容易にする。
  • バージョンの管理: ソフトウェアの異なるバージョンを切り替える際に、シンボリックリンクを更新するだけで済むようにする。
  • 共有リソース: 複数の場所から同じファイルやディレクトリにアクセスできるようにする。
  • ファイルシステムの再編成: 実際のデータを移動せずに、論理的なファイル構造を変更する。

シンボリックリンクを扱う際には、「シンボリックリンク自体を操作する」のか、「シンボリックリンクが指す実体を操作する」のかという区別が重要になります。多くのファイルシステム操作関数には、この挙動を制御するオプションがあります。

技術的詳細

path/filepath.Walk 関数がシンボリックリンクを辿らないという挙動は、設計上の意図によるものです。ファイルシステムを走査する際にシンボリックリンクを辿るかどうかは、アプリケーションの要件によって異なりますが、一般的には以下の理由からデフォルトで辿らない設計が採用されることが多いです。

  1. 無限ループの回避: シンボリックリンクは、循環参照(AがBを指し、BがAを指す、またはディレクトリが自身の親ディレクトリを指すなど)を作成する可能性があります。Walk 関数がシンボリックリンクを無条件に辿ると、このような循環参照に遭遇した場合に無限ループに陥り、プログラムが終了しなくなる可能性があります。
  2. パフォーマンス: シンボリックリンクを辿るには、追加のファイルシステム操作(リンク先の解決)が必要になります。大規模なファイルシステムで多数のシンボリックリンクが存在する場合、これを辿ることで走査のパフォーマンスが著しく低下する可能性があります。
  3. セキュリティ: 悪意のあるユーザーがシンボリックリンクを操作して、プログラムが本来アクセスすべきでないシステム上の機密ファイルやディレクトリにアクセスさせる「パス・トラバーサル攻撃」のリスクを軽減できます。シンボリックリンクを辿らないことで、プログラムが意図しないファイルシステム領域に踏み込むことを防ぎやすくなります。
  4. 予測可能性: Walk 関数の挙動をシンプルかつ予測可能に保つため、シンボリックリンクの解決という複雑なロジックをデフォルトでは含めないという選択がなされています。シンボリックリンクを辿る必要がある場合は、ユーザーが WalkFunc 内で os.Lstatfilepath.EvalSymlinks などの関数を使って明示的に処理を記述することが推奨されます。

このコミットでは、Walk 関数の既存の挙動を変更するのではなく、その挙動をドキュメントに明記することで、ユーザーがこの関数の特性を正しく理解できるようにしています。これにより、ユーザーは Walk 関数を使用する際に、シンボリックリンクが走査の対象外となることを認識し、必要に応じて独自のシンボリックリンク処理ロジックを実装するなどの対応を取ることができます。

Go 1.16で導入された filepath.WalkDir も同様にシンボリックリンクを辿りません。これは、Walk 関数の設計思想が後継関数にも引き継がれていることを示しています。

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

--- a/src/pkg/path/filepath/path.go
+++ b/src/pkg/path/filepath/path.go
@@ -374,6 +374,7 @@ func walk(path string, info os.FileInfo, walkFn WalkFunc) error {
 // and directories are filtered by walkFn. The files are walked in lexical
 // order, which makes the output deterministic but means that for very
 // large directories Walk can be inefficient.
+// Walk does not follow symbolic links.
 func Walk(root string, walkFn WalkFunc) error {
 	info, err := os.Lstat(root)
 	if err != nil {

コアとなるコードの解説

このコミットによるコードの変更は非常にシンプルで、src/pkg/path/filepath/path.go ファイルの Walk 関数のドキュメンテーションコメントに1行追加されただけです。

具体的には、Walk 関数の既存のコメントブロックの最後に、以下の行が追加されました。

// Walk does not follow symbolic links.

この変更は、Walk 関数の実際の動作ロジックには一切影響を与えません。Walk 関数は以前からシンボリックリンクを辿らない設計でしたが、その事実がドキュメントに明記されていなかったため、この1行を追加することで、関数の挙動に関する曖昧さを解消し、ユーザーへの情報提供を改善しています。

これにより、go doc path/filepath.Walk コマンドやGoの公式ドキュメントを参照した際に、Walk 関数がシンボリックリンクを辿らないことが明確に示されるようになります。これは、ユーザーが Walk 関数を正しく理解し、予期せぬ動作に遭遇するのを防ぐ上で非常に重要な改善です。

関連リンク

参考にした情報源リンク