1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5package os 6 7import ( 8 "internal/poll" 9 "io/fs" 10) 11 12// Portable analogs of some common system call errors. 13// 14// Errors returned from this package may be tested against these errors 15// with [errors.Is]. 16var ( 17 // ErrInvalid indicates an invalid argument. 18 // Methods on File will return this error when the receiver is nil. 19 ErrInvalid = fs.ErrInvalid // "invalid argument" 20 21 ErrPermission = fs.ErrPermission // "permission denied" 22 ErrExist = fs.ErrExist // "file already exists" 23 ErrNotExist = fs.ErrNotExist // "file does not exist" 24 ErrClosed = fs.ErrClosed // "file already closed" 25 26 ErrNoDeadline = errNoDeadline() // "file type does not support deadline" 27 ErrDeadlineExceeded = errDeadlineExceeded() // "i/o timeout" 28) 29 30func errNoDeadline() error { return poll.ErrNoDeadline } 31 32// errDeadlineExceeded returns the value for os.ErrDeadlineExceeded. 33// This error comes from the internal/poll package, which is also 34// used by package net. Doing it this way ensures that the net 35// package will return os.ErrDeadlineExceeded for an exceeded deadline, 36// as documented by net.Conn.SetDeadline, without requiring any extra 37// work in the net package and without requiring the internal/poll 38// package to import os (which it can't, because that would be circular). 39func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded } 40 41type timeout interface { 42 Timeout() bool 43} 44 45// PathError records an error and the operation and file path that caused it. 46type PathError = fs.PathError 47 48// SyscallError records an error from a specific system call. 49type SyscallError struct { 50 Syscall string 51 Err error 52} 53 54func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() } 55 56func (e *SyscallError) Unwrap() error { return e.Err } 57 58// Timeout reports whether this error represents a timeout. 59func (e *SyscallError) Timeout() bool { 60 t, ok := e.Err.(timeout) 61 return ok && t.Timeout() 62} 63 64// NewSyscallError returns, as an error, a new [SyscallError] 65// with the given system call name and error details. 66// As a convenience, if err is nil, NewSyscallError returns nil. 67func NewSyscallError(syscall string, err error) error { 68 if err == nil { 69 return nil 70 } 71 return &SyscallError{syscall, err} 72} 73 74// IsExist returns a boolean indicating whether its argument is known to report 75// that a file or directory already exists. It is satisfied by [ErrExist] as 76// well as some syscall errors. 77// 78// This function predates [errors.Is]. It only supports errors returned by 79// the os package. New code should use errors.Is(err, fs.ErrExist). 80func IsExist(err error) bool { 81 return underlyingErrorIs(err, ErrExist) 82} 83 84// IsNotExist returns a boolean indicating whether its argument is known to 85// report that a file or directory does not exist. It is satisfied by 86// [ErrNotExist] as well as some syscall errors. 87// 88// This function predates [errors.Is]. It only supports errors returned by 89// the os package. New code should use errors.Is(err, fs.ErrNotExist). 90func IsNotExist(err error) bool { 91 return underlyingErrorIs(err, ErrNotExist) 92} 93 94// IsPermission returns a boolean indicating whether its argument is known to 95// report that permission is denied. It is satisfied by [ErrPermission] as well 96// as some syscall errors. 97// 98// This function predates [errors.Is]. It only supports errors returned by 99// the os package. New code should use errors.Is(err, fs.ErrPermission). 100func IsPermission(err error) bool { 101 return underlyingErrorIs(err, ErrPermission) 102} 103 104// IsTimeout returns a boolean indicating whether its argument is known 105// to report that a timeout occurred. 106// 107// This function predates [errors.Is], and the notion of whether an 108// error indicates a timeout can be ambiguous. For example, the Unix 109// error EWOULDBLOCK sometimes indicates a timeout and sometimes does not. 110// New code should use errors.Is with a value appropriate to the call 111// returning the error, such as [os.ErrDeadlineExceeded]. 112func IsTimeout(err error) bool { 113 terr, ok := underlyingError(err).(timeout) 114 return ok && terr.Timeout() 115} 116 117func underlyingErrorIs(err, target error) bool { 118 // Note that this function is not errors.Is: 119 // underlyingError only unwraps the specific error-wrapping types 120 // that it historically did, not all errors implementing Unwrap(). 121 err = underlyingError(err) 122 if err == target { 123 return true 124 } 125 // To preserve prior behavior, only examine syscall errors. 126 e, ok := err.(syscallErrorType) 127 return ok && e.Is(target) 128} 129 130// underlyingError returns the underlying error for known os error types. 131func underlyingError(err error) error { 132 switch err := err.(type) { 133 case *PathError: 134 return err.Err 135 case *LinkError: 136 return err.Err 137 case *SyscallError: 138 return err.Err 139 } 140 return err 141} 142