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

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

このコミットは、Go言語のosパッケージにおいて、Windows環境でのディレクトリのファイルモードに実行パーミッション(0111)を含めるように修正するものです。これにより、Windows上でのディレクトリの振る舞いが、Unix系システムにおけるディレクトリの実行権限の解釈(ディレクトリの検索・アクセス権)とより整合性が取れるようになります。

コミット

commit 5b6502356391a59441dee6056f92832295e66f65
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Wed Nov 28 17:01:59 2012 +1100

    os: include 0111 in directory file mode on windows
    
    Fixes #4444.
    
    R=golang-dev, minux.ma, rsc
    CC=golang-dev
    https://golang.org/cl/6858079

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

https://github.com/golang/go/commit/5b6502356391a59441dee6056f92832295e66f65

元コミット内容

os: include 0111 in directory file mode on windows

Fixes #4444.

R=golang-dev, minux.ma, rsc
CC=golang-dev
https://golang.org/cl/6858079

変更の背景

この変更は、Go言語のosパッケージがWindows上でディレクトリのファイルモードを正しく報告しないという問題(Issue #4444)を修正するために行われました。Unix系システムでは、ディレクトリの実行権限(xビット、八進数表記で1)は、そのディレクトリの中身をリストしたり、そのディレクトリをパスの一部として辿ったりするために必要です。しかし、Windowsのファイルシステム(NTFSなど)は、Unixのような厳密な実行権限の概念をディレクトリに適用しません。代わりに、ディレクトリへのアクセスは「フォルダの走査/ファイルの実行」や「コンテンツのリスト/データの読み取り」といった属性によって制御されます。

Goのos.FileModeは、クロスプラットフォームでファイルパーミッションを表現するための抽象化を提供しています。Windows上でディレクトリのStat情報を取得した際に、Unix系システムとの互換性を保つために、ディレクトリには常に実行権限が付与されていると見なす必要がありました。以前の実装では、Windowsのディレクトリに対してStatを実行しても、0111(実行権限)がモードに含まれていなかったため、一部のアプリケーションやテストで予期せぬ動作を引き起こす可能性がありました。このコミットは、この不整合を解消し、Windows上でもディレクトリが「実行可能」(走査可能)であることを明示的に示すように修正しています。

前提知識の解説

ファイルパーミッション(Unix系)

Unix系オペレーティングシステムでは、ファイルやディレクトリには所有者、グループ、その他のユーザーに対する読み取り(r)、書き込み(w)、実行(x)の3種類のパーミッションが設定されます。これらは通常、八進数(オクタル)表記で表現されます。

  • 読み取り (r): 4
  • 書き込み (w): 2
  • 実行 (x): 1

これらの値を組み合わせてパーミッションを表します。例えば、755は以下を意味します。

  • 所有者: rwx (4+2+1=7)
  • グループ: r-x (4+0+1=5)
  • その他: r-x (4+0+1=5)

ディレクトリの場合、実行権限(x)は、そのディレクトリの中身をリストする(lsコマンドなど)ため、またはそのディレクトリをパスの一部として辿る(cdコマンドなど)ために必要です。実行権限がないディレクトリは、たとえ読み取り権限があっても中身を見ることができません。

0111の意味

八進数表記の0111は、所有者、グループ、その他のユーザーに対してそれぞれ実行権限(x)のみが付与されている状態を意味します。ディレクトリにおいては、これは「誰でもこのディレクトリを走査できる」ということを示唆します。

Go言語のos.FileMode

Go言語のosパッケージは、ファイルやディレクトリの情報を抽象化するためにos.FileInfoインターフェースとos.FileMode型を提供しています。os.FileModeは、ファイルの種類(ディレクトリ、シンボリックリンクなど)とパーミッションビットを組み合わせたものです。

  • os.ModeDir: ディレクトリであることを示すビット。
  • os.Stat(path string): 指定されたパスのファイルまたはディレクトリのos.FileInfoを返します。この情報にはファイルモードも含まれます。

Windowsのファイル属性とUnixパーミッションの対応

Windowsのファイルシステム(NTFS)は、Unixのようなビット単位のパーミッションモデルとは異なるアクセス制御リスト(ACL)を使用します。しかし、Goのようなクロスプラットフォーム言語では、Windowsのファイル属性をUnixのパーミッションモデルにマッピングする必要があります。

  • syscall.FILE_ATTRIBUTE_DIRECTORY: Windows APIでディレクトリを示すファイル属性。
  • syscall.FILE_ATTRIBUTE_READONLY: Windows APIで読み取り専用を示すファイル属性。

Goのosパッケージは、mkModeのような内部関数を使って、Windowsのファイル属性をos.FileModeに変換しています。

技術的詳細

このコミットの核心は、Windowsのファイル属性をGoのos.FileModeに変換するロジックの変更です。具体的には、src/pkg/os/stat_windows.go内のmkMode関数が修正されました。

mkMode関数は、Windowsのファイル属性(fa)を受け取り、対応するos.FileModeを生成します。変更前は、ディレクトリの場合にModeDirビットのみを設定していました。

// 変更前
if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
    m |= ModeDir
}

変更後は、ModeDirに加えて、八進数0111(所有者、グループ、その他の実行権限)もOR演算で追加しています。

// 変更後
if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
    m |= ModeDir | 0111 // ここが変更点
}

この変更により、Windows上のディレクトリに対してos.Statを実行すると、返されるos.FileModeには常にModeDir0111の両方が含まれるようになります。これは、Unix系システムにおけるディレクトリの「走査可能」という性質をWindows上でも表現するための重要な対応です。Windowsではディレクトリの実行権限という概念は直接存在しませんが、Goの抽象化レイヤーでこのビットを追加することで、クロスプラットフォームでの一貫した動作を保証します。

また、この変更を検証するために、src/pkg/os/os_test.goに新しいテストケースTestStatDirModeExecが追加されました。このテストは、現在のディレクトリ(.)に対してStatを実行し、そのモードに0111が含まれていることを確認します。これにより、Windows上でのディレクトリモードの修正が正しく機能していることが保証されます。

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

diff --git a/src/pkg/os/os_test.go b/src/pkg/os/os_test.go
index 1940f562de..ecae0f2029 100644
--- a/src/pkg/os/os_test.go
+++ b/src/pkg/os/os_test.go
@@ -1095,3 +1095,15 @@ func TestLargeWriteToConsole(t *testing.T) {
 		t.Errorf("Write to os.Stderr should return %d; got %d", len(b), n)
 	}
 }
+
+func TestStatDirModeExec(t *testing.T) {
+	const mode = 0111
+	const path = "."
+	dir, err := Stat(path)
+	if err != nil {
+		t.Fatalf("Stat %q (looking for mode %#o): %s", path, mode, err)
+	}
+	if dir.Mode()&mode != mode {
+		t.Errorf("Stat %q: mode %#o want %#o", path, dir.Mode(), mode)
+	}
+}
diff --git a/src/pkg/os/stat_windows.go b/src/pkg/os/stat_windows.go
index 4fc6f457e3..c0441a42ae 100644
--- a/src/pkg/os/stat_windows.go
+++ b/src/pkg/os/stat_windows.go
@@ -190,7 +190,7 @@ func mkModTime(mtime syscall.Filetime) time.Time {
 
 func mkMode(fa uint32) (m FileMode) {
 	if fa&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
-		m |= ModeDir
+		m |= ModeDir | 0111
 	}
 	if fa&syscall.FILE_ATTRIBUTE_READONLY != 0 {
 		m |= 0444

コアとなるコードの解説

src/pkg/os/os_test.goの変更

  • TestStatDirModeExecという新しいテスト関数が追加されました。
  • このテストは、現在のディレクトリ(.)のos.FileModeを取得し、そのモードに0111(実行権限ビット)が設定されていることを検証します。
  • dir.Mode()&mode != modeという条件で、取得したモードと期待するモード(0111)のビットAND演算を行い、期待するビットが全てセットされているかを確認しています。これにより、Windows上でのディレクトリの実行権限の報告が正しく行われることを保証します。

src/pkg/os/stat_windows.goの変更

  • mkMode関数内の、Windowsのファイル属性からos.FileModeを生成する部分が修正されました。
  • 以前は、ファイル属性がディレクトリ(syscall.FILE_ATTRIBUTE_DIRECTORY)である場合、m |= ModeDirとしてModeDirビットのみを設定していました。
  • 変更後は、m |= ModeDir | 0111とすることで、ModeDirビットに加えて、八進数0111(所有者、グループ、その他の実行権限)も強制的に設定されるようになりました。
  • この修正により、Windows上でディレクトリのos.FileModeを取得した際に、Unix系システムとの互換性を持たせるために、ディレクトリが「実行可能」(走査可能)であるという情報が正しく含まれるようになります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(osパッケージ、os.FileModeなど)
  • Unix系ファイルパーミッションに関する一般的な情報
  • Windowsファイル属性に関する一般的な情報
  • Go言語のIssueトラッカー(Issue #4444に関する詳細情報があれば、より深く掘り下げることができた可能性がありますが、今回の検索では直接的な情報は見つかりませんでした。)
  • Go言語のGerritコードレビューシステム(Change-IDからアクセス可能)