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

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

このコミットは、Go言語の初期のDarwin(macOS)向けシステムコール実装における、Getdirentries関数の修正に関するものです。具体的には、src/lib/syscall/file_darwin.goファイルが変更されています。

コミット

commit 2f147992b4f3f8a89d1d51d48845f5d310719388
Author: Rob Pike <r@golang.org>
Date:   Mon Feb 9 20:04:36 2009 -0800

    fix Getdirentries: base comes back in r2.
    
    R=rsc
    DELTA=3  (3 added, 0 deleted, 0 changed)
    OCL=24727
    CL=24727

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

https://github.com/golang/go/commit/2f147992b4f3f8a89d1d51d48845f5d310719388

元コミット内容

fix Getdirentries: base comes back in r2.

変更の背景

このコミットは、Darwin(macOS)システムにおけるGetdirentriesシステムコールの挙動に対応するための修正です。Getdirentriesはディレクトリのエントリを読み込むためのシステムコールですが、特定の環境(この場合はDarwin)では、そのオフセット情報(base)がシステムコールの戻り値のセカンダリレジスタ(r2)に格納されて返されるという特性がありました。

Go言語の初期のシステムコールラッパーは、プライマリの戻り値(r1)とエラーコード(err)を主に扱っていましたが、Getdirentriesのようにセカンダリの戻り値も重要な意味を持つ場合、その値を適切に取得して利用する必要がありました。この修正は、Getdirentriesが返すbase値を正しくbasepポインタに書き戻すことで、ディレクトリ読み込みの継続性を保証するために導入されました。

前提知識の解説

システムコール (Syscall)

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

Getdirentries / SYS_GETDIRENTRIES64

Getdirentriesは、Unix系システムでディレクトリの内容を読み取るためのシステムコールです。ディレクトリ内のファイルやサブディレクトリのエントリをバッファに書き込みます。 SYS_GETDIRENTRIES64は、特に64ビットのオフセットを扱うためのGetdirentriesのバリアントで、Darwinを含む一部のシステムで利用されます。これにより、大きなディレクトリやファイルシステムを効率的に処理できます。

Go言語の初期のシステムコール実装とr1, r2

Go言語の初期のsyscallパッケージでは、SyscallSyscall6といった関数が、OSのシステムコールを直接呼び出すための低レベルなラッパーとして提供されていました。これらの関数は、システムコールの戻り値をr1, r2, errという形式で返します。

  • r1: システムコールの主要な戻り値。通常、成功した場合は非負の値(例えば、読み込まれたバイト数など)、エラーの場合は-1が返されます。
  • r2: システムコールのセカンダリな戻り値。一部のシステムコールでは、r1だけでは表現しきれない追加の情報(例えば、Getdirentriesにおける次の読み込み開始位置のオフセットなど)をr2に格納して返します。
  • err: システムコールがエラーを返した場合の、OS固有のエラーコード。

特に、32ビットシステムで64ビットの値を返すシステムコールや、複数の戻り値を持つシステムコールの場合、r1r2の両方が使用されることがあります。このコミットのケースでは、Getdirentriesbaseオフセットをr2に返していました。

unsafe.Pointeruintptr

Go言語におけるunsafe.Pointerは、任意の型のポインタを保持できる特殊なポインタ型です。型安全性を無視してメモリを直接操作することを可能にします。uintptrは、ポインタの値を整数として表現する型です。これらは、C言語のポインタ操作に似た低レベルなメモリ操作や、システムコールへの引数としてポインタを渡す際に使用されます。

技術的詳細

このコミットが修正している問題は、Getdirentriesシステムコールが、次の読み込みを開始すべきオフセット(base)を、GoのSyscall6関数が返すr2レジスタに格納して返すというDarwin特有の挙動にあります。

元のコードでは、Syscall6の戻り値のうち、r1(主要な戻り値)とerr(エラーコード)のみが利用され、r2の値は無視されていました。しかし、Getdirentriesシステムコールは、読み込みが成功した場合に、次に読み込むべきディレクトリ内のオフセットをr2に設定します。このオフセットは、basepというポインタを通じて呼び出し元に返されるべき値です。

修正前は、Getdirentriesが成功しても、basepが更新されないため、連続してディレクトリを読み込む際に常に最初から読み込みが開始されてしまうか、不正なオフセットで処理が続行される可能性がありました。これは、ディレクトリの全エントリを列挙するような処理において、無限ループや不完全な結果につながるバグとなります。

このコミットは、Syscall6の呼び出し結果として得られたr2の値を、システムコールが成功した場合(r1 != -1)に、basepが指すメモリ位置に明示的に書き込むことで、この問題を解決しています。これにより、Getdirentriesの呼び出し元は、正しく次の読み込みオフセットを取得し、ディレクトリの継続的な走査が可能になります。

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

--- a/src/lib/syscall/file_darwin.go
+++ b/src/lib/syscall/file_darwin.go
@@ -96,5 +96,8 @@ func Dup2(fd1, fd2 int64) (ret int64, errno int64) {
 
 func Getdirentries(fd int64, buf *byte, nbytes int64, basep *int64) (ret int64, errno int64) {
 	r1, r2, err := Syscall6(SYS_GETDIRENTRIES64, fd, int64(uintptr(unsafe.Pointer(buf))), nbytes, int64(uintptr(unsafe.Pointer(basep))), 0, 0);
+	if r1 != -1 {
+		*basep = r2
+	}
 	return r1, err;
 }

コアとなるコードの解説

変更はsrc/lib/syscall/file_darwin.goファイルのGetdirentries関数内にあります。

func Getdirentries(fd int64, buf *byte, nbytes int64, basep *int64) (ret int64, errno int64) {
	r1, r2, err := Syscall6(SYS_GETDIRENTRIES64, fd, int64(uintptr(unsafe.Pointer(buf))), nbytes, int64(uintptr(unsafe.Pointer(basep))), 0, 0);
	if r1 != -1 {
		*basep = r2
	}
	return r1, err;
}
  1. r1, r2, err := Syscall6(...): Syscall6関数は、SYS_GETDIRENTRIES64システムコールを呼び出します。引数として、ファイルディスクリプタfd、バッファbuf、読み込むバイト数nbytes、そしてbasep(次の読み込みオフセットを格納するためのポインタ)が渡されます。 Syscall6は、システムコールの戻り値をr1r2、そしてエラーerrとして返します。

  2. if r1 != -1 { ... }: この条件分岐は、システムコールが成功したかどうかをチェックしています。Unix系システムコールでは、通常、成功した場合は非負の値(例えば、読み込まれたバイト数)を返し、エラーの場合は-1を返します。したがって、r1 != -1はシステムコールがエラーなく完了したことを意味します。

  3. *basep = r2: システムコールが成功した場合、r2に格納されている値(Getdirentriesシステムコールが返す次の読み込みオフセット)を、basepが指すメモリ位置に書き込みます。*basepはポインタbasepが指す先の値を意味します。これにより、呼び出し元が提供したbasepポインタを通じて、正しいオフセット情報が更新されます。

この修正により、Getdirentries関数は、Darwin環境において、ディレクトリの読み込みオフセットを正しく管理できるようになり、ディレクトリの継続的な走査が正確に行われるようになりました。

関連リンク

  • Go言語のsyscallパッケージに関するドキュメント: https://pkg.go.dev/syscall
  • Darwinのgetdirentriesシステムコールに関する情報(manページなど)

参考にした情報源リンク