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

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

このコミットは、Go言語のnetパッケージにおける、ホストファイルの読み込みに関するバグ修正です。具体的には、末尾に改行がない単一行のホストファイルを正しく処理できるようにするための変更が含まれています。

コミット

commit 5644774ea5c954a08c1e068be9771429a232ca7c
Author: Josh Bleecher Snyder <josharian@gmail.com>
Date:   Mon Oct 28 19:31:25 2013 -0400

    net: handle single-line non-\n-terminated files correctly in readLine
    
    Fixes #6646.
    
    R=rsc, bradfitz
    CC=golang-dev
    https://golang.org/cl/15960047

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

https://github.com/golang/go/commit/5644774ea5c954a08c1e068be9771429a232ca7c

元コミット内容

このコミットは、netパッケージのreadLine関数が、末尾に改行文字(\n)がない単一行のファイルを正しく処理できない問題を修正します。この問題は、特に/etc/hostsのような設定ファイルを読み込む際に発生する可能性があり、Goアプリケーションがホスト名を解決する際に予期せぬ動作を引き起こす可能性がありました。

変更の背景

Go言語のnetパッケージは、ネットワーク関連の操作、特にDNSルックアップやホスト名の解決を担当しています。多くのUnix系システムでは、/etc/hostsファイルがローカルのホスト名解決に使用されます。このファイルは通常、各エントリが改行で区切られた行として記述されます。しかし、一部の環境や手動での編集によって、ファイルが単一行で構成され、かつその行の末尾に改行文字がない場合があります。

従来のreadLine関数は、行の終端を改行文字またはファイルの終端(EOF)で判断していました。しかし、末尾に改行がない単一行の場合、io.EOFではなくio.ErrUnexpectedEOFが返されることがあり、このケースが適切に処理されていませんでした。その結果、このような形式のホストファイルが正しくパースされず、ホスト名の解決に失敗する可能性がありました。

この問題は、GoのIssue #6646として報告されており、本コミットはその修正を目的としています。

前提知識の解説

  • /etc/hostsファイル: Unix系システムで使用される、IPアドレスとホスト名のマッピングを定義するテキストファイルです。DNSが利用できない場合や、特定のホスト名をローカルでオーバーライドする場合に参照されます。
  • io.EOF: Go言語のioパッケージで定義されているエラーで、入力ストリームの終端に達したことを示します。通常、ファイルの最後まで読み込んだ場合に返されます。
  • io.ErrUnexpectedEOF: Go言語のioパッケージで定義されているエラーで、予期せぬファイルの終端に達したことを示します。例えば、期待されるデータが読み込まれる前にファイルの終端に達した場合などに発生します。今回のケースでは、改行文字を期待しているにもかかわらず、ファイルの終端に達してしまった場合に発生します。
  • bufio.Reader: Go言語のbufioパッケージで提供される、バッファリングされたI/O操作を行うためのリーダーです。効率的なファイル読み込みを可能にします。
  • readLine関数: 一般的に、ファイルやストリームから一行ずつデータを読み込むための関数です。改行文字を区切りとして行を認識します。

技術的詳細

このコミットの主要な変更点は、src/pkg/net/parse.go内のfile構造体のreadLineメソッドにあります。このメソッドは、ホストファイルのようなテキストファイルを読み込み、行ごとに処理するために使用されます。

修正前は、readLineメソッド内でio.EOFのみをファイルの終端として扱っていました。しかし、末尾に改行がない単一行のファイルの場合、bufio.ReaderReadSliceメソッドはio.ErrUnexpectedEOFを返すことがあります。これは、ReadSliceが改行文字を探している途中でファイルの終端に達したことを意味します。

このコミットでは、io.EOFだけでなくio.ErrUnexpectedEOFもファイルの終端を示すものとして扱うように変更されました。これにより、readLineは末尾に改行がない単一行のファイルでも、その行を正しく読み込み、ファイルの終端に達したと認識できるようになります。

また、この修正を検証するために、src/pkg/net/hosts_test.goに新しいテストケースTestSingleLineHostsFileが追加されました。このテストは、末尾に改行がない単一行のホストファイル(testdata/hosts_singleline)を作成し、lookupStaticHost関数がそのファイルから正しいIPアドレスを解決できることを確認します。

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

src/pkg/net/hosts_test.go

--- a/src/pkg/net/hosts_test.go
+++ b/src/pkg/net/hosts_test.go
@@ -53,6 +53,19 @@ func TestLookupStaticHost(t *testing.T) {
 	hostsPath = p
 }
 
+// https://code.google.com/p/go/issues/detail?id=6646
+func TestSingleLineHostsFile(t *testing.T) {
+	p := hostsPath
+	hostsPath = "testdata/hosts_singleline"
+
+	ips := lookupStaticHost("odin")
+	if len(ips) != 1 || ips[0] != "127.0.0.2" {
+		t.Errorf("lookupStaticHost = %v, want %v", ips, []string{"127.0.0.2"})
+	}
+
+	hostsPath = p
+}
+
 func TestLookupHost(t *testing.T) {
 	// Can't depend on this to return anything in particular,
 	// but if it does return something, make sure it doesn't

src/pkg/net/parse.go

--- a/src/pkg/net/parse.go
+++ b/src/pkg/net/parse.go
@@ -54,7 +54,7 @@ func (f *file) readLine() (s string, ok bool) {
 		if n >= 0 {
 			f.data = f.data[0 : ln+n]
 		}
-		if err == io.EOF {
+		if err == io.EOF || err == io.ErrUnexpectedEOF {
 			f.atEOF = true
 		}
 	}

src/pkg/net/testdata/hosts_singleline (新規ファイル)

127.0.0.2	odin
\ No newline at end of file

コアとなるコードの解説

src/pkg/net/parse.go の変更

readLine関数内の以下の行が変更されました。

-		if err == io.EOF {
+		if err == io.EOF || err == io.ErrUnexpectedEOF {

この変更により、f.r.ReadSlice('\n')が返すエラーがio.EOFまたはio.ErrUnexpectedEOFのいずれかである場合に、ファイルの終端(f.atEOF = true)として扱われるようになります。これにより、末尾に改行がないファイルでも、その最後の行が正しく読み込まれ、処理が完了するようになります。

src/pkg/net/hosts_test.go の変更

TestSingleLineHostsFileという新しいテスト関数が追加されました。 このテストは以下の手順を実行します。

  1. 現在のhostsPath(ホストファイルのパス)を保存します。
  2. hostsPathを新しく作成されたテストデータファイルtestdata/hosts_singlelineに設定します。このファイルは127.0.0.2 odinという単一行のみを含み、末尾に改行がありません。
  3. lookupStaticHost("odin")を呼び出し、odinというホスト名に対応するIPアドレスを検索します。
  4. 返されたIPアドレスが期待される値(127.0.0.2)と一致するかどうかを検証します。
  5. 最後に、hostsPathを元の値に戻します。

このテストケースの追加により、末尾に改行がないホストファイルが正しくパースされることが保証されます。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード
  • Go言語のIssueトラッカー (GitHub)
  • ioパッケージのドキュメント
  • bufioパッケージのドキュメント