[インデックス 10709] ファイルの概要
このコミットは、Go言語の標準ライブラリ archive/zip
パッケージにおいて、ZIPアーカイブ内のファイルのパーミッション情報をGoの os.FileMode
型で適切に扱うように変更するものです。これにより、基盤となるOSのファイルモードへの暗黙的な依存関係が修正され、クロスプラットフォームでのZIPファイルの互換性と正確性が向上します。
コミット
- コミットハッシュ:
2cb1aa468108598d5d3fab1c6ff9b6ba1217bf80
- Author: Roger Peppe rogpeppe@gmail.com
- Date: Mon Dec 12 15:22:55 2011 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2cb1aa468108598d5d3fab1c6ff9b6ba1217bf80
元コミット内容
archive/zip: make zip understand os.FileMode.
Fixes implicit dependency on underlying os file modes.
R=rsc, r, n13m3y3r, gustavo, adg
CC=golang-dev
https://golang.org/cl/5440130
変更の背景
ZIPファイルフォーマットは、アーカイブ内の各ファイルに対して「外部ファイル属性 (External File Attributes)」というフィールドを持ち、これにはファイルパーミッションやその他のOS固有の属性が格納されます。以前の archive/zip
パッケージの実装では、この外部属性の解釈が、ZIPファイルが作成されたOS(特にUnix系システム)のファイルモード表現に暗黙的に依存していました。
具体的には、ZIPヘッダーの ExternalAttrs
フィールドからファイルモードを抽出する際に、単にビットシフトして uint32
型として扱っていました。これは、ZIPファイルがUnix系システムで作成された場合に、その ExternalAttrs
の上位16ビットにUnixのファイルモードが格納されるという慣習に基づいています。しかし、Windowsなどの他のOSで作成されたZIPファイルの場合、ExternalAttrs
の意味合いが異なり、MS-DOS属性が格納されることが一般的です。
この暗黙的な依存関係により、異なるOSで作成されたZIPファイルをGoの archive/zip
パッケージで読み込んだ際に、ファイルパーミッションが正しく解釈されない、あるいは設定できないという問題が発生していました。例えば、Windowsで作成されたZIPファイル内のファイルの読み取り専用属性がGoで認識されない、またはUnixで作成されたZIPファイル内の実行可能属性がWindowsで正しく扱われない、といった状況が考えられます。
このコミットは、この問題を解決するために、Goの os.FileMode
型を導入し、ZIPファイルの ExternalAttrs
を、作成元のOS(CreatorVersion
フィールドで識別される)に応じて適切に os.FileMode
に変換・設定するように変更します。これにより、archive/zip
パッケージがより堅牢になり、クロスプラットフォームでのZIPファイルの取り扱いが改善されます。
前提知識の解説
1. os.FileMode
(Go言語)
Go言語の os
パッケージには、ファイルやディレクトリのパーミッションと種類を表す FileMode
型が定義されています。これは uint32
のエイリアスですが、ビットフラグとして特定の意味を持ちます。
- パーミッションビット: 下位9ビット(
0o777
)は、Unix系のファイルパーミッション(所有者、グループ、その他の読み取り/書き込み/実行権限)を表します。 - モードビット: 上位ビットは、ファイルの種類(ディレクトリ、シンボリックリンク、デバイスファイルなど)や特殊なパーミッション(Setuid, Setgid, スティッキービット)を表します。
os.ModeDir
: ディレクトリos.ModeAppend
: 追記専用ファイルos.ModeExclusive
: 排他ロックファイルos.ModeTemporary
: 一時ファイルos.ModeSymlink
: シンボリックリンクos.ModeDevice
: デバイスファイルos.ModeNamedPipe
: 名前付きパイプos.ModeSocket
: ソケットos.ModeSetuid
: Setuidビットos.ModeSetgid
: Setgidビットos.ModeCharDevice
: 文字デバイスos.ModeSticky
: スティッキービットos.ModeIrregular
: 不規則なファイル
os.FileMode
は、GoプログラムがOSに依存せずにファイルパーミッションを抽象的に扱うための重要なメカニズムです。
2. ZIPファイルフォーマットとファイル属性
ZIPファイルフォーマットは、各エントリ(ファイルまたはディレクトリ)のメタデータを「ローカルファイルヘッダー」と「中央ディレクトリファイルヘッダー」に格納します。このメタデータの一部として、ファイル属性に関する情報が含まれます。
CreatorVersion
: このフィールドは、ZIPエントリを作成したアプリケーションやOSのバージョン情報を含みます。特に重要なのは、上位バイトが「作成元OS」を示すコードであることです。0
(FAT): MS-DOS, Windows (FAT/FAT32ファイルシステム)3
(Unix): Unix系OS11
(NTFS): Windows NT/XP/Vista/7 (NTFSファイルシステム)14
(VFAT): Windows (VFATファイルシステム)19
(MacOSX): macOS
ExternalAttrs
: このフィールドは、ファイルが作成されたOSに固有のファイル属性を格納します。- Unix系OSの場合:
ExternalAttrs
の上位16ビット(ExternalAttrs >> 16
)にUnixのファイルモード(st_mode
)が格納されることが一般的です。これにはファイルの種類(通常ファイル、ディレクトリなど)とパーミッションビットが含まれます。S_IFMT
(0xf000): ファイルタイプマスクS_IFDIR
(0x4000): ディレクトリS_IFREG
(0x8000): 通常ファイルS_ISUID
(0x800): SetuidビットS_ISGID
(0x400): Setgidビット
- MS-DOS/Windowsの場合:
ExternalAttrs
の下位バイトにMS-DOSのファイル属性が格納されます。msdosDir
(0x10): ディレクトリ属性msdosReadOnly
(0x01): 読み取り専用属性
- Unix系OSの場合:
このコミットは、CreatorVersion
を参照して ExternalAttrs
の解釈を切り替えることで、異なるOSで作成されたZIPファイルのファイルモードを正確に os.FileMode
に変換できるようにします。
技術的詳細
このコミットの主要な変更は、src/pkg/archive/zip/struct.go
ファイルに集中しています。
-
os
パッケージのインポート:import ("os")
が追加され、os.FileMode
型が利用可能になります。 -
CreatorVersion
定数の追加: ZIPファイルのCreatorVersion
フィールドで使われる、作成元OSを示す新しい定数が追加されました。creatorFAT = 0
creatorUnix = 3
(既存)creatorNTFS = 11
creatorVFAT = 14
creatorMacOSX = 19
これにより、ZIPエントリがどのOSで作成されたかをより正確に識別できるようになります。
-
Unix定数の定義: Unixのファイルモードに関連する定数(
s_IFMT
,s_IFDIR
,s_IFREG
,s_ISUID
,s_ISGID
)が定義されました。これらは、ZIPのExternalAttrs
からUnixモードを解釈する際に使用されます。 -
MS-DOS属性定数の定義: MS-DOSのファイル属性に関連する定数(
msdosDir
,msdosReadOnly
)が定義されました。これらは、MS-DOS/Windowsで作成されたZIPファイルのExternalAttrs
を解釈する際に使用されます。 -
FileHeader.Mode()
メソッドの変更:- 戻り値の型が
uint32
からos.FileMode
に変更されました。 h.CreatorVersion >> 8
の値(作成元OS)に基づいて、ExternalAttrs
の解釈を分岐するswitch
ステートメントが導入されました。creatorUnix
,creatorMacOSX
の場合:unixModeToFileMode(h.ExternalAttrs >> 16)
を呼び出してUnixモードをos.FileMode
に変換します。creatorNTFS
,creatorVFAT
,creatorFAT
の場合:msdosModeToFileMode(h.ExternalAttrs)
を呼び出してMS-DOS属性をos.FileMode
に変換します。
- ファイル名が
/
で終わる場合(ディレクトリを示す)は、os.ModeDir
フラグがmode
に追加されます。 - エラーを返す代わりに、常に
os.FileMode
を返すようになりました。
- 戻り値の型が
-
FileHeader.SetMode()
メソッドの変更:- 引数の型が
uint32
からos.FileMode
に変更されました。 h.CreatorVersion
の作成元OSをcreatorUnix
に設定します。これは、GoがZIPファイルを作成する際に、Unix形式のファイルモードをExternalAttrs
に書き込むことを意図しています。h.ExternalAttrs
には、fileModeToUnixMode(mode) << 16
を使ってos.FileMode
をUnix形式のモードに変換し、上位16ビットに設定します。- さらに、MS-DOS属性も設定されるようになりました。
os.ModeDir
が設定されていればmsdosDir
を、読み取り専用(実行権限がない)であればmsdosReadOnly
をExternalAttrs
に追加します。これは、オリジナルのZIPツールがUnixモードとMS-DOS属性の両方を設定する慣習に合わせたものです。
- 引数の型が
-
新しいヘルパー関数の追加:
msdosModeToFileMode(m uint32) os.FileMode
: MS-DOS属性(ExternalAttrs
の下位バイト)をos.FileMode
に変換します。ディレクトリ属性や読み取り専用属性を考慮します。fileModeToUnixMode(mode os.FileMode) uint32
:os.FileMode
をUnix形式のファイルモード(st_mode
)に変換します。ファイルの種類(ディレクトリ、通常ファイル)と特殊なパーミッション(Setuid, Setgid)を考慮します。unixModeToFileMode(m uint32) os.FileMode
: Unix形式のファイルモード(st_mode
)をos.FileMode
に変換します。ファイルの種類と特殊なパーミッションを考慮します。
これらの変更により、archive/zip
パッケージは、ZIPファイルの ExternalAttrs
フィールドを、作成元OSに応じて適切に解釈し、Goの os.FileMode
型との間で正確に変換できるようになりました。
テストファイル (reader_test.go
, writer_test.go
) も、ZipTestFile
および WriteTest
構造体の Mode
フィールドの型が uint32
から os.FileMode
に変更され、新しい os.FileMode
の値(例: 0644
, 0666
, os.ModeDir | 0777
, 0755 | os.ModeSetuid
)がテストケースに追加されています。また、readTestFile
関数内の mtime
の比較ロジックが改善され、ft.Mtime
が空文字列の場合は比較をスキップするようになりました。
コアとなるコードの変更箇所
src/pkg/archive/zip/struct.go
--- a/src/pkg/archive/zip/struct.go
+++ b/src/pkg/archive/zip/struct.go
@@ -12,7 +12,7 @@ This package does not support ZIP64 or disk spanning.\n package zip
import (
-\t"errors"\n+\t"os"\n \t"time"\n )
@@ -32,7 +32,11 @@ const (
dataDescriptorLen = 12
// Constants for the first byte in CreatorVersion
-\tcreatorUnix = 3\n+\tcreatorFAT = 0\n+\tcreatorUnix = 3\n+\tcreatorNTFS = 11\n+\tcreatorVFAT = 14\n+\tcreatorMacOSX = 19\n )
type FileHeader struct {
@@ -98,17 +102,85 @@ func (h *FileHeader) ModTime() time.Time {
return msDosTimeToTime(h.ModifiedDate, h.ModifiedTime)\n }\n \n+// traditional names for Unix constants\n+const (\n+\ts_IFMT = 0xf000\n+\ts_IFDIR = 0x4000\n+\ts_IFREG = 0x8000\n+\ts_ISUID = 0x800\n+\ts_ISGID = 0x400\n+\n+\tmsdosDir = 0x10\n+\tmsdosReadOnly = 0x01\n+)\n+\n // Mode returns the permission and mode bits for the FileHeader.\n // An error is returned in case the information is not available.\n-func (h *FileHeader) Mode() (mode uint32, err error) {\n-\tif h.CreatorVersion>>8 == creatorUnix {\n-\t\treturn h.ExternalAttrs >> 16, nil\n+func (h *FileHeader) Mode() (mode os.FileMode, err error) {\n+\tswitch h.CreatorVersion >> 8 {\n+\tcase creatorUnix, creatorMacOSX:\n+\t\tmode = unixModeToFileMode(h.ExternalAttrs >> 16)\n+\tcase creatorNTFS, creatorVFAT, creatorFAT:\n+\t\tmode = msdosModeToFileMode(h.ExternalAttrs)\n \t}\n-\treturn 0, errors.New(\"file mode not available\")\n+\tif len(h.Name) > 0 && h.Name[len(h.Name)-1] == \'/\' {\n+\t\tmode |= os.ModeDir\n+\t}\n+\treturn mode, nil\n }\n \n // SetMode changes the permission and mode bits for the FileHeader.\n-func (h *FileHeader) SetMode(mode uint32) {\n+func (h *FileHeader) SetMode(mode os.FileMode) {\n \th.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8\n-\th.ExternalAttrs = mode << 16\n+\th.ExternalAttrs = fileModeToUnixMode(mode) << 16\n+\n+\t// set MSDOS attributes too, as the original zip does.\n+\tif mode&os.ModeDir != 0 {\n+\t\th.ExternalAttrs |= msdosDir\n+\t}\n+\tif mode&0200 == 0 {\n+\t\th.ExternalAttrs |= msdosReadOnly\n+\t}\n+}\n+\n+func msdosModeToFileMode(m uint32) (mode os.FileMode) {\n+\tif m&msdosDir != 0 {\n+\t\tmode = os.ModeDir | 0777\n+\t} else {\n+\t\tmode = 0666\n+\t}\n+\tif m&msdosReadOnly != 0 {\n+\t\tmode &^= 0222\n+\t}\n+\treturn mode\n+}\n+\n+func fileModeToUnixMode(mode os.FileMode) uint32 {\n+\tvar m uint32\n+\tif mode&os.ModeDir != 0 {\n+\t\tm = s_IFDIR\n+\t} else {\n+\t\tm = s_IFREG\n+\t}\n+\tif mode&os.ModeSetuid != 0 {\n+\t\tm |= s_ISUID\n+\t}\n+\tif mode&os.ModeSetgid != 0 {\n+\t\tm |= s_ISGID\n+\t}\n+\treturn m | uint32(mode&0777)\n+}\n+\n+func unixModeToFileMode(m uint32) os.FileMode {\n+\tvar mode os.FileMode\n+\tif m&s_IFMT == s_IFDIR {\n+\t\tmode |= os.ModeDir\n+\t}\n+\tif m&s_ISGID != 0 {\n+\t\tmode |= os.ModeSetgid\n+\t}\n+\tif m&s_ISUID != 0 {\n+\t\tmode |= os.ModeSetuid\n+\t}\n+\treturn mode | os.FileMode(m&0777)\n }\n```
### `src/pkg/archive/zip/reader_test.go`
```diff
--- a/src/pkg/archive/zip/reader_test.go
+++ b/src/pkg/archive/zip/reader_test.go
@@ -25,7 +26,7 @@ type ZipTestFile struct {
Content []byte // if blank, will attempt to compare against File\n File string // name of file to compare to (relative to testdata/)\n Mtime string // modified time in format "mm-dd-yy hh:mm:ss"\n-\tMode uint32\n+\tMode os.FileMode\n }\n \n // Caution: The Mtime values found for the test files should correspond to\n@@ -47,13 +48,13 @@ var tests = []ZipTest{\n \t\t\t\tName: "test.txt",\n \t\t\t\tContent: []byte("This is a test text file.\\n"),\n \t\t\t\tMtime: "09-05-10 12:12:02",\n-\t\t\t\tMode: 0x81a4,\n+\t\t\t\tMode: 0644,\n \t\t\t},\n \t\t\t{\n \t\t\t\tName: "gophercolor16x16.png",\n \t\t\t\tFile: "gophercolor16x16.png",\n \t\t\t\tMtime: "09-05-10 15:52:58",\n-\t\t\t\tMode: 0x81a4,\n+\t\t\t\tMode: 0644,\n \t\t\t},\n \t\t},\n \t},\n@@ -64,6 +65,7 @@ var tests = []ZipTest{\n \t\t\t\tName: "r/r.zip",\n \t\t\t\tFile: "r.zip",\n \t\t\t\tMtime: "03-04-10 00:24:16",\n+\t\t\t\tMode: 0666,\n \t\t\t},\n \t\t},\n \t},\n@@ -76,9 +78,43 @@ var tests = []ZipTest{\n \t\t\t\tName: "filename",\n \t\t\t\tContent: []byte("This is a test textfile.\\n"),\n \t\t\t\tMtime: "02-02-11 13:06:20",\n+\t\t\t\tMode: 0666,\n \t\t\t},\n \t\t},\n \t},\n+\t{\n+\t\t// created in windows XP file manager.\n+\t\tName: "winxp.zip",\n+\t\tFile: crossPlatform,\n+\t},\n+\t{\n+\t\t// created by Zip 3.0 under Linux\n+\t\tName: "unix.zip",\n+\t\tFile: crossPlatform,\n+\t},\n+}\n+\n+var crossPlatform = []ZipTestFile{\n+\t{\n+\t\tName: "hello",\n+\t\tContent: []byte("world \\r\\n"),\n+\t\tMode: 0666,\n+\t},\n+\t{\n+\t\tName: "dir/bar",\n+\t\tContent: []byte("foo \\r\\n"),\n+\t\tMode: 0666,\n+\t},\n+\t{\n+\t\tName: "dir/empty/",\n+\t\tContent: []byte{},\n+\t\tMode: os.ModeDir | 0777,\n+\t},\n+\t{\n+\t\tName: "readonly",\n+\t\tContent: []byte("important \\r\\n"),\n+\t\tMode: 0444,\n+\t},\n }\n \n func TestReader(t *testing.T) {\n@@ -159,13 +195,15 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {\n \t\tt.Errorf("name=%q, want %q", f.Name, ft.Name)\n \t}\n \n-\tmtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)\n-\tif err != nil {\n-\t\tt.Error(err)\n-\t\treturn\n-\t}\n-\tif ft := f.ModTime(); !ft.Equal(mtime) {\n-\t\tt.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)\n+\tif ft.Mtime != "" {\n+\t\tmtime, err := time.Parse("01-02-06 15:04:05", ft.Mtime)\n+\t\tif err != nil {\n+\t\t\tt.Error(err)\n+\t\t\treturn\n+\t\t}\n+\t\tif ft := f.ModTime(); !ft.Equal(mtime) {\n+\t\t\tt.Errorf("%s: mtime=%s, want %s", f.Name, ft, mtime)\n+\t\t}\n \t}\n \n \ttestFileMode(t, f, ft.Mode)\n@@ -191,7 +229,7 @@ func readTestFile(t *testing.T, ft ZipTestFile, f *File) {\n \tr.Close()\n \n \tvar c []byte\n-\tif len(ft.Content) != 0 {\n+\tif ft.Content != nil {\n \t\tc = ft.Content\n \t} else if c, err = ioutil.ReadFile("testdata/" + ft.File); err != nil {\n \t\tt.Error(err)\n@@ -211,7 +249,7 @@ func readTestFile(t *testing.T, f *File, want uint32) {\n \t}\n }\n \n-func testFileMode(t *testing.T, f *File, want uint32) {\n+func testFileMode(t *testing.T, f *File, want os.FileMode) {\n \tmode, err := f.Mode()\n \tif want == 0 {\n \t\tif err == nil {\n@@ -220,7 +258,7 @@ func testFileMode(t *testing.T, f *File, want uint32) {\n \t} else if err != nil {\n \t\tt.Errorf("%s mode: %s", f.Name, err)\n \t} else if mode != want {\n-\t\tt.Errorf("%s mode: want 0x%x, got 0x%x", f.Name, want, mode)\n+\t\tt.Errorf("%s mode: want %v, got %v", f.Name, want, mode)\n \t}\n }\n```
### `src/pkg/archive/zip/writer_test.go`
```diff
--- a/src/pkg/archive/zip/writer_test.go
+++ b/src/pkg/archive/zip/writer_test.go
@@ -8,6 +8,7 @@ import (
"bytes"\n "io/ioutil"\n "math/rand"\n+\t"os"\n "testing"\n )\n \n@@ -17,7 +18,7 @@ type WriteTest struct {
Name string\n Data []byte\n Method uint16\n-\tMode uint32\n+\tMode os.FileMode\n }\n \n var writeTests = []WriteTest{\n@@ -25,12 +26,31 @@ var writeTests = []WriteTest{\n \t\tName: "foo",\n \t\tData: []byte("Rabbits, guinea pigs, gophers, marsupial rats, and quolls."),\n \t\tMethod: Store,\n+\t\tMode: 0666,\n \t},\n \t{\n \t\tName: "bar",\n \t\tData: nil, // large data set in the test\n \t\tMethod: Deflate,\n-\t\tMode: 0x81ed,\n+\t\tMode: 0644,\n+\t},\n+\t{\n+\t\tName: "setuid",\n+\t\tData: []byte("setuid file"),\n+\t\tMethod: Deflate,\n+\t\tMode: 0755 | os.ModeSetuid,\n+\t},\n+\t{\n+\t\tName: "setgid",\n+\t\tData: []byte("setgid file"),\n+\t\tMethod: Deflate,\n+\t\tMode: 0755 | os.ModeSetgid,\n+\t},\n+\t{\n+\t\tName: "setgid",\n+\t\tData: []byte("setgid file"),\n+\t\tMethod: Deflate,\n+\t\tMode: 0755 | os.ModeSetgid,\n \t},\n }\n```
## コアとなるコードの解説
### `FileHeader.Mode() (mode os.FileMode, err error)`
このメソッドは、ZIPエントリの `FileHeader` からファイルモードを抽出し、Goの `os.FileMode` 型として返します。
- **`switch h.CreatorVersion >> 8`**: ZIPエントリを作成したOSを識別します。`CreatorVersion` の上位バイトがOSコードを示します。
- **`case creatorUnix, creatorMacOSX:`**: 作成元がUnix系OSまたはmacOSの場合、`ExternalAttrs` の上位16ビットにUnixのファイルモードが格納されていると仮定し、`unixModeToFileMode` 関数を使って `os.FileMode` に変換します。
- **`case creatorNTFS, creatorVFAT, creatorFAT:`**: 作成元がWindows系OSの場合、`ExternalAttrs` の下位バイトにMS-DOS属性が格納されていると仮定し、`msdosModeToFileMode` 関数を使って `os.FileMode` に変換します。
- **`if len(h.Name) > 0 && h.Name[len(h.Name)-1] == '/'`**: ファイル名がスラッシュで終わる場合、それはディレクトリであることを示唆するため、`os.ModeDir` フラグを `mode` に追加します。これにより、ディレクトリが正しく識別されます。
### `FileHeader.SetMode(mode os.FileMode)`
このメソッドは、Goの `os.FileMode` をZIPエントリの `FileHeader` に設定します。
- **`h.CreatorVersion = h.CreatorVersion&0xff | creatorUnix<<8`**: ZIPエントリの作成元OSを `creatorUnix` に設定します。これは、GoがZIPファイルを作成する際に、Unix形式のファイルモードを `ExternalAttrs` に書き込むことを標準とするためです。
- **`h.ExternalAttrs = fileModeToUnixMode(mode) << 16`**: `os.FileMode` を `fileModeToUnixMode` 関数でUnix形式のファイルモードに変換し、それを `ExternalAttrs` の上位16ビットに設定します。
- **MS-DOS属性の設定**:
- **`if mode&os.ModeDir != 0 { h.ExternalAttrs |= msdosDir }`**: `os.FileMode` がディレクトリを示す場合、MS-DOSのディレクトリ属性 `msdosDir` を `ExternalAttrs` に追加します。
- **`if mode&0200 == 0 { h.ExternalAttrs |= msdosReadOnly }`**: `os.FileMode` が書き込み権限を持たない場合(`0200` は所有者の書き込み権限)、MS-DOSの読み取り専用属性 `msdosReadOnly` を `ExternalAttrs` に追加します。これは、オリジナルのZIPツールがUnixモードとMS-DOS属性の両方を設定する慣習に合わせたもので、クロスプラットフォームでの互換性を高めます。
### `msdosModeToFileMode(m uint32) (mode os.FileMode)`
この関数は、MS-DOS属性(`ExternalAttrs` の下位バイト)を `os.FileMode` に変換します。
- **`if m&msdosDir != 0 { mode = os.ModeDir | 0777 } else { mode = 0666 }`**: MS-DOSのディレクトリ属性が設定されていれば、`os.ModeDir` とデフォルトのパーミッション `0777` を設定します。そうでなければ、デフォルトのファイルパーミッション `0666` を設定します。
- **`if m&msdosReadOnly != 0 { mode &^= 0222 }`**: MS-DOSの読み取り専用属性が設定されていれば、書き込み権限(`0222`)をクリアします。
### `fileModeToUnixMode(mode os.FileMode) uint32`
この関数は、`os.FileMode` をUnix形式のファイルモード(`st_mode`)に変換します。
- **`if mode&os.ModeDir != 0 { m = s_IFDIR } else { m = s_IFREG }`**: `os.FileMode` がディレクトリであれば `s_IFDIR` を、そうでなければ `s_IFREG`(通常ファイル)を設定します。
- **`if mode&os.ModeSetuid != 0 { m |= s_ISUID }`**: Setuidビットが設定されていれば `s_ISUID` を追加します。
- **`if mode&os.ModeSetgid != 0 { m |= s_ISGID }`**: Setgidビットが設定されていれば `s_ISGID` を追加します。
- **`return m | uint32(mode&0777)`**: ファイルの種類と特殊なパーミッションに、`os.FileMode` の下位9ビット(通常のパーミッション)を結合して返します。
### `unixModeToFileMode(m uint32) os.FileMode`
この関数は、Unix形式のファイルモード(`st_mode`)を `os.FileMode` に変換します。
- **`if m&s_IFMT == s_IFDIR { mode |= os.ModeDir }`**: ファイルタイプマスク `s_IFMT` を使って、モードがディレクトリであれば `os.ModeDir` を設定します。
- **`if m&s_ISGID != 0 { mode |= os.ModeSetgid }`**: Setgidビットが設定されていれば `os.ModeSetgid` を設定します。
- **`if m&s_ISUID != 0 { mode |= os.ModeSetuid }`**: Setuidビットが設定されていれば `os.ModeSetuid` を設定します。
- **`return mode | os.FileMode(m&0777)`**: ファイルの種類と特殊なパーミッションに、Unixモードの下位9ビット(通常のパーミッション)を結合して返します。
これらの変更により、`archive/zip` パッケージは、ZIPファイルのファイルモードをより正確かつクロスプラットフォーム互換性のある方法で扱うことができるようになりました。
## 関連リンク
- [https://golang.org/cl/5440130](https://golang.org/cl/5440130)
## 参考にした情報源リンク
- Go言語 `os` パッケージのドキュメント: `os.FileMode`
- ZIPファイルフォーマットの仕様 (例: PKWARE's APPNOTE.TXT)
- Unixファイルパーミッションに関する一般的な知識