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

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

このコミットは、Go言語のosパッケージにおけるPlan 9オペレーティングシステム向けのファイル操作(特にタイムスタンプの変更)に関するバグを修正し、テストが再びパスするようにするためのものです。具体的には、os.Chtimes関数の引数型をint64(ナノ秒単位)からtime.Time型に変更し、Plan 9のシステムコールが期待する形式(Unixエポック秒)に変換することで、ファイルアクセス時刻と更新時刻の設定が正しく行われるようにしています。また、テストのためにFileInfoからアクセス時刻を取得するヘルパー関数atimeが追加されています。

コミット

commit c93ca600eca4cee6dae2495772ae910eb768cec3
Author: Fazlul Shahriar <fshahriar@gmail.com>
Date:   Wed Jan 25 00:15:44 2012 -0800

    os: pass tests on Plan 9 again
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5563046
---
 src/pkg/os/file_plan9.go | 12 ++++++------
 src/pkg/os/stat_plan9.go |  5 +++++
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index 6ee57ff239..7d136eb368 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -7,6 +7,7 @@ package os
 import (
 	"runtime"
 	"syscall"
+	"time"
 )
 
 // File represents an open file descriptor.
@@ -299,15 +300,14 @@ func Chmod(name string, mode FileMode) error {
 // Chtimes changes the access and modification times of the named
 // file, similar to the Unix utime() or utimes() functions.
 //
-// The argument times are in nanoseconds, although the underlying
-// filesystem may truncate or round the values to a more
-// coarse time unit.\n-func Chtimes(name string, atimeNs int64, mtimeNs int64) error {
+// The underlying filesystem may truncate or round the values to a
+// less precise time unit.\n+func Chtimes(name string, atime time.Time, mtime time.Time) error {
 	var d Dir
 	d.Null()
 
-\td.Atime = uint32(atimeNs / 1e9)\n-\td.Mtime = uint32(mtimeNs / 1e9)\n+\td.Atime = uint32(atime.Unix())\n+\td.Mtime = uint32(mtime.Unix())\n 
 \tif e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
 \t\treturn &PathError{\"chtimes\", name, e}\ndiff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go
index 8d3b8a84d5..f731e43740 100644
--- a/src/pkg/os/stat_plan9.go
+++ b/src/pkg/os/stat_plan9.go
@@ -97,3 +97,8 @@ func Stat(name string) (FileInfo, error) {
 func Lstat(name string) (FileInfo, error) {
 	return Stat(name)
 }
+\n+// For testing.\n+func atime(fi FileInfo) time.Time {
+\treturn time.Unix(int64(fi.(*FileStat).Sys.(*Dir).Atime), 0)\n+}\n

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

https://github.com/golang/go/commit/c93ca600eca4cee6dae2495772ae910eb768cec3

元コミット内容

os: pass tests on Plan 9 again

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5563046

変更の背景

このコミットの主な目的は、「Plan 9上でのテストを再びパスさせる」ことです。これは、Go言語のosパッケージがPlan 9オペレーティングシステム上で正しく動作していなかったことを示唆しています。特に、ファイルアクセス時刻と更新時刻を変更するos.Chtimes関数が、Plan 9のシステムコールと期待される引数の形式が合致していなかったために問題が発生していたと考えられます。

Go言語は、その設計思想やツールチェインにおいて、UnixやPlan 9の影響を強く受けています。Goの主要な設計者であるRob PikeとKen Thompsonは、Plan 9の開発にも携わっていました。そのため、Goは様々なプラットフォームに対応していますが、特にPlan 9のようなニッチなOSに対しても互換性を維持しようとする努力が見られます。

このコミットが行われた2012年当時、Goはまだ比較的新しい言語であり、様々なプラットフォームでの安定性確保が重要なフェーズでした。特定のOS(この場合はPlan 9)でテストが失敗するということは、そのOS上でのGoアプリケーションの信頼性に直接影響するため、早急な修正が必要とされた背景があります。

前提知識の解説

Plan 9 from Bell Labs

Plan 9は、ベル研究所で開発された分散オペレーティングシステムです。Unixの「すべてはファイルである」という哲学をさらに推し進め、ネットワーク上のリソース(CPU、ストレージ、デバイスなど)もすべてファイルとして表現し、9Pプロトコルという独自のプロトコルを通じてアクセスすることを特徴としています。Plan 9は、その革新的な設計思想が後の多くのシステムに影響を与えましたが、Unixほど広く普及することはありませんでした。しかし、Go言語の設計にはPlan 9の思想が色濃く反映されており、Goのツールチェインや標準ライブラリの一部はPlan 9のそれから派生しています。

osパッケージ

Go言語の標準ライブラリの一部であり、オペレーティングシステムとの基本的な相互作用を提供します。ファイル操作(作成、読み書き、削除)、ディレクトリ操作、プロセス管理、環境変数へのアクセスなど、OSレベルの機能を提供します。

os.Chmod

ファイルのパーミッション(アクセス権)を変更する関数です。Unix系のシステムにおけるchmodコマンドに相当します。

os.Chtimes

ファイルのアクセス時刻(atime)と更新時刻(mtime)を変更する関数です。Unix系のシステムにおけるutime()utimes()関数に相当します。このコミットの主要な変更点はこの関数にあります。

FileInfoインターフェース

osパッケージで定義されているインターフェースで、ファイルに関する情報(ファイル名、サイズ、パーミッション、更新時刻など)を提供します。os.Statos.Lstat関数がこのインターフェースを実装した値を返します。

Dir構造体 (Plan 9固有)

Plan 9のシステムコールで使用されるファイル属性を表現する構造体です。ファイル名、所有者、グループ、パーミッション、アクセス時刻、更新時刻などが含まれます。syscall.Wstat関数に渡されることで、ファイルのメタデータを変更するために使用されます。

syscall.Wstat

Plan 9のシステムコールの一つで、ファイルのメタデータ(属性)を変更するために使用されます。Dir構造体を引数として受け取り、指定されたファイルの属性を更新します。

time.Time

Go言語の標準ライブラリtimeパッケージで定義されている、特定の時点を表す型です。日付、時刻、タイムゾーン情報を含みます。

time.Time.Unix()メソッド

time.Time型のメソッドで、その時刻をUnixエポック(1970年1月1日UTC)からの経過秒数(int64型)として返します。この秒数は、多くのシステムコールやファイルシステムがタイムスタンプを扱う際に使用する一般的な形式です。

PathError構造体

osパッケージで定義されているエラー型の一つで、パスに関連する操作(ファイルが見つからない、パーミッションがないなど)でエラーが発生した場合に返されます。操作名、パス、元のエラー情報を含みます。

技術的詳細

このコミットの核心は、os.Chtimes関数がPlan 9のシステムコールsyscall.Wstatと連携する際のタイムスタンプの扱いにあります。

変更前、os.ChtimesatimeNs int64mtimeNs int64という引数を受け取っていました。これは、アクセス時刻と更新時刻をナノ秒単位のint64で表現していました。しかし、Plan 9のsyscall.Wstatが期待するDir構造体のAtimeMtimeフィールドはuint32型であり、これは通常、Unixエポックからの秒数を表します。

変更前は、atimeNs / 1e9とすることでナノ秒を秒に変換していましたが、これはGoのtime.Time型が提供する高精度な時刻表現と、Plan 9のシステムコールが期待する秒単位の表現との間のミスマッチを引き起こしていました。特に、time.Time型はナノ秒精度を扱うことができますが、syscall.Wstatは秒単位でしかタイムスタンプを扱えないため、ナノ秒単位のint64を直接渡すのは適切ではありませんでした。

このコミットでは、os.Chtimesの引数をatime time.Timemtime time.Timeに変更しました。これにより、Goの標準的な時刻型を使用するようになり、より自然で型安全なAPIになりました。そして、time.Time型のUnix()メソッドを使用して、time.TimeオブジェクトをUnixエポックからの秒数(int64)に変換し、それをuint32にキャストしてDir構造体のAtimeMtimeフィールドに設定しています。

// 変更前
d.Atime = uint32(atimeNs / 1e9)
d.Mtime = uint32(mtimeNs / 1e9)

// 変更後
d.Atime = uint32(atime.Unix())
d.Mtime = uint32(mtime.Unix())

この変更により、os.ChtimesはGoのtime.Time型で時刻を受け取り、それをPlan 9のシステムコールが理解できるUnixエポック秒に正確に変換して渡すことができるようになりました。これにより、Plan 9上でのファイルタイムスタンプの変更が正しく行われ、関連するテストがパスするようになったと考えられます。

また、src/pkg/os/stat_plan9.goatimeというヘルパー関数が追加されています。これは、FileInfoインターフェースを実装したオブジェクトからアクセス時刻(atime)をtime.Time型で取得するためのものです。これは主にテスト目的で追加されたものであり、FileInfoからFileStat、さらにそのSysフィールド(Plan 9固有のDir構造体)を型アサーションで取り出し、Atimeフィールド(Unixエポック秒)をtime.Unix関数に渡してtime.Time型に変換しています。

// For testing.
func atime(fi FileInfo) time.Time {
	return time.Unix(int64(fi.(*FileStat).Sys.(*Dir).Atime), 0)
}

このヘルパー関数は、os.Chtimesで設定した時刻が、os.Statなどで取得したFileInfoから正しく読み取れることを検証するために使用されます。

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

diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index 6ee57ff239..7d136eb368 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -7,6 +7,7 @@ package os
 import (
 	"runtime"
 	"syscall"
+	"time"
 )
 
 // File represents an open file descriptor.
@@ -299,15 +300,14 @@ func Chmod(name string, mode FileMode) error {
 // Chtimes changes the access and modification times of the named
 // file, similar to the Unix utime() or utimes() functions.
 //
-// The argument times are in nanoseconds, although the underlying
-// filesystem may truncate or round the values to a more
-// coarse time unit.\n-func Chtimes(name string, atimeNs int64, mtimeNs int64) error {
+// The underlying filesystem may truncate or round the values to a
+// less precise time unit.\n+func Chtimes(name string, atime time.Time, mtime time.Time) error {
 	var d Dir
 	d.Null()
 
-\td.Atime = uint32(atimeNs / 1e9)\n-\td.Mtime = uint32(mtimeNs / 1e9)\n+\td.Atime = uint32(atime.Unix())\n+\td.Mtime = uint32(mtime.Unix())\n 
 \tif e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
 \t\treturn &PathError{\"chtimes\", name, e}\ndiff --git a/src/pkg/os/stat_plan9.go b/src/pkg/os/stat_plan9.go
index 8d3b8a84d5..f731e43740 100644
--- a/src/pkg/os/stat_plan9.go
+++ b/src/pkg/os/stat_plan9.go
@@ -97,3 +97,8 @@ func Stat(name string) (FileInfo, error) {
 func Lstat(name string) (FileInfo, error) {
 	return Stat(name)
 }
+\n+// For testing.\n+func atime(fi FileInfo) time.Time {
+\treturn time.Unix(int64(fi.(*FileStat).Sys.(*Dir).Atime), 0)\n+}\n

コアとなるコードの解説

src/pkg/os/file_plan9.go の変更

  1. import "time" の追加: os.Chtimes関数の引数にtime.Time型を使用するため、timeパッケージがインポートされました。

  2. Chtimes関数のシグネチャ変更:

    • 変更前: func Chtimes(name string, atimeNs int64, mtimeNs int64) error
      • アクセス時刻と更新時刻をナノ秒単位のint64で受け取っていました。
    • 変更後: func Chtimes(name string, atime time.Time, mtime time.Time) error
      • アクセス時刻と更新時刻をtime.Time型で受け取るようになりました。これにより、Goの標準的な時刻表現に統一され、APIの使いやすさと型安全性が向上しました。
  3. Dir構造体へのタイムスタンプ設定ロジックの変更:

    • 変更前:
      d.Atime = uint32(atimeNs / 1e9)
      d.Mtime = uint32(mtimeNs / 1e9)
      
      • ナノ秒単位のint641e9(10億)で割ることで秒に変換し、uint32にキャストしていました。
    • 変更後:
      d.Atime = uint32(atime.Unix())
      d.Mtime = uint32(mtime.Unix())
      
      • time.Time型のUnix()メソッドを呼び出すことで、Unixエポックからの経過秒数(int64)を取得し、それをuint32にキャストしてDir構造体のAtimeMtimeフィールドに設定しています。これにより、Plan 9のシステムコールが期待する秒単位のタイムスタンプが正確に渡されるようになりました。

src/pkg/os/stat_plan9.go の変更

  1. atimeヘルパー関数の追加:
    // For testing.
    func atime(fi FileInfo) time.Time {
    	return time.Unix(int64(fi.(*FileStat).Sys.(*Dir).Atime), 0)
    }
    
    • この関数は、FileInfoインターフェースを引数として受け取り、そのファイル情報のアクセス時刻(atime)をtime.Time型で返します。
    • fi.(*FileStat): FileInfoインターフェースの具体的な実装が*FileStat型であることを型アサーションで確認しています。FileStatはPlan 9固有のファイル情報構造体です。
    • (*FileStat).Sys.(*Dir): FileStat構造体のSysフィールドは、基盤となるシステム固有の情報を保持しており、Plan 9の場合は*Dir型(syscall.Dirのエイリアス)に型アサーションされます。
    • (*Dir).Atime: Dir構造体のAtimeフィールドは、Unixエポックからのアクセス秒数をuint32で保持しています。
    • time.Unix(int64(...), 0): time.Unix関数は、Unixエポックからの秒数(int64)とナノ秒オフセット(int64)を引数として受け取り、対応するtime.Timeオブジェクトを生成します。ここでは、Atimeint64にキャストし、ナノ秒オフセットは0としています。
    • この関数は、os.Chtimesで設定した時刻が、os.Statなどでファイル情報を取得した際に正しく読み取れることをテストするために使用されます。

これらの変更により、GoのosパッケージはPlan 9上でファイルタイムスタンプをより正確かつGoのイディオムに沿った形で扱えるようになり、テストのパスに貢献しました。

関連リンク

参考にした情報源リンク