mirror of
https://github.com/NixOS/nix
synced 2025-07-02 09:21:47 +02:00
Merge pull request #8901 from nix-windows/mingw
Build a minimized Nix with MinGW
This commit is contained in:
commit
ac253fb99e
120 changed files with 1435 additions and 341 deletions
12
Makefile
12
Makefile
|
@ -7,6 +7,8 @@ clean-files += $(buildprefix)Makefile.config
|
|||
|
||||
# List makefiles
|
||||
|
||||
include mk/platform.mk
|
||||
|
||||
ifeq ($(ENABLE_BUILD), yes)
|
||||
makefiles = \
|
||||
mk/precompiled-headers.mk \
|
||||
|
@ -20,7 +22,10 @@ makefiles = \
|
|||
src/nix/local.mk \
|
||||
src/libutil-c/local.mk \
|
||||
src/libstore-c/local.mk \
|
||||
src/libexpr-c/local.mk \
|
||||
src/libexpr-c/local.mk
|
||||
|
||||
ifdef HOST_UNIX
|
||||
makefiles += \
|
||||
scripts/local.mk \
|
||||
misc/bash/local.mk \
|
||||
misc/fish/local.mk \
|
||||
|
@ -29,6 +34,7 @@ makefiles = \
|
|||
misc/launchd/local.mk \
|
||||
misc/upstart/local.mk
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(ENABLE_UNIT_TESTS), yes)
|
||||
makefiles += \
|
||||
|
@ -42,6 +48,7 @@ makefiles += \
|
|||
endif
|
||||
|
||||
ifeq ($(ENABLE_FUNCTIONAL_TESTS), yes)
|
||||
ifdef HOST_UNIX
|
||||
makefiles += \
|
||||
tests/functional/local.mk \
|
||||
tests/functional/ca/local.mk \
|
||||
|
@ -51,6 +58,7 @@ makefiles += \
|
|||
tests/functional/test-libstoreconsumer/local.mk \
|
||||
tests/functional/plugins/local.mk
|
||||
endif
|
||||
endif
|
||||
|
||||
# Some makefiles require access to built programs and must be included late.
|
||||
makefiles-late =
|
||||
|
@ -79,8 +87,6 @@ else
|
|||
unexport NIX_HARDENING_ENABLE
|
||||
endif
|
||||
|
||||
include mk/platform.mk
|
||||
|
||||
ifdef HOST_WINDOWS
|
||||
# Windows DLLs are stricter about symbol visibility than Unix shared
|
||||
# objects --- see https://gcc.gnu.org/wiki/Visibility for details.
|
||||
|
|
12
flake.nix
12
flake.nix
|
@ -32,12 +32,6 @@
|
|||
"armv6l-unknown-linux-gnueabihf"
|
||||
"armv7l-unknown-linux-gnueabihf"
|
||||
"x86_64-unknown-netbsd"
|
||||
];
|
||||
|
||||
# Nix doesn't yet build on this platform, so we put it in a
|
||||
# separate list. We just use this for `devShells` and
|
||||
# `nixpkgsFor`, which this depends on.
|
||||
shellCrossSystems = crossSystems ++ [
|
||||
"x86_64-w64-mingw32"
|
||||
];
|
||||
|
||||
|
@ -83,7 +77,7 @@
|
|||
in {
|
||||
inherit stdenvs native;
|
||||
static = native.pkgsStatic;
|
||||
cross = lib.genAttrs shellCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
cross = forAllCrossSystems (crossSystem: make-pkgs crossSystem "stdenv");
|
||||
});
|
||||
|
||||
installScriptFor = tarballs:
|
||||
|
@ -426,8 +420,8 @@
|
|||
in
|
||||
(makeShells "native" nixpkgsFor.${system}.native) //
|
||||
(lib.optionalAttrs (!nixpkgsFor.${system}.native.stdenv.isDarwin)
|
||||
(makeShells "static" nixpkgsFor.${system}.static)) //
|
||||
(lib.genAttrs shellCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv)) //
|
||||
(makeShells "static" nixpkgsFor.${system}.static) //
|
||||
(forAllCrossSystems (crossSystem: let pkgs = nixpkgsFor.${system}.cross.${crossSystem}; in makeShell pkgs pkgs.stdenv))) //
|
||||
{
|
||||
default = self.devShells.${system}.native-stdenvPackages;
|
||||
}
|
||||
|
|
|
@ -46,11 +46,13 @@ AC_DEFUN([ENSURE_NO_GCC_BUG_80431],
|
|||
]])],
|
||||
[status_80431=0],
|
||||
[status_80431=$?],
|
||||
[
|
||||
# Assume we're bug-free when cross-compiling
|
||||
])
|
||||
[status_80431=''])
|
||||
AC_LANG_POP(C++)
|
||||
AS_CASE([$status_80431],
|
||||
[''],[
|
||||
AC_MSG_RESULT(cannot check because cross compiling)
|
||||
AC_MSG_NOTICE(assume we are bug free)
|
||||
],
|
||||
[0],[
|
||||
AC_MSG_RESULT(yes)
|
||||
],
|
||||
|
|
|
@ -42,19 +42,22 @@
|
|||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <grp.h>
|
||||
#include <netdb.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <sys/wait.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <grp.h>
|
||||
# include <netdb.h>
|
||||
# include <pwd.h>
|
||||
# include <sys/resource.h>
|
||||
# include <sys/select.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/utsname.h>
|
||||
# include <sys/wait.h>
|
||||
# include <termios.h>
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "experimental-features.hh"
|
||||
|
||||
using namespace nix;
|
||||
using namespace nix::unix;
|
||||
using std::cin;
|
||||
|
||||
static void handleAlarm(int sig) {
|
||||
|
|
|
@ -181,7 +181,7 @@ Bindings * MixEvalArgs::getAutoArgs(EvalState & state)
|
|||
v->mkString(arg.s);
|
||||
},
|
||||
[&](const AutoArgFile & arg) {
|
||||
v->mkString(readFile(arg.path));
|
||||
v->mkString(readFile(arg.path.string()));
|
||||
},
|
||||
[&](const AutoArgStdin & arg) {
|
||||
v->mkString(readFile(STDIN_FILENO));
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
#include "finally.hh"
|
||||
#include "terminal.hh"
|
||||
|
||||
#include <sys/queue.h>
|
||||
#if HAVE_LOWDOWN
|
||||
#include <lowdown.h>
|
||||
# include <sys/queue.h>
|
||||
# include <lowdown.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
|
|
@ -137,6 +137,7 @@ static constexpr const char * promptForType(ReplPromptType promptType)
|
|||
|
||||
bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptType)
|
||||
{
|
||||
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||
struct sigaction act, old;
|
||||
sigset_t savedSignalMask, set;
|
||||
|
||||
|
@ -161,9 +162,12 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
|||
};
|
||||
|
||||
setupSignals();
|
||||
#endif
|
||||
char * s = readline(promptForType(promptType));
|
||||
Finally doFree([&]() { free(s); });
|
||||
#ifndef _WIN32 // TODO use more signals.hh for this
|
||||
restoreSignals();
|
||||
#endif
|
||||
|
||||
if (g_signal_received) {
|
||||
g_signal_received = 0;
|
||||
|
|
|
@ -33,15 +33,17 @@
|
|||
#include <optional>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
|
||||
#include <sys/resource.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
#ifndef _WIN32 // TODO use portable implementation
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_BOEHMGC
|
||||
|
||||
#define GC_INCLUDE_NEW
|
||||
|
@ -2627,9 +2629,11 @@ void EvalState::maybePrintStats()
|
|||
|
||||
void EvalState::printStatistics()
|
||||
{
|
||||
#ifndef _WIN32 // TODO use portable implementation
|
||||
struct rusage buf;
|
||||
getrusage(RUSAGE_SELF, &buf);
|
||||
float cpuTime = buf.ru_utime.tv_sec + ((float) buf.ru_utime.tv_usec / 1000000);
|
||||
#endif
|
||||
|
||||
uint64_t bEnvs = nrEnvs * sizeof(Env) + nrValuesInEnvs * sizeof(Value *);
|
||||
uint64_t bLists = nrListElems * sizeof(Value *);
|
||||
|
@ -2646,7 +2650,9 @@ void EvalState::printStatistics()
|
|||
if (outPath != "-")
|
||||
fs.open(outPath, std::fstream::out);
|
||||
json topObj = json::object();
|
||||
#ifndef _WIN32 // TODO implement
|
||||
topObj["cpuTime"] = cpuTime;
|
||||
#endif
|
||||
topObj["envs"] = {
|
||||
{"number", nrEnvs},
|
||||
{"elements", nrValuesInEnvs},
|
||||
|
|
|
@ -161,6 +161,8 @@ struct DebugTrace {
|
|||
bool isError;
|
||||
};
|
||||
|
||||
// Don't want Windows function
|
||||
#undef SearchPath
|
||||
|
||||
class EvalState : public std::enable_shared_from_this<EvalState>
|
||||
{
|
||||
|
|
|
@ -28,7 +28,10 @@
|
|||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
@ -331,6 +334,8 @@ static RegisterPrimOp primop_import({
|
|||
}
|
||||
});
|
||||
|
||||
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||
|
||||
/* Want reasonable symbol names, so extern C */
|
||||
/* !!! Should we pass the Pos or the file name too? */
|
||||
extern "C" typedef void (*ValueInitializer)(EvalState & state, Value & v);
|
||||
|
@ -403,6 +408,8 @@ void prim_exec(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
|||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* Return a string representing the type of the expression. */
|
||||
static void prim_typeOf(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||
{
|
||||
|
@ -4593,6 +4600,7 @@ void EvalState::createBaseEnv()
|
|||
)",
|
||||
});
|
||||
|
||||
#ifndef _WIN32 // TODO implement on Windows
|
||||
// Miscellaneous
|
||||
if (evalSettings.enableNativeCode) {
|
||||
addPrimOp({
|
||||
|
@ -4606,6 +4614,7 @@ void EvalState::createBaseEnv()
|
|||
.fun = prim_exec,
|
||||
});
|
||||
}
|
||||
#endif
|
||||
|
||||
addPrimOp({
|
||||
.name = "__traceVerbose",
|
||||
|
|
|
@ -8,6 +8,9 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
// Do not want the windows macro (alias to `SearchPathA`)
|
||||
#undef SearchPath
|
||||
|
||||
/**
|
||||
* A "search path" is a list of ways look for something, used with
|
||||
* `builtins.findFile` and `< >` lookup expressions.
|
||||
|
|
|
@ -26,7 +26,7 @@ ref<InputAccessor> makeStorePathAccessor(
|
|||
// FIXME: should use `store->getFSAccessor()`
|
||||
auto root = std::filesystem::path { store->toRealPath(storePath) };
|
||||
auto accessor = makeFSInputAccessor(root);
|
||||
accessor->setPathDisplay(root);
|
||||
accessor->setPathDisplay(root.string());
|
||||
return accessor;
|
||||
}
|
||||
|
||||
|
|
|
@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
{
|
||||
initLibGit2();
|
||||
|
||||
if (pathExists(path.native())) {
|
||||
if (git_repository_open(Setter(repo), path.c_str()))
|
||||
if (pathExists(path.string())) {
|
||||
if (git_repository_open(Setter(repo), path.string().c_str()))
|
||||
throw Error("opening Git repository '%s': %s", path, git_error_last()->message);
|
||||
} else {
|
||||
if (git_repository_init(Setter(repo), path.c_str(), bare))
|
||||
if (git_repository_init(Setter(repo), path.string().c_str(), bare))
|
||||
throw Error("creating Git repository '%s': %s", path, git_error_last()->message);
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
std::vector<Submodule> parseSubmodules(const std::filesystem::path & configFile)
|
||||
{
|
||||
GitConfig config;
|
||||
if (git_config_open_ondisk(Setter(config), configFile.c_str()))
|
||||
if (git_config_open_ondisk(Setter(config), configFile.string().c_str()))
|
||||
throw Error("parsing .gitmodules file: %s", git_error_last()->message);
|
||||
|
||||
ConfigIterator it;
|
||||
|
@ -288,7 +288,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
|
||||
/* Get submodule info. */
|
||||
auto modulesFile = path / ".gitmodules";
|
||||
if (pathExists(modulesFile))
|
||||
if (pathExists(modulesFile.string()))
|
||||
info.submodules = parseSubmodules(modulesFile);
|
||||
|
||||
return info;
|
||||
|
@ -377,10 +377,10 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
auto dir = this->path;
|
||||
Strings gitArgs;
|
||||
if (shallow) {
|
||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
||||
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--depth", "1", "--", url, refspec };
|
||||
}
|
||||
else {
|
||||
gitArgs = { "-C", dir, "fetch", "--quiet", "--force", "--", url, refspec };
|
||||
gitArgs = { "-C", dir.string(), "fetch", "--quiet", "--force", "--", url, refspec };
|
||||
}
|
||||
|
||||
runProgram(RunOptions {
|
||||
|
@ -426,7 +426,7 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this<GitRepoImpl>
|
|||
.args = {
|
||||
"-c",
|
||||
"gpg.ssh.allowedSignersFile=" + allowedSignersFile,
|
||||
"-C", path,
|
||||
"-C", path.string(),
|
||||
"verify-commit",
|
||||
rev.gitRev()
|
||||
},
|
||||
|
|
|
@ -108,7 +108,9 @@ std::string getArg(const std::string & opt,
|
|||
return *i;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
static void sigHandler(int signo) { }
|
||||
#endif
|
||||
|
||||
|
||||
void initNix()
|
||||
|
@ -121,6 +123,7 @@ void initNix()
|
|||
|
||||
initLibStore();
|
||||
|
||||
#ifndef _WIN32
|
||||
unix::startSignalHandlerThread();
|
||||
|
||||
/* Reset SIGCHLD to its default. */
|
||||
|
@ -135,6 +138,7 @@ void initNix()
|
|||
/* Install a dummy SIGUSR1 handler for use with pthread_kill(). */
|
||||
act.sa_handler = sigHandler;
|
||||
if (sigaction(SIGUSR1, &act, 0)) throw SysError("handling SIGUSR1");
|
||||
#endif
|
||||
|
||||
#if __APPLE__
|
||||
/* HACK: on darwin, we need can’t use sigprocmask with SIGWINCH.
|
||||
|
@ -156,21 +160,26 @@ void initNix()
|
|||
if (sigaction(SIGTRAP, &act, 0)) throw SysError("handling SIGTRAP");
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Register a SIGSEGV handler to detect stack overflows.
|
||||
Why not initLibExpr()? initGC() is essentially that, but
|
||||
detectStackOverflow is not an instance of the init function concept, as
|
||||
it may have to be invoked more than once per process. */
|
||||
detectStackOverflow();
|
||||
#endif
|
||||
|
||||
/* There is no privacy in the Nix system ;-) At least not for
|
||||
now. In particular, store objects should be readable by
|
||||
everybody. */
|
||||
umask(0022);
|
||||
|
||||
#ifndef _WIN32
|
||||
/* Initialise the PRNG. */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srandom(tv.tv_usec);
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -365,6 +374,9 @@ RunPager::RunPager()
|
|||
Pipe toPager;
|
||||
toPager.create();
|
||||
|
||||
#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
throw Error("Commit signature verification not implemented on Windows yet");
|
||||
#else
|
||||
pid = startProcess([&]() {
|
||||
if (dup2(toPager.readSide.get(), STDIN_FILENO) == -1)
|
||||
throw SysError("dupping stdin");
|
||||
|
@ -383,17 +395,20 @@ RunPager::RunPager()
|
|||
std_out = fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0);
|
||||
if (dup2(toPager.writeSide.get(), STDOUT_FILENO) == -1)
|
||||
throw SysError("dupping standard output");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
RunPager::~RunPager()
|
||||
{
|
||||
try {
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
if (pid != -1) {
|
||||
std::cout.flush();
|
||||
dup2(std_out, STDOUT_FILENO);
|
||||
pid.wait();
|
||||
}
|
||||
#endif
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "file-descriptor.hh"
|
||||
#include "processes.hh"
|
||||
#include "args.hh"
|
||||
#include "args/root.hh"
|
||||
|
@ -89,8 +90,10 @@ public:
|
|||
~RunPager();
|
||||
|
||||
private:
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
Pid pid;
|
||||
int std_out;
|
||||
#endif
|
||||
Descriptor std_out;
|
||||
};
|
||||
|
||||
extern volatile ::sig_atomic_t blockInt;
|
||||
|
@ -112,6 +115,7 @@ struct PrintFreed
|
|||
};
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Install a SIGSEGV handler to detect stack overflows.
|
||||
*/
|
||||
|
@ -141,5 +145,6 @@ extern std::function<void(siginfo_t * info, void * ctx)> stackOverflowHandler;
|
|||
* logger. Exits the process immediately after.
|
||||
*/
|
||||
void defaultStackOverflowHandler(siginfo_t * info, void * ctx);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
@ -76,7 +76,11 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
|||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||
if (mkdir(dstFile.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
, 0755
|
||||
#endif
|
||||
) == -1)
|
||||
throw SysError("creating directory '%1%'", dstFile);
|
||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||
createLinks(state, srcFile, dstFile, priority);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "daemon.hh"
|
||||
#include "monitor-fd.hh"
|
||||
#include "signals.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "worker-protocol-impl.hh"
|
||||
|
@ -16,6 +15,10 @@
|
|||
#include "args.hh"
|
||||
#include "git.hh"
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
# include "monitor-fd.hh"
|
||||
#endif
|
||||
|
||||
namespace nix::daemon {
|
||||
|
||||
Sink & operator << (Sink & sink, const Logger::Fields & fields)
|
||||
|
@ -1018,7 +1021,9 @@ void processConnection(
|
|||
TrustedFlag trusted,
|
||||
RecursiveFlag recursive)
|
||||
{
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
|
||||
#endif
|
||||
|
||||
/* Exchange the greeting. */
|
||||
unsigned int magic = readInt(from);
|
||||
|
|
|
@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer
|
|||
|
||||
Sync<State> state_;
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
/* We can't use a std::condition_variable to wake up the curl
|
||||
thread, because it only monitors file descriptors. So use a
|
||||
pipe instead. */
|
||||
Pipe wakeupPipe;
|
||||
#endif
|
||||
|
||||
std::thread workerThread;
|
||||
|
||||
|
@ -539,8 +541,10 @@ struct curlFileTransfer : public FileTransfer
|
|||
fileTransferSettings.httpConnections.get());
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
wakeupPipe.create();
|
||||
fcntl(wakeupPipe.readSide.get(), F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
workerThread = std::thread([&]() { workerThreadEntry(); });
|
||||
}
|
||||
|
@ -561,15 +565,19 @@ struct curlFileTransfer : public FileTransfer
|
|||
auto state(state_.lock());
|
||||
state->quit = true;
|
||||
}
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
writeFull(wakeupPipe.writeSide.get(), " ", false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void workerThreadMain()
|
||||
{
|
||||
/* Cause this thread to be notified on SIGINT. */
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
auto callback = createInterruptCallback([&]() {
|
||||
stopWorkerThread();
|
||||
});
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
unshareFilesystem();
|
||||
|
@ -607,9 +615,11 @@ struct curlFileTransfer : public FileTransfer
|
|||
/* Wait for activity, including wakeup events. */
|
||||
int numfds = 0;
|
||||
struct curl_waitfd extraFDs[1];
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
extraFDs[0].fd = wakeupPipe.readSide.get();
|
||||
extraFDs[0].events = CURL_WAIT_POLLIN;
|
||||
extraFDs[0].revents = 0;
|
||||
#endif
|
||||
long maxSleepTimeMs = items.empty() ? 10000 : 100;
|
||||
auto sleepTimeMs =
|
||||
nextWakeup != std::chrono::steady_clock::time_point()
|
||||
|
@ -693,7 +703,9 @@ struct curlFileTransfer : public FileTransfer
|
|||
throw nix::Error("cannot enqueue download request because the download thread is shutting down");
|
||||
state->incoming.push(item);
|
||||
}
|
||||
#ifndef _WIN32 // TODO need graceful async exit support on Windows?
|
||||
writeFull(wakeupPipe.writeSide.get(), " ");
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_S3
|
||||
|
|
|
@ -9,11 +9,14 @@
|
|||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/utsname.h>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <dlfcn.h>
|
||||
# include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GLIBC__
|
||||
# include <gnu/lib-names.h>
|
||||
# include <nss.h>
|
||||
|
@ -46,7 +49,13 @@ static GlobalConfig::Register rSettings(&settings);
|
|||
|
||||
Settings::Settings()
|
||||
: nixPrefix(NIX_PREFIX)
|
||||
, nixStore(canonPath(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixStore(
|
||||
#ifndef _WIN32
|
||||
// On Windows `/nix/store` is not a canonical path, but we dont'
|
||||
// want to deal with that yet.
|
||||
canonPath
|
||||
#endif
|
||||
(getEnvNonEmpty("NIX_STORE_DIR").value_or(getEnvNonEmpty("NIX_STORE").value_or(NIX_STORE_DIR))))
|
||||
, nixDataDir(canonPath(getEnvNonEmpty("NIX_DATA_DIR").value_or(NIX_DATA_DIR)))
|
||||
, nixLogDir(canonPath(getEnvNonEmpty("NIX_LOG_DIR").value_or(NIX_LOG_DIR)))
|
||||
, nixStateDir(canonPath(getEnvNonEmpty("NIX_STATE_DIR").value_or(NIX_STATE_DIR)))
|
||||
|
@ -56,7 +65,9 @@ Settings::Settings()
|
|||
, nixManDir(canonPath(NIX_MAN_DIR))
|
||||
, nixDaemonSocketFile(canonPath(getEnvNonEmpty("NIX_DAEMON_SOCKET_PATH").value_or(nixStateDir + DEFAULT_SOCKET_PATH)))
|
||||
{
|
||||
#ifndef _WIN32
|
||||
buildUsersGroup = isRootUser() ? "nixbld" : "";
|
||||
#endif
|
||||
allowSymlinkedStore = getEnv("NIX_IGNORE_SYMLINK_STORE") == "1";
|
||||
|
||||
auto sslOverride = getEnv("NIX_SSL_CERT_FILE").value_or(getEnv("SSL_CERT_FILE").value_or(""));
|
||||
|
@ -239,11 +250,15 @@ StringSet Settings::getDefaultExtraPlatforms()
|
|||
|
||||
bool Settings::isWSL1()
|
||||
{
|
||||
#if __linux__
|
||||
struct utsname utsbuf;
|
||||
uname(&utsbuf);
|
||||
// WSL1 uses -Microsoft suffix
|
||||
// WSL2 uses -microsoft-standard suffix
|
||||
return hasSuffix(utsbuf.release, "-Microsoft");
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
Path Settings::getDefaultSSLCertFile()
|
||||
|
@ -341,6 +356,7 @@ void initPlugins()
|
|||
for (const auto & file : pluginFiles) {
|
||||
/* handle is purposefully leaked as there may be state in the
|
||||
DSO needed by the action of the plugin. */
|
||||
#ifndef _WIN32 // TODO implement via DLL loading on Windows
|
||||
void *handle =
|
||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
|
@ -351,6 +367,9 @@ void initPlugins()
|
|||
void (*nix_plugin_entry)() = (void (*)())dlsym(handle, "nix_plugin_entry");
|
||||
if (nix_plugin_entry)
|
||||
nix_plugin_entry();
|
||||
#else
|
||||
throw Error("could not dynamically open plugin file '%s'", file);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -666,6 +666,7 @@ public:
|
|||
Setting<bool> sandboxFallback{this, true, "sandbox-fallback",
|
||||
"Whether to disable sandboxing when the kernel doesn't allow it."};
|
||||
|
||||
#ifndef _WIN32
|
||||
Setting<bool> requireDropSupplementaryGroups{this, isRootUser(), "require-drop-supplementary-groups",
|
||||
R"(
|
||||
Following the principle of least privilege,
|
||||
|
@ -683,6 +684,7 @@ public:
|
|||
(since `root` usually has permissions to call setgroups)
|
||||
and `false` otherwise.
|
||||
)"};
|
||||
#endif
|
||||
|
||||
#if __linux__
|
||||
Setting<std::string> sandboxShmSize{
|
||||
|
|
|
@ -4,9 +4,12 @@ libstore_NAME = libnixstore
|
|||
|
||||
libstore_DIR := $(d)
|
||||
|
||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc $(d)/build/*.cc)
|
||||
libstore_SOURCES := $(wildcard $(d)/*.cc $(d)/builtins/*.cc)
|
||||
ifdef HOST_UNIX
|
||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc)
|
||||
libstore_SOURCES += $(wildcard $(d)/unix/*.cc $(d)/unix/builtins/*.cc $(d)/unix/build/*.cc)
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
libstore_SOURCES += $(wildcard $(d)/windows/*.cc)
|
||||
endif
|
||||
|
||||
libstore_LIBS = libutil
|
||||
|
@ -36,25 +39,39 @@ INCLUDE_libstore := -I $(d) -I $(d)/build
|
|||
ifdef HOST_UNIX
|
||||
INCLUDE_libstore += -I $(d)/unix
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
INCLUDE_libstore += -I $(d)/windows
|
||||
endif
|
||||
|
||||
ifdef HOST_WINDOWS
|
||||
NIX_ROOT = N:\\\\
|
||||
else
|
||||
NIX_ROOT =
|
||||
endif
|
||||
|
||||
# Prefix all but `NIX_STORE_DIR`, since we aren't doing a local store
|
||||
# yet so a "logical" store dir that is the same as unix is prefered.
|
||||
#
|
||||
# Also, it keeps the unit tests working.
|
||||
|
||||
libstore_CXXFLAGS += \
|
||||
$(INCLUDE_libutil) $(INCLUDE_libstore) $(INCLUDE_libstore) \
|
||||
-DNIX_PREFIX=\"$(prefix)\" \
|
||||
-DNIX_PREFIX=\"$(NIX_ROOT)$(prefix)\" \
|
||||
-DNIX_STORE_DIR=\"$(storedir)\" \
|
||||
-DNIX_DATA_DIR=\"$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(localstatedir)/log/nix\" \
|
||||
-DNIX_CONF_DIR=\"$(sysconfdir)/nix\" \
|
||||
-DNIX_BIN_DIR=\"$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(mandir)\" \
|
||||
-DLSOF=\"$(lsof)\"
|
||||
-DNIX_DATA_DIR=\"$(NIX_ROOT)$(datadir)\" \
|
||||
-DNIX_STATE_DIR=\"$(NIX_ROOT)$(localstatedir)/nix\" \
|
||||
-DNIX_LOG_DIR=\"$(NIX_ROOT)$(localstatedir)/log/nix\" \
|
||||
-DNIX_CONF_DIR=\"$(NIX_ROOT)$(sysconfdir)/nix\" \
|
||||
-DNIX_BIN_DIR=\"$(NIX_ROOT)$(bindir)\" \
|
||||
-DNIX_MAN_DIR=\"$(NIX_ROOT)$(mandir)\" \
|
||||
-DLSOF=\"$(NIX_ROOT)$(lsof)\"
|
||||
|
||||
ifeq ($(embedded_sandbox_shell),yes)
|
||||
libstore_CXXFLAGS += -DSANDBOX_SHELL=\"__embedded_sandbox_shell__\"
|
||||
|
||||
$(d)/build/local-derivation-goal.cc: $(d)/embedded-sandbox-shell.gen.hh
|
||||
$(d)/unix/build/local-derivation-goal.cc: $(d)/unix/embedded-sandbox-shell.gen.hh
|
||||
|
||||
$(d)/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||
$(d)/unix/embedded-sandbox-shell.gen.hh: $(sandbox_shell)
|
||||
$(trace-gen) hexdump -v -e '1/1 "0x%x," "\n"' < $< > $@.tmp
|
||||
@mv $@.tmp $@
|
||||
else
|
||||
|
@ -63,11 +80,11 @@ else
|
|||
endif
|
||||
endif
|
||||
|
||||
$(d)/local-store.cc: $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
||||
$(d)/unix/local-store.cc: $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||
|
||||
$(d)/build.cc:
|
||||
$(d)/unix/build.cc:
|
||||
|
||||
clean-files += $(d)/schema.sql.gen.hh $(d)/ca-specific-schema.sql.gen.hh
|
||||
clean-files += $(d)/unix/schema.sql.gen.hh $(d)/unix/ca-specific-schema.sql.gen.hh
|
||||
|
||||
$(eval $(call install-file-in, $(buildprefix)$(d)/nix-store.pc, $(libdir)/pkgconfig, 0644))
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#include "derivations.hh"
|
||||
#include "parsed-derivations.hh"
|
||||
#include "globals.hh"
|
||||
#include "local-store.hh"
|
||||
#include "store-api.hh"
|
||||
#include "thread-pool.hh"
|
||||
#include "realisation.hh"
|
||||
|
|
|
@ -63,7 +63,18 @@ StorePath StorePath::random(std::string_view name)
|
|||
|
||||
StorePath StoreDirConfig::parseStorePath(std::string_view path) const
|
||||
{
|
||||
auto p = canonPath(std::string(path));
|
||||
// On Windows, `/nix/store` is not a canonical path. More broadly it
|
||||
// is unclear whether this function should be using the native
|
||||
// notion of a canonical path at all. For example, it makes to
|
||||
// support remote stores whose store dir is a non-native path (e.g.
|
||||
// Windows <-> Unix ssh-ing).
|
||||
auto p =
|
||||
#ifdef _WIN32
|
||||
path
|
||||
#else
|
||||
canonPath(std::string(path))
|
||||
#endif
|
||||
;
|
||||
if (dirOf(p) != storeDir)
|
||||
throw BadStorePath("path '%s' is not in the Nix store", p);
|
||||
return StorePath(baseNameOf(p));
|
||||
|
|
|
@ -6,69 +6,9 @@
|
|||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
||||
{
|
||||
AutoCloseFD fd;
|
||||
|
||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void deleteLockFile(const Path & path, int fd)
|
||||
{
|
||||
/* Get rid of the lock file. Have to be careful not to introduce
|
||||
races. Write a (meaningless) token to the file to indicate to
|
||||
other processes waiting on this lock that the lock is stale
|
||||
(deleted). */
|
||||
unlink(path.c_str());
|
||||
writeFull(fd, "d");
|
||||
/* Note that the result of unlink() is ignored; removing the lock
|
||||
file is an optimisation, not a necessity. */
|
||||
}
|
||||
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait)
|
||||
{
|
||||
int type;
|
||||
if (lockType == ltRead) type = LOCK_SH;
|
||||
else if (lockType == ltWrite) type = LOCK_EX;
|
||||
else if (lockType == ltNone) type = LOCK_UN;
|
||||
else abort();
|
||||
|
||||
if (wait) {
|
||||
while (flock(fd, type) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
while (flock(fd, type | LOCK_NB) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno == EWOULDBLOCK) return false;
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PathLocks::PathLocks()
|
||||
: deletePaths(false)
|
||||
{
|
||||
|
@ -82,68 +22,6 @@ PathLocks::PathLocks(const PathSet & paths, const std::string & waitMsg)
|
|||
}
|
||||
|
||||
|
||||
bool PathLocks::lockPaths(const PathSet & paths,
|
||||
const std::string & waitMsg, bool wait)
|
||||
{
|
||||
assert(fds.empty());
|
||||
|
||||
/* Note that `fds' is built incrementally so that the destructor
|
||||
will only release those locks that we have already acquired. */
|
||||
|
||||
/* Acquire the lock for each path in sorted order. This ensures
|
||||
that locks are always acquired in the same order, thus
|
||||
preventing deadlocks. */
|
||||
for (auto & path : paths) {
|
||||
checkInterrupt();
|
||||
Path lockPath = path + ".lock";
|
||||
|
||||
debug("locking path '%1%'", path);
|
||||
|
||||
AutoCloseFD fd;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Open/create the lock file. */
|
||||
fd = openLockFile(lockPath, true);
|
||||
|
||||
/* Acquire an exclusive lock. */
|
||||
if (!lockFile(fd.get(), ltWrite, false)) {
|
||||
if (wait) {
|
||||
if (waitMsg != "") printError(waitMsg);
|
||||
lockFile(fd.get(), ltWrite, true);
|
||||
} else {
|
||||
/* Failed to lock this path; release all other
|
||||
locks. */
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug("lock acquired on '%1%'", lockPath);
|
||||
|
||||
/* Check that the lock file hasn't become stale (i.e.,
|
||||
hasn't been unlinked). */
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
throw SysError("statting lock file '%1%'", lockPath);
|
||||
if (st.st_size != 0)
|
||||
/* This lock file has been unlinked, so we're holding
|
||||
a lock on a deleted file. This means that other
|
||||
processes may create and acquire a lock on
|
||||
`lockPath', and proceed. So we must retry. */
|
||||
debug("open lock file '%1%' has become stale", lockPath);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use borrow so that the descriptor isn't closed. */
|
||||
fds.push_back(FDPair(fd.release(), lockPath));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
PathLocks::~PathLocks()
|
||||
{
|
||||
try {
|
||||
|
@ -154,40 +32,10 @@ PathLocks::~PathLocks()
|
|||
}
|
||||
|
||||
|
||||
void PathLocks::unlock()
|
||||
{
|
||||
for (auto & i : fds) {
|
||||
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||
|
||||
if (close(i.first) == -1)
|
||||
printError(
|
||||
"error (ignored): cannot close lock file on '%1%'",
|
||||
i.second);
|
||||
|
||||
debug("lock released on '%1%'", i.second);
|
||||
}
|
||||
|
||||
fds.clear();
|
||||
}
|
||||
|
||||
|
||||
void PathLocks::setDeletion(bool deletePaths)
|
||||
{
|
||||
this->deletePaths = deletePaths;
|
||||
}
|
||||
|
||||
|
||||
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
|
||||
: fd(fd)
|
||||
{
|
||||
if (wait) {
|
||||
if (!lockFile(fd, lockType, false)) {
|
||||
printInfo("%s", waitMsg);
|
||||
acquired = lockFile(fd, lockType, true);
|
||||
}
|
||||
} else
|
||||
acquired = lockFile(fd, lockType, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,22 +5,6 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Open (possibly create) a lock file and return the file descriptor.
|
||||
* -1 is returned if create is false and the lock could not be opened
|
||||
* because it doesn't exist. Any other error throws an exception.
|
||||
*/
|
||||
AutoCloseFD openLockFile(const Path & path, bool create);
|
||||
|
||||
/**
|
||||
* Delete an open lock file.
|
||||
*/
|
||||
void deleteLockFile(const Path & path, int fd);
|
||||
|
||||
enum LockType { ltRead, ltWrite, ltNone };
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait);
|
||||
|
||||
class PathLocks
|
||||
{
|
||||
private:
|
||||
|
@ -40,18 +24,6 @@ public:
|
|||
void setDeletion(bool deletePaths);
|
||||
};
|
||||
|
||||
struct FdLock
|
||||
{
|
||||
int fd;
|
||||
bool acquired = false;
|
||||
|
||||
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
||||
|
||||
~FdLock()
|
||||
{
|
||||
if (acquired)
|
||||
lockFile(fd, ltNone, false);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#include "pathlocks-impl.hh"
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "types.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
|
|
@ -71,11 +71,15 @@ std::pair<ref<SourceAccessor>, CanonPath> RemoteFSAccessor::fetch(const CanonPat
|
|||
auto narAccessor = makeLazyNarAccessor(listing,
|
||||
[cacheFile](uint64_t offset, uint64_t length) {
|
||||
|
||||
AutoCloseFD fd = open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd = toDescriptor(open(cacheFile.c_str(), O_RDONLY
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", cacheFile);
|
||||
|
||||
if (lseek(fd.get(), offset, SEEK_SET) != (off_t) offset)
|
||||
if (lseek(fromDescriptorReadOnly(fd.get()), offset, SEEK_SET) != (off_t) offset)
|
||||
throw SysError("seeking in '%s'", cacheFile);
|
||||
|
||||
std::string buf(length, 0);
|
||||
|
|
|
@ -55,6 +55,9 @@ bool SSHMaster::isMasterRunning() {
|
|||
std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
||||
Strings && command, Strings && extraSshArgs)
|
||||
{
|
||||
#ifdef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
throw UnimplementedError("cannot yet SSH on windows because spawning processes is not yet implemented");
|
||||
#else
|
||||
Path socketPath = startMaster();
|
||||
|
||||
Pipe in, out;
|
||||
|
@ -105,8 +108,8 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
|||
}, options);
|
||||
|
||||
|
||||
in.readSide = -1;
|
||||
out.writeSide = -1;
|
||||
in.readSide = INVALID_DESCRIPTOR;
|
||||
out.writeSide = INVALID_DESCRIPTOR;
|
||||
|
||||
// Wait for the SSH connection to be established,
|
||||
// So that we don't overwrite the password prompt with our progress bar.
|
||||
|
@ -126,15 +129,18 @@ std::unique_ptr<SSHMaster::Connection> SSHMaster::startCommand(
|
|||
conn->in = std::move(in.writeSide);
|
||||
|
||||
return conn;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
|
||||
Path SSHMaster::startMaster()
|
||||
{
|
||||
if (!useMaster) return "";
|
||||
|
||||
auto state(state_.lock());
|
||||
|
||||
if (state->sshMaster != -1) return state->socketPath;
|
||||
if (state->sshMaster != INVALID_DESCRIPTOR) return state->socketPath;
|
||||
|
||||
state->socketPath = (Path) *state->tmpDir + "/ssh.sock";
|
||||
|
||||
|
@ -167,7 +173,7 @@ Path SSHMaster::startMaster()
|
|||
throw SysError("unable to execute '%s'", args.front());
|
||||
}, options);
|
||||
|
||||
out.writeSide = -1;
|
||||
out.writeSide = INVALID_DESCRIPTOR;
|
||||
|
||||
std::string reply;
|
||||
try {
|
||||
|
@ -182,4 +188,6 @@ Path SSHMaster::startMaster()
|
|||
return state->socketPath;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,9 @@ private:
|
|||
|
||||
struct State
|
||||
{
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
Pid sshMaster;
|
||||
#endif
|
||||
std::unique_ptr<AutoDelete> tmpDir;
|
||||
Path socketPath;
|
||||
};
|
||||
|
@ -31,13 +33,19 @@ private:
|
|||
void addCommonSSHOpts(Strings & args);
|
||||
bool isMasterRunning();
|
||||
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
Path startMaster();
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
SSHMaster(const std::string & host, const std::string & keyFile, const std::string & sshPublicHostKey, bool useMaster, bool compress, int logFD = -1);
|
||||
|
||||
struct Connection
|
||||
{
|
||||
#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes.
|
||||
Pid sshPid;
|
||||
#endif
|
||||
AutoCloseFD out, in;
|
||||
};
|
||||
|
||||
|
@ -51,8 +59,6 @@ public:
|
|||
std::unique_ptr<Connection> startCommand(
|
||||
Strings && command,
|
||||
Strings && extraSshArgs = {});
|
||||
|
||||
Path startMaster();
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "archive.hh"
|
||||
#include "callback.hh"
|
||||
#include "git.hh"
|
||||
#include "remote-store.hh"
|
||||
#include "posix-source-accessor.hh"
|
||||
// FIXME this should not be here, see TODO below on
|
||||
// `addMultipleToStore`.
|
||||
|
@ -21,6 +20,10 @@
|
|||
#include "signals.hh"
|
||||
#include "users.hh"
|
||||
|
||||
#ifndef _WIN32
|
||||
# include "remote-store.hh"
|
||||
#endif
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <regex>
|
||||
|
||||
|
@ -1266,9 +1269,10 @@ Derivation Store::readInvalidDerivation(const StorePath & drvPath)
|
|||
|
||||
}
|
||||
|
||||
|
||||
#include "local-store.hh"
|
||||
#include "uds-remote-store.hh"
|
||||
#ifndef _WIN32
|
||||
# include "local-store.hh"
|
||||
# include "uds-remote-store.hh"
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
@ -1286,6 +1290,9 @@ std::pair<std::string, Store::Params> splitUriAndParams(const std::string & uri_
|
|||
return {uri, params};
|
||||
}
|
||||
|
||||
#ifdef _WIN32 // Unused on Windows because the next `#ifndef`
|
||||
[[maybe_unused]]
|
||||
#endif
|
||||
static bool isNonUriPath(const std::string & spec)
|
||||
{
|
||||
return
|
||||
|
@ -1298,6 +1305,9 @@ static bool isNonUriPath(const std::string & spec)
|
|||
|
||||
std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Params & params)
|
||||
{
|
||||
// TODO reenable on Windows once we have `LocalStore` and
|
||||
// `UDSRemoteStore`.
|
||||
#ifndef _WIN32
|
||||
if (uri == "" || uri == "auto") {
|
||||
auto stateDir = getOr(params, "state", settings.nixStateDir);
|
||||
if (access(stateDir.c_str(), R_OK | W_OK) == 0)
|
||||
|
@ -1342,6 +1352,9 @@ std::shared_ptr<Store> openFromNonUri(const std::string & uri, const Store::Para
|
|||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
// The `parseURL` function supports both IPv6 URIs as defined in
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
using namespace nix::unix;
|
||||
|
||||
static std::string gcSocketPath = "/gc-socket/socket";
|
||||
static std::string gcRootsDir = "gcroots";
|
|
@ -52,6 +52,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
using namespace nix::unix;
|
||||
|
||||
std::string LocalStoreConfig::doc()
|
||||
{
|
||||
return
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
using namespace nix::unix;
|
||||
|
||||
#if __linux__
|
||||
|
||||
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
38
src/libstore/unix/pathlocks-impl.hh
Normal file
38
src/libstore/unix/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "file-descriptor.hh"
|
||||
|
||||
namespace nix::unix {
|
||||
|
||||
/**
|
||||
* Open (possibly create) a lock file and return the file descriptor.
|
||||
* -1 is returned if create is false and the lock could not be opened
|
||||
* because it doesn't exist. Any other error throws an exception.
|
||||
*/
|
||||
AutoCloseFD openLockFile(const Path & path, bool create);
|
||||
|
||||
/**
|
||||
* Delete an open lock file.
|
||||
*/
|
||||
void deleteLockFile(const Path & path, int fd);
|
||||
|
||||
enum LockType { ltRead, ltWrite, ltNone };
|
||||
|
||||
bool lockFile(int fd, LockType lockType, bool wait);
|
||||
|
||||
struct FdLock
|
||||
{
|
||||
int fd;
|
||||
bool acquired = false;
|
||||
|
||||
FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg);
|
||||
|
||||
~FdLock()
|
||||
{
|
||||
if (acquired)
|
||||
lockFile(fd, ltNone, false);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
165
src/libstore/unix/pathlocks.cc
Normal file
165
src/libstore/unix/pathlocks.cc
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "pathlocks.hh"
|
||||
#include "util.hh"
|
||||
#include "sync.hh"
|
||||
#include "signals.hh"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
using namespace nix::unix;
|
||||
|
||||
AutoCloseFD unix::openLockFile(const Path & path, bool create)
|
||||
{
|
||||
AutoCloseFD fd;
|
||||
|
||||
fd = open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600);
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
|
||||
void unix::deleteLockFile(const Path & path, int fd)
|
||||
{
|
||||
/* Get rid of the lock file. Have to be careful not to introduce
|
||||
races. Write a (meaningless) token to the file to indicate to
|
||||
other processes waiting on this lock that the lock is stale
|
||||
(deleted). */
|
||||
unlink(path.c_str());
|
||||
writeFull(fd, "d");
|
||||
/* Note that the result of unlink() is ignored; removing the lock
|
||||
file is an optimisation, not a necessity. */
|
||||
}
|
||||
|
||||
|
||||
bool unix::lockFile(int fd, LockType lockType, bool wait)
|
||||
{
|
||||
int type;
|
||||
if (lockType == ltRead) type = LOCK_SH;
|
||||
else if (lockType == ltWrite) type = LOCK_EX;
|
||||
else if (lockType == ltNone) type = LOCK_UN;
|
||||
else abort();
|
||||
|
||||
if (wait) {
|
||||
while (flock(fd, type) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
else
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
while (flock(fd, type | LOCK_NB) != 0) {
|
||||
checkInterrupt();
|
||||
if (errno == EWOULDBLOCK) return false;
|
||||
if (errno != EINTR)
|
||||
throw SysError("acquiring/releasing lock");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool PathLocks::lockPaths(const PathSet & paths,
|
||||
const std::string & waitMsg, bool wait)
|
||||
{
|
||||
assert(fds.empty());
|
||||
|
||||
/* Note that `fds' is built incrementally so that the destructor
|
||||
will only release those locks that we have already acquired. */
|
||||
|
||||
/* Acquire the lock for each path in sorted order. This ensures
|
||||
that locks are always acquired in the same order, thus
|
||||
preventing deadlocks. */
|
||||
for (auto & path : paths) {
|
||||
checkInterrupt();
|
||||
Path lockPath = path + ".lock";
|
||||
|
||||
debug("locking path '%1%'", path);
|
||||
|
||||
AutoCloseFD fd;
|
||||
|
||||
while (1) {
|
||||
|
||||
/* Open/create the lock file. */
|
||||
fd = openLockFile(lockPath, true);
|
||||
|
||||
/* Acquire an exclusive lock. */
|
||||
if (!lockFile(fd.get(), ltWrite, false)) {
|
||||
if (wait) {
|
||||
if (waitMsg != "") printError(waitMsg);
|
||||
lockFile(fd.get(), ltWrite, true);
|
||||
} else {
|
||||
/* Failed to lock this path; release all other
|
||||
locks. */
|
||||
unlock();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
debug("lock acquired on '%1%'", lockPath);
|
||||
|
||||
/* Check that the lock file hasn't become stale (i.e.,
|
||||
hasn't been unlinked). */
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
throw SysError("statting lock file '%1%'", lockPath);
|
||||
if (st.st_size != 0)
|
||||
/* This lock file has been unlinked, so we're holding
|
||||
a lock on a deleted file. This means that other
|
||||
processes may create and acquire a lock on
|
||||
`lockPath', and proceed. So we must retry. */
|
||||
debug("open lock file '%1%' has become stale", lockPath);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Use borrow so that the descriptor isn't closed. */
|
||||
fds.push_back(FDPair(fd.release(), lockPath));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void PathLocks::unlock()
|
||||
{
|
||||
for (auto & i : fds) {
|
||||
if (deletePaths) deleteLockFile(i.second, i.first);
|
||||
|
||||
if (close(i.first) == -1)
|
||||
printError(
|
||||
"error (ignored): cannot close lock file on '%1%'",
|
||||
i.second);
|
||||
|
||||
debug("lock released on '%1%'", i.second);
|
||||
}
|
||||
|
||||
fds.clear();
|
||||
}
|
||||
|
||||
|
||||
FdLock::FdLock(int fd, LockType lockType, bool wait, std::string_view waitMsg)
|
||||
: fd(fd)
|
||||
{
|
||||
if (wait) {
|
||||
if (!lockFile(fd, lockType, false)) {
|
||||
printInfo("%s", waitMsg);
|
||||
acquired = lockFile(fd, lockType, true);
|
||||
}
|
||||
} else
|
||||
acquired = lockFile(fd, lockType, false);
|
||||
}
|
||||
|
||||
|
||||
}
|
37
src/libstore/windows/build.cc
Normal file
37
src/libstore/windows/build.cc
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "store-api.hh"
|
||||
#include "build-result.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
void Store::buildPaths(const std::vector<DerivedPath> & reqs, BuildMode buildMode, std::shared_ptr<Store> evalStore)
|
||||
{
|
||||
unsupported("buildPaths");
|
||||
}
|
||||
|
||||
std::vector<KeyedBuildResult> Store::buildPathsWithResults(
|
||||
const std::vector<DerivedPath> & reqs,
|
||||
BuildMode buildMode,
|
||||
std::shared_ptr<Store> evalStore)
|
||||
{
|
||||
unsupported("buildPathsWithResults");
|
||||
}
|
||||
|
||||
BuildResult Store::buildDerivation(const StorePath & drvPath, const BasicDerivation & drv,
|
||||
BuildMode buildMode)
|
||||
{
|
||||
unsupported("buildDerivation");
|
||||
}
|
||||
|
||||
|
||||
void Store::ensurePath(const StorePath & path)
|
||||
{
|
||||
unsupported("ensurePath");
|
||||
}
|
||||
|
||||
|
||||
void Store::repairPath(const StorePath & path)
|
||||
{
|
||||
unsupported("repairPath");
|
||||
}
|
||||
|
||||
}
|
2
src/libstore/windows/pathlocks-impl.hh
Normal file
2
src/libstore/windows/pathlocks-impl.hh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#pragma once
|
||||
///@file Needed because Unix-specific counterpart
|
16
src/libstore/windows/pathlocks.cc
Normal file
16
src/libstore/windows/pathlocks.cc
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "logging.hh"
|
||||
#include "pathlocks.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
bool PathLocks::lockPaths(const PathSet & _paths, const std::string & waitMsg, bool wait)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PathLocks::unlock()
|
||||
{
|
||||
warn("PathLocks::unlock: not yet implemented");
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,9 @@
|
|||
#include <fstream>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <glob.h>
|
||||
#ifndef _WIN32
|
||||
# include <glob.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -547,6 +549,7 @@ nlohmann::json Args::toJSON()
|
|||
static void _completePath(AddCompletions & completions, std::string_view prefix, bool onlyDirs)
|
||||
{
|
||||
completions.setType(Completions::Type::Filenames);
|
||||
#ifndef _WIN32 // TODO implement globbing completions on Windows
|
||||
glob_t globbuf;
|
||||
int flags = GLOB_NOESCAPE;
|
||||
#ifdef GLOB_ONLYDIR
|
||||
|
@ -564,6 +567,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix,
|
|||
}
|
||||
}
|
||||
globfree(&globbuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Args::completePath(AddCompletions & completions, size_t, std::string_view prefix)
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
# include "namespaces.hh"
|
||||
#endif
|
||||
|
||||
#include <sys/mount.h>
|
||||
#ifndef _WIN32
|
||||
# include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -57,6 +59,7 @@ unsigned int getMaxCPU()
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
rlim_t savedStackSize = 0;
|
||||
|
||||
void setStackSize(rlim_t stackSize)
|
||||
|
@ -79,16 +82,20 @@ void setStackSize(rlim_t stackSize)
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void restoreProcessContext(bool restoreMounts)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
unix::restoreSignals();
|
||||
#endif
|
||||
if (restoreMounts) {
|
||||
#if __linux__
|
||||
restoreMountNamespace();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
if (savedStackSize) {
|
||||
struct rlimit limit;
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0) {
|
||||
|
@ -96,6 +103,7 @@ void restoreProcessContext(bool restoreMounts)
|
|||
setrlimit(RLIMIT_STACK, &limit);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2,7 +2,10 @@
|
|||
///@file
|
||||
|
||||
#include <optional>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
|
@ -14,16 +17,18 @@ namespace nix {
|
|||
*/
|
||||
unsigned int getMaxCPU();
|
||||
|
||||
#ifndef _WIN32 // TODO implement on Windows, if needed.
|
||||
/**
|
||||
* Change the stack size.
|
||||
*/
|
||||
void setStackSize(rlim_t stackSize);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Restore the original inherited Unix process context (such as signal
|
||||
* masks, stack size).
|
||||
|
||||
* See startSignalHandlerThread(), saveSignalMask().
|
||||
* See unix::startSignalHandlerThread(), unix::saveSignalMask().
|
||||
*/
|
||||
void restoreProcessContext(bool restoreMounts = true);
|
||||
|
||||
|
|
|
@ -28,6 +28,13 @@ std::optional<std::string> getEnvNonEmpty(const std::string & key);
|
|||
*/
|
||||
std::map<std::string, std::string> getEnv();
|
||||
|
||||
#ifdef _WIN32
|
||||
/**
|
||||
* Implementation of missing POSIX function.
|
||||
*/
|
||||
int unsetenv(const char * name);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Like POSIX `setenv`, but always overrides.
|
||||
*
|
||||
|
|
|
@ -247,6 +247,23 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef _WIN32
|
||||
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
|
||||
WinError
|
||||
#else
|
||||
SysError
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* Throw an exception for the purpose of checking that exception
|
||||
* handling works; see 'initLibUtil()'.
|
||||
|
|
|
@ -5,6 +5,11 @@
|
|||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
# include <winnt.h>
|
||||
# include <fileapi.h>
|
||||
# include "windows-error.hh"
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -20,7 +25,13 @@ std::string drainFD(Descriptor fd, bool block, const size_t reserveSize)
|
|||
// the parser needs two extra bytes to append terminating characters, other users will
|
||||
// not care very much about the extra memory.
|
||||
StringSink sink(reserveSize + 2);
|
||||
#ifdef _WIN32
|
||||
// non-blocking is not supported this way on Windows
|
||||
assert(block);
|
||||
drainFD(fd, sink);
|
||||
#else
|
||||
drainFD(fd, sink, block);
|
||||
#endif
|
||||
return std::move(sink.s);
|
||||
}
|
||||
|
||||
|
@ -68,9 +79,15 @@ Descriptor AutoCloseFD::get() const
|
|||
void AutoCloseFD::close()
|
||||
{
|
||||
if (fd != INVALID_DESCRIPTOR) {
|
||||
if(::close(fd) == -1)
|
||||
if(
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(fd)
|
||||
#else
|
||||
::close(fd)
|
||||
#endif
|
||||
== -1)
|
||||
/* This should never happen. */
|
||||
throw SysError("closing file descriptor %1%", fd);
|
||||
throw NativeSysError("closing file descriptor %1%", fd);
|
||||
fd = INVALID_DESCRIPTOR;
|
||||
}
|
||||
}
|
||||
|
@ -80,14 +97,16 @@ void AutoCloseFD::fsync()
|
|||
if (fd != INVALID_DESCRIPTOR) {
|
||||
int result;
|
||||
result =
|
||||
#if __APPLE__
|
||||
#ifdef _WIN32
|
||||
::FlushFileBuffers(fd)
|
||||
#elif __APPLE__
|
||||
::fcntl(fd, F_FULLFSYNC)
|
||||
#else
|
||||
::fsync(fd)
|
||||
#endif
|
||||
;
|
||||
if (result == -1)
|
||||
throw SysError("fsync file descriptor %1%", fd);
|
||||
throw NativeSysError("fsync file descriptor %1%", fd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
#include "types.hh"
|
||||
#include "error.hh"
|
||||
|
||||
#ifdef _WIN32
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct Sink;
|
||||
|
@ -12,9 +17,21 @@ struct Source;
|
|||
/**
|
||||
* Operating System capability
|
||||
*/
|
||||
typedef int Descriptor;
|
||||
typedef
|
||||
#if _WIN32
|
||||
HANDLE
|
||||
#else
|
||||
int
|
||||
#endif
|
||||
Descriptor;
|
||||
|
||||
const Descriptor INVALID_DESCRIPTOR = -1;
|
||||
const Descriptor INVALID_DESCRIPTOR =
|
||||
#if _WIN32
|
||||
INVALID_HANDLE_VALUE
|
||||
#else
|
||||
-1
|
||||
#endif
|
||||
;
|
||||
|
||||
/**
|
||||
* Convert a native `Descriptor` to a POSIX file descriptor
|
||||
|
@ -23,17 +40,26 @@ const Descriptor INVALID_DESCRIPTOR = -1;
|
|||
*/
|
||||
static inline Descriptor toDescriptor(int fd)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return (HANDLE) _get_osfhandle(fd);
|
||||
#else
|
||||
return fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a POSIX file descriptor to a native `Descriptor`
|
||||
* Convert a POSIX file descriptor to a native `Descriptor` in read-only
|
||||
* mode.
|
||||
*
|
||||
* This is a no-op except on Windows.
|
||||
*/
|
||||
static inline int fromDescriptor(Descriptor fd, int flags)
|
||||
static inline int fromDescriptorReadOnly(Descriptor fd)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return _open_osfhandle((intptr_t) fd, _O_RDONLY);
|
||||
#else
|
||||
return fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,11 +90,24 @@ void writeLine(Descriptor fd, std::string s);
|
|||
*/
|
||||
std::string drainFD(Descriptor fd, bool block = true, const size_t reserveSize=0);
|
||||
|
||||
void drainFD(Descriptor fd, Sink & sink, bool block = true);
|
||||
/**
|
||||
* The Windows version is always blocking.
|
||||
*/
|
||||
void drainFD(
|
||||
Descriptor fd
|
||||
, Sink & sink
|
||||
#ifndef _WIN32
|
||||
, bool block = true
|
||||
#endif
|
||||
);
|
||||
|
||||
[[gnu::always_inline]]
|
||||
inline Descriptor getStandardOut() {
|
||||
#ifndef _WIN32
|
||||
return STDOUT_FILENO;
|
||||
#else
|
||||
return GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,6 +139,8 @@ public:
|
|||
void close();
|
||||
};
|
||||
|
||||
#ifndef _WIN32 // Not needed on Windows, where we don't fork
|
||||
|
||||
/**
|
||||
* Close all file descriptors except those listed in the given set.
|
||||
* Good practice in child processes.
|
||||
|
@ -111,6 +152,15 @@ void closeMostFDs(const std::set<Descriptor> & exceptions);
|
|||
*/
|
||||
void closeOnExec(Descriptor fd);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# if _WIN32_WINNT >= 0x0600
|
||||
Path handleToPath(Descriptor handle);
|
||||
std::wstring handleToFileName(Descriptor handle);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
MakeError(EndOfFile, Error);
|
||||
|
||||
}
|
||||
|
|
52
src/libutil/file-path.hh
Normal file
52
src/libutil/file-path.hh
Normal file
|
@ -0,0 +1,52 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <optional>
|
||||
#include <filesystem>
|
||||
|
||||
#include "types.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
/**
|
||||
* Paths are just `std::filesystem::path`s.
|
||||
*
|
||||
* @todo drop `NG` suffix and replace the ones in `types.hh`.
|
||||
*/
|
||||
typedef std::filesystem::path PathNG;
|
||||
typedef std::list<Path> PathsNG;
|
||||
typedef std::set<Path> PathSetNG;
|
||||
|
||||
/**
|
||||
* Stop gap until `std::filesystem::path_view` from P1030R6 exists in a
|
||||
* future C++ standard.
|
||||
*
|
||||
* @todo drop `NG` suffix and replace the one in `types.hh`.
|
||||
*/
|
||||
struct PathViewNG : std::basic_string_view<PathNG::value_type>
|
||||
{
|
||||
using string_view = std::basic_string_view<PathNG::value_type>;
|
||||
|
||||
using string_view::string_view;
|
||||
|
||||
PathViewNG(const PathNG & path)
|
||||
: std::basic_string_view<PathNG::value_type>(path.native())
|
||||
{ }
|
||||
|
||||
PathViewNG(const PathNG::string_type & path)
|
||||
: std::basic_string_view<PathNG::value_type>(path)
|
||||
{ }
|
||||
|
||||
const string_view & native() const { return *this; }
|
||||
string_view & native() { return *this; }
|
||||
};
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path);
|
||||
|
||||
PathNG::string_type string_to_os_string(std::string_view s);
|
||||
|
||||
std::optional<PathNG> maybePathNG(PathView path);
|
||||
|
||||
PathNG pathNG(PathView path);
|
||||
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
#include "environment-variables.hh"
|
||||
#include "file-system.hh"
|
||||
#include "file-path.hh"
|
||||
#include "file-path-impl.hh"
|
||||
#include "signals.hh"
|
||||
#include "finally.hh"
|
||||
|
@ -18,6 +19,10 @@
|
|||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace nix {
|
||||
|
@ -128,10 +133,10 @@ std::string_view baseNameOf(std::string_view path)
|
|||
return "";
|
||||
|
||||
auto last = path.size() - 1;
|
||||
while (last > 0 && path[last] == '/')
|
||||
while (last > 0 && NativePathTrait::isPathSep(path[last]))
|
||||
last -= 1;
|
||||
|
||||
auto pos = path.rfind('/', last);
|
||||
auto pos = NativePathTrait::rfindPathSep(path, last);
|
||||
if (pos == path.npos)
|
||||
pos = 0;
|
||||
else
|
||||
|
@ -164,11 +169,16 @@ struct stat stat(const Path & path)
|
|||
return st;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
# define STAT stat
|
||||
#else
|
||||
# define STAT lstat
|
||||
#endif
|
||||
|
||||
struct stat lstat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
if (STAT(path.c_str(), &st))
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
return st;
|
||||
}
|
||||
|
@ -177,7 +187,7 @@ struct stat lstat(const Path & path)
|
|||
std::optional<struct stat> maybeLstat(const Path & path)
|
||||
{
|
||||
std::optional<struct stat> st{std::in_place};
|
||||
if (lstat(path.c_str(), &*st))
|
||||
if (STAT(path.c_str(), &*st))
|
||||
{
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
st.reset();
|
||||
|
@ -207,6 +217,7 @@ bool pathAccessible(const Path & path)
|
|||
|
||||
Path readLink(const Path & path)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
checkInterrupt();
|
||||
std::vector<char> buf;
|
||||
for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) {
|
||||
|
@ -220,13 +231,16 @@ Path readLink(const Path & path)
|
|||
else if (rlSize < bufSize)
|
||||
return std::string(buf.data(), rlSize);
|
||||
}
|
||||
#else
|
||||
// TODO modern Windows does in fact support symlinks
|
||||
throw UnimplementedError("reading symbolic link '%1%'", path);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool isLink(const Path & path)
|
||||
{
|
||||
struct stat st = lstat(path);
|
||||
return S_ISLNK(st.st_mode);
|
||||
return getFileType(path) == DT_LNK;
|
||||
}
|
||||
|
||||
|
||||
|
@ -274,7 +288,12 @@ unsigned char getFileType(const Path & path)
|
|||
|
||||
std::string readFile(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return readFile(fd.get());
|
||||
|
@ -283,7 +302,12 @@ std::string readFile(const Path & path)
|
|||
|
||||
void readFile(const Path & path, Sink & sink)
|
||||
{
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_RDONLY
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%s'", path);
|
||||
drainFD(fd.get(), sink);
|
||||
|
@ -292,7 +316,12 @@ void readFile(const Path & path, Sink & sink)
|
|||
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
||||
{
|
||||
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
, mode));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
try {
|
||||
|
@ -312,7 +341,12 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
|
|||
|
||||
void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
||||
{
|
||||
AutoCloseFD fd = open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode);
|
||||
AutoCloseFD fd = toDescriptor(open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT
|
||||
// TODO
|
||||
#ifndef _WIN32
|
||||
| O_CLOEXEC
|
||||
#endif
|
||||
, mode));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
|
||||
|
@ -339,21 +373,23 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
|
|||
|
||||
void syncParent(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd = open(dirOf(path).c_str(), O_RDONLY, 0);
|
||||
AutoCloseFD fd = toDescriptor(open(dirOf(path).c_str(), O_RDONLY, 0));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
fd.fsync();
|
||||
}
|
||||
|
||||
|
||||
static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
|
||||
static void _deletePath(Descriptor parentfd, const Path & path, uint64_t & bytesFreed)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
checkInterrupt();
|
||||
|
||||
std::string name(baseNameOf(path));
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (fstatat(parentfd, name.c_str(), &st,
|
||||
AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
}
|
||||
|
@ -405,6 +441,10 @@ static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
|
|||
if (errno == ENOENT) return;
|
||||
throw SysError("cannot unlink '%1%'", path);
|
||||
}
|
||||
#else
|
||||
// TODO implement
|
||||
throw UnimplementedError("_deletePath");
|
||||
#endif
|
||||
}
|
||||
|
||||
static void _deletePath(const Path & path, uint64_t & bytesFreed)
|
||||
|
@ -413,7 +453,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed)
|
|||
if (dir == "")
|
||||
dir = "/";
|
||||
|
||||
AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)};
|
||||
AutoCloseFD dirfd = toDescriptor(open(dir.c_str(), O_RDONLY));
|
||||
if (!dirfd) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("opening directory '%1%'", path);
|
||||
|
@ -436,11 +476,15 @@ Paths createDirs(const Path & path)
|
|||
if (path == "/") return created;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1) {
|
||||
if (STAT(path.c_str(), &st) == -1) {
|
||||
created = createDirs(dirOf(path));
|
||||
if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
|
||||
if (mkdir(path.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
, 0777
|
||||
#endif
|
||||
) == -1 && errno != EEXIST)
|
||||
throw SysError("creating directory '%1%'", path);
|
||||
st = lstat(path);
|
||||
st = STAT(path);
|
||||
created.push_back(path);
|
||||
}
|
||||
|
||||
|
@ -526,7 +570,11 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
|
|||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
|
||||
if (mkdir(tmpDir.c_str(), mode) == 0) {
|
||||
if (mkdir(tmpDir.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
, mode
|
||||
#endif
|
||||
) == 0) {
|
||||
#if __FreeBSD__
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
|
@ -552,17 +600,24 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
|||
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
|
||||
// Strictly speaking, this is UB, but who cares...
|
||||
// FIXME: use O_TMPFILE.
|
||||
AutoCloseFD fd(mkstemp((char *) tmpl.c_str()));
|
||||
AutoCloseFD fd = toDescriptor(mkstemp((char *) tmpl.c_str()));
|
||||
if (!fd)
|
||||
throw SysError("creating temporary file '%s'", tmpl);
|
||||
#ifndef _WIN32
|
||||
closeOnExec(fd.get());
|
||||
#endif
|
||||
return {std::move(fd), tmpl};
|
||||
}
|
||||
|
||||
void createSymlink(const Path & target, const Path & link)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
if (symlink(target.c_str(), link.c_str()))
|
||||
throw SysError("creating symlink from '%1%' to '%2%'", link, target);
|
||||
#else
|
||||
// TODO modern Windows does in fact support symlinks
|
||||
throw UnimplementedError("createSymlink");
|
||||
#endif
|
||||
}
|
||||
|
||||
void replaceSymlink(const Path & target, const Path & link)
|
||||
|
@ -583,7 +638,8 @@ void replaceSymlink(const Path & target, const Path & link)
|
|||
}
|
||||
}
|
||||
|
||||
void setWriteTime(const fs::path & p, const struct stat & st)
|
||||
#ifndef _WIN32
|
||||
static void setWriteTime(const fs::path & p, const struct stat & st)
|
||||
{
|
||||
struct timeval times[2];
|
||||
times[0] = {
|
||||
|
@ -597,11 +653,14 @@ void setWriteTime(const fs::path & p, const struct stat & st)
|
|||
if (lutimes(p.c_str(), times) != 0)
|
||||
throw SysError("changing modification time of '%s'", p);
|
||||
}
|
||||
#endif
|
||||
|
||||
void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
// TODO: Rewrite the `is_*` to use `symlink_status()`
|
||||
auto statOfFrom = lstat(from.path().c_str());
|
||||
#endif
|
||||
auto fromStatus = from.symlink_status();
|
||||
|
||||
// Mark the directory as writable so that we can delete its children
|
||||
|
@ -621,7 +680,9 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
|
|||
throw Error("file '%s' has an unsupported type", from.path());
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
setWriteTime(to, statOfFrom);
|
||||
#endif
|
||||
if (andDelete) {
|
||||
if (!fs::is_symlink(fromStatus))
|
||||
fs::permissions(from.path(), fs::perms::owner_write, fs::perm_options::add | fs::perm_options::nofollow);
|
||||
|
@ -648,14 +709,18 @@ void moveFile(const Path & oldName, const Path & newName)
|
|||
auto newPath = fs::path(newName);
|
||||
// For the move to be as atomic as possible, copy to a temporary
|
||||
// directory
|
||||
fs::path temp = createTempDir(newPath.parent_path(), "rename-tmp");
|
||||
fs::path temp = createTempDir(
|
||||
os_string_to_string(PathViewNG { newPath.parent_path() }),
|
||||
"rename-tmp");
|
||||
Finally removeTemp = [&]() { fs::remove(temp); };
|
||||
auto tempCopyTarget = temp / "copy-target";
|
||||
if (e.code().value() == EXDEV) {
|
||||
fs::remove(newPath);
|
||||
warn("Can’t rename %s as %s, copying instead", oldName, newName);
|
||||
copy(fs::directory_entry(oldPath), tempCopyTarget, true);
|
||||
renameFile(tempCopyTarget, newPath);
|
||||
renameFile(
|
||||
os_string_to_string(PathViewNG { tempCopyTarget }),
|
||||
os_string_to_string(PathViewNG { newPath }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
#ifdef _WIN32
|
||||
# include <windef.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
@ -31,6 +34,17 @@
|
|||
#define DT_DIR 3
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Polyfill for MinGW
|
||||
*
|
||||
* Windows does in fact support symlinks, but the C runtime interfaces predate this.
|
||||
*
|
||||
* @todo get rid of this, and stop using `stat` when we want `lstat` too.
|
||||
*/
|
||||
#ifndef S_ISLNK
|
||||
# define S_ISLNK(m) false
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
struct Sink;
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
#include <fcntl.h>
|
||||
|
||||
#include "error.hh"
|
||||
#include "config.hh"
|
||||
#include "fs-sink.hh"
|
||||
|
||||
#if _WIN32
|
||||
# include <fileapi.h>
|
||||
# include "file-path.hh"
|
||||
# include "windows-error.hh"
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
void copyRecursive(
|
||||
|
@ -65,8 +72,14 @@ static GlobalConfig::Register r1(&restoreSinkSettings);
|
|||
void RestoreSink::createDirectory(const Path & path)
|
||||
{
|
||||
Path p = dstPath + path;
|
||||
if (mkdir(p.c_str(), 0777) == -1)
|
||||
throw SysError("creating directory '%1%'", p);
|
||||
if (
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
mkdir(p.c_str(), 0777) == -1
|
||||
#else
|
||||
!CreateDirectoryW(pathNG(p).c_str(), NULL)
|
||||
#endif
|
||||
)
|
||||
throw NativeSysError("creating directory '%1%'", p);
|
||||
};
|
||||
|
||||
struct RestoreRegularFile : CreateRegularFileSink {
|
||||
|
@ -81,18 +94,28 @@ void RestoreSink::createRegularFile(const Path & path, std::function<void(Create
|
|||
{
|
||||
Path p = dstPath + path;
|
||||
RestoreRegularFile crf;
|
||||
crf.fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
|
||||
if (!crf.fd) throw SysError("creating file '%1%'", p);
|
||||
crf.fd =
|
||||
#ifdef _WIN32
|
||||
CreateFileW(pathNG(path).c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)
|
||||
#else
|
||||
open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)
|
||||
#endif
|
||||
;
|
||||
if (!crf.fd) throw NativeSysError("creating file '%1%'", p);
|
||||
func(crf);
|
||||
}
|
||||
|
||||
void RestoreRegularFile::isExecutable()
|
||||
{
|
||||
// Windows doesn't have a notion of executable file permissions we
|
||||
// care about here, right?
|
||||
#ifndef _WIN32
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
throw SysError("fstat");
|
||||
if (fchmod(fd.get(), st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
|
||||
throw SysError("fchmod");
|
||||
#endif
|
||||
}
|
||||
|
||||
void RestoreRegularFile::preallocateContents(uint64_t len)
|
||||
|
|
|
@ -11,6 +11,9 @@ endif
|
|||
ifdef HOST_LINUX
|
||||
libutil_SOURCES += $(wildcard $(d)/linux/*.cc)
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
libutil_SOURCES += $(wildcard $(d)/windows/*.cc)
|
||||
endif
|
||||
|
||||
# Not just for this library itself, but also for downstream libraries using this library
|
||||
|
||||
|
@ -21,6 +24,9 @@ endif
|
|||
ifdef HOST_LINUX
|
||||
INCLUDE_libutil += -I $(d)/linux
|
||||
endif
|
||||
ifdef HOST_WINDOWS
|
||||
INCLUDE_libutil += -I $(d)/windows
|
||||
endif
|
||||
libutil_CXXFLAGS += $(INCLUDE_libutil)
|
||||
|
||||
libutil_LDFLAGS += $(THREAD_LDFLAGS) $(LIBCURL_LIBS) $(SODIUM_LIBS) $(OPENSSL_LIBS) $(LIBBROTLI_LIBS) $(LIBARCHIVE_LIBS) $(BOOST_LDFLAGS) -lboost_context
|
||||
|
|
|
@ -116,7 +116,13 @@ Verbosity verbosity = lvlInfo;
|
|||
void writeToStderr(std::string_view s)
|
||||
{
|
||||
try {
|
||||
writeFull(STDERR_FILENO, s, false);
|
||||
writeFull(
|
||||
#ifdef _WIN32
|
||||
GetStdHandle(STD_ERROR_HANDLE),
|
||||
#else
|
||||
STDERR_FILENO,
|
||||
#endif
|
||||
s, false);
|
||||
} catch (SystemError & e) {
|
||||
/* Ignore failing writes to stderr. We need to ignore write
|
||||
errors to ensure that cleanup code that logs to stderr runs
|
||||
|
@ -132,9 +138,18 @@ Logger * makeSimpleLogger(bool printBuildLogs)
|
|||
|
||||
std::atomic<uint64_t> nextId{0};
|
||||
|
||||
static uint64_t getPid()
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return getpid();
|
||||
#else
|
||||
return GetCurrentProcessId();
|
||||
#endif
|
||||
}
|
||||
|
||||
Activity::Activity(Logger & logger, Verbosity lvl, ActivityType type,
|
||||
const std::string & s, const Logger::Fields & fields, ActivityId parent)
|
||||
: logger(logger), id(nextId++ + (((uint64_t) getpid()) << 32))
|
||||
: logger(logger), id(nextId++ + (((uint64_t) getPid()) << 32))
|
||||
{
|
||||
logger.startActivity(id, lvl, type, s, fields, parent);
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ PosixSourceAccessor::PosixSourceAccessor(std::filesystem::path && root)
|
|||
: root(std::move(root))
|
||||
{
|
||||
assert(root.empty() || root.is_absolute());
|
||||
displayPrefix = root;
|
||||
displayPrefix = root.string();
|
||||
}
|
||||
|
||||
PosixSourceAccessor::PosixSourceAccessor()
|
||||
|
@ -19,10 +19,10 @@ PosixSourceAccessor::PosixSourceAccessor()
|
|||
|
||||
std::pair<PosixSourceAccessor, CanonPath> PosixSourceAccessor::createAtRoot(const std::filesystem::path & path)
|
||||
{
|
||||
std::filesystem::path path2 = absPath(path.native());
|
||||
std::filesystem::path path2 = absPath(path.string());
|
||||
return {
|
||||
PosixSourceAccessor { path2.root_path() },
|
||||
CanonPath { static_cast<std::string>(path2.relative_path()) },
|
||||
CanonPath { path2.relative_path().string() },
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -47,12 +47,16 @@ void PosixSourceAccessor::readFile(
|
|||
|
||||
auto ap = makeAbsPath(path);
|
||||
|
||||
AutoCloseFD fd = open(ap.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
||||
AutoCloseFD fd = toDescriptor(open(ap.string().c_str(), O_RDONLY
|
||||
#ifndef _WIN32
|
||||
| O_NOFOLLOW | O_CLOEXEC
|
||||
#endif
|
||||
));
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", ap.native());
|
||||
throw SysError("opening file '%1%'", ap.string());
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd.get(), &st) == -1)
|
||||
if (fstat(fromDescriptorReadOnly(fd.get()), &st) == -1)
|
||||
throw SysError("statting file");
|
||||
|
||||
sizeCallback(st.st_size);
|
||||
|
@ -62,7 +66,7 @@ void PosixSourceAccessor::readFile(
|
|||
std::array<unsigned char, 64 * 1024> buf;
|
||||
while (left) {
|
||||
checkInterrupt();
|
||||
ssize_t rd = read(fd.get(), buf.data(), (size_t) std::min(left, (off_t) buf.size()));
|
||||
ssize_t rd = read(fromDescriptorReadOnly(fd.get()), buf.data(), (size_t) std::min(left, (off_t) buf.size()));
|
||||
if (rd == -1) {
|
||||
if (errno != EINTR)
|
||||
throw SysError("reading from file '%s'", showPath(path));
|
||||
|
@ -80,7 +84,7 @@ void PosixSourceAccessor::readFile(
|
|||
bool PosixSourceAccessor::pathExists(const CanonPath & path)
|
||||
{
|
||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||
return nix::pathExists(makeAbsPath(path));
|
||||
return nix::pathExists(makeAbsPath(path).string());
|
||||
}
|
||||
|
||||
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
|
||||
|
@ -89,7 +93,7 @@ std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & pa
|
|||
|
||||
// Note: we convert std::filesystem::path to Path because the
|
||||
// former is not hashable on libc++.
|
||||
Path absPath = makeAbsPath(path);
|
||||
Path absPath = makeAbsPath(path).string();
|
||||
|
||||
{
|
||||
auto cache(_cache.lock());
|
||||
|
@ -127,11 +131,13 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
{
|
||||
assertNoSymlinks(path);
|
||||
DirEntries res;
|
||||
for (auto & entry : nix::readDirectory(makeAbsPath(path))) {
|
||||
for (auto & entry : nix::readDirectory(makeAbsPath(path).string())) {
|
||||
std::optional<Type> type;
|
||||
switch (entry.type) {
|
||||
case DT_REG: type = Type::tRegular; break;
|
||||
#ifndef _WIN32
|
||||
case DT_LNK: type = Type::tSymlink; break;
|
||||
#endif
|
||||
case DT_DIR: type = Type::tDirectory; break;
|
||||
}
|
||||
res.emplace(entry.name, type);
|
||||
|
@ -142,7 +148,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
std::string PosixSourceAccessor::readLink(const CanonPath & path)
|
||||
{
|
||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||
return nix::readLink(makeAbsPath(path));
|
||||
return nix::readLink(makeAbsPath(path).string());
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> PosixSourceAccessor::getPhysicalPath(const CanonPath & path)
|
||||
|
|
|
@ -25,6 +25,7 @@ namespace nix {
|
|||
struct Sink;
|
||||
struct Source;
|
||||
|
||||
#ifndef _WIN32
|
||||
class Pid
|
||||
{
|
||||
pid_t pid = -1;
|
||||
|
@ -43,13 +44,16 @@ public:
|
|||
void setKillSignal(int signal);
|
||||
pid_t release();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* Kill all processes running under the specified uid by sending them
|
||||
* a SIGKILL.
|
||||
*/
|
||||
void killUser(uid_t uid);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
|
@ -68,8 +72,9 @@ struct ProcessOptions
|
|||
int cloneFlags = 0;
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
pid_t startProcess(std::function<void()> fun, const ProcessOptions & options = ProcessOptions());
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Run a program and return its stdout in a string (i.e., like the
|
||||
|
@ -84,8 +89,10 @@ struct RunOptions
|
|||
Path program;
|
||||
bool searchPath = true;
|
||||
Strings args;
|
||||
#ifndef _WIN32
|
||||
std::optional<uid_t> uid;
|
||||
std::optional<uid_t> gid;
|
||||
#endif
|
||||
std::optional<Path> chdir;
|
||||
std::optional<std::map<std::string, std::string>> environment;
|
||||
std::optional<std::string> input;
|
||||
|
@ -111,6 +118,7 @@ public:
|
|||
{ }
|
||||
};
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/**
|
||||
* Convert the exit status of a child as returned by wait() into an
|
||||
|
@ -120,4 +128,6 @@ std::string statusToString(int status);
|
|||
|
||||
bool statusOk(int status);
|
||||
|
||||
#endif
|
||||
|
||||
}
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
#include <boost/coroutine2/coroutine.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
# include <fileapi.h>
|
||||
# include "windows-error.hh"
|
||||
#endif
|
||||
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -126,6 +131,14 @@ bool BufferedSource::hasData()
|
|||
|
||||
size_t FdSource::readUnbuffered(char * data, size_t len)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD n;
|
||||
checkInterrupt();
|
||||
if (!::ReadFile(fd, data, len, &n, NULL)) {
|
||||
_good = false;
|
||||
throw WinError("ReadFile when FdSource::readUnbuffered");
|
||||
}
|
||||
#else
|
||||
ssize_t n;
|
||||
do {
|
||||
checkInterrupt();
|
||||
|
@ -133,6 +146,7 @@ size_t FdSource::readUnbuffered(char * data, size_t len)
|
|||
} while (n == -1 && errno == EINTR);
|
||||
if (n == -1) { _good = false; throw SysError("reading from file"); }
|
||||
if (n == 0) { _good = false; throw EndOfFile(std::string(*endOfFileError)); }
|
||||
#endif
|
||||
read += n;
|
||||
return n;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,12 @@
|
|||
#include "environment-variables.hh"
|
||||
#include "sync.hh"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#if _WIN32
|
||||
# include <io.h>
|
||||
# define isatty _isatty
|
||||
#else
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
namespace nix {
|
||||
|
@ -92,6 +97,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
|
|||
static Sync<std::pair<unsigned short, unsigned short>> windowSize{{0, 0}};
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
void updateWindowSize()
|
||||
{
|
||||
struct winsize ws;
|
||||
|
@ -101,6 +107,7 @@ void updateWindowSize()
|
|||
windowSize_->second = ws.ws_col;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
std::pair<unsigned short, unsigned short> getWindowSize()
|
||||
|
|
|
@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s,
|
|||
bool filterAll = false,
|
||||
unsigned int width = std::numeric_limits<unsigned int>::max());
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/**
|
||||
* Recalculate the window size, updating a global variable. Used in the
|
||||
* `SIGWINCH` signal handler.
|
||||
*/
|
||||
void updateWindowSize();
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return the number of rows and columns of the terminal.
|
||||
*
|
||||
|
|
|
@ -81,8 +81,10 @@ void ThreadPool::doWork(bool mainThread)
|
|||
{
|
||||
ReceiveInterrupts receiveInterrupts;
|
||||
|
||||
#ifndef _WIN32 // Does Windows need anything similar for async exit handling?
|
||||
if (!mainThread)
|
||||
unix::interruptCheck = [&]() { return (bool) quit; };
|
||||
#endif
|
||||
|
||||
bool didWork = false;
|
||||
std::exception_ptr exc;
|
||||
|
|
31
src/libutil/unix/file-path.cc
Normal file
31
src/libutil/unix/file-path.cc
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <algorithm>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
|
||||
#include "file-path.hh"
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string os_string_to_string(PathViewNG::string_view path)
|
||||
{
|
||||
return std::string { path };
|
||||
}
|
||||
|
||||
PathNG::string_type string_to_os_string(std::string_view s)
|
||||
{
|
||||
return std::string { s };
|
||||
}
|
||||
|
||||
std::optional<PathNG> maybePathNG(PathView path)
|
||||
{
|
||||
return { path };
|
||||
}
|
||||
|
||||
PathNG pathNG(PathView path)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
|
@ -3,16 +3,20 @@
|
|||
|
||||
#include "types.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifndef _WIN32
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
std::string getUserName();
|
||||
|
||||
#ifndef _WIN32
|
||||
/**
|
||||
* @return the given user's home directory from /etc/passwd.
|
||||
*/
|
||||
Path getHomeOf(uid_t userId);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return $HOME or the user's home directory from /etc/passwd.
|
||||
|
@ -58,6 +62,8 @@ std::string expandTilde(std::string_view path);
|
|||
|
||||
/**
|
||||
* Is the current user UID 0 on Unix?
|
||||
*
|
||||
* Currently always false on Windows, but that may change.
|
||||
*/
|
||||
bool isRootUser();
|
||||
|
||||
|
|
17
src/libutil/windows/environment-variables.cc
Normal file
17
src/libutil/windows/environment-variables.cc
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include "environment-variables.hh"
|
||||
|
||||
#include "processenv.h"
|
||||
|
||||
namespace nix {
|
||||
|
||||
int unsetenv(const char *name)
|
||||
{
|
||||
return -SetEnvironmentVariableA(name, nullptr);
|
||||
}
|
||||
|
||||
int setEnv(const char * name, const char * value)
|
||||
{
|
||||
return -SetEnvironmentVariableA(name, value);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue