[インデックス 13168] ファイルの概要
このコミットは、Go言語の標準ライブラリであるarchive/tar
パッケージのFileInfoHeader
関数を使用するように変更することで、misc/dist
ディレクトリ内のバイナリ配布ツール(bindist.go
)のコードを簡素化し、プラットフォーム固有のファイル統計処理を削除することを目的としています。これにより、コードの重複が解消され、保守性が向上します。
コミット
commit ca6b4d535f56dbfd0f0334bc7747bde20914d7b2
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu May 24 17:32:25 2012 -0700
misc/dist: use archive/tar.FileInfoHeader
Fixes #3299
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6250056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ca6b4d535f56dbfd0f0334bc7747bde20914d7b2
元コミット内容
このコミットの元の内容は、misc/dist
ディレクトリにあるバイナリ配布ツールが、archive/tar
パッケージのFileInfoHeader
関数を使用するように変更されたことを示しています。これにより、#3299
で報告された問題が修正されます。
変更の背景
この変更の背景には、Go言語の標準ライブラリが成熟し、より汎用的な機能を提供するようになったことがあります。以前は、misc/dist/bindist.go
内でos.FileInfo
からtar.Header
を生成するためのカスタム関数tarFileInfoHeader
が実装されていました。このカスタム関数は、ファイルの種類(通常ファイル、ディレクトリ、シンボリックリンクなど)に応じてtar.Header
のフィールドを設定し、さらにDarwin(macOS)とLinux向けのプラットフォーム固有のファイル統計情報(UID, GID, アクセス時刻、変更時刻など)を処理するためのsysStat
というフックを持っていました。
しかし、archive/tar
パッケージにFileInfoHeader
という関数が追加されたことで、このカスタム実装が不要になりました。標準ライブラリの関数を使用することで、コードの重複を避け、より堅牢でテスト済みの実装を利用できるようになります。また、プラットフォーム固有のファイル統計処理も、標準ライブラリの内部で適切に処理されるため、stat_darwin.go
とstat_linux.go
といったファイルが不要になりました。
Fixes #3299
という記述から、この変更が特定のバグや問題の解決に関連していることがわかります。Go issue #3299は、archive/tar
パッケージのFileInfoHeader
関数が、シンボリックリンクのターゲットパスを正しく処理しないという問題でした。このコミットは、os.Readlink
を使用してシンボリックリンクのターゲットパスを取得し、それをtar.FileInfoHeader
に渡すことで、この問題を解決しています。
前提知識の解説
archive/tar
パッケージ: Go言語の標準ライブラリの一部で、TARアーカイブの読み書きをサポートします。TARファイルは、複数のファイルを1つのアーカイブにまとめるための一般的な形式です。os.FileInfo
インターフェース: ファイルに関する情報(ファイル名、サイズ、パーミッション、最終更新時刻など)を提供するGoのインターフェースです。os.Stat
関数などによって返されます。tar.Header
構造体:archive/tar
パッケージで定義されている構造体で、TARアーカイブ内の各ファイルまたはディレクトリのメタデータ(名前、サイズ、モード、タイムスタンプなど)を表します。tar.FileInfoHeader
関数:archive/tar
パッケージで提供される関数で、os.FileInfo
オブジェクトとシンボリックリンクのターゲットパス(シンボリックリンクの場合)を受け取り、対応するtar.Header
構造体を生成します。この関数は、ファイルの種類に応じてtar.Header
のTypeflag
やMode
などのフィールドを適切に設定します。- シンボリックリンク (Symbolic Link): 別のファイルやディレクトリへの参照(ポインタ)として機能する特殊なファイルです。シンボリックリンクをたどると、参照先のファイルやディレクトリにアクセスできます。
os.Readlink
関数: シンボリックリンクが指すターゲットパスを読み取るGoの関数です。misc/dist
ディレクトリ: Goプロジェクト内の雑多な配布関連ツールが置かれているディレクトリです。bindist.go
は、Goのバイナリ配布物を作成するためのツールです。
技術的詳細
このコミットの主要な技術的変更点は、misc/dist/bindist.go
内でカスタム実装されていたtarFileInfoHeader
関数を削除し、Go標準ライブラリのarchive/tar.FileInfoHeader
関数に置き換えたことです。
以前のtarFileInfoHeader
関数は、os.FileInfo
からtar.Header
を生成する際に、ファイルの種類(通常ファイル、ディレクトリ、シンボリックリンク、デバイスファイルなど)を判別し、それに応じてtar.Header
のTypeflag
やMode
を設定していました。特にシンボリックリンクの場合、os.Readlink
を呼び出してリンク先のパスを取得し、tar.Header.Linkname
に設定していました。
このカスタム関数は、sysStat
というグローバル変数を通じて、プラットフォーム固有のファイル統計情報(UID, GID, アクセス時刻、変更時刻など)をtar.Header
に設定するメカニズムも持っていました。このsysStat
は、misc/dist/stat_darwin.go
とmisc/dist/stat_linux.go
でそれぞれDarwinとLinux向けに実装されていました。
今回の変更では、以下の点が実現されました。
tar.FileInfoHeader
の利用:bindist.go
内のmakeTar
関数で、ファイルのos.FileInfo
からtar.Header
を生成する際に、カスタムのtarFileInfoHeader
の代わりにtar.FileInfoHeader
が直接呼び出されるようになりました。- シンボリックリンクの処理の改善:
tar.FileInfoHeader
はシンボリックリンクのターゲットパスを引数として受け取ります。このコミットでは、os.Readlink(path)
を呼び出してシンボリックリンクのターゲットパスを取得し、その結果をtar.FileInfoHeader
に渡すように変更されました。これにより、#3299
で報告されたシンボリックリンクの処理に関する問題が解決されます。 - プラットフォーム固有コードの削除:
archive/tar.FileInfoHeader
が内部でプラットフォーム間の差異を吸収し、必要なファイル統計情報を適切に処理するようになったため、misc/dist/stat_darwin.go
とmisc/dist/stat_linux.go
のファイルが不要となり、削除されました。これにより、コードベースが簡素化され、特定のOSに依存する部分が削減されました。 - コードの簡素化と保守性の向上: カスタムの
tarFileInfoHeader
関数とその関連定数、sysStat
フックがすべて削除されました。これにより、コードの行数が大幅に削減され、archive/tar
パッケージの標準機能に依存することで、将来的な保守が容易になります。
コアとなるコードの変更箇所
misc/dist/bindist.go
--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -581,7 +581,8 @@ func makeTar(targ, workdir string) error {
if *verbose {
log.Printf("adding to tar: %s", name)
}
- hdr, err := tarFileInfoHeader(fi, path)
+ target, _ := os.Readlink(path)
+ hdr, err := tar.FileInfoHeader(fi, target)
if err != nil {
return err
}
@@ -752,64 +753,3 @@ func lookPath(prog string) (absPath string, err error) {
}
return
}
-
-// sysStat, if non-nil, populates h from system-dependent fields of fi.
-var sysStat func(fi os.FileInfo, h *tar.Header) error
-
-// Mode constants from the tar spec.
-const (
- c_ISDIR = 040000
- c_ISFIFO = 010000
- c_ISREG = 0100000
- c_ISLNK = 0120000
- c_ISBLK = 060000
- c_ISCHR = 020000
- c_ISSOCK = 0140000
-)
-
-// tarFileInfoHeader creates a partially-populated Header from an os.FileInfo.
-// The filename parameter is used only in the case of symlinks, to call os.Readlink.
-// If fi is a symlink but filename is empty, an error is returned.
-func tarFileInfoHeader(fi os.FileInfo, filename string) (*tar.Header, error) {
- h := &tar.Header{
- Name: fi.Name(),
- ModTime: fi.ModTime(),
- Mode: int64(fi.Mode().Perm()), // or'd with c_IS* constants later
- }
- switch {
- case fi.Mode()&os.ModeType == 0:
- h.Mode |= c_ISREG
- h.Typeflag = tar.TypeReg
- h.Size = fi.Size()
- case fi.IsDir():
- h.Typeflag = tar.TypeDir
- h.Mode |= c_ISDIR
- case fi.Mode()&os.ModeSymlink != 0:
- h.Typeflag = tar.TypeSymlink
- h.Mode |= c_ISLNK
- if filename == "" {
- return h, fmt.Errorf("archive/tar: unable to populate Header.Linkname of symlinks")
- }
- targ, err := os.Readlink(filename)
- if err != nil {
- return h, err
- }
- h.Linkname = targ
- case fi.Mode()&os.ModeDevice != 0:
- if fi.Mode()&os.ModeCharDevice != 0 {
- h.Mode |= c_ISCHR
- h.Typeflag = tar.TypeChar
- } else {
- h.Mode |= c_ISBLK
- h.Typeflag = tar.TypeBlock
- }
- case fi.Mode()&os.ModeSocket != 0:
- h.Mode |= c_ISSOCK
- default:
- return nil, fmt.Errorf("archive/tar: unknown file mode %v", fi.Mode())
- }
- if sysStat != nil {
- return h, sysStat(fi, h)
- }
- return h, nil
-}
削除されたファイル
misc/dist/stat_darwin.go
misc/dist/stat_linux.go
コアとなるコードの解説
misc/dist/bindist.go
の変更は、makeTar
関数内のtar.Header
生成ロジックに集中しています。
変更前:
hdr, err := tarFileInfoHeader(fi, path)
変更後:
target, _ := os.Readlink(path)
hdr, err := tar.FileInfoHeader(fi, target)
この変更により、カスタムのtarFileInfoHeader
関数が削除され、代わりに標準ライブラリのtar.FileInfoHeader
が使用されるようになりました。tar.FileInfoHeader
は、os.FileInfo
だけでなく、シンボリックリンクの場合のターゲットパスも引数として受け取ります。そのため、os.Readlink(path)
を呼び出してシンボリックリンクのターゲットパスを取得し、それをtar.FileInfoHeader
に渡しています。エラーハンドリングは、os.Readlink
がエラーを返した場合でも、tar.FileInfoHeader
に空のtarget
を渡すことで対応しています(_
でエラーを無視しているため)。
また、bindist.go
からtarFileInfoHeader
関数、関連するsysStat
変数、およびc_IS*
定数がすべて削除されました。これは、これらの機能がarchive/tar.FileInfoHeader
によって内部的に処理されるようになったためです。
さらに、misc/dist/stat_darwin.go
とmisc/dist/stat_linux.go
が完全に削除されました。これらのファイルは、それぞれDarwinとLinuxシステムコールを使用して、ファイル情報からUID、GID、アクセス時刻、変更時刻などのプラットフォーム固有の統計情報をtar.Header
に設定するためのsysStat
フックを実装していました。標準ライブラリのarchive/tar.FileInfoHeader
がこれらの詳細を抽象化し、クロスプラットフォームで適切に処理するようになったため、これらのプラットフォーム固有のファイルは不要になりました。
この変更は、コードの重複を排除し、標準ライブラリの堅牢な実装を利用することで、misc/dist
ツールの信頼性と保守性を向上させています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/ca6b4d535f56dbfd0f0334bc7747bde20914d7b2
- Go Issue #3299: https://golang.org/issue/3299
- Gerrit Change-Id: https://golang.org/cl/6250056
参考にした情報源リンク
- Go issue #3299:
archive/tar: FileInfoHeader should handle symlinks
(https://golang.org/issue/3299) - Go
archive/tar
パッケージのドキュメント (Goのバージョンによって内容が異なる可能性がありますが、一般的な情報源として): https://pkg.go.dev/archive/tar - Go
os
パッケージのドキュメント: https://pkg.go.dev/os - Go
os.FileInfo
インターフェースのドキュメント: https://pkg.go.dev/os#FileInfo - Go
os.Readlink
関数のドキュメント: https://pkg.go.dev/os#Readlink - TARファイル形式に関する一般的な情報 (例: Wikipediaなど)