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

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

このコミットは、Go言語の標準ライブラリosパッケージにおけるファイル情報 (os.FileInfo) とファイルモード (os.FileMode) の型定義を大幅に刷新し、それに伴い関連するコードベース全体を更新するものです。これにより、ファイルの種類(ディレクトリ、通常ファイルなど)を判別する方法がよりGoらしいイディオムに沿った形に変更されました。

コミット

commit 8dce57e169255608b46bb563bb7de1581908aea6
Author: Russ Cox <rsc@golang.org>
Date:   Wed Nov 30 12:04:16 2011 -0500

    os: new FileInfo, FileMode types + update tree
    
    R=golang-dev, r, r, gri, bradfitz, iant, iant, nigeltao, n13m3y3r
    CC=golang-dev
    https://golang.org/cl/5416060

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

https://github.com/golang/go/commit/8dce57e169255608b46bb563bb7de1581908aea6

元コミット内容

os: new FileInfo, FileMode types + update tree

このコミットの目的は、osパッケージ内のFileInfoおよびFileModeの型を新しく定義し、それらの変更に合わせてコードツリー全体を更新することです。

変更の背景

Go言語のosパッケージは、オペレーティングシステムとのインタラクションを抽象化し、ファイルシステム操作のためのプラットフォーム非依存なインターフェースを提供します。以前のos.FileInfoインターフェースには、ファイルがディレクトリであるか (IsDirectory())、通常ファイルであるか (IsRegular()) を直接判別するメソッドが含まれていました。

この設計は機能的には問題ありませんでしたが、ファイルの種類やパーミッションといった「モード」に関する情報を、より統一的かつGoらしい方法で表現するために、FileModeという独立した型を導入する必要性が生じました。これにより、FileInfoはファイルに関する一般的なメタデータ(名前、サイズ、更新時刻など)を提供し、ファイルの種類に関する具体的な情報はFileModeに委譲するという、責務の分離が実現されます。

この変更は、Go言語のAPI設計における一貫性と、Unix系システムにおけるファイルモードの概念(ファイルタイプとパーミッションが単一のビットマスクで表現される)との整合性を高めることを目的としています。

前提知識の解説

  • os.FileInfo: Go言語のosパッケージで定義されているインターフェースで、ファイルに関するメタデータ(ファイル名、サイズ、更新日時、ファイルモードなど)を提供します。ファイルシステム上のエントリ(ファイルやディレクトリなど)の情報を抽象的に扱うために使用されます。
  • ファイルモード (File Mode): ファイルモードは、ファイルの種類(通常ファイル、ディレクトリ、シンボリックリンクなど)と、そのファイルに対するアクセス権限(読み取り、書き込み、実行)を組み合わせた情報です。Unix系システムでは、これらは通常、ビットマスクとして表現されます。
  • インターフェース (Interface): Go言語におけるインターフェースは、メソッドのシグネチャの集合を定義する型です。特定のインターフェースのすべてのメソッドを実装する型は、そのインターフェースを満たすと見なされます。これにより、具体的な実装に依存せずにコードを書くことができ、柔軟性と拡張性が向上します。
  • syscall.Stat_t: オペレーティングシステムのシステムコールを通じて取得される、ファイルの低レベルな統計情報(inode番号、デバイスID、モード、UID、GID、サイズ、タイムスタンプなど)を格納する構造体です。OSによって構造が異なります。

技術的詳細

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

  1. os.FileInfo インターフェースの変更:

    • 以前のos.FileInfoインターフェースからIsRegular()およびIsDirectory()メソッドが削除されました。
    • 代わりに、Mode() FileModeという新しいメソッドが追加されました。このメソッドは、ファイルのモード情報を含むos.FileMode型の値を返します。これにより、ファイルの種類に関する問い合わせはFileInfoオブジェクトから直接行うのではなく、FileInfo.Mode()を介して取得したFileModeオブジェクトに対して行うようになります。
  2. os.FileMode 型の導入と強化:

    • FileModeという新しい型が導入されました。これはuint32のエイリアスであり、ファイルモードのビットマスクを表現します。
    • FileMode型には、IsDir()IsRegular()Perm()などのメソッドが追加されました。これらのメソッドは、ビットマスクを解析してファイルの種類やパーミッションを判別します。
      • IsDir(): ファイルがディレクトリである場合にtrueを返します。
      • IsRegular(): ファイルが通常のファイルである場合にtrueを返します。
      • Perm(): ファイルのパーミッションビットのみを返します。
    • これにより、ファイルの種類やパーミッションに関するロジックがFileMode型にカプセル化され、よりクリーンなAPI設計が実現されました。
  3. os.FileStat 構造体の導入:

    • os.FileInfoインターフェースの具体的な実装として、os.FileStat構造体が導入されました。この構造体は、syscall.Stat_tなどのOS固有のファイル統計情報を内部に持ち、os.FileInfoインターフェースのメソッドを実装します。これにより、OS固有の実装詳細がosパッケージの外部に漏れることなく、抽象化されたFileInfoインターフェースを通じてファイル情報が提供されます。
  4. コードベース全体への波及:

    • os.FileInfoの変更に伴い、osパッケージだけでなく、godocgofixgofmtgoinstallgovetnet/httpio/ioutilなど、os.FileInfoを使用していたGo言語の標準ライブラリ内の多数のファイルが更新されました。
    • 具体的には、fi.IsDirectory()の呼び出しはfi.IsDir()fiFileMode型の場合)またはfi.Mode().IsDir()fiFileInfoインターフェースの場合)に置き換えられました。同様に、fi.IsRegular()!fi.IsDir()または!fi.Mode().IsDir()に置き換えられました。
    • ioutil.ReadDiros.Statos.Lstatなどの関数も、戻り値の型が*os.FileInfoからos.FileInfoインターフェースに変更されました。

この変更は、Go言語のファイルシステムAPIをより堅牢で、表現力豊かで、将来の拡張に対応しやすいものにするための重要なステップでした。

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

このコミットの核心的な変更は、src/pkg/os/types.goにおけるFileInfoインターフェースとFileMode型の定義、およびsrc/pkg/os/file_unix.goなどのプラットフォーム固有の実装におけるStatおよびLstat関数の戻り値の変更です。

src/pkg/os/types.go (変更の抜粋)

--- a/src/pkg/os/types.go
+++ b/src/pkg/os/types.go
@@ -10,20 +10,49 @@
 import (
 	"time"
 )
 
 // FileInfo is the interface that describes a file and is returned by Stat and Lstat.
 //
 // A FileInfo object may or may not be a pointer.
-type FileInfo interface {
-	Name() string       // base name of the file
-	Size() int64        // length in bytes
-	ModTime() time.Time // modification time
-	IsDirectory() bool  // is a directory
-	IsRegular() bool    // is a regular file
-	IsSymlink() bool    // is a symbolic link
-	Mode() uint32       // file mode bits
-	Uid() int           // owner uid
-	Gid() int           // owner gid
-	Dev() uint64        // device number
-	Ino() uint64        // inode number
-	Nlink() uint64      // number of hard links
-	Rdev() uint64       // device number for device special file
-	Blksize() int64     // block size for filesystem I/O
-	Blocks() int64      // number of blocks allocated for file
-	AccessTime() time.Time // last access time
-	ChangeTime() time.Time // last change time (Unix: inode change time)
-	FollowedSymlink() bool // was a symbolic link followed to get this FileInfo
-}
+type FileInfo interface {
+	Name() string       // base name of the file
+	Size() int64        // length in bytes
+	Mode() FileMode     // file mode bits
+	ModTime() time.Time // modification time
+	IsDir() bool        // abbreviation for Mode().IsDir()
+	Sys() interface{}   // underlying data source (can return nil)
+}
+
+// A FileMode represents a file's mode and permission bits.
+// The bits are a union of the standard Unix permission bits
+// (e.g. 0644 for a file, 0755 for a directory) and other
+// mode bits specifying the kind of file (e.g. ModeDir).
+// The values are a subset of the usual system-dependent
+// bits, to be portable across systems.
+type FileMode uint32
+
+// The defined file mode bits are the most significant bits of the
+// FileMode. Only the lower 9 bits are permission bits.
+const (
+	// The single character abbreviations are used by the String method.
+	ModeDir        FileMode = 1 << (32 - 1 - iota) // d: is a directory
+	ModeAppend                                     // a: append-only
+	ModeExclusive                                  // l: exclusive use
+	ModeTemporary                                  // T: temporary file (plan9 only)
+	ModeSymlink                                    // L: symbolic link
+	ModeDevice                                     // D: device file
+	ModeNamedPipe                                  // p: named pipe (FIFO)
+	ModeSocket                                     // S: Unix socket
+	ModeSetuid                                     // u: setuid
+	ModeSetgid                                     // g: setgid
+	ModeCharDevice                                 // c: character device
+	ModeSticky                                     // t: sticky
+	ModeIrregular FileMode = 0xFFF00000            // Mask for the type bits.
+)
+
+// IsDir reports whether m describes a directory.
+// That is, it tests for the ModeDir bit being set in m.
+func (m FileMode) IsDir() bool {
+	return m&ModeDir != 0
+}
+
+// IsRegular reports whether m describes a regular file.
+// That is, it tests that no mode type bits are set.
+func (m FileMode) IsRegular() bool {
+	return m&ModeIrregular == 0
+}
+
+// Perm returns the file permission bits in m.
+func (m FileMode) Perm() FileMode {
+	return m & 0777
+}
+
+// String returns a string representation of the mode.
+// For example, "-rw-r--r--" or "drwxr-xr-x",
+// followed by a string representing the other mode properties.
+func (m FileMode) String() string {
+	buf := make([]byte, 10)
+	copy(buf, "----------")
+
+	if m&ModeDir != 0 {
+		buf[0] = 'd'
+	}
+	if m&ModeSymlink != 0 {
+		buf[0] = 'L'
+	}
+	if m&ModeNamedPipe != 0 {
+		buf[0] = 'p'
+	}
+	if m&ModeSocket != 0 {
+		buf[0] = 'S'
+	}
+	if m&ModeDevice != 0 {
+		buf[0] = 'D'
+	}
+	if m&ModeCharDevice != 0 {
+		buf[0] = 'c'
+	}
+
+	if m&ModeSetuid != 0 {
+		buf[2] = 'u'
+	}
+	if m&ModeSetgid != 0 {
+		buf[5] = 'g'
+	}
+	if m&ModeSticky != 0 {
+		buf[8] = 't'
+	}
+
+	if m&0400 != 0 {
+		buf[1] = 'r'
+	}
+	if m&0200 != 0 {
+		buf[2] = 'w'
+	}
+	if m&0100 != 0 {
+		buf[3] = 'x'
+	}
+	if m&0040 != 0 {
+		buf[4] = 'r'
+	}
+	if m&0020 != 0 {
+		buf[5] = 'w'
+	}
+	if m&0010 != 0 {
+		buf[6] = 'x'
+	}
+	if m&0004 != 0 {
+		buf[7] = 'r'
+	}
+	if m&0002 != 0 {
+		buf[8] = 'w'
+	}
+	if m&0001 != 0 {
+		buf[9] = 'x'
+	}
+
+	return string(buf)
+}
+
+// FileStat is the concrete type that implements FileInfo.
+type FileStat struct {
+	name    string
+	size    int64
+	mode    FileMode
+	modTime time.Time
+	Sys     interface{} // underlying data source (can return nil)
+}
+
+func (fs *FileStat) Name() string       { return fs.name }
+func (fs *FileStat) Size() int64        { return fs.size }
+func (fs *FileStat) Mode() FileMode     { return fs.mode }
+func (fs *FileStat) ModTime() time.Time { return fs.modTime }
+func (fs *FileStat) IsDir() bool        { return fs.Mode().IsDir() }
+func (fs *FileStat) Sys() interface{}   { return fs.Sys }
+
+// SameFile reports whether fi1 and fi2 describe the same file.
+// For example, on Unix it reports whether the device and inode numbers are identical.
+func SameFile(fi1, fi2 FileInfo) bool {
+	fs1, ok1 := fi1.(*FileStat)
+	fs2, ok2 := fi2.(*FileStat)
+	if !ok1 || !ok2 {
+		return false
+	}
+	return sameFile(fs1, fs2)
+}
+
+// For testing.
+func basename(name string) string {
+	i := len(name) - 1
+	for i >= 0 && name[i] == '/' {
+		i--
+	}
+	name = name[:i+1]
+	i = len(name) - 1
+	for i >= 0 && name[i] != '/' {
+		i--
+	}
+	return name[i+1:]
+}

コアとなるコードの解説

上記の差分は、osパッケージのファイル情報に関する型システムを根本的に変更しています。

  1. FileInfo インターフェースの簡素化:

    • 以前のFileInfoインターフェースは、ファイル名、サイズ、更新時刻といった基本的な情報に加えて、IsDirectory()IsRegular()IsSymlink()といったファイルの種類を判別するメソッド、さらにはMode()uint32を返す)、Uid()Gid()などの詳細なシステム情報まで含んでいました。
    • 新しいFileInfoインターフェースは、Name()Size()ModTime()IsDir()Mode()Sys()という、より基本的なメソッドに絞り込まれています。特に、IsDirectory()IsRegular()といったメソッドは削除され、Mode() FileModeというメソッドが追加されました。これにより、ファイルの種類に関する問い合わせはMode()メソッドが返すFileMode型に委譲されることになります。IsDir()Mode().IsDir()のショートカットとして残されています。
    • Sys() interface{}は、OS固有の基盤データ(例: Unix系システムでのsyscall.Stat_t)へのアクセスを提供しますが、これは型アサーションを通じてのみ利用されるべきであり、ポータブルなコードでは直接依存すべきではありません。
  2. FileMode 型の導入と責務の分離:

    • FileModeuint32のエイリアスとして定義され、ファイルの種類とパーミッションビットをカプセル化します。
    • ModeDirModeAppendModeSymlinkなどの定数が定義され、ファイルの種類を示すビットフラグとして機能します。
    • IsDir()IsRegular()Perm()といったメソッドがFileMode型に追加されました。これにより、ファイルの種類やパーミッションに関するロジックがFileMode型自体に集約され、FileInfoインターフェースの責務が明確に分離されました。例えば、ファイルがディレクトリかどうかをチェックするには、fi.Mode().IsDir()と記述するようになります。
    • String()メソッドも追加され、FileModeの値をUnixのls -lコマンドのような形式(例: -rw-r--r--drwxr-xr-x)で表現できるようになりました。
  3. FileStat 構造体とSameFile関数の導入:

    • FileStatFileInfoインターフェースの具体的な実装を提供する構造体です。これは、ファイル名、サイズ、モード、更新時刻、そしてOS固有のシステムデータ(Sysフィールド)を保持します。
    • SameFile(fi1, fi2 FileInfo) bool関数は、2つのFileInfoが同じファイルを指しているかどうかを比較するためのヘルパー関数です。これは、内部的にFileStat型にダウンキャストし、OS固有のデバイス番号とinode番号を比較することで実現されます。

これらの変更により、Go言語のファイルシステムAPIは、よりモジュール化され、型安全性が向上し、ファイルの種類とパーミッションの扱いがより明確になりました。

関連リンク

参考にした情報源リンク