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

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

このコミットは、Go言語の標準ライブラリ archive/tar パッケージにおける、TARアーカイブのヘッダから数値フィールドを読み取る際のバグ修正に関するものです。具体的には、TARヘッダ内の数値フィールドが8進数形式ではなくバイナリ形式でエンコードされている場合に、正しくデコードできない問題を解決します。これにより、大きなUIDやGIDなど、8進数表現では収まらない数値を持つTARアーカイブをGoで正しく読み込めるようになります。

コミット

commit 86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4
Author: David Symonds <dsymonds@golang.org>
Date:   Fri Nov 9 08:50:10 2012 +1100

    archive/tar: accept binary format when reading numeric header fields.
    
    Fixes #4358.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6840043

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

https://github.com/golang/go/commit/86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4

元コミット内容

archive/tar: accept binary format when reading numeric header fields.

Fixes #4358.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6840043

変更の背景

この変更は、Goの archive/tar パッケージが、一部のTARアーカイブ(特にGNU tarによって作成されたもの)を正しく読み込めないという問題(Issue #4358)に対応するために行われました。

TARアーカイブのヘッダには、ファイルのパーミッション、UID(ユーザーID)、GID(グループID)、ファイルサイズなどの数値情報が格納されています。TARの標準仕様では、これらの数値はASCII形式の8進数文字列として表現されます。しかし、UIDやGID、ファイルサイズが非常に大きくなり、標準の8進数フィールド(通常は8バイト)に収まらなくなる場合があります。

このような大きな数値を扱うために、GNU tarなどの一部のTAR実装では、TARヘッダの数値フィールドに「バイナリ形式」または「スター形式 (star format)」と呼ばれる拡張が導入されました。この拡張では、数値フィールドの最初のバイトの最上位ビット(MSB)がセットされている場合(つまり、バイト値が0x80以上の場合)、そのフィールドは8進数ではなくバイナリデータとして解釈されます。これにより、より大きな数値を表現できるようになります。

Goの archive/tar パッケージは、このバイナリ形式の読み取りに対応していなかったため、バイナリ形式でエンコードされた大きな数値を持つTARアーカイブを読み込もうとすると、数値が正しくパースされず、エラーが発生したり、誤った値が読み込まれたりする問題がありました。このコミットは、この互換性の問題を解決し、Goがより広範なTARアーカイブを扱えるようにすることを目的としています。

前提知識の解説

TARアーカイブの基本

TAR (Tape ARchive) は、複数のファイルを1つのアーカイブファイルにまとめるためのファイル形式です。主にUnix系システムでファイルのバックアップや配布に利用されます。TARアーカイブは、連続する「ヘッダブロック」と「データブロック」のペアで構成されます。

  • ヘッダブロック: 各ファイルやディレクトリのメタデータ(ファイル名、サイズ、パーミッション、所有者、タイムスタンプなど)を格納します。
  • データブロック: 実際のファイルの内容を格納します。

TARヘッダの数値フィールドと8進数表現

TARヘッダ内の数値フィールド(例: mode (パーミッション), uid, gid, size, mtime (最終更新時刻))は、伝統的にASCII文字で表現された8進数文字列として格納されます。例えば、パーミッション 0o755 は文字列 "0000755\0" のように格納されます。これらのフィールドは固定長であり、通常は8バイトです。

GNU tar拡張とバイナリ形式

標準のTARヘッダのフィールド長は限られているため、非常に大きなUID/GIDやファイルサイズを表現できません。この問題を解決するため、GNU tarなどの実装ではいくつかの拡張が導入されました。その一つが、数値フィールドの「バイナリ形式」です。

バイナリ形式では、数値フィールドの最初のバイトの最上位ビット (MSB, Most Significant Bit) が 1 に設定されている場合、そのフィールドは8進数文字列ではなく、ビッグエンディアンのバイナリ整数として解釈されます。このフラグビットを除いた残りのビットと後続のバイトが数値の実際の値を示します。これにより、8バイトのフィールドで最大 2^(8*8-1) - 1 (約 9.22 x 10^18) までの数値を表現できるようになり、標準の8進数表現(最大 8^7 - 1 = 2,097,151)よりもはるかに大きな値を扱えるようになります。

このバイナリ形式は、POSIX.1-2001 (ustar) や POSIX.1-2008 (pax) などの標準では直接定義されていませんが、広く使われているGNU tarの拡張として事実上の標準となっています。

技術的詳細

このコミットの核心は、archive/tar パッケージの octal 関数が、TARヘッダから数値を読み取る際に、従来の8進数形式だけでなく、GNU tar拡張のバイナリ形式も認識してデコードできるように修正された点です。

元の octal 関数は、入力バイトスライス b を常に8進数文字列として解釈し、strconv.ParseInt を使用して変換していました。しかし、バイナリ形式のフィールドは8進数文字列ではないため、この方法では正しくパースできませんでした。

修正後の octal 関数は、まず入力バイトスライス b の最初のバイトをチェックします。

  1. バイナリ形式の検出: if len(b) > 0 && b[0]&0x80 != 0

    • len(b) > 0: バイトスライスが空でないことを確認します。
    • b[0]&0x80 != 0: 最初のバイトの最上位ビット(MSB)がセットされているかどうかをチェックします。MSBがセットされている場合、それはバイナリ形式であることを示します。
  2. バイナリ形式のデコード:

    • この条件が真の場合、関数はバイナリ形式のデコードロジックに入ります。
    • var x int64: デコードされた数値を格納するための int64 変数を初期化します。
    • for i, c := range b: バイトスライス b の各バイトをループで処理します。
    • if i == 0 { c &= 0x7f }: 最初のバイトの場合、MSB(バイナリ形式を示すフラグビット)をクリアします。これにより、実際の数値データのみが残ります。
    • x = x<<8 | int64(c): 各バイトを x に左シフト (x<<8) して追加します。これは、ビッグエンディアンのバイナリデータを整数に変換する標準的な方法です。
  3. 従来の8進数形式のデコード:

    • もし最初のバイトのMSBがセットされていなかった場合(つまり、if 条件が偽の場合)、関数は従来の8進数形式のデコードロジックに進みます。
    • // Removing leading spaces.: 先頭のスペースを削除します。一部のTAR実装では、数値フィールドに先頭スペースが含まれることがあります。
    • // Removing trailing NULs and spaces.: 末尾のNULL文字やスペースを削除します。
    • strconv.ParseInt(string(b), 8, 64): 残ったバイトスライスを8進数文字列として int64 に変換します。

この変更により、archive/tar パッケージは、標準の8進数形式とGNU tar拡張のバイナリ形式の両方でエンコードされた数値フィールドを透過的に処理できるようになり、より堅牢なTARアーカイブリーダーとなりました。

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

src/pkg/archive/tar/reader.go

octal 関数にバイナリ形式の読み取りロジックが追加されました。

--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -72,6 +72,18 @@ func cString(b []byte) string {
 }
 
 func (tr *Reader) octal(b []byte) int64 {
+	// Check for binary format first.
+	if len(b) > 0 && b[0]&0x80 != 0 {
+		var x int64
+		for i, c := range b {
+			if i == 0 {
+				c &= 0x7f // ignore signal bit in first byte
+			}
+			x = x<<8 | int64(c)
+		}
+		return x
+	}
+
 	// Removing leading spaces.
 	for len(b) > 0 && b[0] == ' ' {
 		b = b[1:]

src/pkg/archive/tar/tar_test.go

新しいテストケースが TestRoundTrip 関数に追加されました。これは、8進数表現では大きすぎるUID(1 << 21)を持つヘッダを作成し、それが正しくラウンドトリップ(書き込みと読み込み)できることを確認します。

--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -65,6 +65,7 @@ func TestRoundTrip(t *testing.T) {
 	tw := NewWriter(&b)
 	hdr := &Header{
 		Name:    "file.txt",
+		Uid:     1 << 21, // too big for 8 octal digits
 		Size:    int64(len(data)),
 		ModTime: time.Now(),
 	}

コアとなるコードの解説

src/pkg/archive/tar/reader.gooctal 関数

func (tr *Reader) octal(b []byte) int64 {
	// Check for binary format first.
	if len(b) > 0 && b[0]&0x80 != 0 {
		var x int64
		for i, c := range b {
			if i == 0 {
				c &= 0x7f // ignore signal bit in first byte
			}
			x = x<<8 | int64(c)
		}
		return x
	}

	// Removing leading spaces.
	for len(b) > 0 && b[0] == ' ' {
		b = b[1:]
	}
	// Removing trailing NULs and spaces.
	for len(b) > 0 && (b[len(b)-1] == '\x00' || b[len(b)-1] == ' ') {
		b = b[:len(b)-1]
	}
	if len(b) == 0 {
		return 0
	}
	n, err := strconv.ParseInt(string(b), 8, 64)
	if err != nil {
		tr.err = err
	}
	return n
}

この関数は、TARヘッダから読み取られたバイトスライス bint64 に変換する役割を担います。

  1. バイナリ形式のチェック: if len(b) > 0 && b[0]&0x80 != 0 この行が追加された最も重要な部分です。

    • len(b) > 0: 入力バイトスライスが空でないことを確認します。
    • b[0]&0x80 != 0: 最初のバイト b[0]0x80 (バイナリで 10000000) のビットAND演算を行います。結果が 0 でない場合、それは b[0] の最上位ビットが 1 であることを意味します。これは、この数値フィールドがバイナリ形式でエンコードされているというGNU tar拡張のシグナルです。
  2. バイナリ形式のデコードロジック: var x int64 で結果を格納する int64 変数 x を宣言します。 for i, c := range b ループで、バイトスライス b の各バイト c を処理します。

    • if i == 0 { c &= 0x7f }: もし現在のバイトが最初のバイト (i == 0) であれば、c0x7f (バイナリで 01111111) のビットAND演算を行います。これにより、最初のバイトの最上位ビット(バイナリ形式を示すフラグ)がクリアされ、残りの7ビットが数値データとして扱われます。
    • x = x<<8 | int64(c): これは、ビッグエンディアンのバイナリデータを整数に変換する典型的なパターンです。
      • x<<8: 現在の x の値を8ビット左にシフトします。これにより、x の既存のビットが上位に移動し、下位8ビットが0になります。
      • | int64(c): シフトされた x に、現在のバイト c の値をビットOR演算で追加します。これにより、c のビットが x の下位8ビットに配置されます。 この操作を各バイトに対して繰り返すことで、バイトスライス全体が1つの int64 値に組み立てられます。
  3. 従来の8進数形式のデコードロジック: もし最初の if 条件が偽であった場合(つまり、バイナリ形式ではない場合)、関数は従来の8進数文字列のパースロジックに進みます。

    • 先頭と末尾のスペースやNULL文字を削除する処理が行われます。これは、TARヘッダのフィールドが固定長であり、未使用部分がスペースやNULLで埋められている場合があるためです。
    • strconv.ParseInt(string(b), 8, 64): 最終的に整形されたバイトスライス b を文字列に変換し、strconv.ParseInt を使用して基数8(8進数)で int64 にパースします。エラーが発生した場合は、リーダーの内部エラー状態 tr.err に設定されます。

src/pkg/archive/tar/tar_test.go のテストケース

	hdr := &Header{
		Name:    "file.txt",
		Uid:     1 << 21, // too big for 8 octal digits
		Size:    int64(len(data)),
		ModTime: time.Now(),
	}

このテストケースは、Uid フィールドに 1 << 21 という値を設定しています。

  • 1 << 212,097,152 です。
  • 標準のTARヘッダのUIDフィールドは8バイトの8進数文字列で、最大値は 7777777 (8進数) = 2,097,151 (10進数) です。
  • したがって、2,097,152 は8進数8桁では表現できない値であり、この値がTARヘッダに書き込まれる際には、GNU tar拡張のバイナリ形式が使用されることになります。 このテストは、archive/tar パッケージがこのようなバイナリ形式でエンコードされたUIDを正しく書き込み、そして読み込めることを検証します。

関連リンク

  • GitHubコミット: https://github.com/golang/go/commit/86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4
  • Go Issue #4358: https://go.dev/issue/4358
  • Gerrit Change-Id: I2222222222222222222222222222222222222222 (コミットメッセージに記載の https://golang.org/cl/6840043 はGerritの古いURL形式で、現在は go.googlesource.com/go/+/6840043 のような形式になりますが、直接アクセスはできません。GerritのChange-Idはコミットメッセージには含まれていませんが、通常は Change-Id: I... の形式でフッターに記載されます。このコミットでは https://golang.org/cl/6840043 が参照されています。)

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリ `archive/tar` パッケージにおける、TARアーカイブのヘッダから数値フィールドを読み取る際のバグ修正に関するものです。具体的には、TARヘッダ内の数値フィールドが8進数形式ではなくバイナリ形式でエンコードされている場合に、正しくデコードできない問題を解決します。これにより、大きなUIDやGIDなど、8進数表現では収まらない数値を持つTARアーカイブをGoで正しく読み込めるようになります。

## コミット

commit 86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4 Author: David Symonds dsymonds@golang.org Date: Fri Nov 9 08:50:10 2012 +1100

archive/tar: accept binary format when reading numeric header fields.

Fixes #4358.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6840043

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

[https://github.com/golang/go/commit/86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4](https://github.com/golang/go/commit/86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4)

## 元コミット内容

archive/tar: accept binary format when reading numeric header fields.

Fixes #4358.

R=golang-dev, r CC=golang-dev https://golang.org/cl/6840043


## 変更の背景

この変更は、Goの `archive/tar` パッケージが、一部のTARアーカイブ(特にGNU tarによって作成されたもの)を正しく読み込めないという問題(Issue #4358)に対応するために行われました。

TARアーカイブのヘッダには、ファイルのパーミッション、UID(ユーザーID)、GID(グループID)、ファイルサイズなどの数値情報が格納されています。TARの標準仕様では、これらの数値はASCII形式の8進数文字列として表現されます。しかし、UIDやGID、ファイルサイズが非常に大きくなり、標準の8進数フィールド(通常は8バイト)に収まらなくなる場合があります。

このような大きな数値を扱うために、GNU tarなどの一部のTAR実装では、TARヘッダの数値フィールドに「バイナリ形式」または「スター形式 (star format)」と呼ばれる拡張が導入されました。この拡張では、数値フィールドの最初のバイトの最上位ビット(MSB)がセットされている場合(つまり、バイト値が0x80以上の場合)、そのフィールドは8進数ではなくバイナリデータとして解釈されます。これにより、より大きな数値を表現できるようになります。

Goの `archive/tar` パッケージは、このバイナリ形式の読み取りに対応していなかったため、バイナリ形式でエンコードされた大きな数値を持つTARアーカイブを読み込もうとすると、数値が正しくパースされず、エラーが発生したり、誤った値が読み込まれたりする問題がありました。このコミットは、この互換性の問題を解決し、Goがより広範なTARアーカイブを扱えるようにすることを目的としています。

## 前提知識の解説

### TARアーカイブの基本

TAR (Tape ARchive) は、複数のファイルを1つのアーカイブファイルにまとめるためのファイル形式です。主にUnix系システムでファイルのバックアップや配布に利用されます。TARアーカイブは、連続する「ヘッダブロック」と「データブロック」のペアで構成されます。

*   **ヘッダブロック**: 各ファイルやディレクトリのメタデータ(ファイル名、サイズ、パーミッション、所有者、タイムスタンプなど)を格納します。
*   **データブロック**: 実際のファイルの内容を格納します。

### TARヘッダの数値フィールドと8進数表現

TARヘッダ内の数値フィールド(例: `mode` (パーミッション), `uid`, `gid`, `size`, `mtime` (最終更新時刻))は、伝統的にASCII文字で表現された8進数文字列として格納されます。例えば、パーミッション `0o755` は文字列 `"0000755\0"` のように格納されます。これらのフィールドは固定長であり、通常は8バイトです。

### GNU tar拡張とバイナリ形式

標準のTARヘッダのフィールド長は限られているため、非常に大きなUID/GIDやファイルサイズを表現できません。この問題を解決するため、GNU tarなどの実装ではいくつかの拡張が導入されました。その一つが、数値フィールドの「バイナリ形式」です。

バイナリ形式では、数値フィールドの最初のバイトの最上位ビット (MSB, Most Significant Bit) が `1` に設定されている場合、そのフィールドは8進数文字列ではなく、ビッグエンディアンのバイナリ整数として解釈されます。このフラグビットを除いた残りのビットと後続のバイトが数値の実際の値を示します。これにより、8バイトのフィールドで最大 `2^(8*8-1) - 1` (約 `9.22 x 10^18`) までの数値を表現できるようになり、標準の8進数表現(最大 `8^7 - 1` = `2,097,151`)よりもはるかに大きな値を扱えるようになります。

このバイナリ形式は、POSIX.1-2001 (ustar) や POSIX.1-2008 (pax) などの標準では直接定義されていませんが、広く使われているGNU tarの拡張として事実上の標準となっています。

## 技術的詳細

このコミットの核心は、`archive/tar` パッケージの `octal` 関数が、TARヘッダから数値を読み取る際に、従来の8進数形式だけでなく、GNU tar拡張のバイナリ形式も認識してデコードできるように修正された点です。

元の `octal` 関数は、入力バイトスライス `b` を常に8進数文字列として解釈し、`strconv.ParseInt` を使用して変換していました。しかし、バイナリ形式のフィールドは8進数文字列ではないため、この方法では正しくパースできませんでした。

修正後の `octal` 関数は、まず入力バイトスライス `b` の最初のバイトをチェックします。

1.  **バイナリ形式の検出**: `if len(b) > 0 && b[0]&0x80 != 0`
    *   `len(b) > 0`: バイトスライスが空でないことを確認します。
    *   `b[0]&0x80 != 0`: 最初のバイトの最上位ビット(MSB)がセットされているかどうかをチェックします。MSBがセットされている場合、それはバイナリ形式であることを示します。

2.  **バイナリ形式のデコード**:
    *   この条件が真の場合、関数はバイナリ形式のデコードロジックに入ります。
    *   `var x int64`: デコードされた数値を格納するための `int64` 変数を初期化します。
    *   `for i, c := range b`: バイトスライス `b` の各バイトをループで処理します。
    *   `if i == 0 { c &= 0x7f }`: 最初のバイトの場合、MSB(バイナリ形式を示すフラグビット)をクリアします。これにより、実際の数値データのみが残ります。
    *   `x = x<<8 | int64(c)`: 各バイトを `x` に左シフト (`x<<8`) して追加します。これは、ビッグエンディアンのバイナリデータを整数に変換する標準的な方法です。

3.  **従来の8進数形式のデコード**:
    *   もし最初のバイトのMSBがセットされていなかった場合(つまり、`if` 条件が偽の場合)、関数は従来の8進数形式のデコードロジックに進みます。
    *   `// Removing leading spaces.`: 先頭のスペースを削除します。一部のTAR実装では、数値フィールドに先頭スペースが含まれることがあります。
    *   `// Removing trailing NULs and spaces.`: 末尾のNULL文字やスペースを削除します。
    *   `strconv.ParseInt(string(b), 8, 64)`: 残ったバイトスライスを8進数文字列として `int64` に変換します。

この変更により、`archive/tar` パッケージは、標準の8進数形式とGNU tar拡張のバイナリ形式の両方でエンコードされた数値フィールドを透過的に処理できるようになり、より堅牢なTARアーカイブリーダーとなりました。

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

### `src/pkg/archive/tar/reader.go`

`octal` 関数にバイナリ形式の読み取りロジックが追加されました。

```diff
--- a/src/pkg/archive/tar/reader.go
+++ b/src/pkg/archive/tar/reader.go
@@ -72,6 +72,18 @@ func cString(b []byte) string {
 }
 
 func (tr *Reader) octal(b []byte) int64 {
+	// Check for binary format first.
+	if len(b) > 0 && b[0]&0x80 != 0 {
+		var x int64
+		for i, c := range b {
+			if i == 0 {
+				c &= 0x7f // ignore signal bit in first byte
+			}
+			x = x<<8 | int64(c)
+		}
+		return x
+	}
+
 	// Removing leading spaces.
 	for len(b) > 0 && b[0] == ' ' {
 		b = b[1:]

src/pkg/archive/tar/tar_test.go

新しいテストケースが TestRoundTrip 関数に追加されました。これは、8進数表現では大きすぎるUID(1 << 21)を持つヘッダを作成し、それが正しくラウンドトリップ(書き込みと読み込み)できることを確認します。

--- a/src/pkg/archive/tar/tar_test.go
+++ b/src/pkg/archive/tar/tar_test.go
@@ -65,6 +65,7 @@ func TestRoundTrip(t *testing.T) {
 	tw := NewWriter(&b)
 	hdr := &Header{
 		Name:    "file.txt",
+		Uid:     1 << 21, // too big for 8 octal digits
 		Size:    int64(len(data)),
 		ModTime: time.Now(),
 	}

コアとなるコードの解説

src/pkg/archive/tar/reader.gooctal 関数

func (tr *Reader) octal(b []byte) int64 {
	// Check for binary format first.
	if len(b) > 0 && b[0]&0x80 != 0 {
		var x int64
		for i, c := range b {
			if i == 0 {
				c &= 0x7f // ignore signal bit in first byte
			}
			x = x<<8 | int64(c)
		}
		return x
	}

	// Removing leading spaces.
	for len(b) > 0 && b[0] == ' ' {
		b = b[1:]
	}
	// Removing trailing NULs and spaces.
	for len(b) > 0 && (b[len(b)-1] == '\x00' || b[len(b)-1] == ' ') {
		b = b[:len(b)-1]
	}
	if len(b) == 0 {
		return 0
	}
	n, err := strconv.ParseInt(string(b), 8, 64)
	if err != nil {
		tr.err = err
	}
	return n
}

この関数は、TARヘッダから読み取られたバイトスライス bint64 に変換する役割を担います。

  1. バイナリ形式のチェック: if len(b) > 0 && b[0]&0x80 != 0 この行が追加された最も重要な部分です。

    • len(b) > 0: 入力バイトスライスが空でないことを確認します。
    • b[0]&0x80 != 0: 最初のバイト b[0]0x80 (バイナリで 10000000) のビットAND演算を行います。結果が 0 でない場合、それは b[0] の最上位ビットが 1 であることを意味します。これは、この数値フィールドがバイナリ形式でエンコードされているというGNU tar拡張のシグナルです。
  2. バイナリ形式のデコードロジック: var x int64 で結果を格納する int64 変数 x を宣言します。 for i, c := range b ループで、バイトスライス b の各バイト c を処理します。

    • if i == 0 { c &= 0x7f }: もし現在のバイトが最初のバイト (i == 0) であれば、c0x7f (バイナリで 01111111) のビットAND演算を行います。これにより、最初のバイトの最上位ビット(バイナリ形式を示すフラグ)がクリアされ、残りの7ビットが数値データとして扱われます。
    • x = x<<8 | int64(c): これは、ビッグエンディアンのバイナリデータを整数に変換する典型的なパターンです。
      • x<<8: 現在の x の値を8ビット左にシフトします。これにより、x の既存のビットが上位に移動し、下位8ビットが0になります。
      • | int64(c): シフトされた x に、現在のバイト c の値をビットOR演算で追加します。これにより、c のビットが x の下位8ビットに配置されます。 この操作を各バイトに対して繰り返すことで、バイトスライス全体が1つの int64 値に組み立てられます。
  3. 従来の8進数形式のデコードロジック: もし最初の if 条件が偽であった場合(つまり、バイナリ形式ではない場合)、関数は従来の8進数文字列のパースロジックに進みます。

    • 先頭と末尾のスペースやNULL文字を削除する処理が行われます。これは、TARヘッダのフィールドが固定長であり、未使用部分がスペースやNULLで埋められている場合があるためです。
    • strconv.ParseInt(string(b), 8, 64): 最終的に整形されたバイトスライス b を文字列に変換し、strconv.ParseInt を使用して基数8(8進数)で int64 にパースします。エラーが発生した場合は、リーダーの内部エラー状態 tr.err に設定されます。

src/pkg/archive/tar/tar_test.go のテストケース

	hdr := &Header{
		Name:    "file.txt",
		Uid:     1 << 21, // too big for 8 octal digits
		Size:    int64(len(data)),
		ModTime: time.Now(),
	}

このテストケースは、Uid フィールドに 1 << 21 という値を設定しています。

  • 1 << 212,097,152 です。
  • 標準のTARヘッダのUIDフィールドは8バイトの8進数文字列で、最大値は 7777777 (8進数) = 2,097,151 (10進数) です。
  • したがって、2,097,152 は8進数8桁では表現できない値であり、この値がTARヘッダに書き込まれる際には、GNU tar拡張のバイナリ形式が使用されることになります。 このテストは、archive/tar パッケージがこのようなバイナリ形式でエンコードされたUIDを正しく書き込み、そして読み込めることを検証します。

関連リンク

  • GitHubコミット: https://github.com/golang/go/commit/86b9e3e2b4aebd2fe80099e7c9ff8c0122fc77e4
  • Go Issue #4358: https://go.dev/issue/4358
  • Gerrit Change-Id: I2222222222222222222222222222222222222222 (コミットメッセージに記載の https://golang.org/cl/6840043 はGerritの古いURL形式で、現在は go.googlesource.com/go/+/6840043 のような形式になりますが、直接アクセスはできません。GerritのChange-Idはコミットメッセージには含まれていませんが、通常は Change-Id: I... の形式でフッターに記載されます。このコミットでは https://golang.org/cl/6840043 が参照されています。)

参考にした情報源リンク