[インデックス 16271] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるFchflags
関数のプロトタイプ(シグネチャ)の修正に関するものです。特に、Darwin(macOS)およびBSD系のオペレーティングシステム(FreeBSD, NetBSD, OpenBSD)におけるFchflags
関数の第一引数が、誤ってファイルパス(string
)を受け取るように定義されていたのを、正しいファイルディスクリプタ(int
)を受け取るように変更しています。これはAPIの変更を伴いますが、既存のAPIが明らかに誤っていたため、Go 1の互換性ルールに則って許容される修正とされています。
コミット
commit 6de184b385c8a15095cab925f50980e7c62f2ec3
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue May 7 05:20:00 2013 +0800
syscall: fix prototype of Fchflags (API change)
API change, but the old API is obviously wrong.
R=golang-dev, iant, r, rsc
CC=golang-dev
https://golang.org/cl/9157044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6de184b385c8a15095cab925f50980e7c62f2ec3
元コミット内容
syscall: fix prototype of Fchflags (API change)
API change, but the old API is obviously wrong.
(日本語訳)
syscall: Fchflagsのプロトタイプを修正 (API変更)
API変更だが、古いAPIは明らかに間違っていた。
変更の背景
この変更の背景には、Go言語のsyscall
パッケージが提供するFchflags
関数が、基盤となるオペレーティングシステムのシステムコールfchflags(2)
の正しいセマンティクスを反映していなかったという問題があります。
Unix系システムにおけるfchflags(2)
システムコールは、特定のファイルディスクリプタ(開いているファイルやディレクトリを参照する整数値)に関連付けられたファイルフラグを変更するために使用されます。しかし、Goのsyscall
パッケージでは、このFchflags
関数が誤ってファイルパス(string
)を第一引数として受け取るように定義されていました。これは、ファイルパスを受け取るchflags(2)
システムコールと混同されたか、あるいは実装上の誤りであったと考えられます。
Go 1の互換性保証は非常に厳格であり、通常、既存のAPIのシグネチャを変更することは許されません。しかし、このケースでは「古いAPIが明らかに間違っていた」という明確な理由があったため、バグ修正としてAPI変更が特別に許可されました。この修正は、Fchflags
関数がその意図されたシステムコールであるfchflags(2)
と正しく対応するようにするために不可欠でした。
前提知識の解説
Go言語のsyscall
パッケージ
Go言語の標準ライブラリにはsyscall
パッケージが含まれています。このパッケージは、オペレーティングシステムが提供する低レベルのシステムコールへのアクセスを提供します。これにより、Goプログラムはファイル操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する機能と直接対話することができます。syscall
パッケージはOSに依存する部分が多く、各OS(Linux, macOS, Windows, BSDなど)向けに異なる実装が提供されています。
fchflags(2)
とchflags(2)
システムコール
Unix系システム、特にBSD系OS(macOS, FreeBSD, NetBSD, OpenBSDなど)には、ファイルやディレクトリの「フラグ」を操作するためのシステムコールが存在します。これらのフラグは、ファイルの挙動を制御する属性(例: 不変、追加のみ、隠しファイルなど)を定義します。
chflags(2)
: ファイルパス(path
)を指定して、そのファイルのフラグを変更します。fchflags(2)
: 開いているファイルディスクリプタ(fd
)を指定して、そのファイルのフラグを変更します。
このコミットの核心は、GoのFchflags
がfchflags(2)
に対応するはずなのに、誤ってchflags(2)
のようにパスを受け取っていた点にあります。
Go 1 互換性保証
Go言語は、バージョン1.0以降、厳格な互換性保証を維持しています。これは、Go 1で書かれたプログラムは、将来のGoのバージョンでもコンパイルされ、動作し続けることを意味します。この保証は、Goエコシステムの安定性と信頼性の基盤となっています。
しかし、この互換性保証には例外があります。例えば、セキュリティ上の脆弱性の修正や、既存のAPIが「明らかに間違っている」場合のバグ修正など、正当な理由がある場合には、APIの変更が許容されることがあります。今回のFchflags
のケースは、後者の「明らかに間違っている」バグ修正に該当します。
//sys
ディレクティブとzsyscall_*.go
ファイルの生成
Goのsyscall
パッケージでは、システムコールをGoの関数として定義するために、特別なコメントディレクティブ//sys
が使用されます。このディレクティブは、mksyscall.go
などのツールによって解析され、実際のシステムコール呼び出しを行うための低レベルなGoコード(通常はzsyscall_*.go
という命名規則のファイル)が自動生成されます。
例えば、//sys Fchflags(fd int, flags int) (err error)
のような行は、Fchflags
というGo関数が、fd
とflags
という2つの整数引数を取り、エラーを返すシステムコールに対応することをツールに伝えます。
技術的詳細
このコミットの技術的な詳細は、Goのsyscall
パッケージがどのようにOSのシステムコールをラップしているか、そしてそのラッパー関数がどのように生成されるかという点に集約されます。
元の実装では、syscall_darwin.go
などのOS固有のファイルでFchflags
が以下のように定義されていました。
//sys Fchflags(path string, flags int) (err error)
この//sys
ディレクティブは、mksyscall
ツールに対して、Fchflags
関数がpath
という文字列とflags
という整数を受け取るように指示していました。しかし、対応するシステムコールであるfchflags(2)
は、第一引数としてファイルディスクリプタ(整数)を期待します。
この不一致のため、mksyscall
によって生成されるzsyscall_*.go
ファイルでは、文字列のpath
をシステムコールに渡すために、BytePtrFromString
関数とunsafe.Pointer
が使用されていました。
func Fchflags(path string, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path) // 文字列をバイトポインタに変換
if err != nil {
return
}
_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0) // unsafe.Pointerでシステムコールに渡す
if e1 != 0 {
err = e1
}
return
}
これは、fchflags(2)
がファイルディスクリプタを期待しているにもかかわらず、Goのコードがファイルパスのバイトポインタを渡そうとしていたことを意味します。結果として、この関数は意図した通りに動作せず、誤った引数でシステムコールを呼び出すことになり、バグの原因となっていました。
今回の修正では、//sys
ディレクティブの定義を以下のように変更しました。
//sys Fchflags(fd int, flags int) (err error)
これにより、mksyscall
ツールはFchflags
関数がfd
という整数とflags
という整数を受け取るようにコードを生成するようになります。生成されるzsyscall_*.go
ファイルも、BytePtrFromString
やunsafe.Pointer
を使用せず、直接fd
をシステムコールに渡すように変更されました。
func Fchflags(fd int, flags int) (err error) {
_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(fd), uintptr(flags), 0) // fdを直接システムコールに渡す
if e1 != 0 {
err = e1
}
return
}
この変更により、GoのFchflags
関数は、基盤となるfchflags(2)
システムコールの正しいセマンティクスと一致するようになり、機能的なバグが修正されました。api/except.txt
への追加は、このAPI変更がGo 1の互換性保証の例外として扱われることを明示しています。また、doc/go1.1.html
の更新は、この変更がGo 1.1リリースノートに記載され、ユーザーに周知されることを示しています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のファイル群にわたります。
-
api/except.txt
:pkg syscall (darwin-386), func Fchflags(string, int) error
pkg syscall (darwin-386-cgo), func Fchflags(string, int) error
pkg syscall (darwin-amd64), func Fchflags(string, int) error
pkg syscall (darwin-amd64-cgo), func Fchflags(string, int) error
pkg syscall (freebsd-386), func Fchflags(string, int) error
pkg syscall (freebsd-amd64), func Fchflags(string, int) error
これらの行が追加され、Fchflags
関数の古いシグネチャがAPI互換性チェックの例外として登録されました。
-
doc/go1.1.html
:- Go 1.1のリリースノートに、
syscall
パッケージのFchflags
関数のシグネチャ変更が記載されました。- 変更前:
The <code>syscall</code> package has received many updates to make it more inclusive of constants and system calls for each supported operating system.
- 変更後:
The <code>syscall</code> package's <code>Fchflags</code> function on various BSDs (including Darwin) has changed signature. It now takes an int as the first parameter instead of a string. Since this API change fixes a bug, it is permitted by the Go 1 compatibility rules.
- 変更前:
- Go 1.1のリリースノートに、
-
src/pkg/syscall/syscall_darwin.go
,src/pkg/syscall/syscall_freebsd.go
,src/pkg/syscall/syscall_netbsd.go
,src/pkg/syscall/syscall_openbsd.go
:- これらのファイル内で、
Fchflags
関数の//sys
ディレクティブの定義が変更されました。- 変更前:
//sys Fchflags(path string, flags int) (err error)
- 変更後:
//sys Fchflags(fd int, flags int) (err error)
- 変更前:
- これらのファイル内で、
-
src/pkg/syscall/zsyscall_darwin_386.go
,src/pkg/syscall/zsyscall_darwin_amd64.go
,src/pkg/syscall/zsyscall_freebsd_386.go
,src/pkg/syscall/zsyscall_freebsd_amd64.go
,src/pkg/syscall/zsyscall_netbsd_386.go
,src/pkg/syscall/zsyscall_netbsd_amd64.go
,src/pkg/syscall/zsyscall_openbsd_386.go
,src/pkg/syscall/zsyscall_openbsd_amd64.go
:- これらの自動生成されたファイル内で、
Fchflags
関数の実装が変更されました。- 変更前は、
path string
を受け取り、BytePtrFromString
とunsafe.Pointer
を使用してシステムコールに渡していました。 - 変更後は、
fd int
を受け取り、fd
を直接システムコールに渡すようになりました。これにより、BytePtrFromString
とunsafe.Pointer
に関するコードが削除されました。
- 変更前は、
- これらの自動生成されたファイル内で、
コアとなるコードの解説
このコミットの核心は、Goのsyscall
パッケージがOSのシステムコールをどのように抽象化し、そしてその抽象化がどのように修正されたかという点にあります。
Goのsyscall
パッケージでは、各OSのシステムコールに対応するGo関数を定義するために、//sys
という特別なコメントディレクティブを使用します。例えば、src/pkg/syscall/syscall_darwin.go
には、Darwin(macOS)向けのシステムコール定義が含まれています。
変更前:
//sys Fchflags(path string, flags int) (err error)
この行は、Fchflags
というGo関数が、第一引数にstring
型のpath
、第二引数にint
型のflags
を取り、error
を返すことを示しています。この//sys
ディレクティブは、mksyscall
というツールによって解析され、実際のシステムコールを呼び出すための低レベルなGoコードが自動生成されます。
自動生成されるファイル(例: src/pkg/syscall/zsyscall_darwin_386.go
)では、この定義に基づいて以下のようなコードが生成されていました。
func Fchflags(path string, flags int) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path) // 文字列をバイトポインタに変換
if err != nil {
return
}
// SYS_FCHFLAGS は fchflags(2) システムコールに対応する定数
_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(unsafe.Pointer(_p0)), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
return
}
ここで問題だったのは、fchflags(2)
システムコールが期待するのはファイルディスクリプタ(整数)であるにもかかわらず、Goのコードがファイルパスのバイトポインタを渡そうとしていた点です。BytePtrFromString
はGoの文字列をCスタイルのヌル終端バイト配列へのポインタに変換し、unsafe.Pointer
はそのポインタをuintptr
に変換してシステムコールに渡していました。これは、fchflags(2)
の引数型と一致しないため、誤った動作を引き起こしていました。
変更後:
//sys Fchflags(fd int, flags int) (err error)
この修正により、//sys
ディレクティブは、Fchflags
関数が第一引数にint
型のfd
(ファイルディスクリプタ)を受け取るように変更されました。
この変更後、mksyscall
によって自動生成されるコードは以下のようになります。
func Fchflags(fd int, flags int) (err error) {
// SYS_FCHFLAGS は fchflags(2) システムコールに対応する定数
_, _, e1 := Syscall(SYS_FCHFLAGS, uintptr(fd), uintptr(flags), 0)
if e1 != 0 {
err = e1
}
return
}
この新しいコードでは、fd
(ファイルディスクリプタ)が直接uintptr
に変換され、SYS_FCHFLAGS
システムコールに渡されます。これにより、GoのFchflags
関数は、基盤となるfchflags(2)
システムコールの正しいセマンティクスと完全に一致するようになり、バグが修正されました。
この修正は、Goのsyscall
パッケージがOSの低レベルなインターフェースを正確に反映することの重要性を示しています。また、Go 1の互換性保証が厳格である一方で、明らかなバグ修正のためにはAPI変更も許容されるという柔軟性も示しています。
関連リンク
- Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Go 1.1 リリースノート (変更が記載されている可能性のあるページ): https://go.dev/doc/go1.1
- GoのIssue/Change List (CL) 9157044: https://golang.org/cl/9157044
参考にした情報源リンク
fchflags(2)
manページ (例: macOS): https://man.freebsd.org/cgi/man.cgi?query=fchflags&sektion=2 (FreeBSDのmanページですが、概念は共通です)chflags(2)
manページ (例: macOS): https://man.freebsd.org/cgi/man.cgi?query=chflags&sektion=2- Go 1 Compatibility Guarantee: https://go.dev/doc/go1compat
- Goの
mksyscall
ツールに関する情報 (Goのソースコードリポジトリ内): https://github.com/golang/go/tree/master/src/syscall (特にmksyscall.go
やmkerrors.go
などを参照) - Goの
unsafe
パッケージドキュメント: https://pkg.go.dev/unsafe - Goの
BytePtrFromString
関数に関する情報 (Goのソースコードリポジトリ内): https://github.com/golang/go/blob/master/src/syscall/syscall_unix.go (または関連するOS固有のファイル) - Goのシステムコール呼び出しに関する一般的な情報: https://go.dev/src/syscall/syscall_unix.go (Unix系OSの一般的なシステムコール呼び出しのパターン)
- Goの
uintptr
型に関する情報: https://go.dev/ref/spec#Numeric_types - Goの
Syscall
関数に関する情報: https://pkg.go.dev/syscall#Syscall - Goの
api/except.txt
の役割に関する情報: https://go.dev/doc/go1compat#exceptions (Go 1互換性保証の例外に関するセクション) - Goのリリースプロセスとドキュメント更新に関する情報: https://go.dev/doc/contribute#release-process