Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 10963] ファイルの概要

このコミットは、Go言語のsyscallパッケージにおいて、NetBSDオペレーティングシステム上でのディレクトリエントリー読み取り機能(getdirentries)の動作を修正するものです。具体的には、NetBSDのシステムコールであるgetdentsの呼び出し方法と、その戻り値であるdirent構造体の定義を、NetBSDの実際の仕様に合わせて調整しています。これにより、GoプログラムがNetBSD上で正しくディレクトリの内容を列挙できるようになります。

コミット

コミットハッシュ: 43bc8a9b53e212af4d24f9a4960e5c452555efb6 作者: Joel Sing jsing@google.com 日付: 2011年12月22日 木曜日 23:42:43 +1100

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/43bc8a9b53e212af4d24f9b4960e5c452555efb6

元コミット内容

syscall: make getdirentries work on netbsd

R=golang-dev, mikioh.mikioh
CC=golang-dev
https://golang.org/cl/5504068

変更の背景

Go言語のsyscallパッケージは、オペレーティングシステムの低レベルな機能(システムコール)にアクセスするためのインターフェースを提供します。getdirentries関数は、指定されたファイルディスクリプタからディレクトリのエントリーを読み取るための汎用的な関数であり、内部的には各OS固有のシステムコールを呼び出します。

NetBSD環境において、syscallパッケージのgetdirentriesの実装が、実際のNetBSDのgetdentsシステムコールの挙動や、そのシステムコールが返すdirent構造体の定義と一致していませんでした。これにより、GoプログラムがNetBSD上でディレクトリを正しく読み取ることができないという問題が発生していました。

具体的には、以下の点が問題となっていました。

  1. getdirentries関数が、NetBSDのgetdentsシステムコールを正しく呼び出していなかった。
  2. dirent構造体のフィールド(特にFilenoNamlenの型、およびName配列のサイズ)が、NetBSDの定義と異なっていた。

このコミットは、これらの不一致を解消し、NetBSD上でのディレクトリ操作の信頼性を向上させることを目的としています。

前提知識の解説

1. システムコール (System Call)

システムコールは、オペレーティングシステム(OS)のカーネルが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、メモリ管理、プロセス制御など、OSの基本的な機能はシステムコールを通じて提供されます。Go言語のsyscallパッケージは、これらのシステムコールをGoプログラムから直接呼び出すための機能を提供します。

2. getdents システムコール

getdents(get directory entries)は、Unix系OSでディレクトリの内容を読み取るために使用されるシステムコールの一つです。このシステムコールは、指定されたファイルディスクリプタ(ディレクトリを表す)から、ディレクトリ内のエントリー(ファイルやサブディレクトリ)の情報をバッファに書き込みます。各エントリーは通常、direntという構造体で表現されます。

3. dirent 構造体

dirent構造体は、getdentsシステムコールによって返されるディレクトリエントリーの情報を格納するためのデータ構造です。OSによってその定義は多少異なりますが、一般的には以下のフィールドを含みます。

  • d_fileno (または Fileno): ファイルのinode番号。ファイルシステム内で一意な識別子です。
  • d_reclen (または Reclen): このディレクトリエントリーレコード全体の長さ。
  • d_namlen (または Namlen): ファイル名の長さ。
  • d_type (または Type): ファイルの種類(例: 通常ファイル、ディレクトリ、シンボリックリンクなど)。
  • d_name (または Name): ファイル名。

NetBSDにおけるdirent構造体のC言語での定義は、通常 <sys/dirent.h> にあり、以下のような形式です。

struct dirent {
    ino_t       d_fileno;     /* file number of entry */
    uint16_t    d_reclen;     /* length of this record */
    uint16_t    d_namlen;     /* length of string in d_name */
    uint8_t     d_type;       /* file type, see below */
    char        d_name[MAXNAMLEN + 1]; /* name must be NUL-terminated */
};

ここで、ino_tは通常、32ビットまたは64ビットの符号なし整数型であり、64ビットシステムではuint64_tであることが多いです。MAXNAMLENはファイル名の最大長を定義するマクロで、OSやファイルシステムによって値が異なります。

4. Go言語におけるシステムコールラッパーと型定義

Go言語のsyscallパッケージは、各OSのシステムコールをGoの関数としてラップしています。これらのラッパー関数や、システムコールが使用する構造体のGo言語での型定義は、通常、自動生成ツールによって生成されます。

  • zsyscall_netbsd_386.go / zsyscall_netbsd_amd64.go: これらは、NetBSDの32ビット(i386)および64ビット(amd64)アーキテクチャ向けのシステムコールラッパー関数が定義されているファイルです。Syscall関数を介して実際のシステムコールを呼び出すコードが含まれます。
  • ztypes_netbsd_386.go / ztypes_netbsd_amd64.go: これらは、NetBSDのシステムコールが使用するC言語の構造体に対応するGo言語の型定義が記述されているファイルです。

これらのファイルは、Goのビルドプロセス中にmksyscallなどのツールによって生成されるため、手動で編集することは推奨されません(ファイル冒頭にTHIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDITというコメントがあることが多いです)。このコミットでは、生成元のテンプレートや定義が修正され、それによってこれらのzファイルが更新されたと考えられます。

技術的詳細

このコミットの技術的な核心は、NetBSDのgetdentsシステムコールとdirent構造体のGo言語での表現を、NetBSDの実際の定義に厳密に合わせることにあります。

1. getdents システムコールの導入とGetdirentriesの修正

変更前は、syscall_netbsd.go内のGetdirentries関数がTODOコメントと共に未実装の状態でした。このコミットでは、getdentsという新しい内部関数を導入し、Getdirentriesがこのgetdentsを呼び出すように変更しています。

  • getdents関数の追加: zsyscall_netbsd_386.gozsyscall_netbsd_amd64.goに、NetBSDのgetdentsシステムコールを呼び出すためのGoラッパー関数getdentsが追加されました。この関数は、ファイルディスクリプタ(fd)とバッファ(buf)を受け取り、SYS_GETDENTSシステムコール番号を使用してSyscall関数を呼び出します。

    func getdents(fd int, buf []byte) (n int, err error) {
        var _p0 unsafe.Pointer
        if len(buf) > 0 {
            _p0 = unsafe.Pointer(&buf[0])
        } else {
            _p0 = unsafe.Pointer(&_zero)
        }
        r0, _, e1 := Syscall(SYS_GETDENTS, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
        n = int(r0)
        if e1 != 0 {
            err = e1
        }
        return
    }
    

    ここで、unsafe.PointerはGoの型システムをバイパスして任意のメモリを指すポインタを作成するために使用され、C言語のポインタ引数にGoのスライスを渡す際に必要となります。Syscall関数は、システムコール番号と引数を受け取り、システムコールを実行します。

  • Getdirentries関数の修正: syscall_netbsd.go内のGetdirentries関数は、以前はTODOコメントがあり、ENOSYS(システムコールが実装されていないエラー)を返していました。このコミットでは、Getdirentriesが新しく追加されたgetdents関数を呼び出すように変更されました。また、basep *uintptr引数はgetdentsシステムコールでは使用されないため、Getdirentriesのシグネチャからは削除されませんが、内部的には無視されます。

    //sys getdents(fd int, buf []byte) (n int, err error)
    func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
        return getdents(fd, buf)
    }
    

    これにより、GoのGetdirentries関数がNetBSD上で実際に機能するようになりました。

2. Dirent 構造体の修正

NetBSDのdirent構造体のGo言語での表現が、実際のC言語の定義と一致するように修正されました。これは、ztypes_netbsd_386.goztypes_netbsd_amd64.go内のDirent構造体定義に反映されています。

  • Fileno フィールドの型変更:

    • 変更前: Fileno uint32
    • 変更後: Fileno uint64 NetBSDのdirent構造体におけるd_filenoフィールドはino_t型であり、64ビットシステムではuint64_tとして定義されることが一般的です。この変更により、GoのDirent構造体がNetBSDのino_tのサイズと一致し、ファイル番号の正確な表現が可能になります。
  • Namlen フィールドの型変更:

    • 変更前: Namlen uint8
    • 変更後: Namlen uint16 NetBSDのdirent構造体におけるd_namlenフィールドはuint16_t型です。この変更により、ファイル名の長さが正しく表現されるようになります。
  • Name 配列のサイズ変更:

    • 変更前: Name [256]int8
    • 変更後: Name [512]int8 NetBSDのdirent構造体におけるd_nameフィールドはchar d_name[MAXNAMLEN + 1]として定義されます。MAXNAMLENはファイル名の最大長であり、システムによって異なります。以前の256バイトでは、一部のNetBSD環境で長いファイル名を格納しきれない可能性がありました。512バイトに拡張することで、より長いファイル名に対応できるようになり、バッファオーバーフローのリスクを低減します。

これらの構造体定義の変更は、GoのsyscallパッケージがNetBSDのカーネルから返されるdirentデータを正しく解釈するために不可欠です。

3. Sendfile 関数の位置変更と__getdents30の削除

  • Sendfile関数の位置変更: syscall_netbsd.go内で、Sendfile関数とGetdirentries関数の定義が入れ替わっています。これは機能的な変更ではなく、コードの整理または関連する変更による副作用と考えられます。
  • __getdents30のコメントアウト: syscall_netbsd.goのコメントリストから__getdents30が削除されています。これは、NetBSDのgetdentsシステムコールが直接Goのgetdents関数によってラップされるようになったため、以前の(おそらく未実装または不適切な)ラッパーの参照が不要になったことを示唆しています。

コアとなるコードの変更箇所

src/pkg/syscall/syscall_netbsd.go

--- a/src/pkg/syscall/syscall_netbsd.go
+++ b/src/pkg/syscall/syscall_netbsd.go
@@ -74,13 +74,13 @@ func Pipe(p []int) (err error) {
 	return
 }
 
-// TODO
-func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
-	return -1, ENOSYS
+//sys getdents(fd int, buf []byte) (n int, err error)
+func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
+	return getdents(fd, buf)
 }
 
 // TODO
-func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
+func Sendfile(outfd int, infd int, offset *int64, count int) (written int, err error) {
 	return -1, ENOSYS
 }
 
@@ -176,7 +176,6 @@ func Getdirentries(fd int, buf []byte, basep *uintptr) (n int, err error) {
 // __fhstatvfs140
 // __fstat30
 // __getcwd
-// __getdents30
 // __getfh30
 // __getlogin
 // __lstat30

src/pkg/syscall/zsyscall_netbsd_386.go および src/pkg/syscall/zsyscall_netbsd_amd64.go

(両ファイルで同様の変更)

--- a/src/pkg/syscall/zsyscall_netbsd_386.go
+++ b/src/pkg/syscall/zsyscall_netbsd_386.go
@@ -263,6 +263,23 @@ func pipe(p *[2]_C_int) (err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
+func getdents(fd int, buf []byte) (n int, err error) {
+	var _p0 unsafe.Pointer
+	if len(buf) > 0 {
+		_p0 = unsafe.Pointer(&buf[0])
+	} else {
+		_p0 = unsafe.Pointer(&_zero)
+	}
+	r0, _, e1 := Syscall(SYS_GETDENTS, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
+	n = int(r0)
+	if e1 != 0 {
+		err = e1
+	}
+	return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
 func Access(path string, mode uint32) (err error) {
 	_, _, e1 := Syscall(SYS_ACCESS, uintptr(unsafe.Pointer(StringBytePtr(path))), uintptr(mode), 0)
 	if e1 != 0 {

src/pkg/syscall/ztypes_netbsd_386.go および src/pkg/syscall/ztypes_netbsd_amd64.go

(両ファイルで同様の変更)

--- a/src/pkg/syscall/ztypes_netbsd_386.go
+++ b/src/pkg/syscall/ztypes_netbsd_386.go
@@ -127,11 +127,11 @@ type Flock_t struct {
 }
 
 type Dirent struct {
-	Fileno uint32
+	Fileno uint64
 	Reclen uint16
+	Namlen uint16
 	Type   uint8
-	Namlen uint8
-	Name   [256]int8
+	Name   [512]int8
 }
 
 type Fsid struct {

コアとなるコードの解説

syscall_netbsd.go の変更

  • Getdirentries の実装: 以前はTODOコメントがあり、常にエラーを返していたGetdirentries関数が、新しく定義された内部関数getdentsを呼び出すように変更されました。これにより、NetBSD上でのディレクトリエントリーの読み取りが可能になります。
  • SendfileGetdirentries の位置入れ替え: これは機能的な変更ではなく、コードの配置の変更です。
  • __getdents30 の削除: コメントリストから__getdents30が削除されました。これは、getdentsシステムコールが直接Goのラッパー関数として実装されたため、以前の参照が不要になったことを示しています。

zsyscall_netbsd_386.go および zsyscall_netbsd_amd64.go の変更

  • getdents 関数の追加: NetBSDのgetdentsシステムコールをGoから呼び出すための具体的なラッパー関数getdentsが追加されました。この関数は、Syscall関数を使用してSYS_GETDENTSシステムコールを実行し、ファイルディスクリプタ、バッファへのポインタ、バッファサイズを引数として渡します。これにより、GoプログラムがNetBSDカーネルから直接ディレクトリエントリーを取得できるようになります。

ztypes_netbsd_386.go および ztypes_netbsd_amd64.go の変更

  • Dirent 構造体の修正:
    • Fileno フィールドの型が uint32 から uint64 に変更されました。これは、NetBSDのdirent構造体におけるd_fileno(inode番号)が、特に64ビットシステムにおいてuint64_tとして定義されることに対応するためです。これにより、大きなファイルシステムや多数のファイルを持つシステムでも、inode番号が正しく扱えるようになります。
    • Namlen フィールドの型が uint8 から uint16 に変更されました。これは、NetBSDのdirent構造体におけるd_namlen(ファイル名の長さ)がuint16_tとして定義されることに対応するためです。これにより、ファイル名の長さが正確に表現されます。
    • Name 配列のサイズが [256]int8 から [512]int8 に拡張されました。これは、NetBSDのMAXNAMLENが255より大きい場合や、将来的な互換性を考慮して、より長いファイル名を格納できるようにするためです。これにより、ファイル名が途中で切り詰められることなく、完全に読み取れるようになります。

これらの変更により、GoのsyscallパッケージはNetBSDのシステムコールインターフェースと完全に一致し、NetBSD上でのディレクトリ操作が安定して行えるようになりました。

関連リンク

  • Go言語 syscall パッケージのドキュメント: https://pkg.go.dev/syscall
  • NetBSD getdents マニュアルページ (通常はman 2 getdentsで参照可能)
  • NetBSD dirent 構造体定義 (通常は<sys/dirent.h>で参照可能)

参考にした情報源リンク