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

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

このコミットは、Go言語の標準ライブラリであるosos/execos/userパッケージに、Solarisオペレーティングシステム(GOOS=solaris)のサポートを追加するものです。これにより、GoプログラムがSolaris環境でより適切に動作し、Solaris固有のシステムコールやファイルシステム操作を扱えるようになります。

コミット

commit 6d0d08b8495efc033ede78370941e566e45eb7c8
Author: Aram Hăvărneanu <aram@mgk.ro>
Date:   Fri Jan 10 02:49:37 2014 +1100

    os, os/exec, os/user: add support for GOOS=solaris
    
    R=golang-codereviews, dave, minux.ma, gobot, jsing
    CC=golang-codereviews
    https://golang.org/cl/36020043

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

https://github.com/golang/go/commit/6d0d08b8495efc033ede78370941e566e45eb7c8

元コミット内容

os, os/exec, os/user: add support for GOOS=solaris

変更の背景

Go言語はクロスプラットフォーム対応を重視しており、様々なオペレーティングシステム上で動作するように設計されています。このコミットが作成された2014年当時、Goは既にLinux、macOS (darwin)、FreeBSD、OpenBSD、NetBSDなどのUnix系OSをサポートしていましたが、Solarisに対する公式なサポートは限定的でした。

Solarisは、特にエンタープライズ環境で利用されることの多いUNIX系OSであり、Goプログラムをこれらの環境でネイティブに実行できるようにすることは、Go言語の適用範囲を広げ、より多くの開発者や企業がGoを採用する上で重要なステップでした。このコミットは、Goの標準ライブラリがSolarisのシステムコールやファイルシステム構造に適切に対応するための基盤を整備することを目的としています。

前提知識の解説

GOOSとビルドタグ

Go言語には、特定のオペレーティングシステム(OS)やアーキテクチャ(ARCH)向けにコードを条件付きでコンパイルするためのメカニズムがあります。これは「ビルドタグ(build tags)」と呼ばれ、ソースファイルの先頭に// +build <tag>形式のコメントとして記述されます。

  • GOOS: GoプログラムをビルドするターゲットOSを指定する環境変数です。例えば、GOOS=linuxはLinux向け、GOOS=darwinはmacOS向け、そしてこのコミットで追加されたGOOS=solarisはSolaris向けにビルドすることを意味します。
  • ビルドタグの役割: ビルドタグは、コンパイラが特定のOSやアーキテクチャに特化したコードを含めるか除外するかを決定するために使用されます。例えば、// +build darwin dragonfly freebsd linux netbsd openbsdというタグを持つファイルは、これらのOSのいずれかでビルドされる場合にのみコンパイル対象となります。このコミットでは、既存のUnix系OS向けのファイルにsolarisタグを追加することで、Solaris環境でもこれらの共通コードが利用されるようにしています。

Unix系OSにおけるファイルシステムとシステムコール

Goのosパッケージは、ファイルシステム操作やプロセス管理など、OSレベルの機能を提供します。Unix系OS(Linux, macOS, FreeBSD, Solarisなど)は、POSIX標準に準拠した共通のシステムコールインターフェースを多く持っていますが、OSごとに細かな違いや独自の機能も存在します。

  • statシステムコール: ファイルやディレクトリのメタデータ(サイズ、パーミッション、更新日時、inode番号など)を取得するためのシステムコールです。OSによってstat構造体の定義や、タイムスタンプの表現方法(Timespec構造体など)が異なる場合があります。
  • syscallパッケージ: Goのsyscallパッケージは、低レベルのOSプリミティブ(システムコール)への直接的なアクセスを提供します。これにより、GoはOS固有の機能を利用したり、パフォーマンスが重要な処理を行ったりすることができます。
  • FileInfoインターフェース: osパッケージで定義されているインターフェースで、ファイルに関する抽象的な情報(名前、サイズ、モード、更新日時など)を提供します。異なるOSのstat構造体からこのFileInfoインターフェースへの変換ロジックが必要になります。

技術的詳細

このコミットの主要な技術的変更点は以下の通りです。

  1. ビルドタグへのsolarisの追加:

    • src/pkg/os/src/pkg/os/exec/src/pkg/os/user/内の多くの既存のUnix系OS向けファイル(例: dir_unix.go, env_unix_test.go, error_unix.go, lp_unix.go, exec_posix.go, file_posix.go, file_unix.go, os_unix_test.go, path_unix.go, pipe_bsd.go, signal_test.go, signal_unix.go, lookup_unix.go)のビルドタグにsolarisが追加されています。これにより、これらの共通のUnix系OS向け実装がSolarisでもコンパイル・利用されるようになります。
    • pipe_bsd.goは元々BSD系のOS(darwin, dragonfly, freebsd, netbsd, openbsd)向けでしたが、Solarisも同様のパイプ実装を利用できるため、ここにもsolarisが追加されています。
  2. Solaris固有のファイル情報取得 (stat_solaris.go) の追加:

    • src/pkg/os/stat_solaris.goが新規に作成されました。このファイルは、Solarisのsyscall.Stat_t構造体からGoのos.FileInfoインターフェースへの変換ロジックを提供します。
    • fileInfoFromStat関数は、syscall.Stat_tからファイル名と組み合わせてos.FileInfoを生成します。ここでは、Solarisのst.Modeフィールドからファイルの種類(ブロックデバイス、キャラクタデバイス、ディレクトリ、FIFO、シンボリックリンク、ソケット、通常ファイル)や特殊なパーミッション(SetGID, SetUID, Stickyビット)を抽出し、GoのFileModeにマッピングしています。
    • sameFile関数は、2つのファイルが同じデバイス上の同じinodeを参照しているかどうかを比較することで、それらが同じファイルであるかを判断します。これはUnix系OSでファイルを一意に識別する一般的な方法です。
    • timespecToTime関数は、Solarisのsyscall.Timespec構造体(秒とナノ秒で時間を表現)をGoのtime.Time型に変換します。これは、stat情報からファイルの更新日時などを取得する際に必要となります。
    • atime関数はテスト用に、FileInfoからアクセス時刻(atime)を取得するヘルパー関数です。
  3. Solaris固有のシステム情報取得 (sys_solaris.go) の追加:

    • src/pkg/os/sys_solaris.goが新規に作成されました。このファイルは、Solaris固有のシステム情報取得関数を提供します。
    • hostname()関数は、Solarisのsyscall.Gethostname()を呼び出すことで、システムのホスト名を取得します。これはosパッケージのHostname()関数が内部的に利用する可能性があります。
  4. os/exec/exec_test.goのSolaris固有のテスト調整:

    • TestHelperProcess関数内で、Solaris環境におけるファイルディスクリプタのテストに関するコメントが追加されています。これは、Solarisのlibcが独自のファイルをオープンする挙動(Darwinと同様の問題)により、テストが失敗する可能性があることを示唆しています。これは、Goのプロセスが起動する際に、OSのCライブラリが内部的にファイルを開くことがあり、それがGoのファイルディスクリプタの管理と競合するケースがあるためです。

これらの変更により、GoコンパイラはGOOS=solarisが設定された際に、Solaris固有のシステムコール定義やファイル情報構造体を利用して、GoプログラムをSolaris上で正しく動作させることができるようになります。

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

このコミットにおけるコアとなるコードの変更箇所は、主に以下の2つの新規ファイルと、既存ファイルへのビルドタグの追加です。

  1. src/pkg/os/stat_solaris.go (新規追加):

    // Copyright 2009 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package os
    
    import (
    	"syscall"
    	"time"
    )
    
    func sameFile(fs1, fs2 *fileStat) bool {
    	stat1 := fs1.sys.(*syscall.Stat_t)
    	stat2 := fs2.sys.(*syscall.Stat_t)
    	return stat1.Dev == stat2.Dev && stat1.Ino == stat2.Ino
    }
    
    func fileInfoFromStat(st *syscall.Stat_t, name string) FileInfo {
    	fs := &fileStat{
    		name:    basename(name),
    		size:    int64(st.Size),
    		modTime: timespecToTime(st.Mtim),
    		sys:     st,
    	}
    	fs.mode = FileMode(st.Mode & 0777)
    	switch st.Mode & syscall.S_IFMT {
    	case syscall.S_IFBLK:
    		fs.mode |= ModeDevice
    	case syscall.S_IFCHR:
    		fs.mode |= ModeDevice | ModeCharDevice
    	case syscall.S_IFDIR:
    		fs.mode |= ModeDir
    	case syscall.S_IFIFO:
    		fs.mode |= ModeNamedPipe
    	case syscall.S_IFLNK:
    		fs.mode |= ModeSymlink
    	case syscall.S_IFREG:
    		// nothing to do
    	case syscall.S_IFSOCK:
    		fs.mode |= ModeSocket
    	}
    	if st.Mode&syscall.S_ISGID != 0 {
    		fs.mode |= ModeSetgid
    	}
    	if st.Mode&syscall.S_ISUID != 0 {
    		fs.mode |= ModeSetuid
    	}
    	if st.Mode&syscall.S_ISVTX != 0 {
    		fs.mode |= ModeSticky
    	}
    	return fs
    }
    
    func timespecToTime(ts syscall.Timespec) time.Time {
    	return time.Unix(int64(ts.Sec), int64(ts.Nsec))
    }
    
    // For testing.
    func atime(fi FileInfo) time.Time {
    	return timespecToTime(fi.Sys().(*syscall.Stat_t).Atim)
    }
    
  2. src/pkg/os/sys_solaris.go (新規追加):

    // Copyright 2013 The Go Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style
    // license that can be found in the LICENSE file.
    
    package os
    
    import "syscall"
    
    func hostname() (name string, err error) {
    	return syscall.Gethostname()
    }
    
  3. 既存ファイルへのビルドタグの追加: 例: src/pkg/os/dir_unix.go

    --- a/src/pkg/os/dir_unix.go
    +++ b/src/pkg/os/dir_unix.go
    @@ -2,7 +2,7 @@
     // Use of this source code is governed by a BSD-style
     // license that can be found in the LICENSE file.
    
    -// +build darwin dragonfly freebsd linux netbsd openbsd
    +// +build darwin dragonfly freebsd linux netbsd openbsd solaris
    
     package os
    

    同様の変更が、src/pkg/os/env_unix_test.go, src/pkg/os/error_unix.go, src/pkg/os/exec/lp_unix.go, src/pkg/os/exec/lp_unix_test.go, src/pkg/os/exec_posix.go, src/pkg/os/exec_unix.go, src/pkg/os/file_posix.go, src/pkg/os/file_unix.go, src/pkg/os/os_unix_test.go, src/pkg/os/path_unix.go, src/pkg/os/pipe_bsd.go, src/pkg/os/signal/signal_test.go, src/pkg/os/signal/signal_unix.go, src/pkg/os/user/lookup_unix.go など、多数のファイルに対して行われています。

コアとなるコードの解説

src/pkg/os/stat_solaris.go

このファイルは、Solarisシステムにおけるファイルやディレクトリのメタデータ(統計情報)をGoのosパッケージが扱える形式に変換するための中心的なロジックを含んでいます。

  • sameFile関数:

    • Goのosパッケージの内部で、2つのFileInfoが同じ物理ファイルを参照しているかを比較するために使用されます。
    • Solarisでは、syscall.Stat_t構造体のDev(デバイスID)とIno(inode番号)の組み合わせがファイルを一意に識別します。この関数は、これら2つのフィールドが一致するかどうかを確認することで、同じファイルであるかを判断します。
  • fileInfoFromStat関数:

    • Solarisのstatシステムコールから返されるsyscall.Stat_t構造体とファイル名を受け取り、Goのos.FileInfoインターフェースを実装するfileStat構造体のインスタンスを生成します。
    • namesizemodTime(最終更新日時)などの基本的な情報を設定します。modTimeの変換には後述のtimespecToTimeが使われます。
    • st.Modeフィールドは、ファイルのパーミッションと種類(ファイルタイプ)をビットマスクで表現しています。
      • st.Mode & 0777: ファイルのパーミッションビット(rwx)を抽出します。
      • st.Mode & syscall.S_IFMT: ファイルタイプを示すビットを抽出し、switch文でGoのos.FileModeの対応するフラグ(ModeDevice, ModeCharDevice, ModeDir, ModeNamedPipe, ModeSymlink, ModeSocket)を設定します。S_IFREG(通常ファイル)の場合は特別なフラグは不要です。
      • syscall.S_ISGID, syscall.S_ISUID, syscall.S_ISVTX: それぞれSetGID、SetUID、Stickyビットといった特殊なパーミッションフラグをチェックし、対応するGoのFileModeフラグ(ModeSetgid, ModeSetuid, ModeSticky)を設定します。
  • timespecToTime関数:

    • Solarisのsyscall.Timespec構造体は、秒(Sec)とナノ秒(Nsec)で時間を表現します。
    • この関数は、これらの値を使用してGoのtime.Unix関数を呼び出し、Unixエポックからの秒数とナノ秒数に基づいてtime.Timeオブジェクトを生成します。これにより、SolarisのタイムスタンプがGoの標準的な時間表現に変換されます。

src/pkg/os/sys_solaris.go

このファイルは、Solarisシステム固有の低レベルなシステム情報取得機能を提供します。

  • hostname()関数:
    • Goのos.Hostname()関数が内部的に呼び出す可能性のある、システムホスト名を取得するためのSolaris固有の実装です。
    • Solarisのsyscall.Gethostname()システムコールを直接呼び出すことで、ホスト名を取得します。

ビルドタグの追加

既存のUnix系OS向けファイルに// +build solarisタグを追加することは、Goのクロスコンパイル戦略において非常に重要です。これにより、GoコンパイラはGOOS=solarisが設定された際に、これらの共通のUnix系OS向けコードをSolaris環境でも利用できると認識します。これは、多くのUnix系OSがPOSIX標準に準拠しており、ファイル操作やプロセス管理の基本的なインターフェースが共通しているため可能です。Solaris固有の挙動やシステムコールが必要な場合にのみ、stat_solaris.gosys_solaris.goのような専用ファイルが用意されます。

関連リンク

参考にした情報源リンク