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

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

このコミットは、Go言語の標準ライブラリpath/filepathパッケージ内のsymlink_windows.goファイルに対する変更です。このファイルは、Windows環境におけるシンボリックリンクの評価(解決)に関連する機能、特にEvalSymlinks関数の実装を含んでいます。EvalSymlinksは、与えられたパスに含まれるシンボリックリンクを解決し、最終的な物理パスを返すことを目的としています。

コミット

commit 48a2c50a18253a034fd85a7c75bd52ebf383a620
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Wed Mar 28 12:27:36 2012 +1100

    path/filepath: correct comment in EvalSymlinks
    
    R=golang-dev, r
    CC=golang-dev, hcwfrichter
    https://golang.org/cl/5934046

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

https://github.com/golang/go/commit/48a2c50a18253a034fd85a7c75bd52ebf383a620

元コミット内容

path/filepath: correct comment in EvalSymlinks
    
R=golang-dev, r
CC=golang-dev, hcwfrichter
https://golang.org/cl/5934046

変更の背景

このコミットは、path/filepathパッケージのEvalSymlinks関数(Windows固有の実装であるevalSymlinks)内のコメントの修正を目的としています。EvalSymlinksは、パス内のシンボリックリンクを解決し、正規化されたパスを返す役割を担っています。Windowsでは、ドライブレターの大文字・小文字の扱いがパスの同一性において重要になる場合があります。

元のコメントでは、syscall.GetLongPathNameがドライブレターのケースを変更しないこと、そしてEvalSymlinksの結果が一意であるべきであるため、EvalSymlinks('c:\\a')EvalSymlinks('C:\\a')が同じ結果を返す必要があると説明されていました。その上で、「ドライブレターを大文字にする。これはos.Getwdが返すものと一致する」と記述されていました。

しかし、この「os.Getwdが返すものと一致する」という部分は、必ずしも正確ではありませんでした。os.Getwd(現在の作業ディレクトリを取得する関数)の動作は、システムやGoのバージョンによって異なる可能性があり、EvalSymlinksのドライブレター正規化の理由としては不適切であると判断されたようです。EvalSymlinksがドライブレターを大文字に正規化する主な理由は、パスの一意性を保証するためであり、特定の他の関数の動作に合わせるためではありません。このコミットは、そのコメントの記述をより正確なものに修正することを目的としています。

前提知識の解説

シンボリックリンクとジャンクション (Windows)

  • シンボリックリンク (Symbolic Link): Windows Vista以降で導入された、ファイルシステム上の別のファイルやディレクトリへの参照です。Unix/Linuxのシンボリックリンク(ソフトリンク)に似ています。mklinkコマンドで作成できます。
    • mklink <Link> <Target>: ファイルのシンボリックリンク
    • mklink /D <Link> <Target>: ディレクトリのシンボリックリンク
  • ジャンクション (Junction): Windows 2000以降で存在する、ディレクトリ専用のシンボリックリンクのようなものです。NTFSファイルシステムのリパースポイント機能を利用しています。mklink /J <Link> <Target>で作成できます。

Go言語のpath/filepath.EvalSymlinksは、これらのリンクを解決し、最終的な物理パスを特定する役割を担います。

パス正規化 (Path Normalization)

パス正規化とは、ファイルシステム上の同じ場所を指す複数の異なるパス表現を、一意の標準形式に変換するプロセスです。これには以下のような側面が含まれます。

  • 相対パスの解決: . (カレントディレクトリ) や .. (親ディレクトリ) を解決し、絶対パスに変換します。
  • セパレータの統一: Windowsでは\、Unix/Linuxでは/がパスセパレータとして使われますが、Goのpath/filepathは内部的に/を使用し、必要に応じて変換します。
  • 冗長なセパレータの除去: C:\\foo\\\\bar のような冗長なセパレータを C:\foo\bar のように整理します。
  • ドライブレターの正規化: Windowsでは、ドライブレター(例: C:)の大文字・小文字が混在する可能性があります(例: c:\C:\)。これらを一意の形式(通常は大文字)に統一することで、パスの比較やキャッシュの効率化を図ります。

この関数は、与えられたパスに含まれるシンボリックリンクを再帰的に解決し、最終的にリンクではない物理的なパスを返します。Windowsにおいては、ジャンクションも同様に解決の対象となります。この関数は、パスの一意性を保証するために、解決されたパスの正規化も行います。

syscall.GetLongPathName

Windows APIの一つで、短いファイル名(8.3形式)を長いファイル名に変換したり、パスの正規化を行う関数です。ただし、この関数はドライブレターのケースを変更しないという特性があります。

技術的詳細

Goのpath/filepath.EvalSymlinks関数は、クロスプラットフォームでのパス操作を抽象化しつつ、各OSのファイルシステム特性に対応しています。Windows環境では、ドライブレターの大文字・小文字の区別がパスの同一性に影響を与える可能性があるため、EvalSymlinksは解決されたパスのドライブレターを大文字に正規化する処理を含んでいます。

変更前のコメントは以下の通りでした。

	// syscall.GetLongPathName does not change the case of the drive letter,
	// but the result of EvalSymlinks must be unique, so we have
	// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
	// Make drive letter upper case. This matches what os.Getwd returns.

このコメントの最後の行「This matches what os.Getwd returns.」が問題でした。EvalSymlinksがドライブレターを大文字にする主な理由は、EvalSymlinks('c:\\a')EvalSymlinks('C:\\a')が同じ一意のパスを返すようにするためです。これは、パスの比較や、パスをキーとするマップなどでの利用において、予期せぬ不整合を防ぐために重要です。

os.Getwdが返すパスのドライブレターのケースは、GoのバージョンやWindowsの環境設定によって異なる可能性があり、EvalSymlinksの動作の根拠として適切ではありませんでした。EvalSymlinksのドライブレター正規化は、あくまでEvalSymlinks自身の「結果の一意性」という要件を満たすためのものであり、他の関数の出力に合わせるためではありません。

このコミットは、この誤解を招く可能性のある記述を削除し、より正確なコメントに修正することで、コードの意図を明確にしています。

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

--- a/src/pkg/path/filepath/symlink_windows.go
+++ b/src/pkg/path/filepath/symlink_windows.go
@@ -55,7 +55,7 @@ func evalSymlinks(path string) (string, error) {
 	// syscall.GetLongPathName does not change the case of the drive letter,
 	// but the result of EvalSymlinks must be unique, so we have
 	// EvalSymlinks(`c:\a`) == EvalSymlinks(`C:\a`).
-	// Make drive letter upper case. This matches what os.Getwd returns.
+	// Make drive letter upper case.
 	if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' {
 		p = string(p[0]+'A'-'a') + p[1:]
 	}

コアとなるコードの解説

変更されたのは、src/pkg/path/filepath/symlink_windows.goファイル内のevalSymlinks関数におけるコメントの1行です。

元の行: // Make drive letter upper case. This matches what os.Getwd returns.

変更後の行: // Make drive letter upper case.

この変更は、コードのロジック自体には一切影響を与えません。if文以下の実際のコード (if len(p) >= 2 && p[1] == ':' && 'a' <= p[0] && p[0] <= 'z' { p = string(p[0]+'A'-'a') + p[1:] }) は、パスpの最初の文字が小文字のドライブレターである場合に、それを大文字に変換するという処理を行っています。この処理は、EvalSymlinksの結果の一意性を保証するために必要であり、このコミットによってその目的がより明確にコメントで示されるようになりました。

コメントの修正は、コードの可読性と保守性を向上させ、将来の開発者がこの部分のコードの意図を誤解するのを防ぐことを目的としています。

関連リンク

参考にした情報源リンク