diff options
Diffstat (limited to 'src/os/error.go')
-rw-r--r-- | src/os/error.go | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/os/error.go b/src/os/error.go new file mode 100644 index 0000000..704a6fb --- /dev/null +++ b/src/os/error.go @@ -0,0 +1,143 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package os + +import ( + "internal/oserror" + "internal/poll" + "io/fs" +) + +// Portable analogs of some common system call errors. +// +// Errors returned from this package may be tested against these errors +// with errors.Is. +var ( + // ErrInvalid indicates an invalid argument. + // Methods on File will return this error when the receiver is nil. + ErrInvalid = fs.ErrInvalid // "invalid argument" + + ErrPermission = fs.ErrPermission // "permission denied" + ErrExist = fs.ErrExist // "file already exists" + ErrNotExist = fs.ErrNotExist // "file does not exist" + ErrClosed = fs.ErrClosed // "file already closed" + + ErrNoDeadline = errNoDeadline() // "file type does not support deadline" + ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout" +) + +func errClosed() error { return oserror.ErrClosed } +func errNoDeadline() error { return poll.ErrNoDeadline } + +// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded. +// This error comes from the internal/poll package, which is also +// used by package net. Doing this this way ensures that the net +// package will return os.ErrDeadlineExceeded for an exceeded deadline, +// as documented by net.Conn.SetDeadline, without requiring any extra +// work in the net package and without requiring the internal/poll +// package to import os (which it can't, because that would be circular). +func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded } + +type timeout interface { + Timeout() bool +} + +// PathError records an error and the operation and file path that caused it. +type PathError = fs.PathError + +// SyscallError records an error from a specific system call. +type SyscallError struct { + Syscall string + Err error +} + +func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() } + +func (e *SyscallError) Unwrap() error { return e.Err } + +// Timeout reports whether this error represents a timeout. +func (e *SyscallError) Timeout() bool { + t, ok := e.Err.(timeout) + return ok && t.Timeout() +} + +// NewSyscallError returns, as an error, a new SyscallError +// with the given system call name and error details. +// As a convenience, if err is nil, NewSyscallError returns nil. +func NewSyscallError(syscall string, err error) error { + if err == nil { + return nil + } + return &SyscallError{syscall, err} +} + +// IsExist returns a boolean indicating 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. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, os.ErrExist). +func IsExist(err error) bool { + return underlyingErrorIs(err, ErrExist) +} + +// IsNotExist returns a boolean indicating 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. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, os.ErrNotExist). +func IsNotExist(err error) bool { + return underlyingErrorIs(err, ErrNotExist) +} + +// IsPermission returns a boolean indicating whether the error is known to +// report that permission is denied. It is satisfied by ErrPermission as well +// as some syscall errors. +// +// This function predates errors.Is. It only supports errors returned by +// the os package. New code should use errors.Is(err, os.ErrPermission). +func IsPermission(err error) bool { + return underlyingErrorIs(err, ErrPermission) +} + +// IsTimeout returns a boolean indicating whether the error is known +// to report that a timeout occurred. +// +// This function predates errors.Is, and the notion of whether an +// error indicates a timeout can be ambiguous. For example, the Unix +// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not. +// New code should use errors.Is with a value appropriate to the call +// returning the error, such as os.ErrDeadlineExceeded. +func IsTimeout(err error) bool { + terr, ok := underlyingError(err).(timeout) + return ok && terr.Timeout() +} + +func underlyingErrorIs(err, target error) bool { + // Note that this function is not errors.Is: + // underlyingError only unwraps the specific error-wrapping types + // that it historically did, not all errors implementing Unwrap(). + err = underlyingError(err) + if err == target { + return true + } + // To preserve prior behavior, only examine syscall errors. + e, ok := err.(syscallErrorType) + return ok && e.Is(target) +} + +// underlyingError returns the underlying error for known os error types. +func underlyingError(err error) error { + switch err := err.(type) { + case *PathError: + return err.Err + case *LinkError: + return err.Err + case *SyscallError: + return err.Err + } + return err +} |