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 }