[インデックス 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言語のクロスプラットフォーム開発に関する一般的な知識