Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions _codeql_detected_source_root
133 changes: 66 additions & 67 deletions src/windows/wslaservice/exe/WSLASession.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,68 @@ bool IsContainerNameValid(LPCSTR Name)
return length > 0 && length <= WSLA_MAX_CONTAINER_NAME_LENGTH;
}

/// <summary>
/// Handles image-like operations (save image, export container) by managing the HTTP response
/// and relaying data to an output handle.
/// </summary>
/// <param name="RequestCodePair">The HTTP response code and request context from the Docker client</param>
/// <param name="OutputHandle">Handle to the output file where data will be written</param>
/// <param name="SessionTerminatingEvent">Event handle to monitor for session termination</param>
/// <param name="ErrorInfo">Optional pointer to receive error information</param>
/// <param name="FailureMessageFormat">Format string for error messages (must contain %hs for error text)</param>
/// <param name="OnCompletedCallback">Optional callback invoked when data relay completes (not when entire operation succeeds)</param>
/// <exception cref="HRESULT">Throws E_FAIL if the HTTP response code is not 200, or E_ABORT if session terminates</exception>
void HandleImageLikeOperation(
std::pair<uint32_t, std::unique_ptr<DockerHTTPClient::HTTPRequestContext>>& RequestCodePair,
ULONG OutputHandle,
HANDLE SessionTerminatingEvent,
WSLA_ERROR_INFO* ErrorInfo,
const char* FailureMessageFormat,
std::function<void()> OnCompletedCallback = nullptr)
{
wil::unique_handle imageFileHandle{wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(OutputHandle))};

relay::MultiHandleWait io;
auto onCompleted = [&, OnCompletedCallback]() {
io.Cancel();
if (OnCompletedCallback)
{
OnCompletedCallback();
}
};
std::string errorJson;
auto accumulateError = [&](const gsl::span<char>& buffer) {
// If the operation failed, accumulate the error message.
errorJson.append(buffer.data(), buffer.size());
};

if (RequestCodePair.first != 200)
{
io.AddHandle(std::make_unique<relay::ReadHandle>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()}, accumulateError));
}
else
{
io.AddHandle(std::make_unique<relay::RelayHandle<relay::HTTPChunkBasedReadHandle>>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()},
common::relay::HandleWrapper{std::move(imageFileHandle), std::move(onCompleted)}));
io.AddHandle(std::make_unique<relay::EventHandle>(SessionTerminatingEvent, [&]() { THROW_HR(E_ABORT); }));
}

io.Run({});

if (RequestCodePair.first != 200)
{
// Operation failed, parse the error message.
auto error = wsl::shared::FromJson<docker_schema::ErrorResponse>(errorJson.c_str());
if (ErrorInfo != nullptr)
{
ErrorInfo->UserErrorMessage = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(error.message.c_str()).release();
}
THROW_HR_MSG(E_FAIL, FailureMessageFormat, error.message.c_str());
}
}

} // namespace

WSLASession::WSLASession(ULONG id, const WSLA_SESSION_SETTINGS& Settings, wil::unique_tokeninfo_ptr<TOKEN_USER>&& TokenInfo, bool Elevated) :
Expand Down Expand Up @@ -531,42 +593,11 @@ CATCH_RETURN();
void WSLASession::ExportContainerImpl(
std::pair<uint32_t, std::unique_ptr<DockerHTTPClient::HTTPRequestContext>>& RequestCodePair, ULONG OutputHandle, WSLA_ERROR_INFO* Error)
{
wil::unique_handle imageFileHandle{wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(OutputHandle))};
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_dockerClient.has_value());
relay::MultiHandleWait io;
std::optional<boost::beast::http::status> importResult;
auto onCompleted = [&]() {
io.Cancel();
auto onCompletedCallback = []() {
WSL_LOG("OnCompletedCalledForExport", TraceLoggingValue("OnCompletedCalledForExport", "Content"));
};
std::string errorJson;
auto accumulateError = [&](const gsl::span<char>& buffer) {
// If the export failed, accumulate the error message.
errorJson.append(buffer.data(), buffer.size());
};
if (RequestCodePair.first != 200)
{
io.AddHandle(std::make_unique<relay::ReadHandle>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()}, accumulateError));
}
else
{
io.AddHandle(std::make_unique<relay::RelayHandle<relay::HTTPChunkBasedReadHandle>>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()},
common::relay::HandleWrapper{std::move(imageFileHandle), std::move(onCompleted)}));
io.AddHandle(std::make_unique<relay::EventHandle>(m_sessionTerminatingEvent.get(), [&]() { THROW_HR(E_ABORT); }));
}
io.Run({});
if (RequestCodePair.first != 200)
{
// Export failed, parse the error message.
auto error = wsl::shared::FromJson<docker_schema::ErrorResponse>(errorJson.c_str());
if (Error != nullptr)
{
Error->UserErrorMessage = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(error.message.c_str()).release();
}
THROW_HR_MSG(E_FAIL, "Container export failed: %hs", error.message.c_str());
}

HandleImageLikeOperation(RequestCodePair, OutputHandle, m_sessionTerminatingEvent.get(), Error, "Container export failed: %hs", onCompletedCallback);
}

HRESULT WSLASession::SaveImage(ULONG OutHandle, LPCSTR ImageNameOrID, IProgressCallback* ProgressCallback, WSLA_ERROR_INFO* Error)
Expand All @@ -584,39 +615,7 @@ CATCH_RETURN();

void WSLASession::SaveImageImpl(std::pair<uint32_t, std::unique_ptr<DockerHTTPClient::HTTPRequestContext>>& RequestCodePair, ULONG OutputHandle, WSLA_ERROR_INFO* Error)
{
wil::unique_handle imageFileHandle{wsl::windows::common::wslutil::DuplicateHandleFromCallingProcess(ULongToHandle(OutputHandle))};
THROW_HR_IF(HRESULT_FROM_WIN32(ERROR_INVALID_STATE), !m_dockerClient.has_value());
relay::MultiHandleWait io;
std::optional<boost::beast::http::status> importResult;
auto onCompleted = [&]() { io.Cancel(); };
std::string errorJson;
auto accumulateError = [&](const gsl::span<char>& buffer) {
// If the save failed, accumulate the error message.
errorJson.append(buffer.data(), buffer.size());
};
if (RequestCodePair.first != 200)
{
io.AddHandle(std::make_unique<relay::ReadHandle>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()}, std::move(accumulateError)));
}
else
{
io.AddHandle(std::make_unique<relay::RelayHandle<relay::HTTPChunkBasedReadHandle>>(
common::relay::HandleWrapper{RequestCodePair.second->stream.native_handle()},
common::relay::HandleWrapper{std::move(imageFileHandle), std::move(onCompleted)}));
io.AddHandle(std::make_unique<relay::EventHandle>(m_sessionTerminatingEvent.get(), [&]() { THROW_HR(E_ABORT); }));
}
io.Run({});
if (RequestCodePair.first != 200)
{
// Save failed, parse the error message.
auto error = wsl::shared::FromJson<docker_schema::ErrorResponse>(errorJson.c_str());
if (Error != nullptr)
{
Error->UserErrorMessage = wil::make_unique_ansistring<wil::unique_cotaskmem_ansistring>(error.message.c_str()).release();
}
THROW_HR_MSG(E_FAIL, "Image save failed: %hs", error.message.c_str());
}
HandleImageLikeOperation(RequestCodePair, OutputHandle, m_sessionTerminatingEvent.get(), Error, "Image save failed: %hs");
}

HRESULT WSLASession::ListImages(WSLA_IMAGE_INFORMATION** Images, ULONG* Count)
Expand Down