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

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

このコミットは、Go言語の初期のosパッケージにおいて、ファイルやディレクトリのモードビット(パーミッションやファイルタイプ)にアクセスするためのシンプルなアクセサメソッドを追加するものです。具体的には、Dir構造体にファイルタイプを判定するIs*系のメソッドと、パーミッションを取得するPermissionメソッドが追加されています。

コミット

commit cad7a3aefcdfed3176a64a16b2a6151b2fabfd4e
Author: Rob Pike <r@golang.org>
Date:   Mon Feb 9 12:50:54 2009 -0800

    simple accessors for Dir mode bits
    
    R=rsc
    DELTA=71  (71 added, 0 deleted, 0 changed)
    OCL=24687
    CL=24694
---
 src/lib/os/os_types.go                | 34 ++++++++++++++++++++++++++++++++++
 src/lib/syscall/types_amd64_darwin.go | 19 +++++++++++++++++++
 src/lib/syscall/types_amd64_linux.go  | 18 ++++++++++++++++++
 3 files changed, 71 insertions(+)

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

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

元コミット内容

simple accessors for Dir mode bits

変更の背景

このコミットが行われた2009年2月は、Go言語がまだ一般に公開される前の非常に初期の段階でした。Go言語の設計目標の一つに、システムプログラミングにおけるC言語のような低レベルアクセスと、より高レベルな抽象化のバランスを取ることがありました。ファイルシステムへのアクセスは、システムプログラミングにおいて非常に基本的な操作であり、そのファイルの種類やパーミッションを効率的かつ安全に取得する手段は不可欠です。

当時のGo言語のosパッケージ(当時はsrc/lib/osに位置)には、ファイルやディレクトリのメタデータを含むDir構造体が存在していましたが、そのModeフィールドから具体的なファイルタイプ(ディレクトリ、通常ファイル、シンボリックリンクなど)やパーミッション情報を直接的に抽出するための便利なメソッドが提供されていませんでした。ユーザーがこれらの情報を得るためには、Modeフィールドのビットマスクを手動で操作する必要があり、これは冗長でエラーを起こしやすいものでした。

このコミットは、このような低レベルなビット操作を抽象化し、より直感的でGoらしい(Go idiomaticな)方法でファイルモード情報にアクセスできるようにすることを目的としています。これにより、ファイルシステム関連のコードの可読性と保守性が向上し、開発者がより簡単にファイルタイプに応じた処理を記述できるようになります。

前提知識の解説

このコミットを理解するためには、以下の前提知識が必要です。

  1. Unix/Linuxのファイルパーミッションとファイルタイプ:

    • Unix系OSでは、ファイルやディレクトリには「モード」と呼ばれる属性が関連付けられています。このモードは、ファイルのタイプ(通常ファイル、ディレクトリ、シンボリックリンク、デバイスファイルなど)と、そのファイルに対するユーザー、グループ、その他のアクセス権限(読み取り、書き込み、実行)をビット列で表現したものです。
    • ファイルタイプは通常、モードの最上位ビット(S_IFMTマスクで抽出される部分)で表現されます。例えば、S_IFDIRはディレクトリ、S_IFREGは通常ファイルを示します。
    • パーミッションは、通常、モードの下位9ビットで表現され、所有者、グループ、その他のユーザーに対する読み取り(r)、書き込み(w)、実行(x)の権限を示します。これらは8進数表記でrwx7rw-6のように表現されます(例: 0755)。
  2. ビット演算:

    • ファイルモードはビット列であるため、特定の情報を抽出するにはビット演算が用いられます。
    • AND演算子 (&): 特定のビットがセットされているかを確認したり、特定のビット群を抽出したりするために使用されます。例えば、mode & S_IFMTはファイルタイプを示すビット群を抽出します。
    • 比較演算子 (==): 抽出したビット群が特定のファイルタイプ定数と一致するかどうかを判定します。
  3. Go言語の構造体とメソッド:

    • Go言語では、構造体(struct)は関連するデータの集合を定義します。
    • メソッドは、特定の型(この場合はDir構造体)に関連付けられた関数です。メソッドはレシーバ(この場合はdir *Dir)を通じて構造体のフィールドにアクセスできます。
  4. syscallパッケージ:

    • Go言語のsyscallパッケージは、オペレーティングシステムが提供する低レベルなシステムコールへのインターフェースを提供します。ファイルモードの定数(S_IFMT, S_IFDIRなど)は、OS固有の定義に基づいてこのパッケージ内で提供されます。このコミットでは、これらの定数を利用してファイルタイプを判定しています。

技術的詳細

このコミットの主要な変更点は、os.Dir構造体に以下のアクセサメソッドを追加したことです。

  • IsFifo() bool
  • IsChar() bool
  • IsDirectory() bool
  • IsBlock() bool
  • IsRegular() bool
  • IsSymlink() bool
  • IsSocket() bool
  • Permission() int

これらのメソッドは、Dir構造体のModeフィールド(ファイルモードを表す整数値)に対してビット演算を行い、ファイルの種類を判定したり、パーミッション部分を抽出したりします。

ファイルタイプ判定メソッド (Is*系): これらのメソッドは、dir.Modesyscall.S_IFMT(ファイルタイプを示すビットマスク)のビットAND演算を行い、その結果を特定のファイルタイプ定数(例: syscall.S_IFDIR)と比較することで、ファイルがそのタイプであるかどうかを真偽値で返します。 例: (dir.Mode & syscall.S_IFMT) == syscall.S_IFDIR

syscall.S_IFMTは、ファイルモードのビット列のうち、ファイルタイプを示す部分(通常は最上位の数ビット)を抽出するためのマスクです。例えば、Unix系システムでは通常0170000(8進数)のような値になります。 syscall.S_IFIFO, syscall.S_IFCHR, syscall.S_IFDIR, syscall.S_IFBLK, syscall.S_IFREG, syscall.S_IFLNK, syscall.S_IFSOCKはそれぞれ、FIFO(名前付きパイプ)、キャラクタースペシャルファイル、ディレクトリ、ブロックスペシャルファイル、通常ファイル、シンボリックリンク、ソケットに対応するファイルタイプ定数です。

パーミッション取得メソッド (Permission): Permission()メソッドは、dir.Mode0777(8進数)のビットAND演算を行うことで、ファイルモードからパーミッション部分のみを抽出します。0777は、所有者、グループ、その他のユーザーに対する読み取り、書き込み、実行の全パーミッションビットがセットされたマスクです。これにより、ファイルタイプ情報を含まない純粋なパーミッション値(例: 0644)が得られます。

また、このコミットでは、src/lib/syscall/types_amd64_darwin.gosrc/lib/syscall/types_amd64_linux.goに、これらのファイルタイプ定数とパーミッション関連の定数(S_ISUID, S_ISGID, S_ISVTX, S_IRUSR, S_IWUSR, S_IXUSRなど)が追加されています。これらはOS固有の定義に基づいており、Goのsyscallパッケージが各OSのシステムコールと連携するために必要です。

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

src/lib/os/os_types.go

 // An operating-system independent representation of Unix data structures.
 // OS-specific routines in this directory convert the OS-local versions to these.
 
@@ -24,3 +26,35 @@ type Dir struct {
 	Ctime_ns	uint64;	// nanoseconds since 1970
 	Name	string;
 }
+
+func (dir *Dir) IsFifo() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFIFO
+}
+
+func (dir *Dir) IsChar() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFCHR
+}
+
+func (dir *Dir) IsDirectory() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFDIR
+}
+
+func (dir *Dir) IsBlock() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFBLK
+}
+
+func (dir *Dir) IsRegular() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFREG
+}
+
+func (dir *Dir) IsSymlink() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFLNK
+}
+
+func (dir *Dir) IsSocket() bool {
+	return (dir.Mode & syscall.S_IFMT) == syscall.S_IFSOCK
+}
+
+func (dir *Dir) Permission() int {
+	return int(dir.Mode & 0777)
+}

src/lib/syscall/types_amd64_darwin.go

@@ -69,6 +69,25 @@ const (
 	NAME_MAX = 255;
 )
 
+// Dir.Mode bits
+const (
+	S_IFMT = 0170000;	      /* type of file */
+		S_IFIFO  = 0010000;  /* named pipe (fifo) */
+		S_IFCHR  = 0020000;  /* character special */
+		S_IFDIR  = 0040000;  /* directory */
+		S_IFBLK  = 0060000;  /* block special */
+		S_IFREG  = 0100000;  /* regular */
+		S_IFLNK  = 0120000;  /* symbolic link */
+		S_IFSOCK = 0140000;  /* socket */
+		S_IFWHT  = 0160000;  /* whiteout */
+	S_ISUID = 0004000;  /* set user id on execution */
+	S_ISGID = 0002000;  /* set group id on execution */
+	S_ISVTX = 0001000;  /* save swapped text even after use */
+	S_IRUSR = 0000400;  /* read permission, owner */
+	S_IWUSR = 0000200;  /* write permission, owner */
+	S_IXUSR = 0000100;  /* execute/search permission, owner */
+)
+
 type Stat_t struct {
 	Dev	uint32;
 	Mode	uint16;

src/lib/syscall/types_amd64_linux.go

@@ -69,6 +69,24 @@ const (
 	NAME_MAX = 255;
 )
 
+// Dir.Mode bits
+const (
+	S_IFMT = 0170000;	      /* type of file */
+		S_IFIFO  = 0010000;  /* named pipe (fifo) */
+		S_IFCHR  = 0020000;  /* character special */
+		S_IFDIR  = 0040000;  /* directory */
+		S_IFBLK  = 0060000;  /* block special */
+		S_IFREG  = 0100000;  /* regular */
+		S_IFLNK  = 0120000;  /* symbolic link */
+		S_IFSOCK = 0140000;  /* socket */
+	S_ISUID = 0004000;  /* set user id on execution */
+	S_ISGID = 0002000;  /* set group id on execution */
+	S_ISVTX = 0001000;  /* save swapped text even after use */
+	S_IRUSR = 0000400;  /* read permission, owner */
+	S_IWUSR = 0000200;  /* write permission, owner */
+	S_IXUSR = 0000100;  /* execute/search permission, owner */
+)
+
 type Stat_t struct {
 	Dev	uint64;
 	Ino	uint64;

コアとなるコードの解説

このコミットの核心は、os.Dir構造体に対する新しいメソッドの追加と、それらのメソッドが依存するsyscallパッケージ内の定数の定義です。

os_types.goでは、Dir構造体に以下のメソッドが追加されています。

  • IsFifo(), IsChar(), IsDirectory(), IsBlock(), IsRegular(), IsSymlink(), IsSocket(): これらのメソッドは、Dir構造体のModeフィールド(ファイルモードを表す整数値)からファイルタイプを判定します。 dir.Mode & syscall.S_IFMTという式は、Modeフィールドからファイルタイプを示すビット群を抽出します。syscall.S_IFMTは、ファイルモードのビット列のうち、ファイルタイプを示す部分(通常は最上位の数ビット)を抽出するためのマスクです。 抽出されたファイルタイプビットが、それぞれのファイルタイプに対応するsyscall定数(例: syscall.S_IFDIR)と等しいかどうかを比較することで、ファイルがそのタイプであるかを判定し、bool値を返します。 これにより、例えばファイルがディレクトリであるかを判定するために、if (dir.Mode & 0170000) == 0040000のような低レベルなビット操作を直接書く必要がなくなり、if dir.IsDirectory()のように、より読みやすく、意図が明確なコードを書けるようになります。

  • Permission() int: このメソッドは、dir.Mode & 0777というビットAND演算によって、ファイルモードからパーミッション部分のみを抽出します。0777(8進数)は、所有者、グループ、その他のユーザーに対する読み取り、書き込み、実行の全パーミッションビットがセットされたマスクです。これにより、ファイルタイプ情報を含まない純粋なパーミッション値(例: 0644)が整数として返されます。

types_amd64_darwin.gotypes_amd64_linux.goでは、syscallパッケージ内にファイルモードに関連する定数が定義されています。これらは、各OSのシステムヘッダファイルで定義されているファイルモードビットに対応するGoの定数です。

  • S_IFMT: ファイルタイプを抽出するためのマスク。
  • S_IFIFO, S_IFCHR, S_IFDIR, S_IFBLK, S_IFREG, S_IFLNK, S_IFSOCK: それぞれのファイルタイプを示す定数。
  • S_ISUID, S_ISGID, S_ISVTX: 特殊パーミッションビット(SetUID, SetGID, スティッキービット)を示す定数。
  • S_IRUSR, S_IWUSR, S_IXUSR: 所有者の読み取り、書き込み、実行パーミッションを示す定数。

これらの定数は、os.Dirのアクセサメソッドがファイルモードを正しく解釈するために不可欠です。OSごとにこれらの定数の値が異なる場合があるため、amd64_darwin.goamd64_linux.goのようにOS固有のファイルで定義されています。

このコミットは、Go言語の標準ライブラリが、低レベルなシステム情報を抽象化し、開発者にとって使いやすいAPIを提供するという設計哲学の一例を示しています。

関連リンク

参考にした情報源リンク

  • Go言語のGitHubリポジトリ: https://github.com/golang/go
  • Unix系OSのファイルモードに関する一般的な知識 (例: statシステムコール、sys/stat.hヘッダファイルの内容)
  • Go言語の初期の設計に関する議論やドキュメント (公開前の情報のため、特定のURLは提示できませんが、Go言語の歴史的背景を理解する上で参照しました)
  • コミットメッセージと変更されたコードの内容