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

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

このコミットは、Go言語の標準ライブラリであるosパッケージとtimeパッケージにおける、Plan 9オペレーティングシステム向けのビルド修正を目的としています。具体的には、以下の4つのファイルが変更されています。

  • src/pkg/os/file.go: LinkError構造体とそのError()メソッドが追加されました。
  • src/pkg/os/file_posix.go: LinkError構造体とそのError()メソッドが削除されました。
  • src/pkg/time/sys_plan9.go: syscall.Openの呼び出し引数と、preadn関数の戻り値が修正されました。
  • src/pkg/time/zoneinfo_plan9.go: タイムゾーン情報ファイルの読み込みパスが変更され、不要なエラー変数が削除されました。

コミット

このコミットは、Go言語のosおよびtimeパッケージにおけるPlan 9オペレーティングシステムでのビルド問題を解決するためのものです。主な変更点は、LinkError型をfile_posix.goからfile.goへ移動したこと、Plan 9固有のシステムコール呼び出しの修正、およびタイムゾーン情報ファイルの読み込み方法の変更です。

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

https://github.com/golang/go/commit/441538eb574f8ef69a6f11f1d7eee403335931a1

元コミット内容

commit 441538eb574f8ef69a6f11f1d7eee403335931a1
Author: Fazlul Shahriar <fshahriar@gmail.com>
Date:   Mon Feb 20 12:31:24 2012 +1100

    os,time: fix Plan 9 build
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/5689043
---
 src/pkg/os/file.go             | 13 +++++++++++++
 src/pkg/os/file_posix.go       | 13 -------------\n src/pkg/time/sys_plan9.go      |  3 ++-\n src/pkg/time/zoneinfo_plan9.go |  5 ++---\n 4 files changed, 17 insertions(+), 17 deletions(-)\n\ndiff --git a/src/pkg/os/file.go b/src/pkg/os/file.go\nindex ddcaa6fed9..1c3d0172d3 100644\n--- a/src/pkg/os/file.go\n+++ b/src/pkg/os/file.go\n@@ -72,6 +72,19 @@ const (\n \tSEEK_END int = 2 // seek relative to the end\n )\n \n+// LinkError records an error during a link or symlink or rename\n+// system call and the paths that caused it.\n+type LinkError struct {\n+\tOp  string\n+\tOld string\n+\tNew string\n+\tErr error\n+}\n+\n+func (e *LinkError) Error() string {\n+\treturn e.Op + \" \" + e.Old + \" \" + e.New + \": \" + e.Err.Error()\n+}\n+\n // Read reads up to len(b) bytes from the File.\n // It returns the number of bytes read and an error, if any.\n // EOF is signaled by a zero count with err set to io.EOF.\ndiff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go\nindex 2ffc2ee083..073bd56a47 100644\n--- a/src/pkg/os/file_posix.go\n+++ b/src/pkg/os/file_posix.go\n@@ -24,19 +24,6 @@ func epipecheck(file *File, e error) {\n \t}\n }\n \n-// LinkError records an error during a link or symlink or rename\n-// system call and the paths that caused it.\n-type LinkError struct {\n-\tOp  string\n-\tOld string\n-\tNew string\n-\tErr error\n-}\n-\n-func (e *LinkError) Error() string {\n-\treturn e.Op + \" \" + e.Old + \" \" + e.New + \": \" + e.Err.Error()\n-}\n-\n // Link creates newname as a hard link to the oldname file.\n // If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {\ndiff --git a/src/pkg/time/sys_plan9.go b/src/pkg/time/sys_plan9.go\nindex e2f91bccb5..8484729448 100644\n--- a/src/pkg/time/sys_plan9.go\n+++ b/src/pkg/time/sys_plan9.go\n@@ -43,7 +43,7 @@ func readFile(name string) ([]byte, error) {\n }\n \n func open(name string) (uintptr, error) {\n-\tfd, err := syscall.Open(name, syscall.O_RDONLY, 0)\n+\tfd, err := syscall.Open(name, syscall.O_RDONLY)\n \tif err != nil {\n \t\treturn 0, err\n \t}\n@@ -72,4 +72,5 @@ func preadn(fd uintptr, buf []byte, off int) error {\n \t\t}\n \t\tbuf = buf[m:]\n \t}\n+\treturn nil\n }\ndiff --git a/src/pkg/time/zoneinfo_plan9.go b/src/pkg/time/zoneinfo_plan9.go\nindex 0fc2c25c0b..6855238dc8 100644\n--- a/src/pkg/time/zoneinfo_plan9.go\n+++ b/src/pkg/time/zoneinfo_plan9.go\n@@ -8,11 +8,10 @@ package time\n \n import (\n \t\"errors\"\n+\t\"runtime\"\n \t\"syscall\"\n )\n \n-var badData = errors.New(\"malformed time zone information\")\n-\n func isSpace(r rune) bool {\n \treturn r == \' \' || r == \'\\t\' || r == \'\\n\'\n }\n@@ -149,7 +148,7 @@ func initLocal() {\n }\n \n func loadLocation(name string) (*Location, error) {\n-\tif z, err := loadZoneFile(runtime.GOROOT() + \"/lib/time/zoneinfo/\" + name); err == nil {\n+\tif z, err := loadZoneFile(runtime.GOROOT()+\"/lib/time/zoneinfo.zip\", name); err == nil {\n \t\tz.name = name\n \t\treturn z, nil\n \t}\n```

## 変更の背景

このコミットの主な目的は、Go言語の標準ライブラリがPlan 9オペレーティングシステム上で正しくビルドおよび動作するように修正することです。Goはクロスプラットフォーム対応を重視しており、様々なOSで動作するように設計されています。しかし、OS固有のシステムコールやファイルシステムの挙動の違いにより、特定のプラットフォームで問題が発生することがあります。

このコミットが行われた2012年当時、Go言語はまだ比較的新しく、様々なプラットフォームへの対応が進化している段階でした。Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、その設計思想やシステムコールインターフェースはUnix系OSとは異なる部分が多く存在します。

具体的には、以下の問題が修正の背景にあると考えられます。

1.  **`LinkError`の定義場所**: `LinkError`は、ファイルシステム操作(リンク、シンボリックリンク、リネームなど)で発生するエラーを表現するための型です。当初`file_posix.go`(POSIX準拠OS向け)に定義されていましたが、Plan 9を含む他のOSでも共通して使用されるべきエラー型であるため、より汎用的な`file.go`に移動する必要がありました。これにより、クロスプラットフォームでのエラーハンドリングの一貫性が保たれます。
2.  **Plan 9のシステムコールインターフェースの差異**: `syscall.Open`の呼び出しにおいて、Plan 9の`open`システムコールがUnix系OSとは異なる引数を期待していた可能性があります。Unix系では`open(path, flags, mode)`のように`mode`引数(パーミッション)がありますが、Plan 9の`open`は通常`open(path, mode)`のように`mode`引数がファイルアクセスモード(読み取り専用、書き込み専用など)を兼ねるため、パーミッション引数が不要、または異なる意味を持つ場合があります。このコミットでは、不要な`0`引数を削除することで、Plan 9の`open`システムコールに合致させています。
3.  **`preadn`関数の戻り値の欠落**: Go言語では、関数がエラーを返す場合、すべての実行パスでエラーを返すか、`nil`を返す必要があります。`preadn`関数の一部のパスで戻り値が欠落していたため、コンパイルエラーやランタイムエラーの原因となっていました。
4.  **タイムゾーン情報ファイルのパス**: Plan 9におけるタイムゾーン情報の管理方法が、当初想定されていたディレクトリ構造ではなく、`zoneinfo.zip`という単一のアーカイブファイルにまとめられていた可能性があります。これにより、`time`パッケージがタイムゾーン情報を正しく読み込めず、時間関連の機能が動作しない問題が発生していました。

これらの修正は、Go言語がPlan 9環境で安定して動作するための重要なステップでした。

## 前提知識の解説

このコミットを理解するためには、以下の前提知識があると役立ちます。

### Go言語の`os`パッケージと`time`パッケージ

*   **`os`パッケージ**: オペレーティングシステムと対話するための機能を提供します。ファイル操作(作成、読み書き、削除)、ディレクトリ操作、プロセス管理、環境変数へのアクセスなどが含まれます。このコミットでは、ファイル操作に関連するエラー型`LinkError`の定義が変更されています。
*   **`time`パッケージ**: 時間の測定、表示、フォーマット、およびタイムゾーンの処理に関する機能を提供します。このコミットでは、特にPlan 9環境でのタイムゾーン情報の読み込み方法が修正されています。

### Plan 9オペレーティングシステム

*   **Plan 9 from Bell Labs**: ベル研究所で開発された分散オペレーティングシステムです。Unixの設計思想をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルとして表現するという「すべてはファイルである」という原則を徹底しています。
*   **システムコール**: OSのカーネルが提供するサービスをプログラムから利用するためのインターフェースです。Unix系OSとPlan 9では、システムコールの名前や引数、セマンティクスが異なる場合があります。特にファイル操作に関するシステムコールは、OSの設計思想を反映するため、差異が生じやすい部分です。
*   **ファイルシステム**: Plan 9のファイルシステムは、ネットワーク透過性を重視しており、リモートのリソースもローカルファイルのように扱えます。タイムゾーン情報のような設定ファイルも、特定のパスに配置されるか、アーカイブ形式で提供されることがあります。

### Go言語の`syscall`パッケージ

*   **`syscall`パッケージ**: オペレーティングシステムが提供する低レベルなシステムコールに直接アクセスするための機能を提供します。Go言語の標準ライブラリの多くは、この`syscall`パッケージを介してOS固有の機能を利用しています。異なるOSでは、同じ名前のシステムコールでも引数の数や型、戻り値が異なる場合があるため、プラットフォーム固有のコードで`syscall`パッケージを適切に利用する必要があります。

### Go言語のエラーハンドリングとカスタムエラー型

*   Go言語では、エラーは`error`インターフェースを実装する値として扱われます。慣習的に、関数は最後の戻り値として`error`型を返します。
*   **カスタムエラー型**: 特定のエラー状況に関する追加情報(例: どの操作で、どのファイルでエラーが発生したか)を提供するために、独自の構造体を`error`インターフェースを満たすように定義することがよくあります。`LinkError`は、リンク操作中に発生したエラーの詳細を保持するためのカスタムエラー型です。`Error()`メソッドを実装することで、`error`インターフェースを満たします。

### Go言語のビルドプロセスとクロスコンパイル

*   Go言語は、異なるオペレーティングシステムやアーキテクチャ向けに簡単にクロスコンパイルできる強力な機能を持っています。これは、`GOOS`(ターゲットOS)や`GOARCH`(ターゲットアーキテクチャ)といった環境変数を設定することで実現されます。
*   しかし、クロスコンパイルされたバイナリがターゲット環境で正しく動作するためには、標準ライブラリがその環境のシステムコールやファイルシステム構造に適切に対応している必要があります。このコミットは、まさにその対応の一部を修正するものです。

## 技術的詳細

### `LinkError`の移動 (`src/pkg/os/file_posix.go` から `src/pkg/os/file.go` へ)

*   **変更内容**: `LinkError`構造体とその`Error()`メソッドの定義が、`src/pkg/os/file_posix.go`から`src/pkg/os/file.go`へ移動されました。
*   **技術的背景**: `file_posix.go`は、POSIX(Portable Operating System Interface)に準拠したオペレーティングシステム(Linux, macOS, BSDなど)に特化したファイル操作の実装を含んでいます。一方、`file.go`は、より汎用的なファイル操作の定義や、すべてのOSに共通するインターフェースを提供します。
    `LinkError`は、ハードリンク、シンボリックリンク、ファイルのリネームといった操作で発生するエラーを表現するものであり、これらの操作はPOSIX系OSだけでなく、Plan 9のような非POSIX系OSでも概念的に存在します。したがって、このエラー型をPOSIX固有のファイルから汎用的なファイルに移動することで、Goの`os`パッケージが提供するエラーハンドリングがよりクロスプラットフォームで一貫したものになります。これにより、Plan 9を含む様々なOSで`LinkError`を共通して利用できるようになり、ビルド時の依存関係や型の不一致の問題が解消されます。

### Plan 9固有のシステムコール呼び出しの修正 (`src/pkg/time/sys_plan9.go`)

*   **`open`関数の修正**:
    *   **変更前**: `fd, err := syscall.Open(name, syscall.O_RDONLY, 0)`
    *   **変更後**: `fd, err := syscall.Open(name, syscall.O_RDONLY)`
    *   **技術的背景**: Unix系OSの`open`システムコールは通常、`path`, `flags`, `mode`の3つの引数を取ります。`mode`引数は、新しくファイルを作成する際のパーミッションを指定します。しかし、Plan 9の`open`システムコールは、通常`path`と`mode`の2つの引数を取ります。この`mode`はファイルアクセスモード(読み取り専用、書き込み専用など)を兼ねており、Unix系のような別途パーミッションを指定する引数は持ちません。
        このコミットでは、`syscall.Open`の呼び出しから不要な`0`引数(Unix系におけるパーミッション引数に相当)を削除することで、Plan 9のネイティブな`open`システムコールのシグネチャに合致させています。これにより、Plan 9環境でのファイルオープンが正しく行われるようになります。
*   **`preadn`関数の戻り値の追加**:
    *   **変更前**: `}` (関数の最後に`return nil`がない)
    *   **変更後**: `return nil`
    *   **技術的背景**: Go言語では、エラーを返す関数は、すべての可能な実行パスで値を返す必要があります。`preadn`関数は、ファイルの読み込み操作を行う関数であり、エラーが発生しない場合には`nil`を返すことが期待されます。以前の実装では、ループが正常に完了した場合に明示的な`return nil`が欠落しており、これはGoのコンパイラによって「すべてのコードパスが値を返さない」というエラーとして検出される可能性があります。`return nil`を追加することで、関数が常に期待される戻り値を返すことが保証され、コンパイルエラーが解消されます。

### タイムゾーン情報ファイルの読み込みパスの変更 (`src/pkg/time/zoneinfo_plan9.go`)

*   **`badData`エラー変数の削除**:
    *   **変更内容**: `var badData = errors.New("malformed time zone information")`が削除されました。
    *   **技術的背景**: この変数は、おそらく以前のコードでタイムゾーンデータの解析エラーを示すために使用されていましたが、このコミットでタイムゾーン情報の読み込み方法が変更されたため、不要になったか、より汎用的なエラーハンドリングに置き換えられた可能性があります。コードのクリーンアップの一環と考えられます。
*   **`loadLocation`関数のパス修正**:
    *   **変更前**: `loadZoneFile(runtime.GOROOT() + "/lib/time/zoneinfo/" + name)`
    *   **変更後**: `loadZoneFile(runtime.GOROOT() + "/lib/time/zoneinfo.zip", name)`
    *   **技術的背景**: `runtime.GOROOT()`はGoのインストールディレクトリを返します。この変更は、Plan 9環境におけるタイムゾーン情報ファイルの配置方法が変更されたことを示しています。以前は`/lib/time/zoneinfo/`ディレクトリ以下に個別のタイムゾーンファイルが配置されていると想定されていましたが、この修正により、タイムゾーン情報が`zoneinfo.zip`という単一のZIPアーカイブファイルにまとめられていることが前提となりました。
        `loadZoneFile`関数は、おそらくこのZIPファイルから指定された`name`(タイムゾーン名、例: "Asia/Tokyo")に対応する情報を読み出すように変更されたか、またはそのように設計された関数に引数が渡されるようになりました。この変更により、Plan 9環境でGoの`time`パッケージがタイムゾーン情報を正しく解決できるようになり、時間関連の機能が期待通りに動作するようになります。

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

### `src/pkg/os/file.go` (追加)

```go
// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

src/pkg/os/file_posix.go (削除)

// LinkError records an error during a link or symlink or rename
// system call and the paths that caused it.
type LinkError struct {
	Op  string
	Old string
	New string
	Err error
}

func (e *LinkError) Error() string {
	return e.Op + " " + e.Old + " " + e.New + ": " + e.Err.Error()
}

src/pkg/time/sys_plan9.go (修正)

--- a/src/pkg/time/sys_plan9.go
+++ b/src/pkg/time/sys_plan9.go
@@ -43,7 +43,7 @@ func readFile(name string) ([]byte, error) {
 }
 
 func open(name string) (uintptr, error) {
-	fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
+	fd, err := syscall.Open(name, syscall.O_RDONLY)
 	if err != nil {
 		return 0, err
 	}
@@ -72,4 +72,5 @@ func preadn(fd uintptr, buf []byte, off int) error {
 		}
 		buf = buf[m:]
 	}\n+\treturn nil
 }

src/pkg/time/zoneinfo_plan9.go (修正)

--- a/src/pkg/time/zoneinfo_plan9.go
+++ b/src/pkg/time/zoneinfo_plan9.go
@@ -8,11 +8,10 @@ package time
 
 import (
 	"errors"
+\t"runtime"
 	"syscall"
 )
 
-var badData = errors.New("malformed time zone information")
-\n func isSpace(r rune) bool {
 	return r == ' ' || r == '\t' || r == '\n'
 }
@@ -149,7 +148,7 @@ func initLocal() {
 }
 
 func loadLocation(name string) (*Location, error) {
-\tif z, err := loadZoneFile(runtime.GOROOT() + "/lib/time/zoneinfo/" + name); err == nil {
+\tif z, err := loadZoneFile(runtime.GOROOT()+"/lib/time/zoneinfo.zip", name); err == nil {
 		z.name = name
 		return z, nil
 	}

コアとなるコードの解説

LinkErrorの移動

LinkErrorは、ファイルシステム操作(リンク、シンボリックリンク、リネーム)中に発生するエラーをカプセル化するためのカスタムエラー型です。この型は、エラーが発生した操作の種類(Op)、関連する古いパス(Old)、新しいパス(New)、および基となるエラー(Err)という詳細情報を含みます。Error()メソッドを実装することで、Goのerrorインターフェースを満たし、標準的なエラーハンドリングメカニズムに統合されます。

このコミットでは、このLinkErrorの定義がsrc/pkg/os/file_posix.goからsrc/pkg/os/file.goへ移動されました。これは、LinkErrorがPOSIX準拠システムだけでなく、Plan 9のような他のシステムでも共通して使用されるべき汎用的なエラー型であるという認識に基づいています。この移動により、osパッケージ全体でエラー型の一貫性が向上し、クロスプラットフォームでのエラー処理が簡素化されます。

src/pkg/time/sys_plan9.goの修正

  1. open関数のsyscall.Open呼び出し:

    • 変更前はsyscall.Open(name, syscall.O_RDONLY, 0)と、3つの引数でopenシステムコールを呼び出していました。ここで0は、Unix系OSにおけるファイル作成時のパーミッション引数に相当します。
    • 変更後はsyscall.Open(name, syscall.O_RDONLY)と、2つの引数になりました。これは、Plan 9のopenシステムコールが、Unix系とは異なり、ファイルアクセスモード(O_RDONLYなど)を直接2番目の引数として受け取り、別途パーミッション引数を必要としないためです。この修正により、GoのsyscallパッケージがPlan 9のネイティブなopenシステムコールを正しく呼び出せるようになり、ファイルオープン時のエラーが解消されます。
  2. preadn関数のreturn nil追加:

    • preadn関数は、指定されたファイルディスクリプタからオフセットを指定してデータを読み込むためのヘルパー関数です。
    • この関数は、ループ内でデータを読み込み、エラーが発生した場合はそのエラーを返します。しかし、ループが正常に完了し、すべてのデータが読み込まれた場合(つまりエラーが発生しなかった場合)の明示的な戻り値が欠落していました。
    • Go言語の関数は、すべての実行パスで値を返す必要があります。return nilを追加することで、エラーが発生しなかった場合にnilエラーを返すことが保証され、コンパイラによる「すべてのコードパスが値を返さない」というエラーが解消されます。

src/pkg/time/zoneinfo_plan9.goの修正

  1. badDataエラー変数の削除:

    • var badData = errors.New("malformed time zone information")というグローバル変数が削除されました。これは、タイムゾーンデータの解析に関する特定のエラーを示すために使用されていた可能性があります。
    • この変数の削除は、タイムゾーン情報の読み込みロジックが変更され、この特定のエラー変数が不要になったか、より汎用的なエラーハンドリングメカニズムに置き換えられたことを示唆しています。コードの簡素化とクリーンアップの一環と考えられます。
  2. loadLocation関数のタイムゾーンファイルパスの変更:

    • 変更前は、runtime.GOROOT() + "/lib/time/zoneinfo/" + nameというパスを構築し、個別のタイムゾーンファイルを読み込もうとしていました。これは、Unix系OSでタイムゾーン情報が/usr/share/zoneinfo/のようなディレクトリ構造で配置されているのと同様の想定です。
    • 変更後は、runtime.GOROOT() + "/lib/time/zoneinfo.zip"というパスとnameloadZoneFile関数に渡しています。これは、Plan 9環境ではタイムゾーン情報がzoneinfo.zipという単一のZIPアーカイブファイルにまとめられていることを示しています。
    • この修正により、timeパッケージはPlan 9環境でタイムゾーン情報を正しく見つけ、読み込むことができるようになります。これにより、time.LoadLocationなどの関数が期待通りに動作し、Plan 9上での時間関連の処理が正確に行われるようになります。

これらの変更は、Go言語がPlan 9という特定のオペレーティングシステムの特性に適合し、その上で安定した動作を保証するための重要な調整です。

関連リンク

  • Go Gerrit Change-ID: https://golang.org/cl/5689043
    • Goプロジェクトでは、コードレビューシステムとしてGerritを使用しています。このリンクは、このコミットに対応するGerritの変更リスト(Change-ID)を示しており、より詳細なレビューの議論や、この変更がどのように提案され、承認されたかを確認できます。

参考にした情報源リンク