diff --git a/app/buck2_error/BUCK b/app/buck2_error/BUCK index ecbcfe684aa4..262df4e9911e 100644 --- a/app/buck2_error/BUCK +++ b/app/buck2_error/BUCK @@ -32,6 +32,7 @@ rust_library( "fbsource//third-party/rust:hex", "fbsource//third-party/rust:http", "fbsource//third-party/rust:hyper", + "fbsource//third-party/rust:libc", "fbsource//third-party/rust:prost", "fbsource//third-party/rust:prost-types", "fbsource//third-party/rust:ref-cast", diff --git a/app/buck2_error/Cargo.toml b/app/buck2_error/Cargo.toml index a11c8d50b038..78d42d8bc2c1 100644 --- a/app/buck2_error/Cargo.toml +++ b/app/buck2_error/Cargo.toml @@ -17,6 +17,7 @@ fancy-regex = { workspace = true } hex = { workspace = true } http = { workspace = true } hyper = { workspace = true } +libc = { workspace = true } prost = { workspace = true } prost-types = { workspace = true } ref-cast = { workspace = true } diff --git a/app/buck2_error/src/conversion/serde_json.rs b/app/buck2_error/src/conversion/serde_json.rs index 43ab2619faf0..a2de90118662 100644 --- a/app/buck2_error/src/conversion/serde_json.rs +++ b/app/buck2_error/src/conversion/serde_json.rs @@ -9,13 +9,19 @@ use serde_json::error::Category; +use crate::conversion::stds::io::io_error_kind_to_error_tag; + impl From for crate::Error { #[cold] #[track_caller] fn from(value: serde_json::Error) -> Self { let error_tag = match value.classify() { Category::Data | Category::Syntax => crate::ErrorTag::Input, - Category::Eof | Category::Io => crate::ErrorTag::Tier0, + Category::Eof => crate::ErrorTag::Tier0, + Category::Io => match value.io_error_kind() { + Some(error_kind) => io_error_kind_to_error_tag(error_kind), + None => crate::ErrorTag::Tier0, + }, }; crate::conversion::from_any_with_tag(value, error_tag) diff --git a/app/buck2_error/src/conversion/stds.rs b/app/buck2_error/src/conversion/stds.rs index 066006ea466b..d5894326baef 100644 --- a/app/buck2_error/src/conversion/stds.rs +++ b/app/buck2_error/src/conversion/stds.rs @@ -9,7 +9,7 @@ mod array; mod convert; -mod io; +pub(crate) mod io; mod num; mod path; mod str; diff --git a/app/buck2_error/src/conversion/stds/io.rs b/app/buck2_error/src/conversion/stds/io.rs index 3f893fc78305..5996c74b29c8 100644 --- a/app/buck2_error/src/conversion/stds/io.rs +++ b/app/buck2_error/src/conversion/stds/io.rs @@ -7,10 +7,56 @@ * of this source tree. */ -impl From for crate::Error { +use std::io; + +use buck2_data::error::ErrorTag; + +// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- +// "The process cannot access the file because it is being used by another process." +const ERROR_SHARING_VIOLATION: i32 = 32; + +pub(crate) fn io_error_kind_to_error_tag(kind: io::ErrorKind) -> ErrorTag { + match kind { + io::ErrorKind::NotFound => ErrorTag::IoNotFound, + io::ErrorKind::PermissionDenied => ErrorTag::IoPermissionDenied, + io::ErrorKind::TimedOut => ErrorTag::IoTimeout, + io::ErrorKind::ExecutableFileBusy => ErrorTag::IoExecutableFileBusy, + io::ErrorKind::BrokenPipe => ErrorTag::IoBrokenPipe, + io::ErrorKind::StorageFull => ErrorTag::IoStorageFull, + io::ErrorKind::ConnectionAborted => ErrorTag::IoConnectionAborted, + _ => ErrorTag::IoSystem, + } +} + +fn io_error_kind_tag(e: &io::Error) -> ErrorTag { + let kind_tag = io_error_kind_to_error_tag(e.kind()); + if kind_tag != ErrorTag::IoSystem { + return kind_tag; + } + + if let Some(os_error_code) = e.raw_os_error() { + 'from_os: { + let from_os = match os_error_code { + libc::ENOTCONN => ErrorTag::IoNotConnected, + libc::ECONNABORTED => ErrorTag::IoConnectionAborted, + _ => break 'from_os, + }; + return from_os; + } + + if cfg!(windows) && os_error_code == ERROR_SHARING_VIOLATION { + return ErrorTag::IoWindowsSharingViolation; + } + } + + ErrorTag::IoSystem +} + +impl From for crate::Error { #[cold] #[track_caller] fn from(value: std::io::Error) -> Self { - crate::conversion::from_any_with_tag(value, crate::ErrorTag::IoSystem) + let error_tag = io_error_kind_tag(&value); + crate::conversion::from_any_with_tag(value, error_tag) } }