static Client::Error
make_error(const util::IpcError& ipc_error)
{
- auto failure = (ipc_error.failure == util::IpcError::Failure::timeout)
- ? Client::Failure::timeout
- : Client::Failure::error;
- return Client::Error(failure, ipc_error.message);
+ switch (ipc_error.failure) {
+ case util::IpcError::Failure::error:
+ return Client::Error(Client::Failure::error, ipc_error.message);
+ case util::IpcError::Failure::permission_denied:
+ return Client::Error(Client::Failure::permission_denied, ipc_error.message);
+ case util::IpcError::Failure::timeout:
+ return Client::Error(Client::Failure::timeout, ipc_error.message);
+ default:
+ return Client::Error(Client::Failure::error, "internal error");
+ }
}
Client::Client(std::chrono::milliseconds data_timeout,
};
enum class Failure {
- error, // Operation error (protocol error, connection failure, etc.)
- timeout, // Timeout (data timeout or request timeout exceeded)
+ error, // Operation error (protocol/connection error, etc.)
+ permission_denied, // IPC operation prohibited by the operating system
+ timeout, // Timeout (data timeout or request timeout exceeded)
};
struct Error
connect_result.error().message,
timer.measure_ms());
+ if (connect_result.error().failure == Client::Failure::permission_denied) {
+ LOG("Not spawning remote storage helper since IPC access was denied");
+ return tl::unexpected(Failure::error);
+ }
+
if (!spawn) {
return {};
}
// We have the lock. Check again if another process spawned while we waited.
timer.reset();
- if (m_client.connect(m_endpoint)) {
+ connect_result = m_client.connect(m_endpoint);
+ if (connect_result) {
LOG(
"Connected to remote storage helper spawned by another process ({:.2f}"
" ms)",
timer.measure_ms());
return finalize_connection();
}
+ if (connect_result.error().failure == Client::Failure::permission_denied) {
+ LOG("Not spawning remote storage helper since IPC access was denied");
+ return tl::unexpected(Failure::error);
+ }
// No helper exists, spawn it now.
timer.reset();
return finalize_connection();
}
+ if (connect_result.error().failure == Client::Failure::permission_denied) {
+ LOG(
+ "Giving up connecting to spawned remote storage helper since IPC"
+ " access was denied");
+ return tl::unexpected(Failure::error);
+ }
+
std::this_thread::sleep_for(sleep_duration);
}
struct IpcError
{
enum class Failure {
- error, // Permanent error (connection refused, invalid state, etc.)
- timeout, // Transient timeout (may succeed on retry)
+ error, // Permanent error (connection refused, etc.)
+ permission_denied, // IPC operation prohibited by the operating system
+ timeout, // Transient timeout (may succeed on retry)
};
Failure failure;
namespace {
+IpcError::Failure
+failure_from_errno(int error_number)
+{
+ return error_number == EACCES || error_number == EPERM
+ ? IpcError::Failure::permission_denied
+ : IpcError::Failure::error;
+}
+
std::chrono::milliseconds
remaining_timeout(const std::chrono::steady_clock::time_point start_time,
const std::chrono::milliseconds timeout)
IpcError(IpcError::Failure::timeout, FMT("{} timeout", action)));
}
if (errno != EINTR) {
- return tl::unexpected(IpcError(IpcError::Failure::error,
+ return tl::unexpected(IpcError(failure_from_errno(errno),
FMT("Poll failed: {}", strerror(errno))));
}
}
m_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (m_fd == -1) {
return tl::unexpected(
- IpcError(IpcError::Failure::error,
+ IpcError(failure_from_errno(errno),
FMT("Failed to create socket: {}", strerror(errno))));
}
int saved_errno = errno;
close();
return tl::unexpected(IpcError(
- IpcError::Failure::error,
+ failure_from_errno(saved_errno),
FMT("Failed to set socket non-blocking: {}", strerror(saved_errno))));
}
int saved_errno = errno;
close();
return tl::unexpected(IpcError(
- IpcError::Failure::error,
+ failure_from_errno(saved_errno),
FMT("Connection to {} failed: {}", endpoint, strerror(saved_errno))));
}
int saved_errno = errno;
close();
return tl::unexpected(
- IpcError(IpcError::Failure::error,
+ IpcError(failure_from_errno(saved_errno),
FMT("Failed to get socket error: {}", strerror(saved_errno))));
}
if (error != 0) {
close();
return tl::unexpected(
- IpcError(IpcError::Failure::error,
+ IpcError(failure_from_errno(error),
FMT("Connection failed: {}", strerror(error))));
}
}
DWORD error = GetLastError();
if (error != ERROR_PIPE_BUSY) {
- return tl::unexpected(IpcError(IpcError::Failure::error,
+ const auto failure = error == ERROR_ACCESS_DENIED
+ ? IpcError::Failure::permission_denied
+ : IpcError::Failure::error;
+ return tl::unexpected(IpcError(failure,
FMT("Failed to connect to pipe {}: {}",
endpoint,
util::win32_error_message(error))));