Merge pull request #10556 from nix-windows/uds-remote-on-windows

Enable the `unix://` store on Windows
This commit is contained in:
John Ericson 2024-05-02 09:53:00 -04:00 committed by GitHub
commit 1948ec390c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 166 additions and 88 deletions

View file

@ -173,12 +173,13 @@ void initNix(bool loadConfig)
everybody. */ everybody. */
umask(0022); umask(0022);
#ifndef _WIN32
/* Initialise the PRNG. */ /* Initialise the PRNG. */
struct timeval tv; struct timeval tv;
gettimeofday(&tv, 0); gettimeofday(&tv, 0);
#ifndef _WIN32
srandom(tv.tv_usec); srandom(tv.tv_usec);
#endif #endif
srand(tv.tv_usec);
} }

View file

@ -0,0 +1,44 @@
#include "indirect-root-store.hh"
namespace nix {
void IndirectRootStore::makeSymlink(const Path & link, const Path & target)
{
/* Create directories up to `gcRoot'. */
createDirs(dirOf(link));
/* Create the new symlink. */
Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand());
createSymlink(target, tempLink);
/* Atomically replace the old one. */
renameFile(tempLink, link);
}
Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot)
{
Path gcRoot(canonPath(_gcRoot));
if (isInStore(gcRoot))
throw Error(
"creating a garbage collector root (%1%) in the Nix store is forbidden "
"(are you running nix-build inside the store?)", gcRoot);
/* Register this root with the garbage collector, if it's
running. This should be superfluous since the caller should
have registered this root yet, but let's be on the safe
side. */
addTempRoot(storePath);
/* Don't clobber the link if it already exists and doesn't
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
throw Error("cannot create symlink '%1%'; already exists", gcRoot);
makeSymlink(gcRoot, printStorePath(storePath));
addIndirectRoot(gcRoot);
return gcRoot;
}
}

View file

@ -67,6 +67,9 @@ struct IndirectRootStore : public virtual LocalFSStore
* The form this weak-reference takes is implementation-specific. * The form this weak-reference takes is implementation-specific.
*/ */
virtual void addIndirectRoot(const Path & path) = 0; virtual void addIndirectRoot(const Path & path) = 0;
protected:
void makeSymlink(const Path & link, const Path & target);
}; };
} }

View file

@ -21,6 +21,9 @@ libstore_LDFLAGS += $(SQLITE3_LIBS) $(LIBCURL_LIBS) $(THREAD_LDFLAGS)
ifdef HOST_LINUX ifdef HOST_LINUX
libstore_LDFLAGS += -ldl libstore_LDFLAGS += -ldl
endif endif
ifdef HOST_WINDOWS
libstore_LDFLAGS += -lws2_32
endif
$(foreach file,$(libstore_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/sandbox))) $(foreach file,$(libstore_FILES),$(eval $(call install-data-in,$(d)/$(file),$(datadir)/nix/sandbox)))

View file

@ -2,16 +2,20 @@
#include "unix-domain-socket.hh" #include "unix-domain-socket.hh"
#include "worker-protocol.hh" #include "worker-protocol.hh"
#include <cstring>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <cstring> #ifdef _WIN32
# include <winsock2.h>
# include <afunix.h>
#else
# include <sys/socket.h>
# include <sys/un.h>
#endif
namespace nix { namespace nix {
@ -57,7 +61,7 @@ std::string UDSRemoteStore::getUri()
void UDSRemoteStore::Connection::closeWrite() void UDSRemoteStore::Connection::closeWrite()
{ {
shutdown(fd.get(), SHUT_WR); shutdown(toSocket(fd.get()), SHUT_WR);
} }
@ -68,7 +72,7 @@ ref<RemoteStore::Connection> UDSRemoteStore::openConnection()
/* Connect to a daemon that does the privileged work for us. */ /* Connect to a daemon that does the privileged work for us. */
conn->fd = createUnixDomainSocket(); conn->fd = createUnixDomainSocket();
nix::connect(conn->fd.get(), path ? *path : settings.nixDaemonSocketFile); nix::connect(toSocket(conn->fd.get()), path ? *path : settings.nixDaemonSocketFile);
conn->from.fd = conn->fd.get(); conn->from.fd = conn->fd.get();
conn->to.fd = conn->fd.get(); conn->to.fd = conn->fd.get();

View file

@ -35,20 +35,6 @@ static std::string gcSocketPath = "/gc-socket/socket";
static std::string gcRootsDir = "gcroots"; static std::string gcRootsDir = "gcroots";
static void makeSymlink(const Path & link, const Path & target)
{
/* Create directories up to `gcRoot'. */
createDirs(dirOf(link));
/* Create the new symlink. */
Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), random());
createSymlink(target, tempLink);
/* Atomically replace the old one. */
renameFile(tempLink, link);
}
void LocalStore::addIndirectRoot(const Path & path) void LocalStore::addIndirectRoot(const Path & path)
{ {
std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false); std::string hash = hashString(HashAlgorithm::SHA1, path).to_string(HashFormat::Nix32, false);
@ -57,32 +43,6 @@ void LocalStore::addIndirectRoot(const Path & path)
} }
Path IndirectRootStore::addPermRoot(const StorePath & storePath, const Path & _gcRoot)
{
Path gcRoot(canonPath(_gcRoot));
if (isInStore(gcRoot))
throw Error(
"creating a garbage collector root (%1%) in the Nix store is forbidden "
"(are you running nix-build inside the store?)", gcRoot);
/* Register this root with the garbage collector, if it's
running. This should be superfluous since the caller should
have registered this root yet, but let's be on the safe
side. */
addTempRoot(storePath);
/* Don't clobber the link if it already exists and doesn't
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
throw Error("cannot create symlink '%1%'; already exists", gcRoot);
makeSymlink(gcRoot, printStorePath(storePath));
addIndirectRoot(gcRoot);
return gcRoot;
}
void LocalStore::createTempRootsFile() void LocalStore::createTempRootsFile()
{ {
auto fdTempRoots(_fdTempRoots.lock()); auto fdTempRoots(_fdTempRoots.lock());

View file

@ -1,24 +1,31 @@
#include "file-system.hh" #include "file-system.hh"
#include "processes.hh"
#include "unix-domain-socket.hh" #include "unix-domain-socket.hh"
#include "util.hh" #include "util.hh"
#include <sys/socket.h> #ifdef _WIN32
#include <sys/un.h> # include <winsock2.h>
# include <afunix.h>
#else
# include <sys/socket.h>
# include <sys/un.h>
# include "processes.hh"
#endif
#include <unistd.h> #include <unistd.h>
namespace nix { namespace nix {
AutoCloseFD createUnixDomainSocket() AutoCloseFD createUnixDomainSocket()
{ {
AutoCloseFD fdSocket = socket(PF_UNIX, SOCK_STREAM AutoCloseFD fdSocket = toDescriptor(socket(PF_UNIX, SOCK_STREAM
#ifdef SOCK_CLOEXEC #ifdef SOCK_CLOEXEC
| SOCK_CLOEXEC | SOCK_CLOEXEC
#endif #endif
, 0); , 0));
if (!fdSocket) if (!fdSocket)
throw SysError("cannot create Unix domain socket"); throw SysError("cannot create Unix domain socket");
#ifndef _WIN32
closeOnExec(fdSocket.get()); closeOnExec(fdSocket.get());
#endif
return fdSocket; return fdSocket;
} }
@ -32,16 +39,15 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
if (chmod(path.c_str(), mode) == -1) if (chmod(path.c_str(), mode) == -1)
throw SysError("changing permissions on '%1%'", path); throw SysError("changing permissions on '%1%'", path);
if (listen(fdSocket.get(), 100) == -1) if (listen(toSocket(fdSocket.get()), 100) == -1)
throw SysError("cannot listen on socket '%1%'", path); throw SysError("cannot listen on socket '%1%'", path);
return fdSocket; return fdSocket;
} }
static void bindConnectProcHelper( static void bindConnectProcHelper(
std::string_view operationName, auto && operation, std::string_view operationName, auto && operation,
int fd, const std::string & path) Socket fd, const std::string & path)
{ {
struct sockaddr_un addr; struct sockaddr_un addr;
addr.sun_family = AF_UNIX; addr.sun_family = AF_UNIX;
@ -54,6 +60,9 @@ static void bindConnectProcHelper(
auto * psaddr = reinterpret_cast<struct sockaddr *>(&addr); auto * psaddr = reinterpret_cast<struct sockaddr *>(&addr);
if (path.size() + 1 >= sizeof(addr.sun_path)) { if (path.size() + 1 >= sizeof(addr.sun_path)) {
#ifdef _WIN32
throw Error("cannot %s to socket at '%s': path is too long", operationName, path);
#else
Pipe pipe; Pipe pipe;
pipe.create(); pipe.create();
Pid pid = startProcess([&] { Pid pid = startProcess([&] {
@ -83,6 +92,7 @@ static void bindConnectProcHelper(
errno = *errNo; errno = *errNo;
throw SysError("cannot %s to socket at '%s'", operationName, path); throw SysError("cannot %s to socket at '%s'", operationName, path);
} }
#endif
} else { } else {
memcpy(addr.sun_path, path.c_str(), path.size() + 1); memcpy(addr.sun_path, path.c_str(), path.size() + 1);
if (operation(fd, psaddr, sizeof(addr)) == -1) if (operation(fd, psaddr, sizeof(addr)) == -1)
@ -91,7 +101,7 @@ static void bindConnectProcHelper(
} }
void bind(int fd, const std::string & path) void bind(Socket fd, const std::string & path)
{ {
unlink(path.c_str()); unlink(path.c_str());
@ -99,7 +109,7 @@ void bind(int fd, const std::string & path)
} }
void connect(int fd, const std::string & path) void connect(Socket fd, const std::string & path)
{ {
bindConnectProcHelper("connect", ::connect, fd, path); bindConnectProcHelper("connect", ::connect, fd, path);
} }

View file

@ -0,0 +1,84 @@
#pragma once
///@file
#include "types.hh"
#include "file-descriptor.hh"
#ifdef _WIN32
# include <winsock2.h>
#endif
#include <unistd.h>
namespace nix {
/**
* Create a Unix domain socket.
*/
AutoCloseFD createUnixDomainSocket();
/**
* Create a Unix domain socket in listen mode.
*/
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
/**
* Often we want to use `Descriptor`, but Windows makes a slightly
* stronger file descriptor vs socket distinction, at least at the level
* of C types.
*/
using Socket =
#ifdef _WIN32
SOCKET
#else
int
#endif
;
#ifdef _WIN32
/**
* Windows gives this a different name
*/
# define SHUT_WR SD_SEND
#endif
/**
* Convert a `Socket` to a `Descriptor`
*
* This is a no-op except on Windows.
*/
static inline Socket toSocket(Descriptor fd)
{
#ifdef _WIN32
return reinterpret_cast<Socket>(fd);
#else
return fd;
#endif
}
/**
* Convert a `Socket` to a `Descriptor`
*
* This is a no-op except on Windows.
*/
static inline Descriptor fromSocket(Socket fd)
{
#ifdef _WIN32
return reinterpret_cast<Descriptor>(fd);
#else
return fd;
#endif
}
/**
* Bind a Unix domain socket to a path.
*/
void bind(Socket fd, const std::string & path);
/**
* Connect to a Unix domain socket.
*/
void connect(Socket fd, const std::string & path);
}

View file

@ -1,31 +0,0 @@
#pragma once
///@file
#include "types.hh"
#include "file-descriptor.hh"
#include <unistd.h>
namespace nix {
/**
* Create a Unix domain socket.
*/
AutoCloseFD createUnixDomainSocket();
/**
* Create a Unix domain socket in listen mode.
*/
AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode);
/**
* Bind a Unix domain socket to a path.
*/
void bind(int fd, const std::string & path);
/**
* Connect to a Unix domain socket.
*/
void connect(int fd, const std::string & path);
}