github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/syserror/syserror.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package syserror contains syscall error codes exported as error interface
    16  // instead of Errno. This allows for fast comparison and returns when the
    17  // comparand or return value is of type error because there is no need to
    18  // convert from Errno to an interface, i.e., runtime.convT2I isn't called.
    19  package syserror
    20  
    21  import (
    22  	"errors"
    23  
    24  	"golang.org/x/sys/unix"
    25  )
    26  
    27  // The following variables have the same meaning as their syscall equivalent.
    28  var (
    29  	EEXIST     = error(unix.EEXIST)
    30  	EFAULT     = error(unix.EFAULT)
    31  	EIDRM      = error(unix.EIDRM)
    32  	EINTR      = error(unix.EINTR)
    33  	EIO        = error(unix.EIO)
    34  	EISDIR     = error(unix.EISDIR)
    35  	ENOENT     = error(unix.ENOENT)
    36  	ENOEXEC    = error(unix.ENOEXEC)
    37  	ENOMEM     = error(unix.ENOMEM)
    38  	ENOTSOCK   = error(unix.ENOTSOCK)
    39  	ENOSPC     = error(unix.ENOSPC)
    40  	ENOSYS     = error(unix.ENOSYS)
    41  	ENOTDIR    = error(unix.ENOTDIR)
    42  	ENOTTY     = error(unix.ENOTTY)
    43  	EOPNOTSUPP = error(unix.EOPNOTSUPP)
    44  	ERANGE     = error(unix.ERANGE)
    45  	ESRCH      = error(unix.ESRCH)
    46  )
    47  
    48  var (
    49  	// ErrWouldBlock is an internal error used to indicate that an operation
    50  	// cannot be satisfied immediately, and should be retried at a later
    51  	// time, possibly when the caller has received a notification that the
    52  	// operation may be able to complete. It is used by implementations of
    53  	// the kio.File interface.
    54  	ErrWouldBlock = errors.New("request would block")
    55  
    56  	// ErrInterrupted is returned if a request is interrupted before it can
    57  	// complete.
    58  	ErrInterrupted = errors.New("request was interrupted")
    59  
    60  	// ErrExceedsFileSizeLimit is returned if a request would exceed the
    61  	// file's size limit.
    62  	ErrExceedsFileSizeLimit = errors.New("exceeds file size limit")
    63  )
    64  
    65  // errorMap is the map used to convert generic errors into errnos.
    66  var errorMap = map[error]unix.Errno{}
    67  
    68  // errorUnwrappers is an array of unwrap functions to extract typed errors.
    69  var errorUnwrappers = []func(error) (unix.Errno, bool){}
    70  
    71  // AddErrorTranslation allows modules to populate the error map by adding their
    72  // own translations during initialization. Returns if the error translation is
    73  // accepted or not. A pre-existing translation will not be overwritten by the
    74  // new translation.
    75  func AddErrorTranslation(from error, to unix.Errno) bool {
    76  	if _, ok := errorMap[from]; ok {
    77  		return false
    78  	}
    79  
    80  	errorMap[from] = to
    81  	return true
    82  }
    83  
    84  // AddErrorUnwrapper registers an unwrap method that can extract a concrete error
    85  // from a typed, but not initialized, error.
    86  func AddErrorUnwrapper(unwrap func(e error) (unix.Errno, bool)) {
    87  	errorUnwrappers = append(errorUnwrappers, unwrap)
    88  }
    89  
    90  // TranslateError translates errors to errnos, it will return false if
    91  // the error was not registered.
    92  func TranslateError(from error) (unix.Errno, bool) {
    93  	if err, ok := errorMap[from]; ok {
    94  		return err, true
    95  	}
    96  	// Try to unwrap the error if we couldn't match an error
    97  	// exactly.  This might mean that a package has its own
    98  	// error type.
    99  	for _, unwrap := range errorUnwrappers {
   100  		if err, ok := unwrap(from); ok {
   101  			return err, true
   102  		}
   103  	}
   104  	return 0, false
   105  }
   106  
   107  // ConvertIntr converts the provided error code (err) to another one (intr) if
   108  // the first error corresponds to an interrupted operation.
   109  func ConvertIntr(err, intr error) error {
   110  	if err == ErrInterrupted {
   111  		return intr
   112  	}
   113  	return err
   114  }
   115  
   116  // SyscallRestartErrno represents a ERESTART* errno defined in the Linux's kernel
   117  // include/linux/errno.h. These errnos are never returned to userspace
   118  // directly, but are used to communicate the expected behavior of an
   119  // interrupted syscall from the syscall to signal handling.
   120  type SyscallRestartErrno int
   121  
   122  // These numeric values are significant because ptrace syscall exit tracing can
   123  // observe them.
   124  //
   125  // For all of the following errnos, if the syscall is not interrupted by a
   126  // signal delivered to a user handler, the syscall is restarted.
   127  const (
   128  	// ERESTARTSYS is returned by an interrupted syscall to indicate that it
   129  	// should be converted to EINTR if interrupted by a signal delivered to a
   130  	// user handler without SA_RESTART set, and restarted otherwise.
   131  	ERESTARTSYS = SyscallRestartErrno(512)
   132  
   133  	// ERESTARTNOINTR is returned by an interrupted syscall to indicate that it
   134  	// should always be restarted.
   135  	ERESTARTNOINTR = SyscallRestartErrno(513)
   136  
   137  	// ERESTARTNOHAND is returned by an interrupted syscall to indicate that it
   138  	// should be converted to EINTR if interrupted by a signal delivered to a
   139  	// user handler, and restarted otherwise.
   140  	ERESTARTNOHAND = SyscallRestartErrno(514)
   141  
   142  	// ERESTART_RESTARTBLOCK is returned by an interrupted syscall to indicate
   143  	// that it should be restarted using a custom function. The interrupted
   144  	// syscall must register a custom restart function by calling
   145  	// Task.SetRestartSyscallFn.
   146  	ERESTART_RESTARTBLOCK = SyscallRestartErrno(516)
   147  )
   148  
   149  // Error implements error.Error.
   150  func (e SyscallRestartErrno) Error() string {
   151  	// Descriptions are borrowed from strace.
   152  	switch e {
   153  	case ERESTARTSYS:
   154  		return "to be restarted if SA_RESTART is set"
   155  	case ERESTARTNOINTR:
   156  		return "to be restarted"
   157  	case ERESTARTNOHAND:
   158  		return "to be restarted if no handler"
   159  	case ERESTART_RESTARTBLOCK:
   160  		return "interrupted by signal"
   161  	default:
   162  		return "(unknown interrupt error)"
   163  	}
   164  }
   165  
   166  // SyscallRestartErrnoFromReturn returns the SyscallRestartErrno represented by
   167  // rv, the value in a syscall return register.
   168  func SyscallRestartErrnoFromReturn(rv uintptr) (SyscallRestartErrno, bool) {
   169  	switch int(rv) {
   170  	case -int(ERESTARTSYS):
   171  		return ERESTARTSYS, true
   172  	case -int(ERESTARTNOINTR):
   173  		return ERESTARTNOINTR, true
   174  	case -int(ERESTARTNOHAND):
   175  		return ERESTARTNOHAND, true
   176  	case -int(ERESTART_RESTARTBLOCK):
   177  		return ERESTART_RESTARTBLOCK, true
   178  	default:
   179  		return 0, false
   180  	}
   181  }
   182  
   183  func init() {
   184  	AddErrorTranslation(ErrWouldBlock, unix.EWOULDBLOCK)
   185  	AddErrorTranslation(ErrInterrupted, unix.EINTR)
   186  	AddErrorTranslation(ErrExceedsFileSizeLimit, unix.EFBIG)
   187  }