1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-12 21:46:01 +01:00

Always throw the right exception in connect

When `nix::connect` is called with a socket path that's too long, it
forks a process that will `chdir` to the directory of the socket path
and call `::connect` with the relative path (which is hopefully
short-enough).

That works fairly well, except that an exception raised in this
subprocess won't be forwarded to the parent process. Instead the logic
will just notice that the subprocess exited with a non-zero error code,
and throw a generic `Error`. In particular, any failure in the
`::connect` call should throw a `SysError` with the correct error code,
but that's not the case.

Some places try to catch this `SysError` and look at its error code (to
potentially restart for example). But this doesn't work since the
actual error that gets thrown isn't a `SysError`.

Fix that by forwarding the `errno` in case something gets wrong (by
setting the subprocess exit code to it), and throwing a `SysError` with
the right error code in the parent process.
This commit is contained in:
Théophane Hufschmitt 2022-06-15 18:07:13 +02:00
parent 6c0e7450de
commit 2d651ad2d0

View file

@ -1817,6 +1817,7 @@ void connect(int fd, const std::string & path)
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
Pid pid = startProcess([&]() { Pid pid = startProcess([&]() {
try {
Path dir = dirOf(path); Path dir = dirOf(path);
if (chdir(dir.c_str()) == -1) if (chdir(dir.c_str()) == -1)
throw SysError("chdir to '%s' failed", dir); throw SysError("chdir to '%s' failed", dir);
@ -1827,9 +1828,21 @@ void connect(int fd, const std::string & path)
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
throw SysError("cannot connect to socket at '%s'", path); throw SysError("cannot connect to socket at '%s'", path);
_exit(0); _exit(0);
} catch (SysError & e) {
// If this raised a `SysError`, we want to rethrow it in the
// parent process.
// For that, we need to transmit the associated error code,
// which we do by setting it as the return code for the process.
_exit(e.errNo);
} catch (std::exception &) {
_exit(-1);
}
}); });
int status = pid.wait(); int status = pid.wait();
if (status != 0) if (status > 0) {
errno = status;
throw SysError("cannot connect to socket at '%s'", path);
} else if (status < 0)
throw Error("cannot connect to socket at '%s'", path); throw Error("cannot connect to socket at '%s'", path);
} else { } else {
memcpy(addr.sun_path, path.c_str(), path.size() + 1); memcpy(addr.sun_path, path.c_str(), path.size() + 1);