[インデックス 13067] ファイルの概要
このコミットは、Go言語の標準ライブラリにおけるos
パッケージとsyscall
パッケージ内のNetBSD固有の実装に対して、型とフィールド名の修正を行うものです。具体的には、stat
構造体のタイムスタンプフィールド名、およびTimespec
、Timeval
、Kevent_t
構造体に関連する型定義の誤りを修正し、Goの自動生成されるシステムコールファイル(syscall/z*
)との互換性を確保することを目的としています。
コミット
commit b554fb912ffc25f88f19bc7985c83496faa98cc8
Author: Joel Sing <jsing@google.com>
Date: Tue May 15 12:00:13 2012 +1000
os/syscall: correct netbsd types/names
Fix types/names so that these functions work with the generated
syscall/z* files.
R=golang-dev, m4dh4tt3r
CC=golang-dev
https://golang.org/cl/6201078
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b554fb912ffc25f88f19bc7985c83496faa98cc8
元コミット内容
os/syscall: correct netbsd types/names
「NetBSDの型と名前を修正する」
Fix types/names so that these functions work with the generated syscall/z* files.
「これらの関数が生成されたsyscall/z*
ファイルと連携できるように、型と名前を修正する。」
変更の背景
Go言語は、異なるオペレーティングシステム(OS)やアーキテクチャに対応するために、OS固有のシステムコールを抽象化するsyscall
パッケージを提供しています。このパッケージは、多くの場合、C言語のシステムヘッダファイルからGoの構造体や関数定義を自動生成するツール(mkall.sh
など)によって作成されるsyscall/z*
ファイル群に依存しています。
このコミットが行われた2012年当時、GoのNetBSDサポートはまだ成熟段階にあり、OSのAPIや構造体定義との厳密な整合性を取る必要がありました。特に、stat
構造体のようなOSの低レベル情報を提供する構造体や、kevent
のようなイベント通知メカニズムに関連する構造体は、OSのバージョンやアーキテクチャによってフィールド名や型が微妙に異なることがあります。
このコミットの背景には、Goのos
およびsyscall
パッケージがNetBSDの実際のシステムコールインターフェースと完全に一致していなかったという問題がありました。具体的には、以下の問題が考えられます。
- フィールド名の不一致:
syscall.Stat_t
構造体内のタイムスタンプ関連フィールド(最終更新時刻Mtim
、最終アクセス時刻Atim
)が、NetBSDの標準的なC言語のstat
構造体や、Goの自動生成ツールが期待する命名規則(例:Mtimespec
,Atimespec
)と異なっていた可能性があります。これにより、正しいフィールドにアクセスできず、誤ったタイムスタンプが取得されるか、コンパイルエラーが発生する可能性がありました。 - 型の不一致:
Timespec
やTimeval
構造体の秒数フィールド(Sec
)がint32
として定義されていたが、実際にはint64
が必要な場合がありました。これは、非常に大きな時間値(ナノ秒単位で表現される場合)を扱う際にint32
ではオーバーフローが発生し、不正な値になるリスクがありました。また、Kevent_t
構造体のFilter
やFlags
フィールドの型が、NetBSDのカーネルが期待する型(uint32
)と異なっていたため、システムコール呼び出し時に不正な引数が渡される可能性がありました。
これらの不一致は、GoプログラムがNetBSD上でファイルシステム操作やイベント処理を正確に行う上で重大なバグを引き起こす可能性があったため、修正が必要とされました。
前提知識の解説
このコミットを理解するためには、以下の概念が前提となります。
-
Go言語の
os
パッケージ:- Goの標準ライブラリの一部で、OSに依存する機能(ファイルシステム操作、プロセス管理など)を提供します。
os.FileInfo
インターフェースは、ファイルに関する抽象的な情報(名前、サイズ、更新時刻、パーミッションなど)を提供します。os.FileInfo
の実装の一つとして、fileStat
構造体があり、これはsyscall.Stat_t
構造体を内部に保持しています。
-
Go言語の
syscall
パッケージ:- Goの標準ライブラリの一部で、低レベルのOSプリミティブ(システムコール)へのインターフェースを提供します。
- OSごとに異なる実装を持ち、
syscall_netbsd_386.go
やsyscall_netbsd_amd64.go
のようにアーキテクチャ固有のファイルに分割されています。 syscall.Stat_t
は、Unix系OSにおけるstat
システムコールが返すファイルの状態情報を格納する構造体です。これには、ファイルサイズ、パーミッション、タイムスタンプなどが含まれます。syscall.Timespec
は、秒とナノ秒で時間を表現する構造体です。Unix系OSの多くのシステムコールでタイムスタンプの表現に用いられます。syscall.Timeval
は、秒とマイクロ秒で時間を表現する構造体です。一部のシステムコールでタイムスタンプの表現に用いられます。syscall.Kevent_t
は、NetBSDを含むBSD系OSで利用されるkqueue
システムコールにおけるイベント情報を表現する構造体です。イベントの識別子、フィルター、フラグなどが含まれます。
-
stat
システムコール:- Unix系OSでファイルやディレクトリのメタデータ(サイズ、パーミッション、所有者、タイムスタンプなど)を取得するためのシステムコールです。
- 返される情報は
struct stat
(C言語)またはsyscall.Stat_t
(Go言語)に格納されます。
-
kqueue
システムコール:- NetBSDを含むBSD系OSで利用される、効率的なI/Oイベント通知メカニズムです。ファイルディスクリプタのI/O準備完了、プロセスの状態変化、タイマーイベントなど、様々な種類のイベントを監視できます。
- イベントは
kevent
構造体(C言語)またはsyscall.Kevent_t
(Go言語)で表現されます。
-
GoのクロスコンパイルとOS/アーキテクチャ固有のコード:
- Goは、
GOOS
とGOARCH
環境変数を使って異なるOSやアーキテクチャ向けにコンパイルできます。 - ファイル名に
_os.go
や_os_arch.go
(例:_netbsd.go
,_netbsd_386.go
,_netbsd_amd64.go
)というサフィックスを付けることで、特定のOSやアーキテクチャでのみコンパイルされるコードを記述できます。これにより、OSやアーキテクチャ固有のシステムコールや構造体定義を扱うことが可能になります。
- Goは、
-
syscall/z*
ファイル:- Goの
syscall
パッケージには、OSのCヘッダファイルから自動生成されたGoの定数、構造体、関数プロトタイプなどが含まれるファイル群があります。これらは通常zsyscall_os_arch.go
のような命名規則を持ち、mkall.sh
スクリプトなどによって生成されます。これらのファイルは、GoのコードがOSのネイティブAPIと正確に連携するための「接着剤」の役割を果たします。
- Goの
技術的詳細
このコミットは、NetBSDにおけるシステムコール関連の型定義とフィールド名の厳密な整合性を確保するために、以下の具体的な変更を行っています。
-
src/pkg/os/stat_netbsd.go
におけるStat_t
フィールド名の修正:fileInfoFromStat
関数内で、st.Mtim
をst.Mtimespec
に、atime
関数内でst.Atim
をst.Atimespec
にそれぞれ変更しています。- これは、NetBSDの
stat
構造体(syscall.Stat_t
にマッピングされる)において、最終更新時刻(modification time)と最終アクセス時刻(access time)を表すフィールドの実際の名前がMtimespec
とAtimespec
であることを反映しています。Goのsyscall
パッケージがCの構造体をGoの構造体にマッピングする際に、フィールド名が正確に一致している必要があります。この修正により、os.FileInfo
が返すタイムスタンプが、NetBSDシステムから取得された正しい値となることが保証されます。
-
src/pkg/syscall/syscall_netbsd_386.go
およびsrc/pkg/syscall/syscall_netbsd_amd64.go
における型修正:-
NsecToTimespec
関数:ts.Sec
の型をint32
からint64
に変更しています。nsec
(ナノ秒)はint64
型であり、nsec / 1e9
(秒)の計算結果がint32
の範囲を超える可能性があるため、int64
にすることでオーバーフローを防ぎ、より大きな時間値を正確に表現できるようにします。これは、Unixエポックからの経過秒数がint32
の最大値(約68年)を超える可能性を考慮したものです。ts.Nsec
はナノ秒の端数(0から999,999,999)であり、int32
の範囲に収まるため変更はありません。
-
NsecToTimeval
関数:tv.Sec
の型をint32
からint64
に変更しています。Timespec
の場合と同様に、秒数フィールドのオーバーフローを防ぐためです。tv.Usec
の型をint64
からint32
に明示的にキャストしています(amd64
版)。nsec % 1e9 / 1e3
(マイクロ秒の端数)はint32
の範囲に収まるため、明示的なキャストにより型の一貫性を保ちます。386
版では元々int32
へのキャストが行われていました。
-
SetKevent
関数:k.Filter
の型をint16
からuint32
に変更しています。k.Flags
の型をuint16
からuint32
に変更しています。- これは、NetBSDの
kevent
システムコールが期待するKevent_t
構造体のfilter
およびflags
フィールドの実際の型がuint32
であることを反映しています。Goのsyscall
パッケージがこれらのフィールドに値を設定する際に、正しい型を使用することで、カーネルへのシステムコール呼び出しが正しく行われることを保証します。型が一致しない場合、値が切り捨てられたり、メモリレイアウトがずれたりして、予期せぬ動作やセキュリティ上の問題を引き起こす可能性があります。
-
これらの変更は、Goのsyscall
パッケージがNetBSDのネイティブAPIと正確に連携するための基盤を強化し、GoプログラムがNetBSD上で安定して動作することを保証します。特に、自動生成されるsyscall/z*
ファイル群との整合性は、Goのクロスプラットフォーム対応において非常に重要です。
コアとなるコードの変更箇所
src/pkg/os/stat_netbsd.go
--- a/src/pkg/os/stat_netbsd.go
+++ b/src/pkg/os/stat_netbsd.go
@@ -19,7 +19,7 @@ func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
fs := &fileStat{
name: basename(name),
size: int64(st.Size),
- modTime: timespecToTime(st.Mtim),
+ modTime: timespecToTime(st.Mtimespec),
sys: st,
}
fs.mode = FileMode(st.Mode & 0777)
@@ -57,5 +57,5 @@ func timespecToTime(ts syscall.Timespec) time.Time {
// For testing.
func atime(fi FileInfo) time.Time {
- return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
+ return timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec)
}
src/pkg/syscall/syscall_netbsd_386.go
--- a/src/pkg/syscall/syscall_netbsd_386.go
+++ b/src/pkg/syscall/syscall_netbsd_386.go
@@ -9,7 +9,7 @@ func Getpagesize() int { return 4096 }\n func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }\n \n func NsecToTimespec(nsec int64) (ts Timespec) {\n-\tts.Sec = int32(nsec / 1e9)\n+\tts.Sec = int64(nsec / 1e9)\n \tts.Nsec = int32(nsec % 1e9)\n \treturn\n }\n@@ -19,14 +19,14 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)\n func NsecToTimeval(nsec int64) (tv Timeval) {\n \tnsec += 999 // round up to microsecond\n \ttv.Usec = int32(nsec % 1e9 / 1e3)\n-\ttv.Sec = int32(nsec / 1e9)\n+\ttv.Sec = int64(nsec / 1e9)\n \treturn\n }\n \n func SetKevent(k *Kevent_t, fd, mode, flags int) {\n \tk.Ident = uint32(fd)\n-\tk.Filter = int16(mode)\n-\tk.Flags = uint16(flags)\n+\tk.Filter = uint32(mode)\n+\tk.Flags = uint32(flags)\n }\n \n func (iov *Iovec) SetLen(length int) {\
src/pkg/syscall/syscall_netbsd_amd64.go
--- a/src/pkg/syscall/syscall_netbsd_amd64.go
+++ b/src/pkg/syscall/syscall_netbsd_amd64.go
@@ -9,8 +9,8 @@ func Getpagesize() int { return 4096 }\n func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }\n \n func NsecToTimespec(nsec int64) (ts Timespec) {\n-\tts.Sec = int32(nsec / 1e9)\n-\tts.Nsec = nsec % 1e9\n+\tts.Sec = int64(nsec / 1e9)\n+\tts.Nsec = int64(nsec % 1e9)\n \treturn\n }\n \n@@ -18,15 +18,15 @@ func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)\n \n func NsecToTimeval(nsec int64) (tv Timeval) {\n \tnsec += 999 // round up to microsecond\n-\ttv.Usec = nsec % 1e9 / 1e3\n+\ttv.Usec = int32(nsec % 1e9 / 1e3)\n \ttv.Sec = int64(nsec / 1e9)\n \treturn\n }\n \n func SetKevent(k *Kevent_t, fd, mode, flags int) {\n \tk.Ident = uint64(fd)\n-\tk.Filter = int16(mode)\n-\tk.Flags = uint16(flags)\n+\tk.Filter = uint32(mode)\n+\tk.Flags = uint32(flags)\n }\n \n func (iov *Iovec) SetLen(length int) {\
コアとなるコードの解説
src/pkg/os/stat_netbsd.go
fileInfoFromStat
関数は、syscall.Stat_t
構造体からos.FileInfo
インターフェースを実装するfileStat
構造体を生成します。modTime: timespecToTime(st.Mtim)
がmodTime: timespecToTime(st.Mtimespec)
に変更されました。これは、syscall.Stat_t
構造体内の最終更新時刻を表すフィールド名が、Mtim
ではなくMtimespec
であることを修正しています。
atime
関数は、os.FileInfo
から最終アクセス時刻を取得します。return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
がreturn timespecToTime(fi.Sys().(*syscall.Stat_t).Atimespec)
に変更されました。同様に、最終アクセス時刻を表すフィールド名がAtim
ではなくAtimespec
であることを修正しています。
これらの変更により、NetBSDシステムコールから取得したstat
情報が、Goのos
パッケージで正しく解釈され、正確なファイルタイムスタンプがユーザーに提供されるようになります。
src/pkg/syscall/syscall_netbsd_386.go
および src/pkg/syscall/syscall_netbsd_amd64.go
これらのファイルは、それぞれ32ビット(i386)および64ビット(amd64)アーキテクチャにおけるNetBSD固有のシステムコール関連のヘルパー関数や型定義を含んでいます。
-
NsecToTimespec
関数:- ナノ秒(
nsec
)をsyscall.Timespec
構造体に変換する関数です。 ts.Sec = int32(nsec / 1e9)
がts.Sec = int64(nsec / 1e9)
に変更されました。これは、秒数を格納するTimespec.Sec
フィールドの型が、int32
ではオーバーフローする可能性があるため、int64
に拡張されたことを示します。これにより、より長い期間の時間を正確に表現できるようになります。
- ナノ秒(
-
NsecToTimeval
関数:- ナノ秒(
nsec
)をsyscall.Timeval
構造体に変換する関数です。 tv.Sec = int32(nsec / 1e9)
がtv.Sec = int64(nsec / 1e9)
に変更されました。Timespec
と同様に、Timeval.Sec
フィールドもint64
に拡張されました。amd64
版ではtv.Usec = nsec % 1e9 / 1e3
がtv.Usec = int32(nsec % 1e9 / 1e3)
に変更されました。これは、マイクロ秒を格納するTimeval.Usec
フィールドがint32
型であることを明示的に示すためのキャストです。これにより、型の一貫性が保たれます。
- ナノ秒(
-
SetKevent
関数:kqueue
システムコールで使用されるsyscall.Kevent_t
構造体のフィールドを設定するヘルパー関数です。k.Filter = int16(mode)
がk.Filter = uint32(mode)
に変更されました。k.Flags = uint16(flags)
がk.Flags = uint32(flags)
に変更されました。- これらの変更は、NetBSDの
kqueue
システムコールが期待するKevent_t
構造体のFilter
およびFlags
フィールドの実際の型がuint32
であることを反映しています。Goのコードがこれらのフィールドに値を設定する際に、正しい型を使用することで、システムコールが正しく動作し、イベント処理が正確に行われることが保証されます。
これらの型修正は、Goの低レベルなシステムコールインターフェースがNetBSDのカーネルAPIと完全に一致するようにするための重要な調整であり、Goプログラムの安定性と正確性を向上させます。
関連リンク
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - NetBSD
stat
マニュアルページ (例): https://man.netbsd.org/stat.2 - NetBSD
kqueue
マニュアルページ (例): https://man.netbsd.org/kqueue.2
参考にした情報源リンク
- Go言語の公式リポジトリ (GitHub): https://github.com/golang/go
- Gerrit Code Review (Goプロジェクト): https://go.googlesource.com/go/+/refs/heads/master/CONTRIBUTING.md (GerritのCLリンクは直接参照できないため、一般的な情報源として)
- Unix系OSのシステムプログラミングに関する一般的な知識
- Go言語のクロスプラットフォーム開発に関する一般的な知識