[インデックス 15135] ファイルの概要
このコミットは、Go言語のsyscallパッケージにおけるPlan 9オペレーティングシステム向けのstatメッセージのマーシャリング(直列化)およびアンマーシャリング(非直列化)に関するバグ修正を扱っています。具体的には、Qid構造体のフィールドの順序が誤っていたために発生していた問題を解決します。
コミット
commit c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
Author: Anthony Martin <ality@pbrane.org>
Date: Mon Feb 4 19:47:23 2013 -0800
syscall: fix marshaling of stat messages on Plan 9
The order of the Qid fields was reversed. Mea culpa.
R=seed
CC=golang-dev
https://golang.org/cl/7231062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
元コミット内容
syscall: Plan 9におけるstatメッセージのマーシャリングを修正。
Qidフィールドの順序が逆になっていた。私の過失である。
変更の背景
このコミットは、Go言語のsyscallパッケージがPlan 9オペレーティングシステムと連携する際に発生していた重要なバグを修正するために行われました。statメッセージは、ファイルやディレクトリのメタデータ(サイズ、パーミッション、タイムスタンプなど)を表現するために使用されるデータ構造です。Plan 9のファイルシステムプロトコルでは、これらのメッセージが特定のバイト順序でネットワーク経由で送受信されます。
問題は、statメッセージの一部であるQid構造体のフィールドが、Goのsyscallパッケージ内でバイト列に変換(マーシャリング)される際、およびバイト列からGoの構造体に戻される(アンマーシャリング)際に、そのフィールドの順序がPlan 9の期待する順序と逆になっていたことにありました。この誤った順序は、statメッセージのデータが正しく解釈されない原因となり、結果としてファイル操作やシステムコールが期待通りに機能しないという深刻な問題を引き起こしていました。コミットメッセージにある「Mea culpa.」は、この誤りが開発者自身のミスであることを認める表現です。
前提知識の解説
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムプロトコル(9P)を通じてアクセスするという特徴を持っています。Go言語は、その設計思想や開発者の背景(Rob Pike、Ken Thompsonなど、Plan 9の開発者と共通する)から、Plan 9との親和性が高いことで知られています。
Go言語の syscall パッケージ
Go言語の標準ライブラリであるsyscallパッケージは、オペレーティングシステムが提供する低レベルのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムはファイルシステム操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する基本的なサービスを直接利用できます。OSごとにシステムコールのインターフェースやデータ構造が異なるため、syscallパッケージ内には各OSに特化した実装(例: syscall/dir_plan9.go)が含まれています。
stat メッセージと Qid 構造体
Plan 9のファイルシステムプロトコル(9P)において、statメッセージはファイルやディレクトリの属性(メタデータ)を記述するために使用されます。このメッセージには、ファイルの種類、パーミッション、サイズ、タイムスタンプなどの情報が含まれます。
Qid("quad ID"の略)は、Plan 9においてファイルシステム内のオブジェクトを一意に識別するための重要な構造体です。これは、ファイルやディレクトリのパス、バージョン、タイプなどの情報を含みます。Qidは通常、以下の3つのフィールドで構成されます。
Type: ファイルの種類(ディレクトリ、通常ファイルなど)を示す1バイトのフィールド。Vers: ファイルのバージョンを示す4バイト(uint32)のフィールド。ファイルが変更されるたびにインクリメントされます。Path: ファイルシステム内のオブジェクトを一意に識別する8バイト(uint64)のフィールド。
これらのフィールドは、ネットワーク上で送受信される際に特定のバイト順序で直列化(マーシャリング)され、受信側で逆のプロセス(アンマーシャリング)によって元の構造体に復元されます。この順序が誤っていると、データが破損したり、誤ったファイルが参照されたりする原因となります。
マーシャリングとアンマーシャリング
- マーシャリング (Marshaling): プログラム内のデータ構造(例: Goの
struct)を、ネットワーク経由で送信したり、ファイルに保存したりするために、バイト列や特定のデータ形式(JSON, XMLなど)に変換するプロセスです。この際、フィールドの順序、バイトオーダー(エンディアン)、パディングなどが厳密に定義されている必要があります。 - アンマーシャリング (Unmarshaling): マーシャリングされたバイト列やデータ形式を、プログラム内のデータ構造に復元するプロセスです。マーシャリングとアンマーシャリングは、データの整合性を保つために、同じ規則に従って行われる必要があります。
このコミットでは、syscall/dir_plan9.goファイル内のDir構造体のMarshalメソッドとUnmarshalDir関数において、QidのPathとVersフィールドのマーシャリング/アンマーシャリング順序がPlan 9のプロトコル仕様と異なっていたことが問題でした。
技術的詳細
このバグは、src/pkg/syscall/dir_plan9.goファイル内のDir構造体のMarshalメソッドとUnmarshalDir関数に存在していました。これらの関数は、Plan 9のstatメッセージをGoのDir構造体との間で変換する役割を担っています。
具体的には、Qid構造体のフィールドであるPath(uint64)とVers(uint32)のマーシャリングおよびアンマーシャリングの順序が、Plan 9のプロトコル仕様と逆になっていました。
元のコードでは、Qid.PathがQid.Versの前にマーシャリング/アンマーシャリングされていました。
// 元のMarshal (抜粋)
b = pbit64(b, d.Qid.Path) // Path (uint64)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit8(b, d.Qid.Type) // Type (uint8)
// 元のUnmarshalDir (抜粋)
d.Qid.Path, b = gbit64(b) // Path (uint64)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Type, b = gbit8(b) // Type (uint8)
しかし、Plan 9のプロトコルでは、QidのフィールドはType、Vers、Pathの順でバイト列に配置されるべきでした。
このコミットでは、Qid.VersとQid.Pathの順序を入れ替えることで、この問題を修正しています。Qid.Typeは元々正しい位置にありました。
修正後の順序は以下のようになります。
// 修正後のMarshal (抜粋)
b = pbit8(b, d.Qid.Type) // Type (uint8)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit64(b, d.Qid.Path) // Path (uint64)
// 修正後のUnmarshalDir (抜粋)
d.Qid.Type, b = gbit8(b) // Type (uint8)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Path, b = gbit64(b) // Path (uint64)
pbitX関数(例: pbit16, pbit32, pbit64, pbit8)は、指定されたバイト数(16ビット、32ビット、64ビット、8ビット)の値をバイトスライスbに書き込み、更新されたバイトスライスを返します。同様に、gbitX関数はバイトスライスbから指定されたバイト数の値を読み取り、読み取った値と残りのバイトスライスを返します。これらの関数は、Goのsyscallパッケージ内で低レベルのバイト操作を行うためのユーティリティ関数です。
この修正により、GoプログラムがPlan 9システムとstatメッセージを交換する際に、Qid情報が正しく解釈されるようになり、ファイルシステム操作の信頼性が向上しました。
コアとなるコードの変更箇所
diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go
index eee8be44a3..b7ab4cd108 100644
--- a/src/pkg/syscall/dir_plan9.go
+++ b/src/pkg/syscall/dir_plan9.go
@@ -68,9 +68,9 @@ func (d *Dir) Marshal(b []byte) (n int, err error) {
b = pbit16(b, uint16(n)-2)
b = pbit16(b, d.Type)
b = pbit32(b, d.Dev)
- b = pbit64(b, d.Qid.Path)
- b = pbit32(b, d.Qid.Vers)
b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
b = pbit32(b, d.Mode)
b = pbit32(b, d.Atime)
b = pbit32(b, d.Mtime)
@@ -101,9 +101,9 @@ func UnmarshalDir(b []byte) (*Dir, error) {
var d Dir
d.Type, b = gbit16(b)
d.Dev, b = gbit32(b)
- d.Qid.Path, b = gbit64(b)
- d.Qid.Vers, b = gbit32(b)
d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
d.Mode, b = gbit32(b)
d.Atime, b = gbit32(b)
d.Mtime, b = gbit32(b)
コアとなるコードの解説
上記の差分は、src/pkg/syscall/dir_plan9.goファイル内の2つの主要な関数、(*Dir) MarshalとUnmarshalDirにおける変更を示しています。
func (d *Dir) Marshal(b []byte) (n int, err error)
このメソッドは、Dir構造体の内容をバイトスライスbにマーシャリング(直列化)します。変更前は、d.Qid.Pathがd.Qid.Versの前にバイトスライスに書き込まれていました。
- b = pbit64(b, d.Qid.Path): 変更前、Qid.Path(64ビット)が最初に書き込まれていました。- b = pbit32(b, d.Qid.Vers): 変更前、次にQid.Vers(32ビット)が書き込まれていました。b = pbit8(b, d.Qid.Type):Qid.Type(8ビット)は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ b = pbit32(b, d.Qid.Vers): 変更後、Qid.Typeの直後にQid.Versが書き込まれるようになりました。+ b = pbit64(b, d.Qid.Path): 変更後、その次にQid.Pathが書き込まれるようになりました。
この変更により、QidのフィールドがType、Vers、Pathの正しい順序でバイト列に直列化されるようになりました。
func UnmarshalDir(b []byte) (*Dir, error)
この関数は、バイトスライスbからDir構造体の内容をアンマーシャリング(非直列化)します。マーシャリングの逆の操作であり、同様にQid.PathとQid.Versの読み取り順序が修正されています。
d.Type, b = gbit16(b):DirのTypeフィールド(16ビット)の読み取り。これはQid.Typeとは別のフィールドです。d.Dev, b = gbit32(b):DirのDevフィールド(32ビット)の読み取り。- d.Qid.Path, b = gbit64(b): 変更前、Qid.Pathが最初に読み取られていました。- d.Qid.Vers, b = gbit32(b): 変更前、次にQid.Versが読み取られていました。d.Qid.Type, b = gbit8(b):Qid.Typeは変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ d.Qid.Vers, b = gbit32(b): 変更後、Qid.Typeの直後にQid.Versが読み取られるようになりました。+ d.Qid.Path, b = gbit64(b): 変更後、その次にQid.Pathが読み取られるようになりました。
これらの変更により、Plan 9のstatメッセージからQid情報が正しく抽出され、GoのDir構造体にマッピングされることが保証されます。この修正は、GoとPlan 9間の相互運用性において、ファイルシステム操作の正確性を確保するために不可欠でした。
関連リンク
- Go CL 7231062: https://golang.org/cl/7231062
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語
syscallパッケージのドキュメント: https://pkg.go.dev/syscall - Plan 9 File Protocol (9P): https://9p.io/magic/man2html/5/9p
- Qid structure in Plan 9: https://9p.io/magic/man2html/5/stat (stat(5) man page)
- Go言語の
pbitおよびgbit関数の一般的な概念(具体的な実装はGoのソースコードを参照)# [インデックス 15135] ファイルの概要
このコミットは、Go言語のsyscallパッケージにおけるPlan 9オペレーティングシステム向けのstatメッセージのマーシャリング(直列化)およびアンマーシャリング(非直列化)に関するバグ修正を扱っています。具体的には、Qid構造体のフィールドの順序が誤っていたために発生していた問題を解決します。
コミット
commit c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
Author: Anthony Martin <ality@pbrane.org>
Date: Mon Feb 4 19:47:23 2013 -0800
syscall: fix marshaling of stat messages on Plan 9
The order of the Qid fields was reversed. Mea culpa.
R=seed
CC=golang-dev
https://golang.org/cl/7231062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c56bb1d238672f658c4f5f5e1efc0fa88c3b3101
元コミット内容
syscall: Plan 9におけるstatメッセージのマーシャリングを修正。
Qidフィールドの順序が逆になっていた。私の過失である。
変更の背景
このコミットは、Go言語のsyscallパッケージがPlan 9オペレーティングシステムと連携する際に発生していた重要なバグを修正するために行われました。statメッセージは、ファイルやディレクトリのメタデータ(サイズ、パーミッション、タイムスタンプなど)を表現するために使用されるデータ構造です。Plan 9のファイルシステムプロトコルでは、これらのメッセージが特定のバイト順序でネットワーク経由で送受信されます。
問題は、statメッセージの一部であるQid構造体のフィールドが、Goのsyscallパッケージ内でバイト列に変換(マーシャリング)される際、およびバイト列からGoの構造体に戻される(アンマーシャリング)際に、そのフィールドの順序がPlan 9の期待する順序と逆になっていたことにありました。この誤った順序は、statメッセージのデータが正しく解釈されない原因となり、結果としてファイル操作やシステムコールが期待通りに機能しないという深刻な問題を引き起こしていました。コミットメッセージにある「Mea culpa.」は、この誤りが開発者自身のミスであることを認める表現です。
前提知識の解説
Plan 9 from Bell Labs
Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現し、ファイルシステムプロトコル(9P)を通じてアクセスするという特徴を持っています。Go言語は、その設計思想や開発者の背景(Rob Pike、Ken Thompsonなど、Plan 9の開発者と共通する)から、Plan 9との親和性が高いことで知られています。
Go言語の syscall パッケージ
Go言語の標準ライブラリであるsyscallパッケージは、オペレーティングシステムが提供する低レベルのシステムコールにアクセスするための機能を提供します。これにより、Goプログラムはファイルシステム操作、プロセス管理、ネットワーク通信など、OSカーネルが提供する基本的なサービスを直接利用できます。OSごとにシステムコールのインターフェースやデータ構造が異なるため、syscallパッケージ内には各OSに特化した実装(例: syscall/dir_plan9.go)が含まれています。
stat メッセージと Qid 構造体
Plan 9のファイルシステムプロトコル(9P)において、statメッセージはファイルやディレクトリの属性(メタデータ)を記述するために使用されます。このメッセージには、ファイルの種類、パーミッション、サイズ、タイムスタンプなどの情報が含まれます。
Qid("quad ID"の略)は、Plan 9においてファイルシステム内のオブジェクトを一意に識別するための重要な構造体です。これは、ファイルやディレクトリのパス、バージョン、タイプなどの情報を含みます。Qidは通常、以下の3つのフィールドで構成されます。
Type: ファイルの種類(ディレクトリ、通常ファイルなど)を示す1バイトのフィールド。- **``Vers
**: ファイルのバージョンを示す4バイト(uint32`)のフィールド。ファイルが変更されるたびにインクリメントされます。 Path: ファイルシステム内のオブジェクトを一意に識別する8バイト(uint64)のフィールド。
これらのフィールドは、ネットワーク上で送受信される際に特定のバイト順序で直列化(マーシャリング)され、受信側で逆のプロセス(アンマーシャリング)によって元の構造体に復元されます。この順序が誤っていると、データが破損したり、誤ったファイルが参照されたりする原因となります。
マーシャリングとアンマーシャリング
- マーシャリング (Marshaling): プログラム内のデータ構造(例: Goの
struct)を、ネットワーク経由で送信したり、ファイルに保存したりするために、バイト列や特定のデータ形式(JSON, XMLなど)に変換するプロセスです。この際、フィールドの順序、バイトオーダー(エンディアン)、パディングなどが厳密に定義されている必要があります。 - アンマーシャリング (Unmarshaling): マーシャリングされたバイト列やデータ形式を、プログラム内のデータ構造に復元するプロセスです。マーシャリングとアンマーシャリングは、データの整合性を保つために、同じ規則に従って行われる必要があります。
このコミットでは、syscall/dir_plan9.goファイル内のDir構造体のMarshalメソッドとUnmarshalDir関数において、QidのPathとVersフィールドのマーシャリング/アンマーシャリング順序がPlan 9のプロトコル仕様と異なっていたことが問題でした。
技術的詳細
このバグは、src/pkg/syscall/dir_plan9.goファイル内のDir構造体のMarshalメソッドとUnmarshalDir関数に存在していました。これらの関数は、Plan 9のstatメッセージをGoのDir構造体との間で変換する役割を担っています。
具体的には、Qid構造体のフィールドであるPath(uint64)とVers(uint32)のマーシャリングおよびアンマーシャリングの順序が、Plan 9のプロトコル仕様と逆になっていました。
元のコードでは、Qid.PathがQid.Versの前にマーシャリング/アンマーシャリングされていました。
// 元のMarshal (抜粋)
b = pbit64(b, d.Qid.Path) // Path (uint64)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit8(b, d.Qid.Type) // Type (uint8)
// 元のUnmarshalDir (抜粋)
d.Qid.Path, b = gbit64(b) // Path (uint64)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Type, b = gbit8(b) // Type (uint8)
しかし、Plan 9のプロトコルでは、QidのフィールドはType、Vers、Pathの順でバイト列に配置されるべきでした。
このコミットでは、Qid.VersとQid.Pathの順序を入れ替えることで、この問題を修正しています。Qid.Typeは元々正しい位置にありました。
修正後の順序は以下のようになります。
// 修正後のMarshal (抜粋)
b = pbit8(b, d.Qid.Type) // Type (uint8)
b = pbit32(b, d.Qid.Vers) // Vers (uint32)
b = pbit64(b, d.Qid.Path) // Path (uint64)
// 修正後のUnmarshalDir (抜粋)
d.Qid.Type, b = gbit8(b) // Type (uint8)
d.Qid.Vers, b = gbit32(b) // Vers (uint32)
d.Qid.Path, b = gbit64(b) // Path (uint64)
pbitX関数(例: pbit16, pbit32, pbit64, pbit8)は、指定されたバイト数(16ビット、32ビット、64ビット、8ビット)の値をバイトスライスbに書き込み、更新されたバイトスライスを返します。同様に、gbitX関数はバイトスライスbから指定されたバイト数の値を読み取り、読み取った値と残りのバイトスライスを返します。これらの関数は、Goのsyscallパッケージ内で低レベルのバイト操作を行うためのユーティリティ関数です。
この修正により、GoプログラムがPlan 9システムとstatメッセージを交換する際に、Qid情報が正しく解釈されるようになり、ファイルシステム操作の信頼性が向上しました。
コアとなるコードの変更箇所
diff --git a/src/pkg/syscall/dir_plan9.go b/src/pkg/syscall/dir_plan9.go
index eee8be44a3..b7ab4cd108 100644
--- a/src/pkg/syscall/dir_plan9.go
+++ b/src/pkg/syscall/dir_plan9.go
@@ -68,9 +68,9 @@ func (d *Dir) Marshal(b []byte) (n int, err error) {
b = pbit16(b, uint16(n)-2)
b = pbit16(b, d.Type)
b = pbit32(b, d.Dev)
- b = pbit64(b, d.Qid.Path)
- b = pbit32(b, d.Qid.Vers)
b = pbit8(b, d.Qid.Type)
+ b = pbit32(b, d.Qid.Vers)
+ b = pbit64(b, d.Qid.Path)
b = pbit32(b, d.Mode)
b = pbit32(b, d.Atime)
b = pbit32(b, d.Mtime)
@@ -101,9 +101,9 @@ func UnmarshalDir(b []byte) (*Dir, error) {
var d Dir
d.Type, b = gbit16(b)
d.Dev, b = gbit32(b)
- d.Qid.Path, b = gbit64(b)
- d.Qid.Vers, b = gbit32(b)
d.Qid.Type, b = gbit8(b)
+ d.Qid.Vers, b = gbit32(b)
+ d.Qid.Path, b = gbit64(b)
d.Mode, b = gbit32(b)
d.Atime, b = gbit32(b)
d.Mtime, b = gbit32(b)
コアとなるコードの解説
上記の差分は、src/pkg/syscall/dir_plan9.goファイル内の2つの主要な関数、(*Dir) MarshalとUnmarshalDirにおける変更を示しています。
func (d *Dir) Marshal(b []byte) (n int, err error)
このメソッドは、Dir構造体の内容をバイトスライスbにマーシャリング(直列化)します。変更前は、d.Qid.Pathがd.Qid.Versの前にバイトスライスに書き込まれていました。
- b = pbit64(b, d.Qid.Path): 変更前、Qid.Path(64ビット)が最初に書き込まれていました。- b = pbit32(b, d.Qid.Vers): 変更前、次にQid.Vers(32ビット)が書き込まれていました。b = pbit8(b, d.Qid.Type):Qid.Type(8ビット)は変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ b = pbit32(b, d.Qid.Vers): 変更後、Qid.Typeの直後にQid.Versが書き込まれるようになりました。+ b = pbit64(b, d.Qid.Path): 変更後、その次にQid.Pathが書き込まれるようになりました。
この変更により、QidのフィールドがType、Vers、Pathの正しい順序でバイト列に直列化されるようになりました。
func UnmarshalDir(b [][]byte) (*Dir, error)
この関数は、バイトスライスbからDir構造体の内容をアンマーシャリング(非直列化)します。マーシャリングの逆の操作であり、同様にQid.PathとQid.Versの読み取り順序が修正されています。
d.Type, b = gbit16(b):DirのTypeフィールド(16ビット)の読み取り。これはQid.Typeとは別のフィールドです。d.Dev, b = gbit32(b):DirのDevフィールド(32ビット)の読み取り。- d.Qid.Path, b = gbit64(b): 変更前、Qid.Pathが最初に読み取られていました。- d.Qid.Vers, b = gbit32(b): 変更前、次にQid.Versが読み取られていました。d.Qid.Type, b = gbit8(b):Qid.Typeは変更前も後も同じ位置にあり、これはPlan 9のプロトコル仕様と一致しています。+ d.Qid.Vers, b = gbit32(b): 変更後、Qid.Typeの直後にQid.Versが読み取られるようになりました。+ d.Qid.Path, b = gbit64(b): 変更後、その次にQid.Pathが読み取られるようになりました。
これらの変更により、Plan 9のstatメッセージからQid情報が正しく抽出され、GoのDir構造体にマッピングされることが保証されます。この修正は、GoとPlan 9間の相互運用性において、ファイルシステム操作の正確性を確保するために不可欠でした。
関連リンク
- Go CL 7231062: https://golang.org/cl/7231062
参考にした情報源リンク
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Go言語
syscallパッケージのドキュメント: https://pkg.go.dev/syscall - Plan 9 File Protocol (9P): https://9p.io/magic/man2html/5/9p
- Qid structure in Plan 9: https://9p.io/magic/man2html/5/stat (stat(5) man page)
- Go言語の
pbitおよびgbit関数の一般的な概念(具体的な実装はGoのソースコードを参照)