|  | // 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/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 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, fs.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, fs.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, fs.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 | 
|  | } |