| // 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 it 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 its argument 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 its argument 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 its argument 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 its argument 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 | |
| } | |