[インデックス 17548] ファイルの概要
このコミットは、Go言語のツールチェインの一部である cmd/api
コマンドに関連する変更です。src/cmd/api/run.go
ファイルは、go api
コマンドがAPIチェックを実行する際に使用するGoワークスペース(GOPATH)の準備ロジックを含んでいます。具体的には、一時的なGOPATHを生成し、必要なGoツールをチェックアウトする処理を担っています。
コミット
commit b34ec90e1945af8ed22bf96254deb4c637c0bfbc
Author: Robert Daniel Kortschak <dan.kortschak@adelaide.edu.au>
Date: Wed Sep 11 10:50:56 2013 +1000
cmd/api: make api check directory per-user
Fixes #6353.
R=golang-dev, bradfitz, alex.brainman
CC=golang-dev
https://golang.org/cl/13652043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b34ec90e1945af8ed22bf96254deb4c637c0bfbc
元コミット内容
cmd/api: make api check directory per-user
このコミットは、go api
コマンドがAPIチェックを実行する際に使用する一時ディレクトリを、ユーザーごとに分離するように変更します。これにより、複数のユーザーが同じシステムで go api
コマンドを実行した際に発生する可能性のある競合や問題が解決されます。
Fixes #6353.
この変更は、GoのIssue #6353を修正します。
変更の背景
Goの cmd/api
ツールは、GoのAPIが後方互換性を維持しているかを確認するために使用されます。このツールは、一時的なGOPATH環境を作成し、その中にGoのソースコードをチェックアウトしてAPIチェックを実行します。
変更前の実装では、この一時的なGOPATHディレクトリが os.TempDir()
によって返されるシステム共通の一時ディレクトリ内に、固定の名前(gopath-api
)で作成されていました。この設計には、以下のような問題がありました。
- 複数ユーザー環境での競合: 複数のユーザーが同じシステム上で同時に
go api
コマンドを実行した場合、全員が同じ一時ディレクトリを使用しようとします。これにより、ファイルロックの競合、ファイルの破損、予期せぬ動作、または一方のユーザーの操作が他方のユーザーの操作に影響を与えるといった問題が発生する可能性がありました。 - パーミッションの問題: あるユーザーが作成した一時ディレクトリやファイルに対して、別のユーザーが書き込み権限を持たない場合、エラーが発生する可能性がありました。
- クリーンアップの問題: 共有ディレクトリであるため、どのユーザーがクリーンアップを担当すべきか、またはクリーンアップが適切に行われるかどうかが不明確になることがありました。
このコミットは、これらの問題を解決するために、一時GOPATHディレクトリのパスに現在のユーザー名を含めることで、ユーザーごとに独立したディレクトリが作成されるようにします。
前提知識の解説
- GOPATH: Go言語のワークスペースの概念です。Goのソースコード、コンパイルされたパッケージ、実行可能ファイルが配置されるディレクトリ構造を定義します。
go get
やgo install
などのコマンドは、GOPATHに基づいて動作します。 os.TempDir()
: Go標準ライブラリのos
パッケージに含まれる関数で、オペレーティングシステムが一時ファイルやディレクトリを保存するために推奨するデフォルトのディレクトリのパスを返します。例えば、Linuxでは/tmp
、macOSでは/var/folders/...
、WindowsではC:\Users\<username>\AppData\Local\Temp
などが一般的です。os/user
パッケージ: Go標準ライブラリのos/user
パッケージは、現在のユーザーや指定されたユーザーに関する情報を取得するための機能を提供します。user.Current()
関数は、現在のユーザーの情報を表す*User
構造体を返します。この構造体には、ユーザー名 (Username
) やユーザーID (Uid
) などの情報が含まれます。filepath.Join()
: Go標準ライブラリのpath/filepath
パッケージに含まれる関数で、引数として与えられたパス要素を結合して、プラットフォーム固有のパス区切り文字(例:/
または\
)を使用してクリーンなパスを構築します。log.Fatalf()
: Go標準ライブラリのlog
パッケージに含まれる関数で、フォーマットされたエラーメッセージを出力し、プログラムを終了させます。
技術的詳細
このコミットの主要な目的は、cmd/api
ツールが使用する一時的なGOPATHディレクトリをユーザーごとに分離することです。これを実現するために、以下のステップが導入されました。
- 現在のユーザー情報の取得:
os/user
パッケージのuser.Current()
関数を使用して、現在go api
コマンドを実行しているユーザーの情報を取得します。これにより、ユーザー名 (u.Username
) にアクセスできるようになります。 - ユーザー名を含む一時ディレクトリパスの生成: 取得したユーザー名を一時GOPATHディレクトリのパスに組み込みます。具体的には、
os.TempDir()
が返す一時ディレクトリのパスに、gopath-api-
というプレフィックスと、クリーンアップされたユーザー名を結合し、さらにgoToolsVersion
を追加します。gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(u.Username), goToolsVersion)
- ユーザー名のクリーンアップ:
cleanUsername
という新しいヘルパー関数が導入されました。これは、ユーザー名に含まれる可能性のある特殊文字(\
、/
、:
)をアンダースコア(_
)に置換します。これは、ファイルシステムパスとして無効な文字や、パスの解釈に混乱を招く可能性のある文字を排除し、安全なディレクトリ名を作成するためです。例えば、Windowsのドメインユーザー名(例:DOMAIN\username
)や、Unix系システムでパス区切り文字を含むユーザー名に対応するためと考えられます。
この変更により、各ユーザーは自分専用の一時GOPATHディレクトリを持つことになり、複数ユーザー環境での競合やパーミッションの問題が解消されます。
コアとなるコードの変更箇所
src/cmd/api/run.go
ファイルにおいて、prepGoPath
関数と新しいヘルパー関数 cleanUsername
が変更・追加されました。
--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -19,6 +19,7 @@ import (
"net/http"
"os"
"os/exec"
+ "os/user"
"path/filepath"
"strconv"
"strings"
@@ -99,8 +100,13 @@ func forceAPICheck() bool {
func prepGoPath() string {
const tempBase = "go.tools.TMP"
+ u, err := user.Current()
+ if err != nil {
+ log.Fatalf("Error getting current user: %v", err)
+ }
+
// The GOPATH we'll return
- gopath := filepath.Join(os.TempDir(), "gopath-api", goToolsVersion)
+ gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(u.Username), goToolsVersion)
// cloneDir is where we run "hg clone".
cloneDir := filepath.Join(gopath, "src", "code.google.com", "p")
@@ -140,6 +146,18 @@ func prepGoPath() string {
return gopath
}
+func cleanUsername(n string) string {
+ b := make([]rune, len(n))
+ for i, r := range n {
+ if r == '\\' || r == '/' || r == ':' {
+ b[i] = '_'
+ } else {
+ b[i] = r
+ }
+ }
+ return string(b)
+}
+
func goToolsCheckoutGood(dir string) bool {
if _, err := os.Stat(dir); err != nil {
return false
コアとなるコードの解説
-
import "os/user"
の追加:os/user
パッケージは、現在のシステムユーザーの情報を取得するために必要となるため、インポートリストに追加されました。
-
prepGoPath
関数内の変更:u, err := user.Current()
:user.Current()
関数を呼び出して、現在実行中のユーザーの情報を取得します。この関数は*user.User
型のポインタとエラーを返します。
if err != nil { log.Fatalf("Error getting current user: %v", err) }
:user.Current()
の呼び出しでエラーが発生した場合(例: ユーザー情報が取得できない場合)、log.Fatalf
を使用してエラーメッセージを出力し、プログラムを異常終了させます。これは、ユーザー情報が一時ディレクトリのパス生成に不可欠であるため、致命的なエラーとして扱われます。
gopath := filepath.Join(os.TempDir(), "gopath-api-"+cleanUsername(u.Username), goToolsVersion)
:- 変更前のコードでは
gopath := filepath.Join(os.TempDir(), "gopath-api", goToolsVersion)
でした。 - この行が、一時GOPATHディレクトリのパスを生成する核心部分です。
os.TempDir()
はシステムの一時ディレクトリのパスを返します。"gopath-api-" + cleanUsername(u.Username)
の部分が新しく追加されました。cleanUsername(u.Username)
は、現在のユーザー名 (u.Username
) を安全な形式に変換した文字列を返します。これにより、生成されるパスはos.TempDir()/gopath-api-username_cleaned/goToolsVersion
のようになり、ユーザーごとに一意のディレクトリが確保されます。goToolsVersion
は、Goツールのバージョンを示す定数で、異なるバージョンのツールが共存できるようにするためのものです。
- 変更前のコードでは
-
cleanUsername
関数の追加:func cleanUsername(n string) string
:- この関数は、入力された文字列
n
(ユーザー名)を受け取り、クリーンアップされた文字列を返します。
- この関数は、入力された文字列
b := make([]rune, len(n))
:- 結果の文字列を構築するための
rune
スライスを、入力文字列と同じ長さで初期化します。rune
を使用することで、マルチバイト文字(UTF-8)も正しく処理できます。
- 結果の文字列を構築するための
for i, r := range n
:- 入力文字列
n
をrune
単位でイテレートします。i
はインデックス、r
は現在のrune
です。
- 入力文字列
if r == '\\' || r == '/' || r == ':'
:- 現在の
rune
がバックスラッシュ (\
)、スラッシュ (/
)、またはコロン (:
) のいずれかであるかをチェックします。これらの文字は、ファイルシステムパスの区切り文字や特殊な意味を持つ文字として扱われる可能性があるため、安全のために置換されます。
- 現在の
b[i] = '_'
またはb[i] = r
:- もし
r
が上記の特殊文字のいずれかであれば、b[i]
にアンダースコア (_
) を代入します。 - そうでなければ、
b[i]
に元のrune
r
をそのまま代入します。
- もし
return string(b)
:- 構築された
rune
スライスb
を文字列に変換して返します。
- 構築された
この変更により、go api
コマンドは、各ユーザーの環境で独立して動作し、一時ファイルの競合やパーミッションの問題を回避できるようになりました。
関連リンク
- Go CL (Change List): https://golang.org/cl/13652043
参考にした情報源リンク
- Go言語の公式ドキュメント:
os
パッケージ: https://pkg.go.dev/osos/user
パッケージ: https://pkg.go.dev/os/userpath/filepath
パッケージ: https://pkg.go.dev/path/filepathlog
パッケージ: https://pkg.go.dev/log
- GOPATHに関する情報: https://go.dev/doc/gopath_code
(注: コミットメッセージに記載されている Fixes #6353
のIssueは、現在のGoのIssueトラッカーでは直接見つけることができませんでした。これは、古いIssueトラッカーからの移行や、Issue番号の再割り当てなど、時間の経過による変更が原因である可能性があります。)