[インデックス 12631] ファイルの概要
このコミットは、Go言語プロジェクトのmisc/dist
ディレクトリにあるbindist.go
ファイルに対する変更です。このファイルは、Goのバイナリ配布物(tarアーカイブ)を作成する際の処理を担当しています。具体的には、tarアーカイブにファイルを追加する際に、ファイルのパーミッション(モードビット)が正しく設定されるように修正されています。
コミット
commit d6ea81e0b9f078253151dbca0341f44ba7e9d466
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Wed Mar 14 08:24:11 2012 -0700
misc/dist: don't lose mode bits when setting tar permissions
R=golang-dev, bsiegert, rsc
CC=golang-dev
https://golang.org/cl/5822046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d6ea81e0b9f078253151dbca0341f44ba7e9d466
元コミット内容
このコミットの目的は、Goのバイナリ配布物を作成する際に、tarアーカイブ内のファイルのパーミッションを設定するロジックを修正することです。以前の実装では、実行可能ファイルには0755
、それ以外のファイルには0644
というパーミッションを「強制的に」設定していました。この「強制」が問題で、ファイルの既存の特殊なモードビット(例: setuid, setgid, sticky bitなど)が失われる可能性がありました。このコミットは、これらの重要なモードビットを保持しつつ、基本的な読み書き実行パーミッションを適切に設定するように改善しています。
変更の背景
Goプロジェクトでは、様々なプラットフォーム向けにコンパイルされたバイナリ配布物を提供しています。これらの配布物は通常、tarアーカイブ形式で提供されます。tarアーカイブを作成する際、各ファイルにはそのパーミッション情報(モードビット)が記録されます。
元のコードでは、tarヘッダのモードビットを直接0755
または0644
に上書きしていました。これは、一般的なファイルパーミッションを設定する上では問題ないように見えますが、Unix/Linuxシステムには、通常の読み書き実行パーミッション以外にも、ファイルの動作に影響を与える特別なモードビットが存在します。例えば、setuid
ビットが設定された実行ファイルは、そのファイルの所有者の権限で実行されます。setgid
ビットはグループIDに関連し、sticky bit
はディレクトリ内のファイル削除権限に影響します。
これらの特殊なモードビットが、tarアーカイブ作成時に意図せず失われると、配布されたバイナリやスクリプトが期待通りに動作しない、あるいはセキュリティ上の問題を引き起こす可能性がありました。このコミットは、このようなモードビットの損失を防ぎ、ファイルの本来の属性を尊重しつつ、基本的なパーミッションを適用することを目的としています。
前提知識の解説
Unix/Linuxファイルパーミッション(モードビット)
Unix/Linuxシステムでは、ファイルやディレクトリにはパーミッションが設定されており、これは「モードビット」として表現されます。これは通常、8進数(例: 755
, 644
)で表されます。
- 所有者 (Owner): ファイルの所有者に対するパーミッション。
- グループ (Group): ファイルのグループに属するユーザーに対するパーミッション。
- その他 (Others): 上記以外のすべてのユーザーに対するパーミッション。
各カテゴリには、読み取り (r=4), 書き込み (w=2), 実行 (x=1) の権限があります。これらを合計して8進数で表現します(例: rwx
= 4+2+1=7, rw-
= 4+2+0=6)。
さらに、これらとは別に「特殊パーミッションビット」が存在します。
- SetUID (SUID): 実行ファイルに設定されると、そのファイルを実行したユーザーではなく、ファイルの所有者の権限で実行されます。8進数で
4000
として表現されます。 - SetGID (SGID): 実行ファイルに設定されると、そのファイルを実行したユーザーではなく、ファイルのグループの権限で実行されます。ディレクトリに設定されると、そのディレクトリ内に作成される新しいファイルやサブディレクトリは、親ディレクトリのグループを継承します。8進数で
2000
として表現されます。 - Sticky Bit: ディレクトリに設定されると、そのディレクトリ内のファイルは、ファイルの所有者、ディレクトリの所有者、またはrootユーザーのみが削除または名前変更できます。8進数で
1000
として表現されます。
これらの特殊パーミッションビットは、通常のrwx
パーミッションの前に付加されて、例えば4755
(SUID + 755)のように表現されます。
ビット演算子
このコミットでは、Go言語におけるビット演算子が使用されています。
&
(ビットAND): 両方のビットが1の場合に1を返します。|
(ビットOR): どちらかのビットが1の場合に1を返します。^
(ビットXOR): 両方のビットが異なる場合に1を返します。~
(ビットNOT): ビットを反転させます(Goでは^
を単項演算子として使用)。
特に重要なのは、hdr.Mode&^0777
という表現です。
0777
は8進数で、バイナリでは111111111
(9ビット)となります。これは、ユーザー、グループ、その他の読み書き実行パーミッションのすべてのビットがセットされた状態を表します。^0777
は、0777
のビットを反転させます。これにより、パーミッションビット以外のすべてのビットが1になり、パーミッションビットが0になります。hdr.Mode&^0777
は、hdr.Mode
からパーミッションビットのみをクリアし、他のモードビット(特殊パーミッションビットなど)を保持する効果があります。
os.FileInfo
とos.FileMode
(Go言語)
Go言語のos
パッケージは、オペレーティングシステムとのインタフェースを提供します。
os.FileInfo
インターフェースは、ファイルに関する情報(名前、サイズ、モード、最終更新時刻など)を提供します。os.FileMode
型は、ファイルのモードとパーミッションビットを表す型です。os.FileMode.Perm()
メソッドは、os.FileMode
から通常のパーミッションビット(rwx
)のみを抽出して返します。特殊パーミッションビットは含まれません。
技術的詳細
このコミットの技術的な核心は、tarアーカイブのヘッダに設定するファイルのモードビットの操作方法の変更にあります。
元のコードでは、以下のようにモードを設定していました。
// Force mode to 0755 for executables, 0644 for everything else.
if hdr.Mode&0111 != 0 { // 実行可能ビットが立っているかチェック
hdr.Mode = 0755 // モードを0755に強制設定
} else {
hdr.Mode = 0644 // モードを0644に強制設定
}
このロジックの問題点は、hdr.Mode = 0755
やhdr.Mode = 0644
のように直接代入している点です。これにより、hdr.Mode
が持っていた可能性のあるSetUID、SetGID、Sticky Bitなどの特殊なモードビットが完全に失われてしまいます。例えば、元のファイルが4755
(SUID + 755)であったとしても、この処理を通ると0755
になってしまい、SUIDビットが失われます。
新しいコードでは、この問題を解決するためにビット演算子を巧みに使用しています。
// Force permissions to 0755 for executables, 0644 for everything else.
if fi.Mode().Perm()&0111 != 0 { // ファイルの実際のパーミッションで実行可能ビットをチェック
hdr.Mode = hdr.Mode&^0777 | 0755 // 既存のモードからパーミッションビットをクリアし、0755をORする
} else {
hdr.Mode = hdr.Mode&^0777 | 0644 // 既存のモードからパーミッションビットをクリアし、0644をORする
}
この変更により、以下の2つの重要な改善がなされています。
-
モードビットの保持:
hdr.Mode&^0777
という部分が、既存のhdr.Mode
から通常のパーミッションビット(0777
に対応する部分)のみをクリアします。これにより、SetUID、SetGID、Sticky Bitなどの特殊なモードビットはそのまま保持されます。 その後、| 0755
または| 0644
とすることで、クリアされたパーミッションビットの場所に、指定された新しいパーミッション(0755
または0644
)が設定されます。 結果として、特殊なモードビットは維持されつつ、基本的な読み書き実行パーミッションが適切に設定されるようになります。 -
実行可能チェックの正確性: 条件式が
hdr.Mode&0111 != 0
からfi.Mode().Perm()&0111 != 0
に変更されました。hdr.Mode
はtarヘッダのモードであり、場合によっては既に操作されている可能性があります。fi.Mode().Perm()
は、os.FileInfo
から取得したファイルの実際のモードから、純粋なパーミッションビットのみを返します。これにより、実行可能かどうかの判断が、ファイルの実際のパーミッションに基づいてより正確に行われるようになります。0111
は実行可能ビット(所有者、グループ、その他の実行ビット)を表します。
この修正により、Goの配布物に含まれるファイルが、元のファイルが持っていた重要な属性を失うことなく、期待されるパーミッションで展開されることが保証されます。
コアとなるコードの変更箇所
misc/dist/bindist.go
ファイルのmakeTar
関数内の以下の部分が変更されました。
--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -546,11 +546,11 @@ func makeTar(targ, workdir string) error {
thdr.Uid = 0
thdr.Gid = 0
- // Force mode to 0755 for executables, 0644 for everything else.
- if hdr.Mode&0111 != 0 {
- hdr.Mode = 0755
+ // Force permissions to 0755 for executables, 0644 for everything else.
+ if fi.Mode().Perm()&0111 != 0 {
+ hdr.Mode = hdr.Mode&^0777 | 0755
} else {
- hdr.Mode = 0644
+ hdr.Mode = hdr.Mode&^0777 | 0644
}
err = tw.WriteHeader(hdr)
コアとなるコードの解説
変更されたコードブロックは、makeTar
関数内で各ファイルをtarアーカイブに追加する直前に実行されます。
-
fi.Mode().Perm()&0111 != 0
:fi
は、現在処理しているファイルに関するos.FileInfo
オブジェクトです。fi.Mode()
は、そのファイルのos.FileMode
を返します。.Perm()
メソッドは、os.FileMode
から通常のパーミッションビット(rwx
)のみを抽出します。&0111
は、抽出されたパーミッションビットに対して、実行可能ビット(所有者、グループ、その他の実行ビット)が一つでも立っているか(つまり、ファイルが実行可能であるか)をチェックします。- この条件が
true
の場合、ファイルは実行可能であると判断されます。
-
hdr.Mode = hdr.Mode&^0777 | 0755
(実行可能ファイルの場合):hdr.Mode
は、tarヘッダに設定されるファイルのモードです。hdr.Mode&^0777
: 既存のhdr.Mode
から、通常のパーミッションビット(0777
に対応する部分)をクリアします。これにより、SetUID、SetGID、Sticky Bitなどの特殊なモードビットはそのまま残ります。| 0755
: クリアされたパーミッションビットの場所に、実行可能ファイルに推奨されるパーミッション0755
(所有者: rwx, グループ: rx, その他: rx)をビットORで設定します。- この結果、特殊なモードビットを保持しつつ、基本的なパーミッションが
0755
に設定されたhdr.Mode
が生成されます。
-
hdr.Mode = hdr.Mode&^0777 | 0644
(実行可能ファイルでない場合):- 同様に、既存の
hdr.Mode
から通常のパーミッションビットをクリアします。 | 0644
: クリアされたパーミッションビットの場所に、非実行可能ファイルに推奨されるパーミッション0644
(所有者: rw, グループ: r, その他: r)をビットORで設定します。- この結果、特殊なモードビットを保持しつつ、基本的なパーミッションが
0644
に設定されたhdr.Mode
が生成されます。
- 同様に、既存の
この修正により、tarアーカイブ作成時にファイルのパーミッションがより堅牢かつ正確に処理されるようになり、配布物の信頼性が向上しました。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のIssueトラッカー (関連するIssueがある場合): https://github.com/golang/go/issues
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにある
golang.org/cl/5822046
はこのシステムへのリンクです)
参考にした情報源リンク
- Unixのファイルパーミッションに関する一般的な情報源 (例: Wikipedia, 各種Linux/Unixドキュメント)
- Go言語の
os
パッケージに関するドキュメント - Go言語のビット演算子に関する情報 (Goの公式ドキュメントやチュートリアル)
- https://go.dev/tour/basics/13 (Go Tour: Constants)
- https://go.dev/ref/spec#Operators (Go Language Specification: Operators)