[インデックス 11282] ファイルの概要
このコミットは、Go言語のos
パッケージおよび関連するユーティリティにおいて、ファイルパーミッションを表す型をuint32
からos.FileMode
へ変更するものです。これにより、ファイルモードの表現がより型安全になり、可読性と移植性が向上します。
コミット
commit 6454a3eb150218e13e71cecd48638e673dc6c304
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Jan 19 15:45:18 2012 -0800
os: use FileMode instead of uint32 in various functions
Fixes #2733
R=chickencha, ality, rsc
CC=golang-dev
https://golang.org/cl/5553064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6454a3eb150218e13e71cecd48638e673dc6c304
元コミット内容
os: use FileMode instead of uint32 in various functions
Fixes #2733
R=chickencha, ality, rsc
CC=golang-dev
https://golang.org/cl/5553064
変更の背景
この変更の背景には、Go言語のファイル操作における型安全性の向上と、より表現豊かなAPIの提供という目的があります。従来のuint32
型は、ファイルパーミッションだけでなく、様々なビットフラグを表現するために汎用的に使用される整数型です。しかし、ファイルパーミッションのような特定の意味を持つ値に対してuint32
を使用すると、以下のような問題が生じる可能性があります。
- 型安全性の欠如:
uint32
は単なる数値であるため、誤った値(例えば、ファイルパーミッションとして意味をなさない値)が渡されてもコンパイル時に検出できません。os.FileMode
のような専用の型を導入することで、Goの型システムがこれらのエラーを早期に捕捉できるようになります。 - 可読性の低下: コードを読む際に、
uint32
がファイルパーミッションを表しているのか、それとも別の目的の数値なのかを判断するために、追加のコンテキストが必要になります。os.FileMode
を使用することで、その変数がファイルパーミッションに関連するものであることが一目で明確になります。 - 移植性の問題: ファイルパーミッションは、Unix系システムとWindows系システムでその表現方法や意味合いが異なる場合があります。
os.FileMode
は、Go言語が提供する抽象化レイヤーであり、異なるOS間でのファイルパーミッションの取り扱いを統一し、開発者がプラットフォームの違いを意識することなくコードを書けるようにします。uint32
を直接使用すると、OS固有のパーミッションビットを直接操作することになり、移植性の問題を引き起こす可能性があります。 - 拡張性:
os.FileMode
は単なる数値ではなく、メソッドを持つ型として定義されています。これにより、将来的にファイルモードに関連する追加の機能や操作を、この型に直接関連付けることが容易になります。例えば、Perm()
メソッドのように、パーミッションビットのみを抽出する機能などが提供されます。
これらの理由から、ファイルパーミッションを扱うGoの標準ライブラリ関数において、より適切で堅牢なos.FileMode
型への移行が進められました。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
1. ファイルパーミッション (File Permissions)
Unix系OSにおいて、ファイルやディレクトリにはアクセス権限が設定されています。これは、誰がそのファイルに対して読み取り(read)、書き込み(write)、実行(execute)の操作を行えるかを定義します。パーミッションは通常、3桁または4桁の8進数で表現されます。
- 所有者 (Owner): ファイルを作成したユーザーの権限。
- グループ (Group): ファイルの所有グループに属するユーザーの権限。
- その他 (Others): 上記以外のすべてのユーザーの権限。
各権限は以下の数値に対応します。
- 読み取り (r): 4
- 書き込み (w): 2
- 実行 (x): 1
例えば、0644
というパーミッションは以下を意味します。
- 所有者:
6
(4+2 = 読み取り+書き込み) - グループ:
4
(読み取りのみ) - その他:
4
(読み取りのみ)
先頭の0
は8進数であることを示します。4桁の場合、先頭の桁は特殊なパーミッション(SetUID, SetGID, Sticky Bit)を表します。
2. uint32
型
uint32
はGo言語の組み込み型で、32ビットの符号なし整数を表します。0から4,294,967,295までの値を格納できます。汎用的な数値計算やビットフラグの操作によく使用されます。
3. os.FileMode
型
Go言語のos
パッケージで定義されている型で、ファイルやディレクトリのモードビット(パーミッションや種類)を表します。これはuint32
のエイリアスとして定義されていますが、Goの型システムによってuint32
とは異なる型として扱われます。これにより、ファイルモードとして意味のある値のみが渡されることを期待できるようになります。
os.FileMode
は、ファイルパーミッション(rwx
ビット)だけでなく、ファイルの種類(ディレクトリ、シンボリックリンクなど)や特殊なパーミッション(SetUID, SetGID, Sticky Bit)も表現できます。
os.ModeDir
: ディレクトリos.ModeAppend
: 追記専用ファイルos.ModeExclusive
: 排他ロックファイルos.ModeTemporary
: 一時ファイルos.ModeSymlink
: シンボリックリンクos.ModeDevice
: デバイスファイルos.ModeNamedPipe
: 名前付きパイプ (FIFO)os.ModeSocket
: Unixドメインソケットos.ModeSetuid
: SetUIDビットos.ModeSetgid
: SetGIDビットos.ModeSticky
: スティッキービット
これらのモードはビットマスクとして定義されており、os.FileMode
の値はこれらのビットの組み合わせで構成されます。
4. システムコール (System Calls)
システムコールは、オペレーティングシステムが提供するサービスをプログラムが利用するためのインターフェースです。ファイルを作成したり、パーミッションを変更したりする操作は、直接ハードウェアを操作するのではなく、OSのカーネルが提供するシステムコールを介して行われます。
例えば、Unix系OSでは、ファイルのパーミッションを変更するためにchmod
システムコールが使用されます。このシステムコールは、パーミッションを数値(通常はuint32
のような整数型)で受け取ります。Go言語のsyscall
パッケージは、これらの低レベルなシステムコールをGoプログラムから呼び出すための機能を提供します。
技術的詳細
このコミットの核心は、Go言語のos
パッケージが提供するファイルモードの抽象化と、基盤となるシステムコールとの間の変換メカニズムにあります。
os.FileMode
の役割
os.FileMode
は、Go言語がファイルパーミッションやファイルの種類を表現するために導入した型です。これは単なるuint32
のエイリアスですが、Goの型システムはこれを独立した型として扱います。これにより、開発者はファイルモードを扱う際に、より意味のある型を使用できるようになります。
os.FileMode
は、ファイルパーミッション(rwx
ビット)だけでなく、os.ModeDir
(ディレクトリ)、os.ModeSymlink
(シンボリックリンク)、os.ModeSetuid
(SetUIDビット)などの特殊なモードビットも保持できます。これらのモードビットは、os.FileMode
型の値に対してビット演算を行うことで、設定したり、確認したりすることができます。
例えば、os.FileMode(0644)
は、所有者に読み書き、グループとその他に読み取り権限を与えるファイルモードを表します。
syscallMode
関数の導入
Goのos
パッケージは、クロスプラットフォームなファイル操作APIを提供しますが、その内部では各OS固有のシステムコールを呼び出しています。Unix系OSのchmod
やmkdir
などのシステムコールは、パーミッションをuint32
のような整数型で受け取ります。
このコミットで導入されたsyscallMode
関数(src/pkg/os/file_posix.go
に追加)は、os.FileMode
型の値を、基盤となるシステムコールが期待するuint32
型のパーミッションビットに変換する役割を担っています。
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm()) // Goのパーミッションビットをuint32に変換
if i&ModeSetuid != 0 {
o |= syscall.S_ISUID // SetUIDビットをシステムコール用のフラグに変換
}
if i&ModeSetgid != 0 {
o |= syscall.S_ISGID // SetGIDビットをシステムコール用のフラグに変換
}
if i&ModeSticky != 0 {
o |= syscall.S_ISVTX // スティッキービットをシステムコール用のフラグに変換
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}
この関数は、以下の変換を行います。
i.Perm()
:os.FileMode
のPerm()
メソッドは、ファイルパーミッションのrwx
ビットのみを抽出して返します。これをuint32
にキャストします。ModeSetuid
,ModeSetgid
,ModeSticky
:os.FileMode
がこれらの特殊なモードビットを含んでいる場合、それぞれ対応するsyscall
パッケージの定数(syscall.S_ISUID
,syscall.S_ISGID
,syscall.S_ISVTX
)を結果のuint32
にOR演算で追加します。これらの定数は、Unix系システムコールが特殊なパーミッションを表現するために使用するビットフラグです。
このsyscallMode
関数を介することで、Goのコードはos.FileMode
という高レベルな抽象化された型を使用しつつ、内部的にはOS固有のシステムコールと正しく連携できるようになります。これにより、Goのファイル操作APIの移植性と堅牢性が向上します。
影響範囲
この変更は、os
パッケージだけでなく、os
パッケージのファイルモード関連の関数を呼び出している他の標準ライブラリパッケージ(io/ioutil
、cmd/go
、cmd/hgpatch
など)にも波及しています。これらのパッケージの関数シグネチャも、uint32
からos.FileMode
に変更されています。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルでuint32
からos.FileMode
への型変更が行われています。
src/cmd/go/build.go
:builder.copyFile
関数とbuilder.install
関数で、ファイルパーミッションの型がuint32
からos.FileMode
に変更されました。src/cmd/hgpatch/main.go
:os.Chmod
の呼び出しとmkdirAll
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。src/pkg/io/ioutil/ioutil.go
:WriteFile
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。src/pkg/os/file.go
:Mkdir
関数とFile.Chmod
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。また、Mkdir
関数内でsyscallMode
が使用されています。src/pkg/os/file_posix.go
:syscallMode
関数が新しく追加されました。Chmod
関数とFile.Chmod
関数で、パーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。
src/pkg/os/file_unix.go
:OpenFile
関数のパーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。src/pkg/os/file_windows.go
:openFile
関数とOpenFile
関数のパーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。src/pkg/os/path.go
:MkdirAll
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。
例: src/pkg/os/file_posix.go
の変更
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -81,18 +81,34 @@ func Rename(oldname, newname string) error {
return nil
}
+// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
+func syscallMode(i FileMode) (o uint32) {
+ o |= uint32(i.Perm())
+ if i&ModeSetuid != 0 {
+ o |= syscall.S_ISUID
+ }
+ if i&ModeSetgid != 0 {
+ o |= syscall.S_ISGID
+ }
+ if i&ModeSticky != 0 {
+ o |= syscall.S_ISVTX
+ }
+ // No mapping for Go's ModeTemporary (plan9 only).
+ return
+}
+
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
-func Chmod(name string, mode uint32) error {
- if e := syscall.Chmod(name, mode); e != nil {
+func Chmod(name string, mode FileMode) error {
+ if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
// Chmod changes the mode of the file to mode.
-func (f *File) Chmod(mode uint32) error {
- if e := syscall.Fchmod(f.fd, mode); e != nil {
+func (f *File) Chmod(mode FileMode) error {
+ if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
return &PathError{"chmod", f.name, e}
}
return nil
コアとなるコードの解説
このコミットの最も重要な変更は、src/pkg/os/file_posix.go
にsyscallMode
関数が追加され、os
パッケージ内のファイル操作関数がこの新しいヘルパー関数を使用してos.FileMode
をシステムコールが期待するuint32
に変換するようになった点です。
以前は、os.Mkdir
やos.Chmod
のような関数は、直接uint32
型のパーミッション引数を受け取り、それをsyscall
パッケージの対応する関数に渡していました。このアプローチは、Goの型システムがファイルパーミッションのセマンティクスを強制しないため、誤った値が渡されるリスクがありました。
新しいアプローチでは、これらの関数はos.FileMode
型の引数を受け取るようになります。そして、内部でsyscallMode
関数を呼び出して、os.FileMode
の値をuint32
に変換してからsyscall
関数に渡します。
例えば、os.Chmod
関数の変更を見てみましょう。
変更前:
func Chmod(name string, mode uint32) error {
if e := syscall.Chmod(name, mode); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
変更後:
func Chmod(name string, mode FileMode) error {
if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
この変更により、os.Chmod
を呼び出す側はos.FileMode
型の値を渡すことが期待されるため、コードの意図がより明確になります。また、os.FileMode
が持つPerm()
メソッドや、ModeSetuid
などのビットフラグを直接利用できるため、より表現力豊かなコードを書くことができます。
syscallMode
関数は、Goのポータブルなファイルモード表現(os.FileMode
)と、Unix系OSのシステムコールが要求する低レベルなパーミッションビット(uint32
)との間の橋渡しをします。これにより、Goのos
パッケージは、異なるOS環境下でも一貫したファイル操作APIを提供しつつ、内部的には各OSのネイティブなシステムコールを効率的に利用できるようになります。これは、Go言語のクロスプラットフォーム設計における重要な側面の一つです。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/6454a3eb150218e13e71cecd48638e673dc6c304
- Go Issue #2733: https://golang.org/issue/2733
- Go CL 5553064: https://golang.org/cl/5553064
参考にした情報源リンク
- Go言語
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Unixファイルパーミッション (Wikipedia): https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%91%E3%83%BC%E3%83%9F%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3
- Go言語におけるファイルモードの扱いに関する議論 (Stack Overflowなど、一般的なGoのファイル操作に関する情報)```markdown
[インデックス 11282] ファイルの概要
このコミットは、Go言語のos
パッケージおよび関連するユーティリティにおいて、ファイルパーミッションを表す型をuint32
からos.FileMode
へ変更するものです。これにより、ファイルモードの表現がより型安全になり、可読性と移植性が向上します。
コミット
commit 6454a3eb150218e13e71cecd48638e673dc6c304
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Jan 19 15:45:18 2012 -0800
os: use FileMode instead of uint32 in various functions
Fixes #2733
R=chickencha, ality, rsc
CC=golang-dev
https://golang.org/cl/5553064
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6454a3eb150218e13e71cecd48638e673dc6c304
元コミット内容
os: use FileMode instead of uint32 in various functions
Fixes #2733
R=chickencha, ality, rsc
CC=golang-dev
https://golang.org/cl/5553064
変更の背景
この変更の背景には、Go言語のファイル操作における型安全性の向上と、より表現豊かなAPIの提供という目的があります。従来のuint32
型は、ファイルパーミッションだけでなく、様々なビットフラグを表現するために汎用的に使用される整数型です。しかし、ファイルパーミッションのような特定の意味を持つ値に対してuint32
を使用すると、以下のような問題が生じる可能性があります。
- 型安全性の欠如:
uint32
は単なる数値であるため、誤った値(例えば、ファイルパーミッションとして意味をなさない値)が渡されてもコンパイル時に検出できません。os.FileMode
のような専用の型を導入することで、Goの型システムがこれらのエラーを早期に捕捉できるようになります。 - 可読性の低下: コードを読む際に、
uint32
がファイルパーミッションを表しているのか、それとも別の目的の数値なのかを判断するために、追加のコンテキストが必要になります。os.FileMode
を使用することで、その変数がファイルパーミッションに関連するものであることが一目で明確になります。 - 移植性の問題: ファイルパーミッションは、Unix系システムとWindows系システムでその表現方法や意味合いが異なる場合があります。
os.FileMode
は、Go言語が提供する抽象化レイヤーであり、異なるOS間でのファイルパーミッションの取り扱いを統一し、開発者がプラットフォームの違いを意識することなくコードを書けるようにします。uint32
を直接使用すると、OS固有のパーミッションビットを直接操作することになり、移植性の問題を引き起こす可能性があります。 - 拡張性:
os.FileMode
は単なる数値ではなく、メソッドを持つ型として定義されています。これにより、将来的にファイルモードに関連する追加の機能や操作を、この型に直接関連付けることが容易になります。例えば、Perm()
メソッドのように、パーミッションビットのみを抽出する機能などが提供されます。
これらの理由から、ファイルパーミッションを扱うGoの標準ライブラリ関数において、より適切で堅牢なos.FileMode
型への移行が進められました。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
1. ファイルパーミッション (File Permissions)
Unix系OSにおいて、ファイルやディレクトリにはアクセス権限が設定されています。これは、誰がそのファイルに対して読み取り(read)、書き込み(write)、実行(execute)の操作を行えるかを定義します。パーミッションは通常、3桁または4桁の8進数で表現されます。
- 所有者 (Owner): ファイルを作成したユーザーの権限。
- グループ (Group): ファイルの所有グループに属するユーザーの権限。
- その他 (Others): 上記以外のすべてのユーザーの権限。
各権限は以下の数値に対応します。
- 読み取り (r): 4
- 書き込み (w): 2
- 実行 (x): 1
例えば、0644
というパーミッションは以下を意味します。
- 所有者:
6
(4+2 = 読み取り+書き込み) - グループ:
4
(読み取りのみ) - その他:
4
(読み取りのみ)
先頭の0
は8進数であることを示します。4桁の場合、先頭の桁は特殊なパーミッション(SetUID, SetGID, Sticky Bit)を表します。
2. uint32
型
uint32
はGo言語の組み込み型で、32ビットの符号なし整数を表します。0から4,294,967,295までの値を格納できます。汎用的な数値計算やビットフラグの操作によく使用されます。
3. os.FileMode
型
Go言語のos
パッケージで定義されている型で、ファイルやディレクトリのモードビット(パーミッションや種類)を表します。これはuint32
のエイリアスとして定義されていますが、Goの型システムによってuint32
とは異なる型として扱われます。これにより、ファイルモードとして意味のある値のみが渡されることを期待できるようになります。
os.FileMode
は、ファイルパーミッション(rwx
ビット)だけでなく、ファイルの種類(ディレクトリ、シンボリックリンクなど)や特殊なパーミッション(SetUID, SetGID, Sticky Bit)も表現できます。
os.ModeDir
: ディレクトリos.ModeAppend
: 追記専用ファイルos.ModeExclusive
: 排他ロックファイルos.ModeTemporary
: 一時ファイルos.ModeSymlink
: シンボリックリンクos.ModeDevice
: デバイスファイルos.ModeNamedPipe
: 名前付きパイプ (FIFO)os.ModeSocket
: Unixドメインソケットos.ModeSetuid
: SetUIDビットos.ModeSetgid
: SetGIDビットos.ModeSticky
: スティッキービット
これらのモードはビットマスクとして定義されており、os.FileMode
の値はこれらのビットの組み合わせで構成されます。
4. システムコール (System Calls)
システムコールは、オペレーティングシステムが提供するサービスをプログラムが利用するためのインターフェースです。ファイルを作成したり、パーミッションを変更したりする操作は、直接ハードウェアを操作するのではなく、OSのカーネルが提供するシステムコールを介して行われます。
例えば、Unix系OSでは、ファイルのパーミッションを変更するためにchmod
システムコールが使用されます。このシステムコールは、パーミッションを数値(通常はuint32
のような整数型)で受け取ります。Go言語のsyscall
パッケージは、これらの低レベルなシステムコールをGoプログラムから呼び出すための機能を提供します。
技術的詳細
このコミットの核心は、Go言語のos
パッケージが提供するファイルモードの抽象化と、基盤となるシステムコールとの間の変換メカニズムにあります。
os.FileMode
の役割
os.FileMode
は、Go言語がファイルパーミッションやファイルの種類を表現するために導入した型です。これは単なるuint32
のエイリアスですが、Goの型システムはこれを独立した型として扱います。これにより、開発者はファイルモードを扱う際に、より意味のある型を使用できるようになります。
os.FileMode
は、ファイルパーミッション(rwx
ビット)だけでなく、os.ModeDir
(ディレクトリ)、os.ModeSymlink
(シンボリックリンク)、os.ModeSetuid
(SetUIDビット)などの特殊なモードビットも保持できます。これらのモードビットは、os.FileMode
型の値に対してビット演算を行うことで、設定したり、確認したりすることができます。
例えば、os.FileMode(0644)
は、所有者に読み書き、グループとその他に読み取り権限を与えるファイルモードを表します。
syscallMode
関数の導入
Goのos
パッケージは、クロスプラットフォームなファイル操作APIを提供しますが、その内部では各OS固有のシステムコールを呼び出しています。Unix系OSのchmod
やmkdir
などのシステムコールは、パーミッションをuint32
のような整数型で受け取ります。
このコミットで導入されたsyscallMode
関数(src/pkg/os/file_posix.go
に追加)は、os.FileMode
型の値を、基盤となるシステムコールが期待するuint32
型のパーミッションビットに変換する役割を担っています。
// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
func syscallMode(i FileMode) (o uint32) {
o |= uint32(i.Perm()) // Goのパーミッションビットをuint32に変換
if i&ModeSetuid != 0 {
o |= syscall.S_ISUID // SetUIDビットをシステムコール用のフラグに変換
}
if i&ModeSetgid != 0 {
o |= syscall.S_ISGID // SetGIDビットをシステムコール用のフラグに変換
}
if i&ModeSticky != 0 {
o |= syscall.S_ISVTX // スティッキービットをシステムコール用のフラグに変換
}
// No mapping for Go's ModeTemporary (plan9 only).
return
}
この関数は、以下の変換を行います。
i.Perm()
:os.FileMode
のPerm()
メソッドは、ファイルパーミッションのrwx
ビットのみを抽出して返します。これをuint32
にキャストします。ModeSetuid
,ModeSetgid
,ModeSticky
:os.FileMode
がこれらの特殊なモードビットを含んでいる場合、それぞれ対応するsyscall
パッケージの定数(syscall.S_ISUID
,syscall.S_ISGID
,syscall.S_ISVTX
)を結果のuint32
にOR演算で追加します。これらの定数は、Unix系システムコールが特殊なパーミッションを表現するために使用するビットフラグです。
このsyscallMode
関数を介することで、Goのコードはos.FileMode
という高レベルな抽象化された型を使用しつつ、内部的にはOS固有のシステムコールと正しく連携できるようになります。これにより、Goのファイル操作APIの移植性と堅牢性が向上します。
影響範囲
この変更は、os
パッケージだけでなく、os
パッケージのファイルモード関連の関数を呼び出している他の標準ライブラリパッケージ(io/ioutil
、cmd/go
、cmd/hgpatch
など)にも波及しています。これらのパッケージの関数シグネチャも、uint32
からos.FileMode
に変更されています。
コアとなるコードの変更箇所
このコミットでは、主に以下のファイルでuint32
からos.FileMode
への型変更が行われています。
src/cmd/go/build.go
:builder.copyFile
関数とbuilder.install
関数で、ファイルパーミッションの型がuint32
からos.FileMode
に変更されました。src/cmd/hgpatch/main.go
:os.Chmod
の呼び出しとmkdirAll
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。src/pkg/io/ioutil/ioutil.go
:WriteFile
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。src/pkg/os/file.go
:Mkdir
関数とFile.Chmod
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。また、Mkdir
関数内でsyscallMode
が使用されています。src/pkg/os/file_posix.go
:syscallMode
関数が新しく追加されました。Chmod
関数とFile.Chmod
関数で、パーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。
src/pkg/os/file_unix.go
:OpenFile
関数のパーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。src/pkg/os/file_windows.go
:openFile
関数とOpenFile
関数のパーミッション引数がuint32
からos.FileMode
に変更され、syscallMode
関数が使用されるようになりました。src/pkg/os/path.go
:MkdirAll
関数のパーミッション引数がuint32
からos.FileMode
に変更されました。
例: src/pkg/os/file_posix.go
の変更
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -81,18 +81,34 @@ func Rename(oldname, newname string) error {
return nil
}
+// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
+func syscallMode(i FileMode) (o uint32) {
+ o |= uint32(i.Perm())
+ if i&ModeSetuid != 0 {
+ o |= syscall.S_ISUID
+ }
+ if i&ModeSetgid != 0 {
+ o |= syscall.S_ISGID
+ }
+ if i&ModeSticky != 0 {
+ o |= syscall.S_ISVTX
+ }
+ // No mapping for Go's ModeTemporary (plan9 only).
+ return
+}
+
// Chmod changes the mode of the named file to mode.
// If the file is a symbolic link, it changes the mode of the link's target.
-func Chmod(name string, mode uint32) error {
- if e := syscall.Chmod(name, mode); e != nil {
+func Chmod(name string, mode FileMode) error {
+ if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
// Chmod changes the mode of the file to mode.
-func (f *File) Chmod(mode uint32) error {
- if e := syscall.Fchmod(f.fd, mode); e != nil {
+func (f *File) Chmod(mode FileMode) error {
+ if e := syscall.Fchmod(f.fd, syscallMode(mode)); e != nil {
return &PathError{"chmod", f.name, e}
}
return nil
コアとなるコードの解説
このコミットの最も重要な変更は、src/pkg/os/file_posix.go
にsyscallMode
関数が追加され、os
パッケージ内のファイル操作関数がこの新しいヘルパー関数を使用してos.FileMode
をシステムコールが期待するuint32
に変換するようになった点です。
以前は、os.Mkdir
やos.Chmod
のような関数は、直接uint32
型のパーミッション引数を受け取り、それをsyscall
パッケージの対応する関数に渡していました。このアプローチは、Goの型システムがファイルパーミッションのセマンティクスを強制しないため、誤った値が渡されるリスクがありました。
新しいアプローチでは、これらの関数はos.FileMode
型の引数を受け取るようになります。そして、内部でsyscallMode
関数を呼び出して、os.FileMode
の値をuint32
に変換してからsyscall
関数に渡します。
例えば、os.Chmod
関数の変更を見てみましょう。
変更前:
func Chmod(name string, mode uint32) error {
if e := syscall.Chmod(name, mode); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
変更後:
func Chmod(name string, mode FileMode) error {
if e := syscall.Chmod(name, syscallMode(mode)); e != nil {
return &PathError{"chmod", name, e}
}
return nil
}
この変更により、os.Chmod
を呼び出す側はos.FileMode
型の値を渡すことが期待されるため、コードの意図がより明確になります。また、os.FileMode
が持つPerm()
メソッドや、ModeSetuid
などのビットフラグを直接利用できるため、より表現力豊かなコードを書くことができます。
syscallMode
関数は、Goのポータブルなファイルモード表現(os.FileMode
)と、Unix系OSのシステムコールが要求する低レベルなパーミッションビット(uint32
)との間の橋渡しをします。これにより、Goのos
パッケージは、異なるOS環境下でも一貫したファイル操作APIを提供しつつ、内部的には各OSのネイティブなシステムコールを効率的に利用できるようになります。これは、Go言語のクロスプラットフォーム設計における重要な側面の一つです。
関連リンク
- GitHubコミット: https://github.com/golang/go/commit/6454a3eb150218e13e71cecd48638e673dc6c304
- Go Issue #2733: https://golang.org/issue/2733
- Go CL 5553064: https://golang.org/cl/5553064
参考にした情報源リンク
- Go言語
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Unixファイルパーミッション (Wikipedia): https://ja.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%91%E3%83%BC%E3%83%9F%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3
- Go言語におけるファイルモードの扱いに関する議論 (Stack Overflowなど、一般的なGoのファイル操作に関する情報)