diff --git a/Cargo.lock b/Cargo.lock index 36b6299f50..dc58dc695d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -744,6 +744,7 @@ dependencies = [ "futures", "indexmap", "lazy_static", + "libc", "log", "parking_lot", "pin-project", @@ -2021,9 +2022,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.103" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6" +checksum = "a60553f9a9e039a333b4e9b20573b9e9b9c0bb3a11e201ccc48ef4283456d673" [[package]] name = "libffi" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2e180a494f..32527180b9 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -62,7 +62,7 @@ http = "0.2.4" import_map = "0.3.3" jsonc-parser = { version = "0.17.0", features = ["serde"] } lazy_static = "1.4.0" -libc = "0.2.101" +libc = "0.2.106" log = { version = "0.4.14", features = ["serde"] } lspower = "1.1.0" notify = "5.0.0-pre.12" diff --git a/core/01_core.js b/core/01_core.js index 24b844453c..9d4bab65d5 100644 --- a/core/01_core.js +++ b/core/01_core.js @@ -119,6 +119,10 @@ const err = errorBuilder ? errorBuilder(res.message) : new Error( `Unregistered error class: "${className}"\n ${res.message}\n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass().`, ); + // Set .code if error was a known OS error, see error_codes.rs + if (res.code) { + err.code = res.code; + } // Strip unwrapOpResult() and errorBuilder() calls from stack trace ErrorCaptureStackTrace(err, unwrapOpResult); throw err; diff --git a/core/Cargo.toml b/core/Cargo.toml index 0d31029888..698717778b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -18,6 +18,7 @@ futures = "0.3.16" # TODO(lucacasonato): unlock when https://github.com/tkaitchuck/aHash/issues/95 is resolved indexmap = "=1.6.2" lazy_static = "1.4.0" +libc = "0.2.106" log = "0.4.14" parking_lot = "0.11.1" pin-project = "1.0.7" diff --git a/core/error_codes.rs b/core/error_codes.rs new file mode 100644 index 0000000000..fc613426fd --- /dev/null +++ b/core/error_codes.rs @@ -0,0 +1,212 @@ +use crate::error::AnyError; + +pub(crate) fn get_error_code(err: &AnyError) -> Option<&'static str> { + err + .downcast_ref::() + .map(|e| match e.raw_os_error() { + Some(code) => get_os_error_code(code), + None => get_io_error_code(e), + }) + .and_then(|code| match code.is_empty() { + true => None, + false => Some(code), + }) +} + +fn get_io_error_code(err: &std::io::Error) -> &'static str { + // not exhaustive but simple and possibly sufficient once `io_error_more` is stabilized (https://github.com/rust-lang/rust/issues/86442) + // inversion of https://github.com/rust-lang/rust/blob/dca3f1b786efd27be3b325ed1e01e247aa589c3b/library/std/src/sys/unix/mod.rs#L138-L185 + // TODO(@AaronO): revisit as `io_error_more` lands in rust stable + use std::io::ErrorKind; + match err.kind() { + // ErrorKind::ArgumentListTooLong => "E2BIG", + ErrorKind::AddrInUse => "EADDRINUSE", + ErrorKind::AddrNotAvailable => "EADDRNOTAVAIL", + // ErrorKind::ResourceBusy => "EBUSY", + ErrorKind::ConnectionAborted => "ECONNABORTED", + ErrorKind::ConnectionRefused => "ECONNREFUSED", + ErrorKind::ConnectionReset => "ECONNRESET", + // ErrorKind::Deadlock => "EDEADLK", + // ErrorKind::FilesystemQuotaExceeded => "EDQUOT", + ErrorKind::AlreadyExists => "EEXIST", + // ErrorKind::FileTooLarge => "EFBIG", + // ErrorKind::HostUnreachable => "EHOSTUNREACH", + ErrorKind::Interrupted => "EINTR", + ErrorKind::InvalidInput => "EINVAL", + // ErrorKind::IsADirectory => "EISDIR", + // ErrorKind::FilesystemLoop => "ELOOP", + ErrorKind::NotFound => "ENOENT", + ErrorKind::OutOfMemory => "ENOMEM", + // ErrorKind::StorageFull => "ENOSPC", + ErrorKind::Unsupported => "ENOSYS", + // ErrorKind::TooManyLinks => "EMLINK", + // ErrorKind::FilenameTooLong => "ENAMETOOLONG", + // ErrorKind::NetworkDown => "ENETDOWN", + // ErrorKind::NetworkUnreachable => "ENETUNREACH", + ErrorKind::NotConnected => "ENOTCONN", + // ErrorKind::NotADirectory => "ENOTDIR", + // ErrorKind::DirectoryNotEmpty => "ENOTEMPTY", + ErrorKind::BrokenPipe => "EPIPE", + // ErrorKind::ReadOnlyFilesystem => "EROFS", + // ErrorKind::NotSeekable => "ESPIPE", + // ErrorKind::StaleNetworkFileHandle => "ESTALE", + ErrorKind::TimedOut => "ETIMEDOUT", + // ErrorKind::ExecutableFileBusy => "ETXTBSY", + // ErrorKind::CrossesDevices => "EXDEV", + ErrorKind::PermissionDenied => "EACCES", // NOTE: Collides with EPERM ... + _ => "", + } +} + +/// Maps OS errno codes to string names +/// derived from libuv: https://github.com/libuv/libuv/blob/26b2e5dbb6301756644d6e4cf6ca9c49c00513d3/include/uv/errno.h +/// generated with tools/codegen_error_codes.js +#[cfg(unix)] +fn get_os_error_code(errno: i32) -> &'static str { + match errno { + libc::E2BIG => "E2BIG", + libc::EACCES => "EACCES", + libc::EADDRINUSE => "EADDRINUSE", + libc::EADDRNOTAVAIL => "EADDRNOTAVAIL", + libc::EAFNOSUPPORT => "EAFNOSUPPORT", + libc::EAGAIN => "EAGAIN", + libc::EALREADY => "EALREADY", + libc::EBADF => "EBADF", + libc::EBUSY => "EBUSY", + libc::ECANCELED => "ECANCELED", + libc::ECONNABORTED => "ECONNABORTED", + libc::ECONNREFUSED => "ECONNREFUSED", + libc::ECONNRESET => "ECONNRESET", + libc::EEXIST => "EEXIST", + libc::EFAULT => "EFAULT", + libc::EHOSTUNREACH => "EHOSTUNREACH", + libc::EINVAL => "EINVAL", + libc::EIO => "EIO", + libc::EISCONN => "EISCONN", + libc::EISDIR => "EISDIR", + libc::ELOOP => "ELOOP", + libc::EMFILE => "EMFILE", + libc::EMSGSIZE => "EMSGSIZE", + libc::ENAMETOOLONG => "ENAMETOOLONG", + libc::ENETUNREACH => "ENETUNREACH", + libc::ENOBUFS => "ENOBUFS", + libc::ENOENT => "ENOENT", + libc::ENOMEM => "ENOMEM", + libc::ENOSPC => "ENOSPC", + libc::ENOTCONN => "ENOTCONN", + libc::ENOTEMPTY => "ENOTEMPTY", + libc::ENOTSOCK => "ENOTSOCK", + libc::ENOTSUP => "ENOTSUP", + libc::EPERM => "EPERM", + libc::EPIPE => "EPIPE", + libc::EPROTONOSUPPORT => "EPROTONOSUPPORT", + libc::EROFS => "EROFS", + libc::ETIMEDOUT => "ETIMEDOUT", + libc::EXDEV => "EXDEV", + libc::ESOCKTNOSUPPORT => "ESOCKTNOSUPPORT", + _ => "", + } +} + +#[cfg(windows)] +fn get_os_error_code(errno: i32) -> &'static str { + match errno { + 998 => "EACCES", // ERROR_NOACCESS + 10013 => "EACCES", // WSAEACCES + 1920 => "EACCES", // ERROR_CANT_ACCESS_FILE + 1227 => "EADDRINUSE", // ERROR_ADDRESS_ALREADY_ASSOCIATED + 10048 => "EADDRINUSE", // WSAEADDRINUSE + 10049 => "EADDRNOTAVAIL", // WSAEADDRNOTAVAIL + 10047 => "EAFNOSUPPORT", // WSAEAFNOSUPPORT + 10035 => "EAGAIN", // WSAEWOULDBLOCK + 10037 => "EALREADY", // WSAEALREADY + 1004 => "EBADF", // ERROR_INVALID_FLAGS + 6 => "EBADF", // ERROR_INVALID_HANDLE + 33 => "EBUSY", // ERROR_LOCK_VIOLATION + 231 => "EBUSY", // ERROR_PIPE_BUSY + 32 => "EBUSY", // ERROR_SHARING_VIOLATION + 995 => "ECANCELED", // ERROR_OPERATION_ABORTED + 10004 => "ECANCELED", // WSAEINTR + 1236 => "ECONNABORTED", // ERROR_CONNECTION_ABORTED + 10053 => "ECONNABORTED", // WSAECONNABORTED + 1225 => "ECONNREFUSED", // ERROR_CONNECTION_REFUSED + 10061 => "ECONNREFUSED", // WSAECONNREFUSED + 64 => "ECONNRESET", // ERROR_NETNAME_DELETED + 10054 => "ECONNRESET", // WSAECONNRESET + 183 => "EEXIST", // ERROR_ALREADY_EXISTS + 80 => "EEXIST", // ERROR_FILE_EXISTS + 111 => "EFAULT", // ERROR_BUFFER_OVERFLOW + 10014 => "EFAULT", // WSAEFAULT + 1232 => "EHOSTUNREACH", // ERROR_HOST_UNREACHABLE + 10065 => "EHOSTUNREACH", // WSAEHOSTUNREACH + 122 => "EINVAL", // ERROR_INSUFFICIENT_BUFFER + 13 => "EINVAL", // ERROR_INVALID_DATA + 87 => "EINVAL", // ERROR_INVALID_PARAMETER + 1464 => "EINVAL", // ERROR_SYMLINK_NOT_SUPPORTED + 10022 => "EINVAL", // WSAEINVAL + 10046 => "EINVAL", // WSAEPFNOSUPPORT + 1102 => "EIO", // ERROR_BEGINNING_OF_MEDIA + 1111 => "EIO", // ERROR_BUS_RESET + 23 => "EIO", // ERROR_CRC + 1166 => "EIO", // ERROR_DEVICE_DOOR_OPEN + 1165 => "EIO", // ERROR_DEVICE_REQUIRES_CLEANING + 1393 => "EIO", // ERROR_DISK_CORRUPT + 1129 => "EIO", // ERROR_EOM_OVERFLOW + 1101 => "EIO", // ERROR_FILEMARK_DETECTED + 31 => "EIO", // ERROR_GEN_FAILURE + 1106 => "EIO", // ERROR_INVALID_BLOCK_LENGTH + 1117 => "EIO", // ERROR_IO_DEVICE + 1104 => "EIO", // ERROR_NO_DATA_DETECTED + 205 => "EIO", // ERROR_NO_SIGNAL_SENT + 110 => "EIO", // ERROR_OPEN_FAILED + 1103 => "EIO", // ERROR_SETMARK_DETECTED + 156 => "EIO", // ERROR_SIGNAL_REFUSED + 10056 => "EISCONN", // WSAEISCONN + 1921 => "ELOOP", // ERROR_CANT_RESOLVE_FILENAME + 4 => "EMFILE", // ERROR_TOO_MANY_OPEN_FILES + 10024 => "EMFILE", // WSAEMFILE + 10040 => "EMSGSIZE", // WSAEMSGSIZE + 206 => "ENAMETOOLONG", // ERROR_FILENAME_EXCED_RANGE + 1231 => "ENETUNREACH", // ERROR_NETWORK_UNREACHABLE + 10051 => "ENETUNREACH", // WSAENETUNREACH + 10055 => "ENOBUFS", // WSAENOBUFS + 161 => "ENOENT", // ERROR_BAD_PATHNAME + 267 => "ENOENT", // ERROR_DIRECTORY + 203 => "ENOENT", // ERROR_ENVVAR_NOT_FOUND + 2 => "ENOENT", // ERROR_FILE_NOT_FOUND + 123 => "ENOENT", // ERROR_INVALID_NAME + 15 => "ENOENT", // ERROR_INVALID_DRIVE + 4392 => "ENOENT", // ERROR_INVALID_REPARSE_DATA + 126 => "ENOENT", // ERROR_MOD_NOT_FOUND + 3 => "ENOENT", // ERROR_PATH_NOT_FOUND + 11001 => "ENOENT", // WSAHOST_NOT_FOUND + 11004 => "ENOENT", // WSANO_DATA + 8 => "ENOMEM", // ERROR_NOT_ENOUGH_MEMORY + 14 => "ENOMEM", // ERROR_OUTOFMEMORY + 82 => "ENOSPC", // ERROR_CANNOT_MAKE + 112 => "ENOSPC", // ERROR_DISK_FULL + 277 => "ENOSPC", // ERROR_EA_TABLE_FULL + 1100 => "ENOSPC", // ERROR_END_OF_MEDIA + 39 => "ENOSPC", // ERROR_HANDLE_DISK_FULL + 2250 => "ENOTCONN", // ERROR_NOT_CONNECTED + 10057 => "ENOTCONN", // WSAENOTCONN + 145 => "ENOTEMPTY", // ERROR_DIR_NOT_EMPTY + 10038 => "ENOTSOCK", // WSAENOTSOCK + 50 => "ENOTSUP", // ERROR_NOT_SUPPORTED + 5 => "EPERM", // ERROR_ACCESS_DENIED + 1314 => "EPERM", // ERROR_PRIVILEGE_NOT_HELD + 230 => "EPIPE", // ERROR_BAD_PIPE + 232 => "EPIPE", // ERROR_NO_DATA + 233 => "EPIPE", // ERROR_PIPE_NOT_CONNECTED + 10058 => "EPIPE", // WSAESHUTDOWN + 10043 => "EPROTONOSUPPORT", // WSAEPROTONOSUPPORT + 19 => "EROFS", // ERROR_WRITE_PROTECT + 121 => "ETIMEDOUT", // ERROR_SEM_TIMEOUT + 10060 => "ETIMEDOUT", // WSAETIMEDOUT + 17 => "EXDEV", // ERROR_NOT_SAME_DEVICE + 1 => "EISDIR", // ERROR_INVALID_FUNCTION + 208 => "E2BIG", // ERROR_META_EXPANSION_TOO_LONG + 10044 => "ESOCKTNOSUPPORT", // WSAESOCKTNOSUPPORT + _ => "", + } +} diff --git a/core/lib.rs b/core/lib.rs index eff1397cf4..abf7028134 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -3,6 +3,7 @@ mod async_cancel; mod async_cell; mod bindings; pub mod error; +mod error_codes; mod extensions; mod flags; mod gotham_state; diff --git a/core/ops.rs b/core/ops.rs index ab233ef296..f62fe7a8e6 100644 --- a/core/ops.rs +++ b/core/ops.rs @@ -140,6 +140,7 @@ pub struct OpError { #[serde(rename = "$err_class_name")] class_name: &'static str, message: String, + code: Option<&'static str>, } pub fn serialize_op_result( @@ -151,6 +152,7 @@ pub fn serialize_op_result( Err(err) => OpResult::Err(OpError { class_name: (state.borrow().get_error_class_fn)(&err), message: err.to_string(), + code: crate::error_codes::get_error_code(&err), }), } } diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml index 60f11d7030..fd09a9bc33 100644 --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -69,7 +69,7 @@ fs3 = "0.5.0" http = "0.2.4" hyper = { version = "0.14.12", features = ["server", "stream", "http1", "http2", "runtime"] } lazy_static = "1.4.0" -libc = "0.2.101" +libc = "0.2.106" log = "0.4.14" notify = "=5.0.0-pre.12" regex = "1.5.4"