// 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 syserr contains sandbox-internal errors. These errors are distinct // from both the errors returned by host system calls and the errors returned // to sandboxed applications. package syserr import ( "fmt" "golang.org/x/sys/unix" "gvisor.dev/gvisor/pkg/abi/linux" "gvisor.dev/gvisor/pkg/syserror" ) // Error represents an internal error. type Error struct { // message is the human readable form of this Error. message string // noTranslation indicates that this Error cannot be translated to a // linux.Errno. noTranslation bool // errno is the linux.Errno this Error should be translated to. nil means // that this Error should be translated to a nil linux.Errno. errno *linux.Errno } // New creates a new Error and adds a translation for it. // // New must only be called at init. func New(message string, linuxTranslation *linux.Errno) *Error { err := &Error{message: message, errno: linuxTranslation} if linuxTranslation == nil { return err } // TODO(b/34162363): Remove this. errno := linuxTranslation.Number() if errno <= 0 || errno >= len(linuxBackwardsTranslations) { panic(fmt.Sprint("invalid errno: ", errno)) } e := error(unix.Errno(errno)) // syserror.ErrWouldBlock gets translated to syserror.EWOULDBLOCK and // enables proper blocking semantics. This should temporary address the // class of blocking bugs that keep popping up with the current state of // the error space. if e == syserror.EWOULDBLOCK { e = syserror.ErrWouldBlock } linuxBackwardsTranslations[errno] = linuxBackwardsTranslation{err: e, ok: true} return err } // NewDynamic creates a new error with a dynamic error message and an errno // translation. // // NewDynamic should only be used sparingly and not be used for static error // messages. Errors with static error messages should be declared with New as // global variables. func NewDynamic(message string, linuxTranslation *linux.Errno) *Error { return &Error{message: message, errno: linuxTranslation} } // NewWithoutTranslation creates a new Error. If translation is attempted on // the error, translation will fail. // // NewWithoutTranslation may be called at any time, but static errors should // be declared as global variables and dynamic errors should be used sparingly. func NewWithoutTranslation(message string) *Error { return &Error{message: message, noTranslation: true} } func newWithHost(message string, linuxTranslation *linux.Errno, hostErrno unix.Errno) *Error { e := New(message, linuxTranslation) addLinuxHostTranslation(hostErrno, e) return e } // String implements fmt.Stringer.String. func (e *Error) String() string { if e == nil { return "" } return e.message } type linuxBackwardsTranslation struct { err error ok bool } // TODO(b/34162363): Remove this. var linuxBackwardsTranslations [maxErrno]linuxBackwardsTranslation // ToError translates an Error to a corresponding error value. // // TODO(b/34162363): Remove this. func (e *Error) ToError() error { if e == nil { return nil } if e.noTranslation { panic(fmt.Sprintf("error %q does not support translation", e.message)) } if e.errno == nil { return nil } errno := e.errno.Number() if errno <= 0 || errno >= len(linuxBackwardsTranslations) || !linuxBackwardsTranslations[errno].ok { panic(fmt.Sprintf("unknown error %q (%d)", e.message, errno)) } return linuxBackwardsTranslations[errno].err } // ToLinux converts the Error to a Linux ABI error that can be returned to the // application. func (e *Error) ToLinux() *linux.Errno { if e.noTranslation { panic(fmt.Sprintf("No Linux ABI translation available for %q", e.message)) } return e.errno } // TODO(b/34162363): Remove or replace most of these errors. // // Some of the errors should be replaced with package specific errors and // others should be removed entirely. var ( ErrNotPermitted = newWithHost("operation not permitted", linux.EPERM, unix.EPERM) ErrNoFileOrDir = newWithHost("no such file or directory", linux.ENOENT, unix.ENOENT) ErrNoProcess = newWithHost("no such process", linux.ESRCH, unix.ESRCH) ErrInterrupted = newWithHost("interrupted system call", linux.EINTR, unix.EINTR) ErrIO = newWithHost("I/O error", linux.EIO, unix.EIO) ErrDeviceOrAddress = newWithHost("no such device or address", linux.ENXIO, unix.ENXIO) ErrTooManyArgs = newWithHost("argument list too long", linux.E2BIG, unix.E2BIG) ErrEcec = newWithHost("exec format error", linux.ENOEXEC, unix.ENOEXEC) ErrBadFD = newWithHost("bad file number", linux.EBADF, unix.EBADF) ErrNoChild = newWithHost("no child processes", linux.ECHILD, unix.ECHILD) ErrTryAgain = newWithHost("try again", linux.EAGAIN, unix.EAGAIN) ErrNoMemory = newWithHost("out of memory", linux.ENOMEM, unix.ENOMEM) ErrPermissionDenied = newWithHost("permission denied", linux.EACCES, unix.EACCES) ErrBadAddress = newWithHost("bad address", linux.EFAULT, unix.EFAULT) ErrNotBlockDevice = newWithHost("block device required", linux.ENOTBLK, unix.ENOTBLK) ErrBusy = newWithHost("device or resource busy", linux.EBUSY, unix.EBUSY) ErrExists = newWithHost("file exists", linux.EEXIST, unix.EEXIST) ErrCrossDeviceLink = newWithHost("cross-device link", linux.EXDEV, unix.EXDEV) ErrNoDevice = newWithHost("no such device", linux.ENODEV, unix.ENODEV) ErrNotDir = newWithHost("not a directory", linux.ENOTDIR, unix.ENOTDIR) ErrIsDir = newWithHost("is a directory", linux.EISDIR, unix.EISDIR) ErrInvalidArgument = newWithHost("invalid argument", linux.EINVAL, unix.EINVAL) ErrFileTableOverflow = newWithHost("file table overflow", linux.ENFILE, unix.ENFILE) ErrTooManyOpenFiles = newWithHost("too many open files", linux.EMFILE, unix.EMFILE) ErrNotTTY = newWithHost("not a typewriter", linux.ENOTTY, unix.ENOTTY) ErrTestFileBusy = newWithHost("text file busy", linux.ETXTBSY, unix.ETXTBSY) ErrFileTooBig = newWithHost("file too large", linux.EFBIG, unix.EFBIG) ErrNoSpace = newWithHost("no space left on device", linux.ENOSPC, unix.ENOSPC) ErrIllegalSeek = newWithHost("illegal seek", linux.ESPIPE, unix.ESPIPE) ErrReadOnlyFS = newWithHost("read-only file system", linux.EROFS, unix.EROFS) ErrTooManyLinks = newWithHost("too many links", linux.EMLINK, unix.EMLINK) ErrBrokenPipe = newWithHost("broken pipe", linux.EPIPE, unix.EPIPE) ErrDomain = newWithHost("math argument out of domain of func", linux.EDOM, unix.EDOM) ErrRange = newWithHost("math result not representable", linux.ERANGE, unix.ERANGE) ErrDeadlock = newWithHost("resource deadlock would occur", linux.EDEADLOCK, unix.EDEADLOCK) ErrNameTooLong = newWithHost("file name too long", linux.ENAMETOOLONG, unix.ENAMETOOLONG) ErrNoLocksAvailable = newWithHost("no record locks available", linux.ENOLCK, unix.ENOLCK) ErrInvalidSyscall = newWithHost("invalid system call number", linux.ENOSYS, unix.ENOSYS) ErrDirNotEmpty = newWithHost("directory not empty", linux.ENOTEMPTY, unix.ENOTEMPTY) ErrLinkLoop = newWithHost("too many symbolic links encountered", linux.ELOOP, unix.ELOOP) ErrNoMessage = newWithHost("no message of desired type", linux.ENOMSG, unix.ENOMSG) ErrIdentifierRemoved = newWithHost("identifier removed", linux.EIDRM, unix.EIDRM) ErrChannelOutOfRange = newWithHost("channel number out of range", linux.ECHRNG, unix.ECHRNG) ErrLevelTwoNotSynced = newWithHost("level 2 not synchronized", linux.EL2NSYNC, unix.EL2NSYNC) ErrLevelThreeHalted = newWithHost("level 3 halted", linux.EL3HLT, unix.EL3HLT) ErrLevelThreeReset = newWithHost("level 3 reset", linux.EL3RST, unix.EL3RST) ErrLinkNumberOutOfRange = newWithHost("link number out of range", linux.ELNRNG, unix.ELNRNG) ErrProtocolDriverNotAttached = newWithHost("protocol driver not attached", linux.EUNATCH, unix.EUNATCH) ErrNoCSIAvailable = newWithHost("no CSI structure available", linux.ENOCSI, unix.ENOCSI) ErrLevelTwoHalted = newWithHost("level 2 halted", linux.EL2HLT, unix.EL2HLT) ErrInvalidExchange = newWithHost("invalid exchange", linux.EBADE, unix.EBADE) ErrInvalidRequestDescriptor = newWithHost("invalid request descriptor", linux.EBADR, unix.EBADR) ErrExchangeFull = newWithHost("exchange full", linux.EXFULL, unix.EXFULL) ErrNoAnode = newWithHost("no anode", linux.ENOANO, unix.ENOANO) ErrInvalidRequestCode = newWithHost("invalid request code", linux.EBADRQC, unix.EBADRQC) ErrInvalidSlot = newWithHost("invalid slot", linux.EBADSLT, unix.EBADSLT) ErrBadFontFile = newWithHost("bad font file format", linux.EBFONT, unix.EBFONT) ErrNotStream = newWithHost("device not a stream", linux.ENOSTR, unix.ENOSTR) ErrNoDataAvailable = newWithHost("no data available", linux.ENODATA, unix.ENODATA) ErrTimerExpired = newWithHost("timer expired", linux.ETIME, unix.ETIME) ErrStreamsResourceDepleted = newWithHost("out of streams resources", linux.ENOSR, unix.ENOSR) ErrMachineNotOnNetwork = newWithHost("machine is not on the network", linux.ENONET, unix.ENONET) ErrPackageNotInstalled = newWithHost("package not installed", linux.ENOPKG, unix.ENOPKG) ErrIsRemote = newWithHost("object is remote", linux.EREMOTE, unix.EREMOTE) ErrNoLink = newWithHost("link has been severed", linux.ENOLINK, unix.ENOLINK) ErrAdvertise = newWithHost("advertise error", linux.EADV, unix.EADV) ErrSRMount = newWithHost("srmount error", linux.ESRMNT, unix.ESRMNT) ErrSendCommunication = newWithHost("communication error on send", linux.ECOMM, unix.ECOMM) ErrProtocol = newWithHost("protocol error", linux.EPROTO, unix.EPROTO) ErrMultihopAttempted = newWithHost("multihop attempted", linux.EMULTIHOP, unix.EMULTIHOP) ErrRFS = newWithHost("RFS specific error", linux.EDOTDOT, unix.EDOTDOT) ErrInvalidDataMessage = newWithHost("not a data message", linux.EBADMSG, unix.EBADMSG) ErrOverflow = newWithHost("value too large for defined data type", linux.EOVERFLOW, unix.EOVERFLOW) ErrNetworkNameNotUnique = newWithHost("name not unique on network", linux.ENOTUNIQ, unix.ENOTUNIQ) ErrFDInBadState = newWithHost("file descriptor in bad state", linux.EBADFD, unix.EBADFD) ErrRemoteAddressChanged = newWithHost("remote address changed", linux.EREMCHG, unix.EREMCHG) ErrSharedLibraryInaccessible = newWithHost("can not access a needed shared library", linux.ELIBACC, unix.ELIBACC) ErrCorruptedSharedLibrary = newWithHost("accessing a corrupted shared library", linux.ELIBBAD, unix.ELIBBAD) ErrLibSectionCorrupted = newWithHost(".lib section in a.out corrupted", linux.ELIBSCN, unix.ELIBSCN) ErrTooManySharedLibraries = newWithHost("attempting to link in too many shared libraries", linux.ELIBMAX, unix.ELIBMAX) ErrSharedLibraryExeced = newWithHost("cannot exec a shared library directly", linux.ELIBEXEC, unix.ELIBEXEC) ErrIllegalByteSequence = newWithHost("illegal byte sequence", linux.EILSEQ, unix.EILSEQ) ErrShouldRestart = newWithHost("interrupted system call should be restarted", linux.ERESTART, unix.ERESTART) ErrStreamPipe = newWithHost("streams pipe error", linux.ESTRPIPE, unix.ESTRPIPE) ErrTooManyUsers = newWithHost("too many users", linux.EUSERS, unix.EUSERS) ErrNotASocket = newWithHost("socket operation on non-socket", linux.ENOTSOCK, unix.ENOTSOCK) ErrDestinationAddressRequired = newWithHost("destination address required", linux.EDESTADDRREQ, unix.EDESTADDRREQ) ErrMessageTooLong = newWithHost("message too long", linux.EMSGSIZE, unix.EMSGSIZE) ErrWrongProtocolForSocket = newWithHost("protocol wrong type for socket", linux.EPROTOTYPE, unix.EPROTOTYPE) ErrProtocolNotAvailable = newWithHost("protocol not available", linux.ENOPROTOOPT, unix.ENOPROTOOPT) ErrProtocolNotSupported = newWithHost("protocol not supported", linux.EPROTONOSUPPORT, unix.EPROTONOSUPPORT) ErrSocketNotSupported = newWithHost("socket type not supported", linux.ESOCKTNOSUPPORT, unix.ESOCKTNOSUPPORT) ErrEndpointOperation = newWithHost("operation not supported on transport endpoint", linux.EOPNOTSUPP, unix.EOPNOTSUPP) ErrProtocolFamilyNotSupported = newWithHost("protocol family not supported", linux.EPFNOSUPPORT, unix.EPFNOSUPPORT) ErrAddressFamilyNotSupported = newWithHost("address family not supported by protocol", linux.EAFNOSUPPORT, unix.EAFNOSUPPORT) ErrAddressInUse = newWithHost("address already in use", linux.EADDRINUSE, unix.EADDRINUSE) ErrAddressNotAvailable = newWithHost("cannot assign requested address", linux.EADDRNOTAVAIL, unix.EADDRNOTAVAIL) ErrNetworkDown = newWithHost("network is down", linux.ENETDOWN, unix.ENETDOWN) ErrNetworkUnreachable = newWithHost("network is unreachable", linux.ENETUNREACH, unix.ENETUNREACH) ErrNetworkReset = newWithHost("network dropped connection because of reset", linux.ENETRESET, unix.ENETRESET) ErrConnectionAborted = newWithHost("software caused connection abort", linux.ECONNABORTED, unix.ECONNABORTED) ErrConnectionReset = newWithHost("connection reset by peer", linux.ECONNRESET, unix.ECONNRESET) ErrNoBufferSpace = newWithHost("no buffer space available", linux.ENOBUFS, unix.ENOBUFS) ErrAlreadyConnected = newWithHost("transport endpoint is already connected", linux.EISCONN, unix.EISCONN) ErrNotConnected = newWithHost("transport endpoint is not connected", linux.ENOTCONN, unix.ENOTCONN) ErrShutdown = newWithHost("cannot send after transport endpoint shutdown", linux.ESHUTDOWN, unix.ESHUTDOWN) ErrTooManyRefs = newWithHost("too many references: cannot splice", linux.ETOOMANYREFS, unix.ETOOMANYREFS) ErrTimedOut = newWithHost("connection timed out", linux.ETIMEDOUT, unix.ETIMEDOUT) ErrConnectionRefused = newWithHost("connection refused", linux.ECONNREFUSED, unix.ECONNREFUSED) ErrHostDown = newWithHost("host is down", linux.EHOSTDOWN, unix.EHOSTDOWN) ErrNoRoute = newWithHost("no route to host", linux.EHOSTUNREACH, unix.EHOSTUNREACH) ErrAlreadyInProgress = newWithHost("operation already in progress", linux.EALREADY, unix.EALREADY) ErrInProgress = newWithHost("operation now in progress", linux.EINPROGRESS, unix.EINPROGRESS) ErrStaleFileHandle = newWithHost("stale file handle", linux.ESTALE, unix.ESTALE) ErrStructureNeedsCleaning = newWithHost("structure needs cleaning", linux.EUCLEAN, unix.EUCLEAN) ErrIsNamedFile = newWithHost("is a named type file", linux.ENOTNAM, unix.ENOTNAM) ErrRemoteIO = newWithHost("remote I/O error", linux.EREMOTEIO, unix.EREMOTEIO) ErrQuotaExceeded = newWithHost("quota exceeded", linux.EDQUOT, unix.EDQUOT) ErrNoMedium = newWithHost("no medium found", linux.ENOMEDIUM, unix.ENOMEDIUM) ErrWrongMediumType = newWithHost("wrong medium type", linux.EMEDIUMTYPE, unix.EMEDIUMTYPE) ErrCanceled = newWithHost("operation canceled", linux.ECANCELED, unix.ECANCELED) ErrNoKey = newWithHost("required key not available", linux.ENOKEY, unix.ENOKEY) ErrKeyExpired = newWithHost("key has expired", linux.EKEYEXPIRED, unix.EKEYEXPIRED) ErrKeyRevoked = newWithHost("key has been revoked", linux.EKEYREVOKED, unix.EKEYREVOKED) ErrKeyRejected = newWithHost("key was rejected by service", linux.EKEYREJECTED, unix.EKEYREJECTED) ErrOwnerDied = newWithHost("owner died", linux.EOWNERDEAD, unix.EOWNERDEAD) ErrNotRecoverable = newWithHost("state not recoverable", linux.ENOTRECOVERABLE, unix.ENOTRECOVERABLE) // ErrWouldBlock translates to EWOULDBLOCK which is the same as EAGAIN // on Linux. ErrWouldBlock = New("operation would block", linux.EWOULDBLOCK) ) // FromError converts a generic error to an *Error. // // TODO(b/34162363): Remove this function. func FromError(err error) *Error { if err == nil { return nil } if errno, ok := err.(unix.Errno); ok { return FromHost(errno) } if errno, ok := syserror.TranslateError(err); ok { return FromHost(errno) } panic("unknown error: " + err.Error()) }