[インデックス 12632] ファイルの概要
このコミットは、Go言語の os パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist() 関数が ERROR_PATH_NOT_FOUND エラーも適切に処理するように修正され、また os.IsExist() および os.IsNotExist() のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。
コミット
commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Mar 14 23:54:40 2012 +0800
os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
Also update documentation about IsExist() and IsNotExist(), they are not
about files only.
R=rsc
CC=golang-dev
https://golang.org/cl/5794073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc
元コミット内容
os: IsNotExist() はWindows上で ERROR_PATH_NOT_FOUND も考慮すべきである。
また、IsExist() と IsNotExist() のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。
変更の背景
Go言語の os パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist() と os.IsNotExist() が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。
しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir 試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND というエラーが返されることがありました。従来の os.IsNotExist() はこの ERROR_PATH_NOT_FOUND を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist() が true を返さないという不整合が生じていました。
このコミットは、この不整合を解消し、Windows上での os.IsNotExist() の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。
前提知識の解説
- Go言語の
osパッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。 os.IsExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合にtrueを返します。os.IsNotExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合にtrueを返します。os.IsPermission(err error) bool: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合にtrueを返します。syscallパッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例:syscall.ENOENT,syscall.EEXISTなど)が定義されています。- Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
ERROR_FILE_NOT_FOUND(0x2): 指定されたファイルが見つかりません。ERROR_PATH_NOT_FOUND(0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。ERROR_ALREADY_EXISTS(0xB7): 指定されたファイルは既に存在します。ERROR_ACCESS_DENIED(0x5): アクセスが拒否されました。
- POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常
ENOENT(Error NO ENTry) が返されます。ファイルが既に存在する場合はEEXIST(Error EXIST) が返されます。 os.PathError:osパッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op(操作名)、Path(パス)、Err(元のエラー) のフィールドを持ちます。
技術的詳細
このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go ファイルにあります。
-
isNotExist関数の修正:- 変更前:
return err == syscall.ENOENT || err == ErrNotExist - 変更後:
return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExistこの変更により、Windows上でos.IsNotExist()が、ファイルが見つからないことを示すERROR_FILE_NOT_FOUNDに加えて、パスが見つからないことを示すERROR_PATH_NOT_FOUNDも「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへのos.Chdir()呼び出しがERROR_PATH_NOT_FOUNDを返した場合でも、os.IsNotExist()が正しくtrueを返すようになります。
- 変更前:
-
isExist関数の修正:- 変更前:
return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist - 変更後:
return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExistsyscall.EEXISTはPOSIXシステムで使われるエラーコードであり、Windows固有のerror_windows.goからは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。WindowsではERROR_ALREADY_EXISTSやERROR_FILE_EXISTSが同等の意味を持ちます。
- 変更前:
-
isPermission関数の修正:- 変更前:
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission - 変更後:
return err == ErrPermission同様に、syscall.EACCES(アクセス拒否) とsyscall.EPERM(操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。WindowsではErrPermissionがより抽象化された権限エラーを示します。
- 変更前:
-
ドキュメントの更新 (
src/pkg/os/error.go):IsExistとIsNotExistのコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
-
テストケースの追加 (
src/pkg/os/error_test.go):TestErrIsNotExistという新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()やos.Chdir()を試行してos.IsNotExist()が正しく機能するかを検証します。特に、ネストされた存在しないパス(例:/tmp/nonexistent_dir/nonexistent_file)に対してもテストが行われ、ERROR_PATH_NOT_FOUNDのシナリオがカバーされていることを確認しています。
これらの変更により、Goの os パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。
コアとなるコードの変更箇所
src/pkg/os/error.go
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ func NewSyscallError(syscall string, err error) error {
return &SyscallError{syscall, err}
}
-// IsExist returns whether the error is known to report that a file already exists.
-// It is satisfied by ErrExist as well as some syscall errors.
+// IsExist returns whether the error is known to report that a file or directory
+// already exists. It is satisfied by ErrExist as well as some syscall errors.
func IsExist(err error) bool {
return isExist(err)
}
-// IsNotExist returns whether the error is known to report that a file does not exist.
-// It is satisfied by ErrNotExist as well as some syscall errors.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}
src/pkg/os/error_test.go
--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
package os_test
import (
+\t"fmt"
"io/ioutil"
"os"
+\t"path/filepath"
"testing"
)
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
return
}
}
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```
### `src/pkg/os/error_windows.go`
```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
\t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
}
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
}
func isPermission(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
}
コアとなるコードの解説
-
src/pkg/os/error.go:IsExistとIsNotExist関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
-
src/pkg/os/error_test.go:testErrNotExistヘルパー関数が追加され、os.Openとos.Chdirの両方でos.IsNotExistが正しく動作するかを検証します。特にos.Chdirはディレクトリ操作であり、ERROR_PATH_NOT_FOUNDが発生しやすいシナリオをカバーします。TestErrIsNotExist関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対してtestErrNotExistを呼び出し、os.IsNotExistの堅牢性をテストしています。checkErrorPredicateヘルパー関数は、エラー述語(os.IsExistやos.IsNotExistなど)が期待通りにtrueを返すかを汎用的にチェックするために導入されました。
-
src/pkg/os/error_windows.go:isNotExist関数内で、Windows固有のエラーコードsyscall.ERROR_PATH_NOT_FOUNDがos.IsNotExistの判定基準に追加されました。これにより、Windows環境でパスが存在しない場合にos.IsNotExistが正しくtrueを返すようになります。isExist関数からsyscall.EEXISTが削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。isPermission関数からsyscall.EACCESとsyscall.EPERMが削除されました。これらもPOSIXシステムのエラーコードであり、WindowsではErrPermissionがより抽象的な権限エラーを表すため、整理されました。
これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。
関連リンク
- Go言語の
osパッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscallパッケージドキュメント: https://pkg.go.dev/syscall - Windows System Error Codes: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
参考にした情報源リンク
- https://golang.org/cl/5794073 (元のGo Gerritの変更リスト)
- https://pkg.go.dev/os
- https://pkg.go.dev/syscall
- https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
- https://en.wikipedia.org/wiki/POSIX
- https://en.wikipedia.org/wiki/Errno
- https://go.dev/doc/effective_go#errors# [インデックス 12632] ファイルの概要
このコミットは、Go言語の os パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist() 関数が ERROR_PATH_NOT_FOUND エラーも適切に処理するように修正され、また os.IsExist() および os.IsNotExist() のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。
コミット
commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Mar 14 23:54:40 2012 +0800
os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
Also update documentation about IsExist() and IsNotExist(), they are not
about files only.
R=rsc
CC=golang-dev
https://golang.org/cl/5794073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc
元コミット内容
os: IsNotExist() はWindows上で ERROR_PATH_NOT_FOUND も考慮すべきである。
また、IsExist() と IsNotExist() のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。
変更の背景
Go言語の os パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist() と os.IsNotExist() が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。
しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir 試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND というエラーが返されることがありました。従来の os.IsNotExist() はこの ERROR_PATH_NOT_FOUND を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist() が true を返さないという不整合が生じていました。
このコミットは、この不整合を解消し、Windows上での os.IsNotExist() の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。
前提知識の解説
- Go言語の
osパッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。 os.IsExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合にtrueを返します。os.IsNotExist(err error) bool: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合にtrueを返します。os.IsPermission(err error) bool: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合にtrueを返します。syscallパッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例:syscall.ENOENT,syscall.EEXISTなど)が定義されています。- Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
ERROR_FILE_NOT_FOUND(0x2): 指定されたファイルが見つかりません。ERROR_PATH_NOT_FOUND(0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。ERROR_ALREADY_EXISTS(0xB7): 指定されたファイルは既に存在します。ERROR_ACCESS_DENIED(0x5): アクセスが拒否されました。
- POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常
ENOENT(Error NO ENTry) が返されます。ファイルが既に存在する場合はEEXIST(Error EXIST) が返されます。 os.PathError:osパッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op(操作名)、Path(パス)、Err(元のエラー) のフィールドを持ちます。
技術的詳細
このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go ファイルにあります。
-
isNotExist関数の修正:- 変更前:
return err == syscall.ENOENT || err == ErrNotExist - 変更後:
return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExistこの変更により、Windows上でos.IsNotExist()が、ファイルが見つからないことを示すERROR_FILE_NOT_FOUNDに加えて、パスが見つからないことを示すERROR_PATH_NOT_FOUNDも「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへのos.Chdir()呼び出しがERROR_PATH_NOT_FOUNDを返した場合でも、os.IsNotExist()が正しくtrueを返すようになります。
- 変更前:
-
isExist関数の修正:- 変更前:
return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist - 変更後:
return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExistsyscall.EEXISTはPOSIXシステムで使われるエラーコードであり、Windows固有のerror_windows.goからは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。WindowsではERROR_ALREADY_EXISTSやERROR_FILE_EXISTSが同等の意味を持ちます。
- 変更前:
-
isPermission関数の修正:- 変更前:
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission - 変更後:
return err == ErrPermission同様に、syscall.EACCES(アクセス拒否) とsyscall.EPERM(操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。WindowsではErrPermissionがより抽象化された権限エラーを示します。
- 変更前:
-
ドキュメントの更新 (
src/pkg/os/error.go):IsExistとIsNotExistのコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
-
テストケースの追加 (
src/pkg/os/error_test.go):TestErrIsNotExistという新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()やos.Chdir()を試行してos.IsNotExist()が正しく機能するかを検証します。特に、ネストされた存在しないパス(例:/tmp/nonexistent_dir/nonexistent_file)に対してもテストが行われ、ERROR_PATH_NOT_FOUNDのシナリオがカバーされていることを確認しています。
これらの変更により、Goの os パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。
コアとなるコードの変更箇所
src/pkg/os/error.go
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ func NewSyscallError(syscall string, err error) error {
return &SyscallError{syscall, err}
}
-// IsExist returns whether the error is known to report that a file already exists.
-// It is satisfied by ErrExist as well as some syscall errors.
+// IsExist returns whether the error is known to report that a file or directory
+// already exists. It is satisfied by ErrExist as well as some syscall errors.
func IsExist(err error) bool {
return isExist(err)
}
-// IsNotExist returns whether the error is known to report that a file does not exist.
-// It is satisfied by ErrNotExist as well as some syscall errors.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}
src/pkg/os/error_test.go
--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
package os_test
import (
+\t"fmt"
"io/ioutil"
"os"
+\t"path/filepath"
"testing"
)
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
return
}
}
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```
### `src/pkg/os/error_windows.go`
```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
\t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
}
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
}
func isPermission(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
}
コアとなるコードの解説
-
src/pkg/os/error.go:IsExistとIsNotExist関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
-
src/pkg/os/error_test.go:testErrNotExistヘルパー関数が追加され、os.Openとos.Chdirの両方でos.IsNotExistが正しく動作するかを検証します。特にos.Chdirはディレクトリ操作であり、ERROR_PATH_NOT_FOUNDが発生しやすいシナリオをカバーします。TestErrIsNotExist関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対してtestErrNotExistを呼び出し、os.IsNotExistの堅牢性をテストしています。checkErrorPredicateヘルパー関数は、エラー述語(os.IsExistやos.IsNotExistなど)が期待通りにtrueを返すかを汎用的にチェックするために導入されました。
-
src/pkg/os/error_windows.go:isNotExist関数内で、Windows固有のエラーコードsyscall.ERROR_PATH_NOT_FOUNDがos.IsNotExistの判定基準に追加されました。これにより、Windows環境でパスが存在しない場合にos.IsNotExistが正しくtrueを返すようになります。isExist関数からsyscall.EEXISTが削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。isPermission関数からsyscall.EACCESとsyscall.EPERMが削除されました。これらもPOSIXシステムのエラーコードであり、WindowsではErrPermissionがより抽象的な権限エラーを表すため、整理されました。
これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。
関連リンク
- Go言語の
osパッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscallパッケージドキュメント: https://pkg.go.dev/syscall - Windows System Error Codes: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes