From b7fd471f84276ce5d2b0ad91d4601ce7b6f22df1 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Fri, 9 Jan 2026 20:54:10 +0300 Subject: [PATCH 1/2] libutil: Inline windows-error.hh into error.hh This way each consumer of NativeSysError doesn't have to also conditionally include the windows-error.hh, which is very cumbersome. And we can't include windows-error.hh in error.hh because of a circular import. --- src/libstore/windows/pathlocks.cc | 1 - src/libutil/file-descriptor.cc | 1 - src/libutil/fs-sink.cc | 1 - src/libutil/include/nix/util/error.hh | 85 ++++++++++++++----- src/libutil/include/nix/util/muxable-pipe.hh | 1 - src/libutil/serialise.cc | 1 - src/libutil/windows/current-process.cc | 2 +- src/libutil/windows/file-descriptor.cc | 1 - src/libutil/windows/file-system.cc | 1 - .../windows/include/nix/util/meson.build | 1 - .../windows/include/nix/util/windows-error.hh | 54 ------------ src/libutil/windows/muxable-pipe.cc | 1 - src/libutil/windows/processes.cc | 1 - src/libutil/windows/users.cc | 1 - src/libutil/windows/windows-async-pipe.cc | 1 - src/libutil/windows/windows-error.cc | 4 +- 16 files changed, 70 insertions(+), 87 deletions(-) delete mode 100644 src/libutil/windows/include/nix/util/windows-error.hh diff --git a/src/libstore/windows/pathlocks.cc b/src/libstore/windows/pathlocks.cc index 32d9e7c0f..e37b1e15b 100644 --- a/src/libstore/windows/pathlocks.cc +++ b/src/libstore/windows/pathlocks.cc @@ -7,7 +7,6 @@ # include # include # include -# include "nix/util/windows-error.hh" namespace nix { diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 6e07e6e88..6c6b1f9d1 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -6,7 +6,6 @@ #ifdef _WIN32 # include # include -# include "nix/util/windows-error.hh" #endif namespace nix { diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 521a10c9a..2ba3d98bc 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -7,7 +7,6 @@ #ifdef _WIN32 # include # include "nix/util/file-path.hh" -# include "nix/util/windows-error.hh" #endif #include "util-config-private.hh" diff --git a/src/libutil/include/nix/util/error.hh b/src/libutil/include/nix/util/error.hh index af1bd68af..542fc8559 100644 --- a/src/libutil/include/nix/util/error.hh +++ b/src/libutil/include/nix/util/error.hh @@ -27,6 +27,9 @@ #include #include #include +#ifdef _WIN32 +# include +#endif namespace nix { @@ -288,25 +291,6 @@ public: } }; -#ifdef _WIN32 -namespace windows { -class WinError; -} -#endif - -/** - * Convenience alias for when we use a `errno`-based error handling - * function on Unix, and `GetLastError()`-based error handling on on - * Windows. - */ -using NativeSysError = -#ifdef _WIN32 - windows::WinError -#else - SysError -#endif - ; - /** * Throw an exception for the purpose of checking that exception * handling works; see 'initLibUtil()'. @@ -326,4 +310,67 @@ void panic(std::string_view msg); */ [[gnu::noinline, gnu::cold, noreturn]] void unreachable(std::source_location loc = std::source_location::current()); +#ifdef _WIN32 + +namespace windows { + +/** + * Windows Error type. + * + * Unless you need to catch a specific error number, don't catch this in + * portable code. Catch `SystemError` instead. + */ +class WinError : public SystemError +{ +public: + DWORD lastError; + + /** + * Construct using the explicitly-provided error number. + * `FormatMessageA` will be used to try to add additional + * information to the message. + */ + template + WinError(DWORD lastError, const Args &... args) + : SystemError("") + , lastError(lastError) + { + auto hf = HintFmt(args...); + err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError)); + } + + /** + * Construct using `GetLastError()` and the ambient "last error". + * + * Be sure to not perform another last-error-modifying operation + * before calling this constructor! + */ + template + WinError(const Args &... args) + : WinError(GetLastError(), args...) + { + } + +private: + + std::string renderError(DWORD lastError); +}; + +} // namespace windows + +#endif + +/** + * Convenience alias for when we use a `errno`-based error handling + * function on Unix, and `GetLastError()`-based error handling on on + * Windows. + */ +using NativeSysError = +#ifdef _WIN32 + windows::WinError +#else + SysError +#endif + ; + } // namespace nix diff --git a/src/libutil/include/nix/util/muxable-pipe.hh b/src/libutil/include/nix/util/muxable-pipe.hh index f15c8e5f8..f0f762d60 100644 --- a/src/libutil/include/nix/util/muxable-pipe.hh +++ b/src/libutil/include/nix/util/muxable-pipe.hh @@ -10,7 +10,6 @@ # include #else # include -# include "nix/util/windows-error.hh" #endif namespace nix { diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 403539dcc..8297f5599 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -13,7 +13,6 @@ #ifdef _WIN32 # include -# include "nix/util/windows-error.hh" #else # include #endif diff --git a/src/libutil/windows/current-process.cc b/src/libutil/windows/current-process.cc index 4bc866bb3..d4392e227 100644 --- a/src/libutil/windows/current-process.cc +++ b/src/libutil/windows/current-process.cc @@ -1,5 +1,5 @@ #include "nix/util/current-process.hh" -#include "nix/util/windows-error.hh" +#include "nix/util/error.hh" #include #ifdef _WIN32 diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc index 57994eac0..cf4bccc11 100644 --- a/src/libutil/windows/file-descriptor.cc +++ b/src/libutil/windows/file-descriptor.cc @@ -2,7 +2,6 @@ #include "nix/util/signals.hh" #include "nix/util/finally.hh" #include "nix/util/serialise.hh" -#include "nix/util/windows-error.hh" #include "nix/util/file-path.hh" #include diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc index 79931d591..b93fd0d7d 100644 --- a/src/libutil/windows/file-system.cc +++ b/src/libutil/windows/file-system.cc @@ -1,5 +1,4 @@ #include "nix/util/file-system.hh" -#include "nix/util/windows-error.hh" #include "nix/util/logging.hh" namespace nix { diff --git a/src/libutil/windows/include/nix/util/meson.build b/src/libutil/windows/include/nix/util/meson.build index 1bd56c4bd..08285c3b9 100644 --- a/src/libutil/windows/include/nix/util/meson.build +++ b/src/libutil/windows/include/nix/util/meson.build @@ -5,5 +5,4 @@ include_dirs += include_directories('../..') headers += files( 'signals-impl.hh', 'windows-async-pipe.hh', - 'windows-error.hh', ) diff --git a/src/libutil/windows/include/nix/util/windows-error.hh b/src/libutil/windows/include/nix/util/windows-error.hh deleted file mode 100644 index a45425ad4..000000000 --- a/src/libutil/windows/include/nix/util/windows-error.hh +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once -///@file - -#ifdef _WIN32 -# include - -# include "nix/util/error.hh" - -namespace nix::windows { - -/** - * Windows Error type. - * - * Unless you need to catch a specific error number, don't catch this in - * portable code. Catch `SystemError` instead. - */ -class WinError : public SystemError -{ -public: - DWORD lastError; - - /** - * Construct using the explicitly-provided error number. - * `FormatMessageA` will be used to try to add additional - * information to the message. - */ - template - WinError(DWORD lastError, const Args &... args) - : SystemError("") - , lastError(lastError) - { - auto hf = HintFmt(args...); - err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError)); - } - - /** - * Construct using `GetLastError()` and the ambient "last error". - * - * Be sure to not perform another last-error-modifying operation - * before calling this constructor! - */ - template - WinError(const Args &... args) - : WinError(GetLastError(), args...) - { - } - -private: - - std::string renderError(DWORD lastError); -}; - -} // namespace nix::windows -#endif diff --git a/src/libutil/windows/muxable-pipe.cc b/src/libutil/windows/muxable-pipe.cc index b2eff70e6..f258f0f41 100644 --- a/src/libutil/windows/muxable-pipe.cc +++ b/src/libutil/windows/muxable-pipe.cc @@ -1,6 +1,5 @@ #ifdef _WIN32 # include -# include "nix/util/windows-error.hh" # include "nix/util/logging.hh" # include "nix/util/util.hh" diff --git a/src/libutil/windows/processes.cc b/src/libutil/windows/processes.cc index f8f2900e5..597be27f0 100644 --- a/src/libutil/windows/processes.cc +++ b/src/libutil/windows/processes.cc @@ -10,7 +10,6 @@ #include "nix/util/serialise.hh" #include "nix/util/file-system.hh" #include "nix/util/util.hh" -#include "nix/util/windows-error.hh" #include #include diff --git a/src/libutil/windows/users.cc b/src/libutil/windows/users.cc index eb92e7ab6..36392ff50 100644 --- a/src/libutil/windows/users.cc +++ b/src/libutil/windows/users.cc @@ -2,7 +2,6 @@ #include "nix/util/users.hh" #include "nix/util/environment-variables.hh" #include "nix/util/file-system.hh" -#include "nix/util/windows-error.hh" #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN diff --git a/src/libutil/windows/windows-async-pipe.cc b/src/libutil/windows/windows-async-pipe.cc index f6a82a139..09b72277a 100644 --- a/src/libutil/windows/windows-async-pipe.cc +++ b/src/libutil/windows/windows-async-pipe.cc @@ -2,7 +2,6 @@ #ifdef _WIN32 # include "nix/util/windows-async-pipe.hh" -# include "nix/util/windows-error.hh" namespace nix::windows { diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc index dd731dce2..f04b70a21 100644 --- a/src/libutil/windows/windows-error.cc +++ b/src/libutil/windows/windows-error.cc @@ -1,5 +1,7 @@ #ifdef _WIN32 -# include "nix/util/windows-error.hh" + +# include "nix/util/error.hh" + # include # define WIN32_LEAN_AND_MEAN # include From ccdd1f1c65a7326f41d698a522a1102347af7f41 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Fri, 9 Jan 2026 21:06:29 +0300 Subject: [PATCH 2/2] libstore: Fix mingw build This also adds a utility for opening a file descriptor from a path in readonly mode. Previous commit helps a bit with error handling, since now we just throw a NativeSysError. --- src/libstore/remote-fs-accessor.cc | 4 +++- src/libutil/include/nix/util/file-system.hh | 7 +++++++ src/libutil/include/nix/util/nar-accessor.hh | 3 ++- src/libutil/nar-accessor.cc | 13 ++++--------- src/libutil/unix/file-system.cc | 5 +++++ src/libutil/windows/file-system.cc | 16 ++++++++++++++-- 6 files changed, 35 insertions(+), 13 deletions(-) diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc index da232de2e..0fd0945da 100644 --- a/src/libstore/remote-fs-accessor.cc +++ b/src/libstore/remote-fs-accessor.cc @@ -21,7 +21,9 @@ RemoteFSAccessor::RemoteFSAccessor( std::filesystem::path RemoteFSAccessor::makeCacheFile(std::string_view hashPart, const std::string & ext) { assert(cacheDir); - return (*cacheDir / hashPart) + "." + ext; + auto res = (*cacheDir / hashPart); + res.concat(concatStrings(".", ext)); + return res; } ref RemoteFSAccessor::addToCache(std::string_view hashPart, std::string && nar) diff --git a/src/libutil/include/nix/util/file-system.hh b/src/libutil/include/nix/util/file-system.hh index ab1a90647..f27394b08 100644 --- a/src/libutil/include/nix/util/file-system.hh +++ b/src/libutil/include/nix/util/file-system.hh @@ -164,6 +164,13 @@ std::filesystem::path readLink(const std::filesystem::path & path); */ Descriptor openDirectory(const std::filesystem::path & path); +/** + * Open a `Descriptor` with read-only access to the given file. + * + * @note For directories use @ref openDirectory. + */ +Descriptor openFileReadonly(const std::filesystem::path & path); + /** * Read the contents of a file into a string. */ diff --git a/src/libutil/include/nix/util/nar-accessor.hh b/src/libutil/include/nix/util/nar-accessor.hh index 745c79f60..0a801711b 100644 --- a/src/libutil/include/nix/util/nar-accessor.hh +++ b/src/libutil/include/nix/util/nar-accessor.hh @@ -4,6 +4,7 @@ #include "nix/util/memory-source-accessor.hh" #include +#include #include @@ -30,7 +31,7 @@ using GetNarBytes = std::function; /** * The canonical GetNarBytes function for a seekable Source. */ -GetNarBytes seekableGetNarBytes(const Path & path); +GetNarBytes seekableGetNarBytes(const std::filesystem::path & path); GetNarBytes seekableGetNarBytes(Descriptor fd); diff --git a/src/libutil/nar-accessor.cc b/src/libutil/nar-accessor.cc index e43d43aac..7387007e6 100644 --- a/src/libutil/nar-accessor.cc +++ b/src/libutil/nar-accessor.cc @@ -1,6 +1,7 @@ #include "nix/util/nar-accessor.hh" #include "nix/util/file-descriptor.hh" #include "nix/util/archive.hh" +#include "nix/util/error.hh" #include #include @@ -263,17 +264,11 @@ ref makeLazyNarAccessor(Source & source, GetNarBytes getNarBytes return make_ref(source, getNarBytes); } -GetNarBytes seekableGetNarBytes(const Path & path) +GetNarBytes seekableGetNarBytes(const std::filesystem::path & path) { - AutoCloseFD fd = toDescriptor(open( - path.c_str(), - O_RDONLY -#ifdef O_CLOEXEC - | O_CLOEXEC -#endif - )); + AutoCloseFD fd = openFileReadonly(path); if (!fd) - throw SysError("opening NAR cache file '%s'", path); + throw NativeSysError("opening NAR cache file '%s'", path); return [inner = seekableGetNarBytes(fd.get()), fd = make_ref(std::move(fd))]( uint64_t offset, uint64_t length) { return inner(offset, length); }; diff --git a/src/libutil/unix/file-system.cc b/src/libutil/unix/file-system.cc index 692f7fd07..cebcc4fab 100644 --- a/src/libutil/unix/file-system.cc +++ b/src/libutil/unix/file-system.cc @@ -27,6 +27,11 @@ Descriptor openDirectory(const std::filesystem::path & path) return open(path.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC); } +Descriptor openFileReadonly(const std::filesystem::path & path) +{ + return open(path.c_str(), O_RDONLY | O_CLOEXEC); +} + std::filesystem::path defaultTempDir() { return getEnvNonEmpty("TMPDIR").value_or("/tmp"); diff --git a/src/libutil/windows/file-system.cc b/src/libutil/windows/file-system.cc index b93fd0d7d..eb7e12fc9 100644 --- a/src/libutil/windows/file-system.cc +++ b/src/libutil/windows/file-system.cc @@ -23,10 +23,22 @@ Descriptor openDirectory(const std::filesystem::path & path) path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, + /*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); + /*hTemplateFile=*/nullptr); +} + +Descriptor openFileReadonly(const std::filesystem::path & path) +{ + return CreateFileW( + path.c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, + /*lpSecurityAttributes=*/nullptr, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + /*hTemplateFile=*/nullptr); } std::filesystem::path defaultTempDir()