github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/errors/linuxerr/internal.go (about)

     1  // Copyright 2021 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 linuxerr
    16  
    17  import (
    18  	"github.com/nicocha30/gvisor-ligolo/pkg/abi/linux/errno"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/errors"
    20  )
    21  
    22  var (
    23  	// ErrWouldBlock is an internal error used to indicate that an operation
    24  	// cannot be satisfied immediately, and should be retried at a later
    25  	// time, possibly when the caller has received a notification that the
    26  	// operation may be able to complete. It is used by implementations of
    27  	// the kio.File interface.
    28  	ErrWouldBlock = errors.New(errno.EWOULDBLOCK, "request would block")
    29  
    30  	// ErrInterrupted is returned if a request is interrupted before it can
    31  	// complete.
    32  	ErrInterrupted = errors.New(errno.EINTR, "request was interrupted")
    33  
    34  	// ErrExceedsFileSizeLimit is returned if a request would exceed the
    35  	// file's size limit.
    36  	ErrExceedsFileSizeLimit = errors.New(errno.E2BIG, "exceeds file size limit")
    37  )
    38  
    39  var errorMap = map[error]*errors.Error{
    40  	ErrWouldBlock:           EWOULDBLOCK,
    41  	ErrInterrupted:          EINTR,
    42  	ErrExceedsFileSizeLimit: EFBIG,
    43  }
    44  
    45  // errorUnwrappers is an array of unwrap functions to extract typed errors.
    46  var errorUnwrappers = []func(error) (*errors.Error, bool){}
    47  
    48  // AddErrorUnwrapper registers an unwrap method that can extract a concrete error
    49  // from a typed, but not initialized, error.
    50  func AddErrorUnwrapper(unwrap func(e error) (*errors.Error, bool)) {
    51  	errorUnwrappers = append(errorUnwrappers, unwrap)
    52  }
    53  
    54  // TranslateError translates errors to errnos, it will return false if
    55  // the error was not registered.
    56  func TranslateError(from error) (*errors.Error, bool) {
    57  	if err, ok := errorMap[from]; ok {
    58  		return err, true
    59  	}
    60  	// Try to unwrap the error if we couldn't match an error
    61  	// exactly.  This might mean that a package has its own
    62  	// error type.
    63  	for _, unwrap := range errorUnwrappers {
    64  		if err, ok := unwrap(from); ok {
    65  			return err, true
    66  		}
    67  	}
    68  	return nil, false
    69  }
    70  
    71  // These errors are significant because ptrace syscall exit tracing can
    72  // observe them.
    73  //
    74  // For all of the following errors, if the syscall is not interrupted by a
    75  // signal delivered to a user handler, the syscall is restarted.
    76  var (
    77  	// ERESTARTSYS is returned by an interrupted syscall to indicate that it
    78  	// should be converted to EINTR if interrupted by a signal delivered to a
    79  	// user handler without SA_RESTART set, and restarted otherwise.
    80  	ERESTARTSYS = errors.New(errno.ERESTARTSYS, "to be restarted if SA_RESTART is set")
    81  
    82  	// ERESTARTNOINTR is returned by an interrupted syscall to indicate that it
    83  	// should always be restarted.
    84  	ERESTARTNOINTR = errors.New(errno.ERESTARTNOINTR, "to be restarted")
    85  
    86  	// ERESTARTNOHAND is returned by an interrupted syscall to indicate that it
    87  	// should be converted to EINTR if interrupted by a signal delivered to a
    88  	// user handler, and restarted otherwise.
    89  	ERESTARTNOHAND = errors.New(errno.ERESTARTNOHAND, "to be restarted if no handler")
    90  
    91  	// ERESTART_RESTARTBLOCK is returned by an interrupted syscall to indicate
    92  	// that it should be restarted using a custom function. The interrupted
    93  	// syscall must register a custom restart function by calling
    94  	// Task.SetRestartSyscallFn.
    95  	ERESTART_RESTARTBLOCK = errors.New(errno.ERESTART_RESTARTBLOCK, "interrupted by signal")
    96  )
    97  
    98  var restartMap = map[int]*errors.Error{
    99  	-int(errno.ERESTARTSYS):           ERESTARTSYS,
   100  	-int(errno.ERESTARTNOINTR):        ERESTARTNOINTR,
   101  	-int(errno.ERESTARTNOHAND):        ERESTARTNOHAND,
   102  	-int(errno.ERESTART_RESTARTBLOCK): ERESTART_RESTARTBLOCK,
   103  }
   104  
   105  // IsRestartError checks if a given error is a restart error.
   106  func IsRestartError(err error) bool {
   107  	switch err {
   108  	case ERESTARTSYS, ERESTARTNOINTR, ERESTARTNOHAND, ERESTART_RESTARTBLOCK:
   109  		return true
   110  	default:
   111  		return false
   112  	}
   113  }
   114  
   115  // SyscallRestartErrorFromReturn returns the SyscallRestartErrno represented by
   116  // rv, the value in a syscall return register.
   117  func SyscallRestartErrorFromReturn(rv uintptr) (*errors.Error, bool) {
   118  	err, ok := restartMap[int(rv)]
   119  	return err, ok
   120  }
   121  
   122  // ConvertIntr converts the provided error code (err) to another one (intr) if
   123  // the first error corresponds to an interrupted operation.
   124  func ConvertIntr(err, intr error) error {
   125  	if err == ErrInterrupted {
   126  		return intr
   127  	}
   128  	return err
   129  }