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

[インデックス 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.gostat_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.HeaderTypeflagModeなどのフィールドを適切に設定します。
  • シンボリックリンク (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.HeaderTypeflagModeを設定していました。特にシンボリックリンクの場合、os.Readlinkを呼び出してリンク先のパスを取得し、tar.Header.Linknameに設定していました。

このカスタム関数は、sysStatというグローバル変数を通じて、プラットフォーム固有のファイル統計情報(UID, GID, アクセス時刻、変更時刻など)をtar.Headerに設定するメカニズムも持っていました。このsysStatは、misc/dist/stat_darwin.gomisc/dist/stat_linux.goでそれぞれDarwinとLinux向けに実装されていました。

今回の変更では、以下の点が実現されました。

  1. tar.FileInfoHeaderの利用: bindist.go内のmakeTar関数で、ファイルのos.FileInfoからtar.Headerを生成する際に、カスタムのtarFileInfoHeaderの代わりにtar.FileInfoHeaderが直接呼び出されるようになりました。
  2. シンボリックリンクの処理の改善: tar.FileInfoHeaderはシンボリックリンクのターゲットパスを引数として受け取ります。このコミットでは、os.Readlink(path)を呼び出してシンボリックリンクのターゲットパスを取得し、その結果をtar.FileInfoHeaderに渡すように変更されました。これにより、#3299で報告されたシンボリックリンクの処理に関する問題が解決されます。
  3. プラットフォーム固有コードの削除: archive/tar.FileInfoHeaderが内部でプラットフォーム間の差異を吸収し、必要なファイル統計情報を適切に処理するようになったため、misc/dist/stat_darwin.gomisc/dist/stat_linux.goのファイルが不要となり、削除されました。これにより、コードベースが簡素化され、特定のOSに依存する部分が削減されました。
  4. コードの簡素化と保守性の向上: カスタムの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.gomisc/dist/stat_linux.goが完全に削除されました。これらのファイルは、それぞれDarwinとLinuxシステムコールを使用して、ファイル情報からUID、GID、アクセス時刻、変更時刻などのプラットフォーム固有の統計情報をtar.Headerに設定するためのsysStatフックを実装していました。標準ライブラリのarchive/tar.FileInfoHeaderがこれらの詳細を抽象化し、クロスプラットフォームで適切に処理するようになったため、これらのプラットフォーム固有のファイルは不要になりました。

この変更は、コードの重複を排除し、標準ライブラリの堅牢な実装を利用することで、misc/distツールの信頼性と保守性を向上させています。

関連リンク

参考にした情報源リンク

  • 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など)