diff options
Diffstat (limited to 'pkg/syserror/syserror.go')
-rw-r--r-- | pkg/syserror/syserror.go | 153 |
1 files changed, 153 insertions, 0 deletions
diff --git a/pkg/syserror/syserror.go b/pkg/syserror/syserror.go new file mode 100644 index 000000000..345653544 --- /dev/null +++ b/pkg/syserror/syserror.go @@ -0,0 +1,153 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package syserror contains syscall error codes exported as error interface +// instead of Errno. This allows for fast comparison and returns when the +// comparand or return value is of type error because there is no need to +// convert from Errno to an interface, i.e., runtime.convT2I isn't called. +package syserror + +import ( + "errors" + "syscall" +) + +// The following variables have the same meaning as their syscall equivalent. +var ( + E2BIG = error(syscall.E2BIG) + EACCES = error(syscall.EACCES) + EAGAIN = error(syscall.EAGAIN) + EBADF = error(syscall.EBADF) + EBUSY = error(syscall.EBUSY) + ECHILD = error(syscall.ECHILD) + ECONNREFUSED = error(syscall.ECONNREFUSED) + ECONNRESET = error(syscall.ECONNRESET) + EDEADLK = error(syscall.EDEADLK) + EEXIST = error(syscall.EEXIST) + EFAULT = error(syscall.EFAULT) + EFBIG = error(syscall.EFBIG) + EIDRM = error(syscall.EIDRM) + EINTR = error(syscall.EINTR) + EINVAL = error(syscall.EINVAL) + EIO = error(syscall.EIO) + EISDIR = error(syscall.EISDIR) + ELIBBAD = error(syscall.ELIBBAD) + ELOOP = error(syscall.ELOOP) + EMFILE = error(syscall.EMFILE) + EMSGSIZE = error(syscall.EMSGSIZE) + ENAMETOOLONG = error(syscall.ENAMETOOLONG) + ENOATTR = ENODATA + ENODATA = error(syscall.ENODATA) + ENODEV = error(syscall.ENODEV) + ENOENT = error(syscall.ENOENT) + ENOEXEC = error(syscall.ENOEXEC) + ENOLCK = error(syscall.ENOLCK) + ENOLINK = error(syscall.ENOLINK) + ENOMEM = error(syscall.ENOMEM) + ENOSPC = error(syscall.ENOSPC) + ENOSYS = error(syscall.ENOSYS) + ENOTDIR = error(syscall.ENOTDIR) + ENOTEMPTY = error(syscall.ENOTEMPTY) + ENOTSUP = error(syscall.ENOTSUP) + ENOTTY = error(syscall.ENOTTY) + ENXIO = error(syscall.ENXIO) + EOPNOTSUPP = error(syscall.EOPNOTSUPP) + EOVERFLOW = error(syscall.EOVERFLOW) + EPERM = error(syscall.EPERM) + EPIPE = error(syscall.EPIPE) + ERANGE = error(syscall.ERANGE) + EROFS = error(syscall.EROFS) + ESPIPE = error(syscall.ESPIPE) + ESRCH = error(syscall.ESRCH) + ETIMEDOUT = error(syscall.ETIMEDOUT) + EUSERS = error(syscall.EUSERS) + EWOULDBLOCK = error(syscall.EWOULDBLOCK) + EXDEV = error(syscall.EXDEV) +) + +var ( + // ErrWouldBlock is an internal error used to indicate that an operation + // cannot be satisfied immediately, and should be retried at a later + // time, possibly when the caller has received a notification that the + // operation may be able to complete. It is used by implementations of + // the kio.File interface. + ErrWouldBlock = errors.New("request would block") + + // ErrInterrupted is returned if a request is interrupted before it can + // complete. + ErrInterrupted = errors.New("request was interrupted") + + // ErrExceedsFileSizeLimit is returned if a request would exceed the + // file's size limit. + ErrExceedsFileSizeLimit = errors.New("exceeds file size limit") +) + +// errorMap is the map used to convert generic errors into errnos. +var errorMap = map[error]syscall.Errno{} + +// errorUnwrappers is an array of unwrap functions to extract typed errors. +var errorUnwrappers = []func(error) (syscall.Errno, bool){} + +// AddErrorTranslation allows modules to populate the error map by adding their +// own translations during initialization. Returns if the error translation is +// accepted or not. A pre-existing translation will not be overwritten by the +// new translation. +func AddErrorTranslation(from error, to syscall.Errno) bool { + if _, ok := errorMap[from]; ok { + return false + } + + errorMap[from] = to + return true +} + +// AddErrorUnwrapper registers an unwrap method that can extract a concrete error +// from a typed, but not initialized, error. +func AddErrorUnwrapper(unwrap func(e error) (syscall.Errno, bool)) { + errorUnwrappers = append(errorUnwrappers, unwrap) +} + +// TranslateError translates errors to errnos, it will return false if +// the error was not registered. +func TranslateError(from error) (syscall.Errno, bool) { + err, ok := errorMap[from] + if ok { + return err, ok + } + // Try to unwrap the error if we couldn't match an error + // exactly. This might mean that a package has its own + // error type. + for _, unwrap := range errorUnwrappers { + err, ok := unwrap(from) + if ok { + return err, ok + } + } + return 0, false +} + +// ConvertIntr converts the provided error code (err) to another one (intr) if +// the first error corresponds to an interrupted operation. +func ConvertIntr(err, intr error) error { + if err == ErrInterrupted { + return intr + } + return err +} + +func init() { + AddErrorTranslation(ErrWouldBlock, syscall.EWOULDBLOCK) + AddErrorTranslation(ErrInterrupted, syscall.EINTR) + AddErrorTranslation(ErrExceedsFileSizeLimit, syscall.EFBIG) +} |