diff --git a/src/windows/wslcsession/WSLCContainer.cpp b/src/windows/wslcsession/WSLCContainer.cpp index 182ca416c..a5a73fc3f 100644 --- a/src/windows/wslcsession/WSLCContainer.cpp +++ b/src/windows/wslcsession/WSLCContainer.cpp @@ -306,8 +306,41 @@ WSLCContainerNetworkType DockerNetworkModeToWSLCNetworkType(const std::string& m std::uint64_t ParseDockerTimestamp(const std::string& timestamp) { // Docker timestamps are UTC ISO 8601, e.g. "2026-03-05T10:30:00.123456789Z". + // This function returns epoch seconds, so strip optional fractional seconds before parsing. + std::string normalizedTimestamp = timestamp; + + const auto timeSeparator = normalizedTimestamp.find('T'); + if (timeSeparator != std::string::npos) + { + const auto fractionalSeparator = normalizedTimestamp.find('.', timeSeparator + 1); + const auto zuluPos = normalizedTimestamp.find('Z', timeSeparator + 1); + const auto plusOffsetPos = normalizedTimestamp.find('+', timeSeparator + 1); + const auto minusOffsetPos = normalizedTimestamp.find('-', timeSeparator + 1); + + auto timezonePos = std::string::npos; + if (zuluPos != std::string::npos) + { + timezonePos = zuluPos; + } + if (plusOffsetPos != std::string::npos && (timezonePos == std::string::npos || plusOffsetPos < timezonePos)) + { + timezonePos = plusOffsetPos; + } + if (minusOffsetPos != std::string::npos && (timezonePos == std::string::npos || minusOffsetPos < timezonePos)) + { + timezonePos = minusOffsetPos; + } + + if (fractionalSeparator != std::string::npos && (timezonePos == std::string::npos || fractionalSeparator < timezonePos)) + { + normalizedTimestamp.erase( + fractionalSeparator, + (timezonePos == std::string::npos) ? std::string::npos : (timezonePos - fractionalSeparator)); + } + } + std::chrono::sys_seconds utcSeconds; - std::istringstream stream(timestamp); + std::istringstream stream(normalizedTimestamp); stream >> std::chrono::parse("%FT%H:%M:%S%Z", utcSeconds); THROW_HR_IF_MSG(E_INVALIDARG, stream.fail(), "Failed to parse timestamp '%hs'", timestamp.c_str()); @@ -825,7 +858,9 @@ void WSLCContainerImpl::Stop(WSLCSignal Signal, LONG TimeoutSeconds, bool Kill) // Don't wait for the container to stop if we're not sending SIGKILL, since it may not stop the container. // N.B. If the signal was SIGTERM for instance, we'll receive the stop notification via OnEvent(). - bool waitForStop = !Kill || (SignalArg.value_or(WSLCSignalSIGKILL) == WSLCSignalSIGKILL); + // When Kill is true but the requested signal is not SIGKILL, do not wait synchronously for stop. + const bool isKillSignal = (SignalArg.value_or(WSLCSignalSIGKILL) == WSLCSignalSIGKILL); + const bool waitForStop = !Kill || isKillSignal; try { @@ -978,7 +1013,7 @@ void WSLCContainerImpl::Export(WSLCHandle OutHandle) const } // Release the lock so the container can still be interacted with while the export is in progress. - // Passed this point, no member variables can be accessed. + // Past this point, no member variables can be accessed. lock.reset(); io.Run({}); @@ -1795,7 +1830,7 @@ void WSLCContainerImpl::MapPorts() if (!e.VmMapping.VmPort) { // Reuse existing vm port allocation when possible. - // This is required because the same container can be bind the port number for different families or protocols. + // This is required because the same container can bind the port number for different families or protocols. auto existing = allocatedPorts.find(e.ContainerPort); if (existing != allocatedPorts.end()) { diff --git a/src/windows/wslcsession/WSLCSession.cpp b/src/windows/wslcsession/WSLCSession.cpp index 61c8f68c4..1707d41a0 100644 --- a/src/windows/wslcsession/WSLCSession.cpp +++ b/src/windows/wslcsession/WSLCSession.cpp @@ -29,6 +29,7 @@ using wsl::windows::service::wslc::WSLCVirtualMachine; constexpr auto c_containerdStorage = "/var/lib/docker"; constexpr auto c_containerdSocket = "/run/containerd/containerd.sock"; +constexpr auto c_dockerdReadyLogLine = "API listen on /var/run/docker.sock"; constexpr DWORD c_processTerminateTimeoutMs = 30 * 1000; constexpr DWORD c_processKillTimeoutMs = 10 * 1000; @@ -42,12 +43,14 @@ std::string IndentLines(const std::string& input, const std::string& prefix) } std::string result = prefix; - for (size_t i = 0; i < input.size(); i++) + for (auto it = input.begin(); it != input.end(); ++it) { - result.push_back(input[i]); - if (i + 1 < input.size()) + result.push_back(*it); + + const auto next = std::next(it); + if (next != input.end()) { - if (input[i] == '\n' || (input[i] == '\r' && input[i + 1] != '\n')) + if (*it == '\n' || (*it == '\r' && *next != '\n')) { result.append(prefix); } @@ -423,8 +426,6 @@ try return; } - constexpr auto c_dockerdReadyLogLine = "API listen on /var/run/docker.sock"; - std::string entry = {Buffer.begin(), Buffer.end()}; WSL_LOG( "ContainerdLog", @@ -765,7 +766,7 @@ try } }; - static constexpr char c_logId[] = "log"; + constexpr auto c_logId = "log"; auto flushLine = [&]() { if (needsNewline) @@ -1223,7 +1224,7 @@ try CATCH_AND_THROW_DOCKER_USER_ERROR("Failed to list images"); // Compute the number of entries - one entry per tag, or one per image if no tags - auto entries = std::accumulate(images.begin(), images.end(), 0, [](auto sum, const auto& e) { + auto entries = std::accumulate(images.begin(), images.end(), size_t{0}, [](auto sum, const auto& e) { return sum + (e.RepoTags.empty() ? 1 : e.RepoTags.size()); }); diff --git a/test/linux/unit_tests/lxtfs.c b/test/linux/unit_tests/lxtfs.c index dd6ed2153..43f99beb5 100644 --- a/test/linux/unit_tests/lxtfs.c +++ b/test/linux/unit_tests/lxtfs.c @@ -149,7 +149,7 @@ void LxtFsUtimeRoundToNt(struct timespec* Timespec); // // All real timestamps are offset from the year 2000 because FAT can only -// accept timestamps afer 1980. +// accept timestamps after 1980. BASIC_TEST_CASE BasicTestCases[] = { {{{FS_UNIX_TIME_2000 + 1111111, 2222222}, {FS_UNIX_TIME_2000 + 3333333, 4444444}}, @@ -231,7 +231,7 @@ Return Value: } snprintf(ExpectedOptions, sizeof(ExpectedOptions), "rw,%s", Options); - snprintf(ExpectedCombinedOptions, sizeof(ExpectedOptions), "rw,noatime,%s", Options); + snprintf(ExpectedCombinedOptions, sizeof(ExpectedCombinedOptions), "rw,noatime,%s", Options); LxtCheckResult(MountCheckIsMount(Target, ParentId, Source, "drvfs", MountRoot, "rw,noatime", ExpectedOptions, ExpectedCombinedOptions, 0)); } @@ -2056,7 +2056,7 @@ int LxtFsTestSetup(PLXT_ARGS Args, const char* TestDir, const char* DrvFsDir, bo DrvFsDir - Supplies the DrvFs directory to use for testing. This must start with a slash, and be relative from the root of the DrvFs mount. - UseDrvFs - Supplies a pa value indicating whether DrvFs is being used. + UseDrvFs - Supplies a value indicating whether DrvFs is being used. Return Value: @@ -2231,7 +2231,11 @@ Return Value: if (LxtFsTimestampDiff(Timestamp1, Timestamp2) <= 0) { - LxtLogError("Time %lld.%.9ld not greater than time %lld.%.9ld", Timestamp1, Timestamp2); + LxtLogError("Time %lld.%.9ld not greater than time %lld.%.9ld", + (long long)Timestamp1->tv_sec, + (long)Timestamp1->tv_nsec, + (long long)Timestamp2->tv_sec, + (long)Timestamp2->tv_nsec); Result = LXT_RESULT_FAILURE; goto ErrorExit; diff --git a/tools/setup-dev-env.ps1 b/tools/setup-dev-env.ps1 index 9b8ff6ac6..6bba3e1c0 100644 --- a/tools/setup-dev-env.ps1 +++ b/tools/setup-dev-env.ps1 @@ -55,6 +55,12 @@ else } $configPath = Join-Path $configDir $configFile +if (-not (Test-Path -LiteralPath $configPath -PathType Leaf)) +{ + Write-Host "Configuration file not found: $configPath" -ForegroundColor Red + Write-Host "Ensure the repository is fully checked out and the .config folder contains $configFile." -ForegroundColor Yellow + exit 1 +} # ── Run WinGet Configuration ──────────────────────────────────────── Write-Host "" @@ -72,7 +78,7 @@ if ($LASTEXITCODE -ne 0) winget configure -f "$configPath" --accept-configuration-agreements if ($LASTEXITCODE -ne 0) { - Write-Host "Failed to apply WinGet configuration file: $configFile" -ForegroundColor Red + Write-Host "Failed to apply WinGet configuration file: $configPath" -ForegroundColor Red exit 1 } diff --git a/tools/test/run-tests.ps1 b/tools/test/run-tests.ps1 index a2377ce43..6208b8cda 100644 --- a/tools/test/run-tests.ps1 +++ b/tools/test/run-tests.ps1 @@ -12,7 +12,7 @@ .PARAMETER TestDataPath Path to test data folder. Defaults to ".\test_data". .PARAMETER Package - Path to the wsl.msix package to install. Defaults to ".\wsl.msix". + Path to the wsl.msix package to install. Defaults to ".\installer.msix". .PARAMETER UnitTestsPath Path to the linux/unit_tests directory to copy and install the unit tests. .PARAMETER PullRequest @@ -103,9 +103,9 @@ if ($AttachDebugger) else { te.exe $teArgList - if (!$?) + if ($LASTEXITCODE -ne 0) { - exit 1 + exit $LASTEXITCODE } }