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

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

このコミットは、Go言語のツールチェインにおける cmd/dist コマンドの修正に関するものです。具体的には、pprof ツールがインストールされる際に、Unix系システム上で実行権限が正しく設定されるように変更が加えられています。これにより、pprof がツールディレクトリにインストールされた後、ユーザーが直接実行できるようになります。

コミット

commit d36426995a3919cb8d6ebd8fac502e764f6e28ed
Author: Bobby Powers <bobbypowers@gmail.com>
Date:   Tue Feb 21 16:49:30 2012 -0500

    cmd/dist: fix pprof permissions
    
    When installing pprof into the tools directory, it needs to
    have execute permissions on unix-like systems.
    
    Fixes issues 3077.
    
    R=golang-dev, rsc, minux.ma
    CC=golang-dev
    https://golang.org/cl/5675095

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

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

元コミット内容

cmd/dist: fix pprof permissions

pprof をツールディレクトリにインストールする際、Unix系システムでは実行権限が必要となる。

Issue 3077 を修正。

変更の背景

この変更は、Go言語のビルドシステムの一部である cmd/distpprof ツールをインストールする際に発生していた問題に対処するものです。具体的には、Unix系オペレーティングシステム(Linux, macOSなど)において、ファイルが実行可能であるためには、そのファイルに実行権限(executable permission)が付与されている必要があります。

以前の cmd/dist の実装では、pprof がツールディレクトリにコピーされる際に、この実行権限が適切に設定されていませんでした。その結果、ユーザーが pprof を実行しようとすると、「Permission denied」(アクセス拒否)エラーが発生し、ツールが利用できないという問題(Issue 3077)が報告されていました。

このコミットは、pprof のインストールプロセスにおいて、ファイルコピー時に明示的に実行権限を付与することで、この問題を解決することを目的としています。これにより、ビルド後に pprof がすぐに利用できるようになります。

前提知識の解説

1. pprof

pprof は、Go言語のプログラムのプロファイリングデータを視覚化するためのツールです。CPU使用率、メモリ割り当て、ゴルーチン(goroutine)のスタックトレースなど、様々なパフォーマンスメトリクスを収集し、グラフやテキスト形式で表示することができます。これにより、開発者はアプリケーションのパフォーマンスボトルネックを特定し、最適化を行うことができます。pprof はGoの標準ツールの一部として提供されており、Goのビルドシステムによってインストールされます。

2. ファイルの実行権限 (Executable Permissions)

Unix系オペレーティングシステムでは、ファイルには所有者、グループ、その他のユーザーに対して読み取り(read)、書き込み(write)、実行(execute)の3種類の権限が設定されます。実行権限は、そのファイルがプログラムとして実行可能であるかどうかを決定します。例えば、シェルスクリプトやコンパイルされたバイナリファイルを実行するには、実行権限が必要です。

権限は通常、数値(例: 755)または記号(例: rwxr-xr-x)で表現されます。

  • r (read): 読み取り権限
  • w (write): 書き込み権限
  • x (execute): 実行権限

0755 という数値は、以下を意味します。

  • 最初の 0: 特殊権限(ここでは無視)
  • 7 (111): 所有者に読み取り、書き込み、実行権限
  • 5 (101): グループに読み取り、実行権限
  • 5 (101): その他のユーザーに読み取り、実行権限

3. cmd/dist

cmd/dist は、Go言語のソースコードからGoツールチェイン全体をビルドするための内部コマンドです。Goのコンパイラ、リンカ、標準ライブラリ、各種ツール(go fmt, go vet, pprof など)のビルドとインストールをオーケストレーションします。これはGoの自己ホスト型(self-hosting)ビルドプロセスの中核をなす部分であり、GoのソースコードからGoのバイナリを生成するために使用されます。

4. Goのビルドプロセス

Goのビルドプロセスは、make.bash (Unix系) や make.bat (Windows) といったスクリプトによって開始されます。これらのスクリプトは内部的に cmd/dist を呼び出し、Goのソースコードをコンパイルし、必要なバイナリやライブラリを適切なディレクトリ(通常は $GOROOT/bin$GOROOT/pkg など)に配置します。このプロセス中に、pprof のようなツールもビルドされ、インストールされます。

技術的詳細

このコミットの技術的な核心は、ファイル書き込み関数 writefile に新しい引数 exec を追加し、ファイルコピー関数 copy を介して、特定のファイル(特に pprof のような実行可能ファイル)が書き込まれる際に実行権限を付与できるようにした点です。

writefile 関数の変更

元の writefile 関数は、単にバッファの内容をファイルに書き込むだけでした。この変更により、writefile は3番目の引数 int exec を受け取るようになりました。

  • exec が非ゼロの場合、ファイルが書き込まれた後に chmod(fd, 0755) が呼び出され、そのファイルに実行権限が付与されます。
  • exec がゼロの場合、権限は変更されません。

これは、Unix系システム (src/cmd/dist/unix.c) の実装に chmod システムコールを追加することで実現されています。Windowsシステム (src/cmd/dist/windows.c) の writefile 関数にも exec 引数が追加されていますが、WindowsのファイルシステムはUnixのような実行権限の概念を持たないため、USED(exec) マクロを使って引数が未使用であることを示し、実際の権限変更は行われません。

copy 関数の変更

copy 関数は、ソースファイルの内容を読み取り、デスティネーションファイルに書き込む役割を担っています。この変更により、copy 関数も3番目の引数 int exec を受け取るようになりました。そして、この exec 引数をそのまま内部で呼び出す writefile 関数に渡すように修正されました。

install 関数の変更

install 関数は、Goツールチェインの様々なコンポーネントをインストールする際に copy 関数を呼び出します。このコミットでは、install 関数内で pprof をツールディレクトリにコピーする箇所 (if(hasprefix(dir, "misc/"))) で、copy 関数を呼び出す際に exec 引数に 1 を渡すように変更されました。これにより、pprof がコピーされる際に実行権限が付与されるようになります。

他のファイル(例えば、pkg/runtime 関連のヘッダーファイルなど)をコピーする際には、exec 引数に 0 が渡されており、これらのファイルには実行権限が付与されないようになっています。これは、ヘッダーファイルが実行可能である必要がないため、適切な権限管理が行われていることを示します。

この修正により、cmd/distpprof のような実行可能ファイルを正しくインストールし、ユーザーが追加の手順なしにそれらを実行できるようになります。

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

src/cmd/dist/a.h (ヘッダーファイルの変更)

--- a/src/cmd/dist/a.h
+++ b/src/cmd/dist/a.h
@@ -120,7 +120,7 @@ void	runv(Buf *b, char *dir, int mode, Vec *argv);
 void	bgrunv(char *dir, int mode, Vec *argv);
 void	bgwait(void);
 bool	streq(char*, char*);
-void	writefile(Buf*, char*);
+void	writefile(Buf*, char*, int);
 void	xatexit(void (*f)(void));
 void	xexit(int);
 void	xfree(void*);

src/cmd/dist/build.c (主要な変更箇所)

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -27,7 +27,7 @@ char *slash;	// / for unix, \ for windows
 bool	rebuildall = 0;
 
 static bool shouldbuild(char*, char*);
-static void copy(char*, char*);
+static void copy(char*, char*, int);
 static char *findgoversion(void);
 
 // The known architecture letters.
@@ -567,7 +567,7 @@ install(char *dir)\
 	// For misc/prof, copy into the tool directory and we're done.
 	if(hasprefix(dir, "misc/")) {
 		copy(bpathf(&b, "%s/%s", tooldir, name),
-\t\t\tbpathf(&b1, "%s/misc/%s", goroot, name));
+\t\t\tbpathf(&b1, "%s/misc/%s", goroot, name), 1);\
 		goto out;
 	}
 
@@ -1051,7 +1051,7 @@ out:
 
 // copy copies the file src to dst, via memory (so only good for small files).
 static void
-copy(char *dst, char *src)\
+copy(char *dst, char *src, int exec)\
 {\
 	Buf b;\
 	\
@@ -1060,7 +1060,7 @@ copy(char *dst, char *src)\
 
 	binit(&b);\
 	readfile(&b, src);\
-\twritefile(&b, dst);\
+\twritefile(&b, dst, exec);\
 	bfree(&b);\
 }

src/cmd/dist/unix.c (Unix系システムでの writefile の実装)

--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -351,9 +351,10 @@ readfile(Buf *b, char *file)\
 	close(fd);\
 }\
 
-// writefile writes b to the named file, creating it if needed.\
+// writefile writes b to the named file, creating it if needed.  if\
+// exec is non-zero, marks the file as executable.\
 void\
-writefile(Buf *b, char *file)\
+writefile(Buf *b, char *file, int exec)\
 {\
 	int fd;\
 	\
@@ -362,9 +363,11 @@ writefile(Buf *b, char *file)\
 	\tfatal("create %s: %s", file, strerror(errno));\
 	if(write(fd, b->p, b->len) != b->len)\
 	\tfatal("short write: %s", strerror(errno));\
+\tif(exec)\
+\t\tfchmod(fd, 0755);\
 	close(fd);\
 }\
-\t\
+\
 // xmkdir creates the directory p.\
 void\
 xmkdir(char *p)\

src/cmd/dist/windows.c (Windows系システムでの writefile の実装)

--- a/src/cmd/dist/windows.c
+++ b/src/cmd/dist/windows.c
@@ -539,12 +539,14 @@ readfile(Buf *b, char *file)\
 }\
 
 void\
-writefile(Buf *b, char *file)\
+writefile(Buf *b, char *file, int exec)\
 {\
 	HANDLE h;\
 	Rune *r;\
 	DWORD n;\
 \
+\tUSED(exec);\
+\
 	if(vflag > 2)\
 	\txprintf("write %s\\n", file);\
 	torune(&r, file);\

コアとなるコードの解説

このコミットの主要な変更は、ファイル書き込み関数 writefile とファイルコピー関数 copyexec という新しい整数型引数を導入したことです。

  1. writefile 関数のシグネチャ変更:

    • 元の void writefile(Buf *b, char *file) から void writefile(Buf *b, char *file, int exec) へと変更されました。
    • この exec 引数は、書き込むファイルに実行権限を付与するかどうかを制御します。
    • src/cmd/dist/unix.c の実装では、exec が非ゼロの場合、chmod(fd, 0755) が呼び出されます。chmod はUnix系システムでファイルのパーミッションを変更するためのシステムコールです。0755 は、所有者に読み書き実行、グループとその他に読み取り実行の権限を与えます。
    • src/cmd/dist/windows.c の実装では、exec 引数は USED(exec); とマークされており、WindowsのファイルシステムではUnixのような実行権限の概念がないため、実際には何も行われません。これはクロスプラットフォーム対応のための適切な処理です。
  2. copy 関数のシグネチャ変更と伝播:

    • 元の static void copy(char *dst, char *src) から static void copy(char *dst, char *src, int exec) へと変更されました。
    • copy 関数は、ソースファイルの内容を読み込み、それを writefile を使ってデスティネーションファイルに書き込みます。この変更により、copy 関数が受け取った exec 引数をそのまま writefile に渡すようになりました。これにより、copy を呼び出す側が、コピー先のファイルに実行権限を付与するかどうかを制御できるようになります。
  3. install 関数での pprof の権限設定:

    • src/cmd/dist/build.c 内の install 関数は、Goツールチェインの様々なコンポーネントをインストールする役割を担っています。
    • 特に misc/ ディレクトリにあるツール(この場合は pprof)をコピーする際に、copy 関数を呼び出す箇所が copy(..., 1) と変更されました。
    • 1 を渡すことで、pprof がツールディレクトリにコピーされる際に、writefile 関数を通じて実行権限 0755 が付与されるようになります。
    • 他のファイル(例えば、pkg/runtime 関連のヘッダーファイルなど)をコピーする際には、copy(..., 0)0 が渡されており、これらのファイルには実行権限が付与されないようになっています。これは、ヘッダーファイルが実行可能である必要がないため、適切な権限管理が行われていることを示します。

これらの変更により、Goのビルドプロセスにおいて pprof のような実行可能ファイルが正しく実行権限を持ってインストールされるようになり、ユーザーがビルド後にすぐにこれらのツールを利用できるようになりました。

関連リンク

参考にした情報源リンク