Source file src/os/error.go

     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  
     5  package os
     6  
     7  import (
     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].
    16  var (
    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  
    30  func 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).
    39  func errDeadlineExceeded() error { return poll.ErrDeadlineExceeded }
    40  
    41  type timeout interface {
    42  	Timeout() bool
    43  }
    44  
    45  // PathError records an error and the operation and file path that caused it.
    46  type PathError = fs.PathError
    47  
    48  // SyscallError records an error from a specific system call.
    49  type SyscallError struct {
    50  	Syscall string
    51  	Err     error
    52  }
    53  
    54  func (e *SyscallError) Error() string { return e.Syscall + ": " + e.Err.Error() }
    55  
    56  func (e *SyscallError) Unwrap() error { return e.Err }
    57  
    58  // Timeout reports whether this error represents a timeout.
    59  func (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.
    67  func 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).
    80  func 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).
    90  func 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).
   100  func 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].
   112  func IsTimeout(err error) bool {
   113  	terr, ok := underlyingError(err).(timeout)
   114  	return ok && terr.Timeout()
   115  }
   116  
   117  func 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.
   131  func 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  

View as plain text