Skip to content
Merged
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
4 changes: 2 additions & 2 deletions src/libexpr/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ path_start
/* Absolute paths are always interpreted relative to the
root filesystem accessor, rather than the accessor of the
current Nix expression. */
Path path(canonPath(literal));
auto path = canonPath(literal).string();
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
Expand All @@ -442,7 +442,7 @@ path_start
});

auto basePath = std::filesystem::path(state->basePath.path.abs());
Path path(absPath(literal, &basePath).string());
auto path = absPath(literal, &basePath).string();
/* add back in the trailing '/' to the first segment */
if (literal.size() > 1 && literal.back() == '/')
path += '/';
Expand Down
131 changes: 121 additions & 10 deletions src/libutil/include/nix/util/error.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "nix/util/fmt.hh"
#include "nix/util/config.hh"

#include <concepts>
#include <cstring>
#include <list>
#include <memory>
Expand Down Expand Up @@ -287,32 +288,60 @@ protected:
/**
* Just here to allow derived classes to use the right constructor
* (the protected one).
*
* This one indicates the prebuilt `HintFmt` one with the explicit `errorDetails`
*/
struct Disambig
struct DisambigHintFmt
{};

/**
* Just here to allow derived classes to use the right constructor
* (the protected one).
*
* This one indicates the varargs one to build the `HintFmt` with the explicit `errorDetails`
*/
struct DisambigVarArgs
{};

/**
* Protected constructor that takes a pre-built HintFmt.
* Use this when the error message needs to be constructed before
* capturing errno/GetLastError().
*/
SystemError(DisambigHintFmt, std::error_code errorCode, std::string_view errorDetails, const HintFmt & hf)
: CloneableError(HintFmt{"%s: %s", Uncolored(hf.str()), errorDetails})
, errorCode(errorCode)
, errorDetails(errorDetails)
{
}

/**
* Protected constructor for subclasses that provide their own error message.
* The error message is appended to the formatted hint.
*/
template<typename... Args>
SystemError(Disambig, std::error_code errorCode, std::string_view errorDetails, Args &&... args)
: CloneableError("")
, errorCode(errorCode)
, errorDetails(errorDetails)
SystemError(DisambigVarArgs, std::error_code errorCode, std::string_view errorDetails, Args &&... args)
: SystemError(DisambigHintFmt{}, errorCode, errorDetails, HintFmt{std::forward<Args>(args)...})
{
auto hf = HintFmt(std::forward<Args>(args)...);
err.msg = HintFmt("%s: %s", Uncolored(hf.str()), errorDetails);
}

public:
/**
* Construct with an error code. The error code's message is automatically
* appended to the error message.
*/
SystemError(std::error_code errorCode, const HintFmt & hf)
: SystemError(DisambigHintFmt{}, errorCode, errorCode.message(), hf)
{
}

/**
* Construct with an error code. The error code's message is automatically
* appended to the error message.
*/
template<typename... Args>
SystemError(std::error_code errorCode, Args &&... args)
: SystemError(Disambig{}, errorCode, errorCode.message(), std::forward<Args>(args)...)
: SystemError(DisambigVarArgs{}, errorCode, errorCode.message(), std::forward<Args>(args)...)
{
}

Expand Down Expand Up @@ -355,14 +384,27 @@ public:
template<typename... Args>
SysError(int errNo, Args &&... args)
: CloneableError(
Disambig{},
DisambigVarArgs{},
std::make_error_code(static_cast<std::errc>(errNo)),
strerror(errNo),
std::forward<Args>(args)...)
, errNo(errNo)
{
}

/**
* Construct using the explicitly-provided error number. `strerror`
* will be used to try to add additional information to the message.
*
* Unlike above, the `HintFmt` already exists rather than being made on
* the spot.
*/
SysError(int errNo, const HintFmt & hf)
: CloneableError(DisambigHintFmt{}, std::make_error_code(static_cast<std::errc>(errNo)), strerror(errNo), hf)
, errNo(errNo)
{
}

/**
* Construct using the ambient `errno`.
*
Expand All @@ -374,6 +416,34 @@ public:
: SysError(errno, std::forward<Args>(args)...)
{
}

/**
* Construct using the ambient `errno` and a function that produces
* a `HintFmt`. errno is read first, then the function is called, so
* the function is safe to modify `errno`.
*/
SysError(auto && mkHintFmt)
requires std::invocable<decltype(mkHintFmt)> && std::same_as<std::invoke_result_t<decltype(mkHintFmt)>, HintFmt>
: SysError(captureErrno(std::forward<decltype(mkHintFmt)>(mkHintFmt)))
{
}

private:
/**
* Helper to ensure errno is captured before mkHintFmt is called.
* C++ argument evaluation order is unspecified, so we can't rely on
* `SysError(errno, mkHintFmt())` evaluating errno first.
*/
static std::pair<int, HintFmt> captureErrno(auto && mkHintFmt)
{
int e = errno;
return {e, mkHintFmt()};
}

SysError(std::pair<int, HintFmt> && p)
: SysError(p.first, std::move(p.second))
{
}
};

/**
Expand Down Expand Up @@ -437,14 +507,29 @@ public:
template<typename... Args>
WinError(DWORD lastError, Args &&... args)
: CloneableError(
Disambig{},
DisambigVarArgs{},
std::error_code(lastError, std::system_category()),
renderError(lastError),
std::forward<Args>(args)...)
, lastError(lastError)
{
}

/**
* Construct using the explicitly-provided error number.
* `FormatMessageA` will be used to try to add additional
* information to the message.
*
* Unlike above, the `HintFmt` already exists rather than being made on
* the spot.
*/
WinError(DWORD lastError, const HintFmt & hf)
: CloneableError(
DisambigHintFmt{}, std::error_code(lastError, std::system_category()), renderError(lastError), hf)
, lastError(lastError)
{
}

/**
* Construct using `GetLastError()` and the ambient "last error".
*
Expand All @@ -457,7 +542,33 @@ public:
{
}

/**
* Construct using `GetLastError()` and a function that produces a
* `HintFmt`. `GetLastError()` is called first, then the function is
* called, so the function is safe to modify the last error.
*/
WinError(auto && mkHintFmt)
requires std::invocable<decltype(mkHintFmt)> && std::same_as<std::invoke_result_t<decltype(mkHintFmt)>, HintFmt>
: WinError(captureLastError(std::forward<decltype(mkHintFmt)>(mkHintFmt)))
{
}

private:
/**
* Helper to ensure GetLastError() is captured before mkHintFmt is called.
* C++ argument evaluation order is unspecified, so we can't rely on
* `WinError(GetLastError(), mkHintFmt())` evaluating GetLastError() first.
*/
static std::pair<DWORD, HintFmt> captureLastError(auto && mkHintFmt)
{
DWORD e = GetLastError();
return {e, mkHintFmt()};
}

WinError(std::pair<DWORD, HintFmt> && p)
: WinError(p.first, std::move(p.second))
{
}

static std::string renderError(DWORD lastError);
};
Expand Down
26 changes: 12 additions & 14 deletions src/libutil/unix/file-system-at.cc
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@ void unix::fchmodatTryNoFollow(Descriptor dirFd, const CanonPath & path, mode_t
if (errno == ENOSYS)
fchmodat2Unsupported.test_and_set();
else {
auto savedErrno = errno;
throw SysError(savedErrno, "fchmodat2 %s", PathFmt(descriptorToPath(dirFd) / path.rel()));
throw SysError([&] { return HintFmt("fchmodat2 %s", PathFmt(descriptorToPath(dirFd) / path.rel())); });
}
} else
return;
Expand All @@ -83,11 +82,11 @@ void unix::fchmodatTryNoFollow(Descriptor dirFd, const CanonPath & path, mode_t
#ifdef __linux__
AutoCloseFD pathFd = ::openat(dirFd, path.rel_c_str(), O_PATH | O_NOFOLLOW | O_CLOEXEC);
if (!pathFd) {
auto savedErrno = errno;
throw SysError(
savedErrno,
"opening %s to get an O_PATH file descriptor (fchmodat2 is unsupported)",
PathFmt(descriptorToPath(dirFd) / path.rel()));
throw SysError([&] {
return HintFmt(
"opening %s to get an O_PATH file descriptor (fchmodat2 is unsupported)",
PathFmt(descriptorToPath(dirFd) / path.rel()));
});
}

struct ::stat st;
Expand All @@ -107,9 +106,9 @@ void unix::fchmodatTryNoFollow(Descriptor dirFd, const CanonPath & path, mode_t
if (errno == ENOENT)
dontHaveProc.test_and_set();
else {
auto savedErrno = errno;
throw SysError(
savedErrno, "chmod %s (%s)", selfProcFdPath, PathFmt(descriptorToPath(dirFd) / path.rel()));
throw SysError([&] {
return HintFmt("chmod %s (%s)", selfProcFdPath, PathFmt(descriptorToPath(dirFd) / path.rel()));
});
}
} else
return;
Expand All @@ -131,8 +130,7 @@ void unix::fchmodatTryNoFollow(Descriptor dirFd, const CanonPath & path, mode_t
);

if (res == -1) {
auto savedErrno = errno;
throw SysError(savedErrno, "fchmodat %s", PathFmt(descriptorToPath(dirFd) / path.rel()));
throw SysError([&] { return HintFmt("fchmodat %s", PathFmt(descriptorToPath(dirFd) / path.rel())); });
}
}

Expand Down Expand Up @@ -217,8 +215,8 @@ OsString readLinkAt(Descriptor dirFd, const CanonPath & path)
buf.resize(bufSize);
ssize_t rlSize = ::readlinkat(dirFd, path.rel_c_str(), buf.data(), bufSize);
if (rlSize == -1) {
auto savedErrno = errno;
throw SysError(savedErrno, "reading symbolic link %1%", PathFmt(descriptorToPath(dirFd) / path.rel()));
throw SysError(
[&] { return HintFmt("reading symbolic link %1%", PathFmt(descriptorToPath(dirFd) / path.rel())); });
} else if (rlSize < bufSize)
return {buf.data(), static_cast<std::size_t>(rlSize)};
}
Expand Down
3 changes: 1 addition & 2 deletions src/libutil/windows/current-process.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ std::chrono::microseconds getCpuUserTime()
FILETIME userTime;

if (!GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime)) {
auto lastError = GetLastError();
throw windows::WinError(lastError, "failed to get CPU time");
throw windows::WinError("failed to get CPU time");
}

ULARGE_INTEGER uLargeInt;
Expand Down
21 changes: 8 additions & 13 deletions src/libutil/windows/file-descriptor.cc
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ std::make_unsigned_t<off_t> getFileSize(Descriptor fd)
{
LARGE_INTEGER li;
if (!GetFileSizeEx(fd, &li)) {
auto lastError = GetLastError();
throw WinError(lastError, "getting size of file %s", PathFmt(descriptorToPath(fd)));
throw WinError([&] { return HintFmt("getting size of file %s", PathFmt(descriptorToPath(fd))); });
}
return li.QuadPart;
}
Expand Down Expand Up @@ -49,13 +48,10 @@ size_t readOffset(Descriptor fd, off_t offset, std::span<std::byte> buffer)
ov.OffsetHigh = static_cast<DWORD>(offset >> 32);
DWORD n;
if (!ReadFile(fd, buffer.data(), static_cast<DWORD>(buffer.size()), &n, &ov)) {
auto lastError = GetLastError();
throw WinError(
lastError,
"reading %1% bytes at offset %2% from %3%",
buffer.size(),
offset,
PathFmt(descriptorToPath(fd)));
throw WinError([&] {
return HintFmt(
"reading %1% bytes at offset %2% from %3%", buffer.size(), offset, PathFmt(descriptorToPath(fd)));
});
}
return static_cast<size_t>(n);
}
Expand All @@ -66,8 +62,8 @@ size_t write(Descriptor fd, std::span<const std::byte> buffer, bool allowInterru
checkInterrupt(); // For consistency with unix
DWORD n;
if (!WriteFile(fd, buffer.data(), static_cast<DWORD>(buffer.size()), &n, NULL)) {
auto lastError = GetLastError();
throw WinError(lastError, "writing %1% bytes to %2%", buffer.size(), PathFmt(descriptorToPath(fd)));
throw WinError(
[&] { return HintFmt("writing %1% bytes to %2%", buffer.size(), PathFmt(descriptorToPath(fd))); });
}
return static_cast<size_t>(n);
}
Expand Down Expand Up @@ -125,8 +121,7 @@ off_t lseek(HANDLE h, off_t offset, int whence)
void syncDescriptor(Descriptor fd)
{
if (!::FlushFileBuffers(fd)) {
auto lastError = GetLastError();
throw WinError(lastError, "flushing file %s", PathFmt(descriptorToPath(fd)));
throw WinError([&] { return HintFmt("flushing file %s", PathFmt(descriptorToPath(fd))); });
}
}

Expand Down
16 changes: 9 additions & 7 deletions src/libutil/windows/muxable-pipe.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@

namespace nix {

using namespace nix::windows;

void MuxablePipePollState::poll(HANDLE ioport, std::optional<unsigned int> timeout)
{
/* We are on at least Windows Vista / Server 2008 and can get many
(countof(oentries)) statuses in one API call. */
if (!GetQueuedCompletionStatusEx(
ioport, oentries, sizeof(oentries) / sizeof(*oentries), &removed, timeout ? *timeout : INFINITE, false)) {
windows::WinError winError("GetQueuedCompletionStatusEx");
if (winError.lastError != WAIT_TIMEOUT)
throw winError;
auto lastError = GetLastError();
if (lastError != WAIT_TIMEOUT)
throw WinError(lastError, "GetQueuedCompletionStatusEx");
assert(removed == 0);
} else {
assert(0 < removed && removed <= sizeof(oentries) / sizeof(*oentries));
Expand Down Expand Up @@ -52,12 +54,12 @@ void MuxablePipePollState::iterate(
// here is possible (but not obligatory) to call
// `handleRead` and repeat ReadFile immediately
} else {
windows::WinError winError("ReadFile(%s, ..)", (*p)->readSide.get());
if (winError.lastError == ERROR_BROKEN_PIPE) {
auto lastError = GetLastError();
if (lastError == ERROR_BROKEN_PIPE) {
handleEOF((*p)->readSide.get());
nextp = channels.erase(p); // no need to maintain `channels` ?
} else if (winError.lastError != ERROR_IO_PENDING)
throw winError;
} else if (lastError != ERROR_IO_PENDING)
throw WinError(lastError, "ReadFile(%s, ..)", (*p)->readSide.get());
}
}
break;
Expand Down
Loading