[インデックス 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-dev
と bradfitz
が指定され、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
関数の戻り値として返されます。
シンボリックリンク (Symbolic Link)
シンボリックリンク(またはソフトリンク、symlink)は、ファイルシステム内の別のファイルやディレクトリへの参照(ポインタ)として機能する特殊な種類のファイルです。これは、WindowsのショートカットやmacOSのエイリアスに似ていますが、ファイルシステムレベルで動作します。
シンボリックリンクは、元のファイルやディレクトリのパスを格納しており、シンボリックリンクにアクセスすると、オペレーティングシステムは自動的にその参照先のファイルやディレクトリにリダイレクトします。
シンボリックリンクの主な用途は以下の通りです。
- パスの短縮: 長いパスを持つファイルやディレクトリへのアクセスを容易にする。
- バージョンの管理: ソフトウェアの異なるバージョンを切り替える際に、シンボリックリンクを更新するだけで済むようにする。
- 共有リソース: 複数の場所から同じファイルやディレクトリにアクセスできるようにする。
- ファイルシステムの再編成: 実際のデータを移動せずに、論理的なファイル構造を変更する。
シンボリックリンクを扱う際には、「シンボリックリンク自体を操作する」のか、「シンボリックリンクが指す実体を操作する」のかという区別が重要になります。多くのファイルシステム操作関数には、この挙動を制御するオプションがあります。
技術的詳細
path/filepath.Walk
関数がシンボリックリンクを辿らないという挙動は、設計上の意図によるものです。ファイルシステムを走査する際にシンボリックリンクを辿るかどうかは、アプリケーションの要件によって異なりますが、一般的には以下の理由からデフォルトで辿らない設計が採用されることが多いです。
- 無限ループの回避: シンボリックリンクは、循環参照(AがBを指し、BがAを指す、またはディレクトリが自身の親ディレクトリを指すなど)を作成する可能性があります。
Walk
関数がシンボリックリンクを無条件に辿ると、このような循環参照に遭遇した場合に無限ループに陥り、プログラムが終了しなくなる可能性があります。 - パフォーマンス: シンボリックリンクを辿るには、追加のファイルシステム操作(リンク先の解決)が必要になります。大規模なファイルシステムで多数のシンボリックリンクが存在する場合、これを辿ることで走査のパフォーマンスが著しく低下する可能性があります。
- セキュリティ: 悪意のあるユーザーがシンボリックリンクを操作して、プログラムが本来アクセスすべきでないシステム上の機密ファイルやディレクトリにアクセスさせる「パス・トラバーサル攻撃」のリスクを軽減できます。シンボリックリンクを辿らないことで、プログラムが意図しないファイルシステム領域に踏み込むことを防ぎやすくなります。
- 予測可能性:
Walk
関数の挙動をシンプルかつ予測可能に保つため、シンボリックリンクの解決という複雑なロジックをデフォルトでは含めないという選択がなされています。シンボリックリンクを辿る必要がある場合は、ユーザーがWalkFunc
内でos.Lstat
やfilepath.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
関数を正しく理解し、予期せぬ動作に遭遇するのを防ぐ上で非常に重要な改善です。
関連リンク
- Go Issue #4759: https://github.com/golang/go/issues/4759
- Gerrit Change-ID: https://golang.org/cl/7304043
参考にした情報源リンク
- Go言語
path/filepath
パッケージ公式ドキュメント: https://pkg.go.dev/path/filepath#Walk - Go言語
os
パッケージ公式ドキュメント (os.Lstat, os.FileInfo): https://pkg.go.dev/os - Stack Overflow:
filepath.Walk
does not follow symlinks: https://stackoverflow.com/questions/20800300/filepath-walk-does-not-follow-symlinks - Go issue #17540 (Windowsでのシンボリックリンク挙動に関する議論): https://github.com/golang/go/issues/17540
- Go issue #4759 (シンボリックリンクを辿らないことのドキュメント化): https://github.com/golang/go/issues/4759