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

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

コミット

commit fb3b27329ef584d51f9cdb9a500cc45037439e3d
Author: Anthony Martin <ality@pbrane.org>
Date:   Fri Jan 20 20:01:29 2012 -0800

    os: fix Plan 9 build after more FileMode changes
    
    This should go in after Brad's CL 5553064.
    
    R=bradfitz, rsc
    CC=golang-dev
    https://golang.org/cl/5555056

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

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

元コミット内容

このコミットは、Go言語のosパッケージにおけるPlan 9ビルドの修正を目的としています。具体的には、FileModeの変更が導入された後に発生した問題を解決しています。コミットメッセージには「This should go in after Brad's CL 5553064.」とあり、Brad Fitzpatrickによる別の変更(CL 5553064)が適用された後にこの修正が必要となることを示唆しています。

変更の概要は以下の通りです。

  • src/pkg/os/file_plan9.go ファイルが修正されました。
  • 26行が追加され、13行が削除されました。

変更の背景

このコミットの背景には、Go言語のosパッケージにおけるファイルモード(FileMode)の扱いに関する変更があります。特に、os.FileMode型が導入され、ファイルパーミッションの表現方法が改善されたことが挙げられます。

以前のos.Chmod関数は、Unixスタイルのパーミッション(例: 0755)を表す整数値を受け入れていたと考えられます。しかし、Goのos.FileMode型は、ファイルのモードとパーミッションビットを構造化して表現するための型であり、APIの一貫性と型安全性を高めるために、os.Chmodがこのos.FileMode型を直接受け入れるように変更する提案が2012年初頭に行われました。

このコミットは、そのFileModeの変更がPlan 9環境でのビルドに影響を与えたため、その問題を修正するために作成されました。コミットメッセージで言及されている「Brad's CL 5553064」は、os.Chmodos.FileModeを受け入れるようにする変更に関連していると推測されます。この修正は、その先行する変更によって引き起こされたPlan 9固有のビルド問題を解決するためのものです。

前提知識の解説

Go言語のosパッケージとファイル操作

Go言語のosパッケージは、オペレーティングシステム(OS)の機能へのプラットフォームに依存しないインターフェースを提供します。これには、ファイルシステム操作(ファイルの作成、読み書き、削除、パーミッション変更など)が含まれます。

os.FileMode

os.FileModeは、Go言語でファイルのパーミッションとモード(ディレクトリ、シンボリックリンク、デバイスファイルなど)を表すために使用される型です。これはuint32のエイリアスであり、下位9ビットが標準的なUnixのrwxrwxrwxパーミッションに対応し、上位ビットが特殊なファイル属性(例: ModeDirModeSymlinkModeDeviceModeAppendModeExclusiveModeTemporaryなど)を示します。

Plan 9

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Go言語は、その設計思想や一部の概念においてPlan 9の影響を受けています。Go言語の標準ライブラリには、Plan 9固有のシステムコールやファイルシステム操作を扱うためのコードが含まれていることがあります。このコミットがfile_plan9.goを修正しているのは、GoがPlan 9環境もサポートしているためです。

syscallパッケージ

Go言語のsyscallパッケージは、低レベルのオペレーティングシステムプリミティブへのインターフェースを提供します。これには、ファイル操作のためのシステムコール(例: syscall.Opensyscall.Createsyscall.Fwstatsyscall.Wstat)が含まれます。osパッケージの多くの関数は、内部的にsyscallパッケージを利用してOS固有の操作を実行します。

DMAPPEND, DMEXCL, DMTMP (Plan 9固有のファイルモード)

Plan 9のファイルモードには、Unixのパーミッションとは異なる独自のビットがあります。

  • syscall.DMAPPEND: ファイルが追記モードで開かれていることを示します。
  • syscall.DMEXCL: ファイルが排他的に開かれていることを示します。
  • syscall.DMTMP: ファイルが一時ファイルであることを示します。

これらは、Goのos.FileModeが持つModeAppendModeExclusiveModeTemporaryといったフラグに対応し、Plan 9のシステムコールに渡す際に適切に変換する必要があります。

技術的詳細

このコミットの主要な技術的変更点は、os.FileMode型とPlan 9のシステムコールにおけるファイルモードの変換と適用方法の調整です。

  1. syscallMode関数の導入: 新しいsyscallMode関数が導入されました。この関数はos.FileMode型の引数を受け取り、Plan 9のシステムコールが期待するuint32型のモードビットに変換します。

    • i.Perm(): 標準的なUnixパーミッションビットを抽出します。
    • i&ModeAppend != 0: os.ModeAppendフラグが設定されていれば、Plan 9のsyscall.DMAPPENDビットを追加します。
    • i&ModeExclusive != 0: os.ModeExclusiveフラグが設定されていれば、Plan 9のsyscall.DMEXCLビットを追加します。
    • i&ModeTemporary != 0: os.ModeTemporaryフラグが設定されていれば、Plan 9のsyscall.DMTMPビットを追加します。 この関数により、Goの抽象的なFileModeがPlan 9の具体的なシステムコール引数に正しくマッピングされるようになります。
  2. OpenFile関数のシグネチャ変更: OpenFile関数のperm引数の型がuint32からFileModeに変更されました。これにより、OpenFileがGoのFileMode型を直接受け入れるようになり、APIの一貫性が向上しました。

  3. syscall.Create呼び出しの修正: OpenFile関数内でsyscall.Createを呼び出す際、perm引数に直接FileModeを渡すのではなく、新しく導入されたsyscallMode(perm)を介して変換された値が渡されるようになりました。これにより、Plan 9のシステムコールが正しいモードビットを受け取るようになります。

  4. Chmod関数の修正:

    • File.ChmodとグローバルなChmod関数のmode引数の型がuint32からFileModeに変更されました。
    • chmodMask定数が導入されました。これは、syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePermModePerm0777に相当)を組み合わせたもので、Plan 9固有のモードビットと標準パーミッションビットをマスクするために使用されます。
    • ファイルモードの適用ロジックが変更されました。以前はmask^uint32(0777))を使ってパーミッションを操作していましたが、新しいロジックではodir.Mode&^chmodMask | syscallMode(mode)&chmodMaskという形で、既存のモードからchmodMaskで指定されたビットをクリアし、新しいsyscallMode(mode)からchmodMaskで指定されたビットを適用しています。これにより、Plan 9固有のモードビットもChmodによって適切に設定・変更できるようになりました。

これらの変更は、Goのos.FileModeの抽象化とPlan 9の低レベルなファイルモード表現との間のギャップを埋め、FileModeの変更が導入された後もPlan 9環境でファイル操作が正しく機能するようにするためのものです。

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

--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -56,12 +56,27 @@ func epipecheck(file *File, e error) {
 // On Unix-like systems, it is "/dev/null"; on Windows, "NUL".
 const DevNull = "/dev/null"
 
+// syscallMode returns the syscall-specific mode bits from Go's portable mode bits.
+func syscallMode(i FileMode) (o uint32) {
+	o |= uint32(i.Perm())
+	if i&ModeAppend != 0 {
+		o |= syscall.DMAPPEND
+	}
+	if i&ModeExclusive != 0 {
+		o |= syscall.DMEXCL
+	}
+	if i&ModeTemporary != 0 {
+		o |= syscall.DMTMP
+	}
+	return
+}
+
 // OpenFile is the generalized open call; most users will use Open
 // or Create instead.  It opens the named file with specified flag
 // (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
 // methods on the returned File can be used for I/O.
 // It returns the File and an error, if any.
-func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
+func OpenFile(name string, flag int, perm FileMode) (file *File, err error) {
  	var (
  	\t\tfd     int
  	\t\te      error
@@ -89,12 +104,12 @@ func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
 
  	syscall.ForkLock.RLock()
  	if (create && trunc) || excl {
-\t\tfd, e = syscall.Create(name, flag, perm)
+\t\tfd, e = syscall.Create(name, flag, syscallMode(perm))
  	} else {
  	\t\tfd, e = syscall.Open(name, flag)
  	\t\tif e != nil && create {
  	\t\t\tvar e1 error
-\t\t\tfd, e1 = syscall.Create(name, flag, perm)
+\t\t\tfd, e1 = syscall.Create(name, flag, syscallMode(perm))
  	\t\t\tif e1 == nil {\
  	\t\t\t\te = nil
  	\t\t\t}
@@ -162,18 +177,18 @@ func (f *File) Truncate(size int64) error {
  	return nil
  }
  
+const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm)
+\n // Chmod changes the mode of the file to mode.
-func (f *File) Chmod(mode uint32) error {
+func (f *File) Chmod(mode FileMode) error {
  	var d Dir
-\tvar mask = ^uint32(0777)\
 
-\td.Null()\
  	odir, e := dirstat(f)
  	if e != nil {
  	\t\treturn &PathError{"chmod", f.name, e}
  	}
-\
-\td.Mode = (odir.Mode & mask) | (mode &^ mask)
+\td.Null()
+\td.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
  	if e := syscall.Fwstat(f.fd, pdir(nil, &d)); e != nil {
  	\t\treturn &PathError{"chmod", f.name, e}
  	}
@@ -266,17 +281,15 @@ func Rename(oldname, newname string) error {
 }
 
 // Chmod changes the mode of the named file to mode.
-func Chmod(name string, mode uint32) error {
+func Chmod(name string, mode FileMode) error {
  	var d Dir
-\tvar mask = ^uint32(0777)\
 
-\td.Null()\
  	odir, e := dirstat(name)
  	if e != nil {
  	\t\treturn &PathError{"chmod", name, e}
  	}
-\
-\td.Mode = (odir.Mode & mask) | (mode &^ mask)
+\td.Null()
+\td.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask
  	if e := syscall.Wstat(name, pdir(nil, &d)); e != nil {
  	\t\treturn &PathError{"chmod", name, e}
  	}

コアとなるコードの解説

このコミットは、GoのosパッケージがPlan 9環境でファイルモードを正しく扱うための重要な修正を含んでいます。

  1. syscallMode関数の追加: この関数は、Goのos.FileMode型(i)をPlan 9のシステムコールが理解できるuint32型のモードビット(o)に変換する役割を担います。

    • uint32(i.Perm()): FileModeから標準的なUnixパーミッションビットを抽出します。
    • if i&ModeAppend != 0 { o |= syscall.DMAPPEND }: os.ModeAppendフラグが設定されている場合、Plan 9固有の追記モードビットsyscall.DMAPPENDを結果に追加します。
    • 同様に、ModeExclusiveModeTemporaryについても、それぞれsyscall.DMEXCLsyscall.DMTMPにマッピングしています。 これにより、Goの抽象的なファイルモードがPlan 9の具体的なシステムコールに正しく渡されるようになります。
  2. OpenFile関数の変更:

    • func OpenFile(name string, flag int, perm uint32) から func OpenFile(name string, flag int, perm FileMode) へと、perm引数の型がuint32からFileModeに変更されました。これは、GoのAPIがより型安全になり、os.FileModeのセマンティクスを直接利用できるようになるための変更です。
    • syscall.Create(name, flag, perm) の呼び出しが syscall.Create(name, flag, syscallMode(perm)) に変更されました。これにより、OpenFileに渡されたFileModeが、syscallMode関数を介してPlan 9のシステムコールが期待する形式に変換されてから渡されるようになります。
  3. Chmod関連関数の変更:

    • File.ChmodとグローバルなChmod関数のシグネチャも同様に、mode引数の型がuint32からFileModeに変更されました。
    • const chmodMask = uint32(syscall.DMAPPEND | syscall.DMEXCL | syscall.DMTMP | ModePerm): この定数は、Plan 9固有のモードビット(追記、排他、一時)と標準パーミッションビット(ModePerm0777に相当)を組み合わせたマスクを定義しています。これは、Chmod操作で変更を許可するビットの範囲を明確にするために使用されます。
    • d.Mode = (odir.Mode & mask) | (mode &^ mask) という古いモード設定ロジックが削除され、d.Null()の呼び出しが追加されました。
    • 新しいモード設定ロジックは d.Mode = odir.Mode&^chmodMask | syscallMode(mode)&chmodMask です。
      • odir.Mode&^chmodMask: 既存のファイルモード(odir.Mode)から、chmodMaskで指定されたビット(Plan 9固有のモードと標準パーミッション)をクリアします。
      • syscallMode(mode)&chmodMask: 新しいFileModemode)をsyscallModeでPlan 9形式に変換し、その結果をchmodMaskでマスクします。これにより、Chmodで変更が意図されたビットのみが選択されます。
      • これら2つの結果をOR演算子で結合することで、既存のモードのうちchmodMaskで保護されていないビットはそのままに、chmodMaskで指定されたビットのみが新しいmodeの値に基づいて更新されます。

これらの変更により、Goのosパッケージは、FileModeの抽象化を維持しつつ、Plan 9のファイルシステムが持つ特定のモードビットを正確に操作できるようになりました。これは、Goのクロスプラットフォーム互換性を維持する上で重要な修正です。

関連リンク

参考にした情報源リンク