// 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 }