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

[インデックス 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.FileInfoos.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 = 0755hdr.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つの重要な改善がなされています。

  1. モードビットの保持: hdr.Mode&^0777という部分が、既存のhdr.Modeから通常のパーミッションビット(0777に対応する部分)のみをクリアします。これにより、SetUID、SetGID、Sticky Bitなどの特殊なモードビットはそのまま保持されます。 その後、| 0755または| 0644とすることで、クリアされたパーミッションビットの場所に、指定された新しいパーミッション(0755または0644)が設定されます。 結果として、特殊なモードビットは維持されつつ、基本的な読み書き実行パーミッションが適切に設定されるようになります。

  2. 実行可能チェックの正確性: 条件式が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アーカイブ作成時にファイルのパーミッションがより堅牢かつ正確に処理されるようになり、配布物の信頼性が向上しました。

関連リンク

参考にした情報源リンク