From 8433027e353b03bcbf36fae180394aaf8a21a19b Mon Sep 17 00:00:00 2001 From: John Ericson Date: Sat, 2 Sep 2023 17:35:16 -0400 Subject: [PATCH] Build a minimized Nix with MinGW At this point many features are stripped out, but this works: - Can run libnix{util,store,expr} unit tests - Can run some Nix commands Co-Authored-By volth Co-Authored-By Brian McKenna --- Makefile | 12 +- m4/gcc_bug_80431.m4 | 8 +- precompiled-headers.h | 21 +-- src/libcmd/common-eval-args.cc | 2 +- src/libcmd/markdown.cc | 4 +- src/libcmd/repl-interacter.cc | 4 + src/libexpr/eval.cc | 10 +- src/libexpr/eval.hh | 2 + src/libexpr/primops.cc | 11 +- src/libexpr/search-path.hh | 3 + src/libfetchers/fs-input-accessor.cc | 2 +- src/libfetchers/git-utils.cc | 16 +- src/libmain/shared.cc | 15 ++ src/libmain/shared.hh | 7 +- src/libmain/{ => unix}/stack.cc | 0 src/libstore/builtins/buildenv.cc | 6 +- src/libstore/daemon.cc | 7 +- src/libstore/filetransfer.cc | 12 ++ src/libstore/globals.cc | 17 +- src/libstore/globals.hh | 2 + src/libstore/local.mk | 17 +- src/libstore/misc.cc | 1 - src/libstore/profiles.hh | 1 + src/libstore/remote-fs-accessor.cc | 8 +- src/libstore/ssh.cc | 16 +- src/libstore/ssh.hh | 10 +- src/libstore/store-api.cc | 21 ++- src/libstore/{ => unix}/build/child.cc | 0 src/libstore/{ => unix}/build/child.hh | 0 .../{ => unix}/build/derivation-goal.cc | 0 .../{ => unix}/build/derivation-goal.hh | 0 .../build/drv-output-substitution-goal.cc | 0 .../build/drv-output-substitution-goal.hh | 0 src/libstore/{ => unix}/build/entry-points.cc | 0 src/libstore/{ => unix}/build/goal.cc | 0 src/libstore/{ => unix}/build/goal.hh | 0 .../{ => unix}/build/hook-instance.cc | 0 .../{ => unix}/build/hook-instance.hh | 0 .../{ => unix}/build/local-derivation-goal.cc | 0 .../{ => unix}/build/local-derivation-goal.hh | 0 src/libstore/{ => unix}/build/personality.cc | 0 src/libstore/{ => unix}/build/personality.hh | 0 .../{ => unix}/build/sandbox-defaults.sb | 0 .../{ => unix}/build/sandbox-minimal.sb | 0 .../{ => unix}/build/sandbox-network.sb | 0 .../{ => unix}/build/substitution-goal.cc | 0 .../{ => unix}/build/substitution-goal.hh | 0 src/libstore/{ => unix}/build/worker.cc | 0 src/libstore/{ => unix}/build/worker.hh | 0 src/libstore/{ => unix}/builtins/fetchurl.cc | 0 .../{ => unix}/builtins/unpack-channel.cc | 0 .../{ => unix}/ca-specific-schema.sql | 0 src/libstore/{ => unix}/gc.cc | 0 .../{ => unix}/local-overlay-store.cc | 0 .../{ => unix}/local-overlay-store.hh | 0 .../{ => unix}/local-overlay-store.md | 0 src/libstore/{ => unix}/local-store.cc | 0 src/libstore/{ => unix}/local-store.hh | 0 src/libstore/{ => unix}/local-store.md | 0 src/libstore/{ => unix}/lock.cc | 0 src/libstore/{ => unix}/lock.hh | 0 src/libstore/{ => unix}/optimise-store.cc | 0 .../{ => unix}/posix-fs-canonicalise.cc | 0 .../{ => unix}/posix-fs-canonicalise.hh | 0 src/libstore/{ => unix}/schema.sql | 0 src/libstore/{ => unix}/uds-remote-store.cc | 0 src/libstore/{ => unix}/uds-remote-store.hh | 0 src/libstore/{ => unix}/uds-remote-store.md | 0 src/libstore/windows/build.cc | 37 +++++ src/libutil/args.cc | 6 +- src/libutil/current-process.cc | 10 +- src/libutil/current-process.hh | 9 +- src/libutil/environment-variables.hh | 7 + src/libutil/error.hh | 17 ++ src/libutil/file-descriptor.cc | 27 +++- src/libutil/file-descriptor.hh | 60 ++++++- src/libutil/file-path.hh | 52 ++++++ src/libutil/file-system.cc | 109 ++++++++++--- src/libutil/file-system.hh | 14 ++ src/libutil/fs-sink.cc | 31 +++- src/libutil/local.mk | 6 + src/libutil/logging.cc | 19 ++- src/libutil/posix-source-accessor.cc | 28 ++-- src/libutil/{unix => }/processes.hh | 12 +- src/libutil/serialise.cc | 14 ++ src/libutil/terminal.cc | 9 +- src/libutil/terminal.hh | 4 + src/libutil/thread-pool.cc | 2 + src/libutil/unix/file-path.cc | 31 ++++ src/libutil/users.hh | 8 +- src/libutil/windows/environment-variables.cc | 17 ++ src/libutil/windows/file-descriptor.cc | 148 ++++++++++++++++++ src/libutil/windows/file-path.cc | 52 ++++++ src/libutil/windows/processes.cc | 48 ++++++ src/libutil/windows/signals-impl.hh | 41 +++++ src/libutil/windows/users.cc | 50 ++++++ src/libutil/windows/windows-error.cc | 31 ++++ src/libutil/windows/windows-error.hh | 51 ++++++ src/nix-env/nix-env.cc | 10 +- src/nix-instantiate/nix-instantiate.cc | 2 +- src/nix-store/nix-store.cc | 29 +++- src/nix/cat.cc | 3 +- src/nix/develop.cc | 9 +- src/nix/dump-path.cc | 4 +- src/nix/eval.cc | 8 +- src/nix/local.mk | 12 +- src/nix/log.cc | 2 +- src/nix/main.cc | 30 +++- src/nix/prefetch.cc | 2 +- src/nix/sigs.cc | 4 +- tests/unit/libutil/tests.cc | 2 + 111 files changed, 1162 insertions(+), 140 deletions(-) rename src/libmain/{ => unix}/stack.cc (100%) rename src/libstore/{ => unix}/build/child.cc (100%) rename src/libstore/{ => unix}/build/child.hh (100%) rename src/libstore/{ => unix}/build/derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/drv-output-substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/entry-points.cc (100%) rename src/libstore/{ => unix}/build/goal.cc (100%) rename src/libstore/{ => unix}/build/goal.hh (100%) rename src/libstore/{ => unix}/build/hook-instance.cc (100%) rename src/libstore/{ => unix}/build/hook-instance.hh (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.cc (100%) rename src/libstore/{ => unix}/build/local-derivation-goal.hh (100%) rename src/libstore/{ => unix}/build/personality.cc (100%) rename src/libstore/{ => unix}/build/personality.hh (100%) rename src/libstore/{ => unix}/build/sandbox-defaults.sb (100%) rename src/libstore/{ => unix}/build/sandbox-minimal.sb (100%) rename src/libstore/{ => unix}/build/sandbox-network.sb (100%) rename src/libstore/{ => unix}/build/substitution-goal.cc (100%) rename src/libstore/{ => unix}/build/substitution-goal.hh (100%) rename src/libstore/{ => unix}/build/worker.cc (100%) rename src/libstore/{ => unix}/build/worker.hh (100%) rename src/libstore/{ => unix}/builtins/fetchurl.cc (100%) rename src/libstore/{ => unix}/builtins/unpack-channel.cc (100%) rename src/libstore/{ => unix}/ca-specific-schema.sql (100%) rename src/libstore/{ => unix}/gc.cc (100%) rename src/libstore/{ => unix}/local-overlay-store.cc (100%) rename src/libstore/{ => unix}/local-overlay-store.hh (100%) rename src/libstore/{ => unix}/local-overlay-store.md (100%) rename src/libstore/{ => unix}/local-store.cc (100%) rename src/libstore/{ => unix}/local-store.hh (100%) rename src/libstore/{ => unix}/local-store.md (100%) rename src/libstore/{ => unix}/lock.cc (100%) rename src/libstore/{ => unix}/lock.hh (100%) rename src/libstore/{ => unix}/optimise-store.cc (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.cc (100%) rename src/libstore/{ => unix}/posix-fs-canonicalise.hh (100%) rename src/libstore/{ => unix}/schema.sql (100%) rename src/libstore/{ => unix}/uds-remote-store.cc (100%) rename src/libstore/{ => unix}/uds-remote-store.hh (100%) rename src/libstore/{ => unix}/uds-remote-store.md (100%) create mode 100644 src/libstore/windows/build.cc create mode 100644 src/libutil/file-path.hh rename src/libutil/{unix => }/processes.hh (95%) create mode 100644 src/libutil/unix/file-path.cc create mode 100644 src/libutil/windows/environment-variables.cc create mode 100644 src/libutil/windows/file-descriptor.cc create mode 100644 src/libutil/windows/file-path.cc create mode 100644 src/libutil/windows/processes.cc create mode 100644 src/libutil/windows/signals-impl.hh create mode 100644 src/libutil/windows/users.cc create mode 100644 src/libutil/windows/windows-error.cc create mode 100644 src/libutil/windows/windows-error.hh diff --git a/Makefile b/Makefile index 0028c957a..ba5a6cd92 100644 --- a/Makefile +++ b/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. diff --git a/m4/gcc_bug_80431.m4 b/m4/gcc_bug_80431.m4 index e42f01956..cdc4ddb40 100644 --- a/m4/gcc_bug_80431.m4 +++ b/m4/gcc_bug_80431.m4 @@ -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) ], diff --git a/precompiled-headers.h b/precompiled-headers.h index f52f1cab8..e1a3f8cc0 100644 --- a/precompiled-headers.h +++ b/precompiled-headers.h @@ -42,19 +42,22 @@ #include #include #include -#include -#include -#include #include -#include -#include -#include #include #include #include -#include -#include -#include #include +#ifndef _WIN32 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif + #include diff --git a/src/libcmd/common-eval-args.cc b/src/libcmd/common-eval-args.cc index b87bbbc27..c6ee0d0b2 100644 --- a/src/libcmd/common-eval-args.cc +++ b/src/libcmd/common-eval-args.cc @@ -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)); diff --git a/src/libcmd/markdown.cc b/src/libcmd/markdown.cc index d62ff0d96..88c3f640b 100644 --- a/src/libcmd/markdown.cc +++ b/src/libcmd/markdown.cc @@ -3,9 +3,9 @@ #include "finally.hh" #include "terminal.hh" -#include #if HAVE_LOWDOWN -#include +# include +# include #endif namespace nix { diff --git a/src/libcmd/repl-interacter.cc b/src/libcmd/repl-interacter.cc index 3e34ecdb6..eb4361e25 100644 --- a/src/libcmd/repl-interacter.cc +++ b/src/libcmd/repl-interacter.cc @@ -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; diff --git a/src/libexpr/eval.cc b/src/libexpr/eval.cc index a21e1d75e..72da1c465 100644 --- a/src/libexpr/eval.cc +++ b/src/libexpr/eval.cc @@ -33,15 +33,17 @@ #include #include #include -#include #include #include #include -#include #include #include +#ifndef _WIN32 // TODO use portable implementation +# include +#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}, diff --git a/src/libexpr/eval.hh b/src/libexpr/eval.hh index df388d93e..af65fdcba 100644 --- a/src/libexpr/eval.hh +++ b/src/libexpr/eval.hh @@ -161,6 +161,8 @@ struct DebugTrace { bool isError; }; +// Don't want Windows function +#undef SearchPath class EvalState : public std::enable_shared_from_this { diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 7e7952735..f03acc2da 100644 --- a/src/libexpr/primops.cc +++ b/src/libexpr/primops.cc @@ -28,7 +28,10 @@ #include #include #include -#include + +#ifndef _WIN32 +# include +#endif #include @@ -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", diff --git a/src/libexpr/search-path.hh b/src/libexpr/search-path.hh index ce78135b5..231752ea6 100644 --- a/src/libexpr/search-path.hh +++ b/src/libexpr/search-path.hh @@ -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. diff --git a/src/libfetchers/fs-input-accessor.cc b/src/libfetchers/fs-input-accessor.cc index d85363808..2bbe53e11 100644 --- a/src/libfetchers/fs-input-accessor.cc +++ b/src/libfetchers/fs-input-accessor.cc @@ -26,7 +26,7 @@ ref 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; } diff --git a/src/libfetchers/git-utils.cc b/src/libfetchers/git-utils.cc index 5ecd825b7..a4a00374c 100644 --- a/src/libfetchers/git-utils.cc +++ b/src/libfetchers/git-utils.cc @@ -151,11 +151,11 @@ struct GitRepoImpl : GitRepo, std::enable_shared_from_this { 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 std::vector 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 /* 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 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 .args = { "-c", "gpg.ssh.allowedSignersFile=" + allowedSignersFile, - "-C", path, + "-C", path.string(), "verify-commit", rev.gitRev() }, diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 4c9051d3b..a43a00f16 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -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(); } diff --git a/src/libmain/shared.hh b/src/libmain/shared.hh index 99c3dffab..3c657d2b7 100644 --- a/src/libmain/shared.hh +++ b/src/libmain/shared.hh @@ -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 stackOverflowHandler; * logger. Exits the process immediately after. */ void defaultStackOverflowHandler(siginfo_t * info, void * ctx); +#endif } diff --git a/src/libmain/stack.cc b/src/libmain/unix/stack.cc similarity index 100% rename from src/libmain/stack.cc rename to src/libmain/unix/stack.cc diff --git a/src/libstore/builtins/buildenv.cc b/src/libstore/builtins/buildenv.cc index 31a6b32f1..e009f5b9d 100644 --- a/src/libstore/builtins/buildenv.cc +++ b/src/libstore/builtins/buildenv.cc @@ -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); diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc index def2c80b2..47d6d5541 100644 --- a/src/libstore/daemon.cc +++ b/src/libstore/daemon.cc @@ -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(from.fd) : nullptr; +#endif /* Exchange the greeting. */ unsigned int magic = readInt(from); diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index df89b5bd1..219b60c44 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -516,10 +516,12 @@ struct curlFileTransfer : public FileTransfer Sync 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 diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc index 4229fb4df..ae268e54e 100644 --- a/src/libstore/globals.cc +++ b/src/libstore/globals.cc @@ -9,11 +9,14 @@ #include #include #include -#include -#include #include +#ifndef _WIN32 +# include +# include +#endif + #ifdef __GLIBC__ # include # include @@ -56,7 +59,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 +244,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 +350,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 +361,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 } } diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh index 4bdbe3333..852dba764 100644 --- a/src/libstore/globals.hh +++ b/src/libstore/globals.hh @@ -666,6 +666,7 @@ public: Setting sandboxFallback{this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; +#ifndef _WIN32 Setting 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 sandboxShmSize{ diff --git a/src/libstore/local.mk b/src/libstore/local.mk index 0a91fce4b..b74a047bf 100644 --- a/src/libstore/local.mk +++ b/src/libstore/local.mk @@ -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 @@ -55,9 +58,9 @@ libstore_CXXFLAGS += \ 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 @@ -66,11 +69,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)) diff --git a/src/libstore/misc.cc b/src/libstore/misc.cc index cc8ad3d02..cc3f4884f 100644 --- a/src/libstore/misc.cc +++ b/src/libstore/misc.cc @@ -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" diff --git a/src/libstore/profiles.hh b/src/libstore/profiles.hh index 193c0bf21..b10a72330 100644 --- a/src/libstore/profiles.hh +++ b/src/libstore/profiles.hh @@ -8,6 +8,7 @@ #include "types.hh" #include "pathlocks.hh" +#include #include diff --git a/src/libstore/remote-fs-accessor.cc b/src/libstore/remote-fs-accessor.cc index b44edfe89..20f1d826c 100644 --- a/src/libstore/remote-fs-accessor.cc +++ b/src/libstore/remote-fs-accessor.cc @@ -71,11 +71,15 @@ std::pair, 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); diff --git a/src/libstore/ssh.cc b/src/libstore/ssh.cc index 30fe73adb..04f458279 100644 --- a/src/libstore/ssh.cc +++ b/src/libstore/ssh.cc @@ -55,6 +55,9 @@ bool SSHMaster::isMasterRunning() { std::unique_ptr 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::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::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 + } diff --git a/src/libstore/ssh.hh b/src/libstore/ssh.hh index 08bb43dfa..3b1a0827a 100644 --- a/src/libstore/ssh.hh +++ b/src/libstore/ssh.hh @@ -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 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 startCommand( Strings && command, Strings && extraSshArgs = {}); - - Path startMaster(); }; } diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc index 79beeebbd..118e5de9f 100644 --- a/src/libstore/store-api.cc +++ b/src/libstore/store-api.cc @@ -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 #include @@ -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 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 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 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 diff --git a/src/libstore/build/child.cc b/src/libstore/unix/build/child.cc similarity index 100% rename from src/libstore/build/child.cc rename to src/libstore/unix/build/child.cc diff --git a/src/libstore/build/child.hh b/src/libstore/unix/build/child.hh similarity index 100% rename from src/libstore/build/child.hh rename to src/libstore/unix/build/child.hh diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/unix/build/derivation-goal.cc similarity index 100% rename from src/libstore/build/derivation-goal.cc rename to src/libstore/unix/build/derivation-goal.cc diff --git a/src/libstore/build/derivation-goal.hh b/src/libstore/unix/build/derivation-goal.hh similarity index 100% rename from src/libstore/build/derivation-goal.hh rename to src/libstore/unix/build/derivation-goal.hh diff --git a/src/libstore/build/drv-output-substitution-goal.cc b/src/libstore/unix/build/drv-output-substitution-goal.cc similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.cc rename to src/libstore/unix/build/drv-output-substitution-goal.cc diff --git a/src/libstore/build/drv-output-substitution-goal.hh b/src/libstore/unix/build/drv-output-substitution-goal.hh similarity index 100% rename from src/libstore/build/drv-output-substitution-goal.hh rename to src/libstore/unix/build/drv-output-substitution-goal.hh diff --git a/src/libstore/build/entry-points.cc b/src/libstore/unix/build/entry-points.cc similarity index 100% rename from src/libstore/build/entry-points.cc rename to src/libstore/unix/build/entry-points.cc diff --git a/src/libstore/build/goal.cc b/src/libstore/unix/build/goal.cc similarity index 100% rename from src/libstore/build/goal.cc rename to src/libstore/unix/build/goal.cc diff --git a/src/libstore/build/goal.hh b/src/libstore/unix/build/goal.hh similarity index 100% rename from src/libstore/build/goal.hh rename to src/libstore/unix/build/goal.hh diff --git a/src/libstore/build/hook-instance.cc b/src/libstore/unix/build/hook-instance.cc similarity index 100% rename from src/libstore/build/hook-instance.cc rename to src/libstore/unix/build/hook-instance.cc diff --git a/src/libstore/build/hook-instance.hh b/src/libstore/unix/build/hook-instance.hh similarity index 100% rename from src/libstore/build/hook-instance.hh rename to src/libstore/unix/build/hook-instance.hh diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc similarity index 100% rename from src/libstore/build/local-derivation-goal.cc rename to src/libstore/unix/build/local-derivation-goal.cc diff --git a/src/libstore/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh similarity index 100% rename from src/libstore/build/local-derivation-goal.hh rename to src/libstore/unix/build/local-derivation-goal.hh diff --git a/src/libstore/build/personality.cc b/src/libstore/unix/build/personality.cc similarity index 100% rename from src/libstore/build/personality.cc rename to src/libstore/unix/build/personality.cc diff --git a/src/libstore/build/personality.hh b/src/libstore/unix/build/personality.hh similarity index 100% rename from src/libstore/build/personality.hh rename to src/libstore/unix/build/personality.hh diff --git a/src/libstore/build/sandbox-defaults.sb b/src/libstore/unix/build/sandbox-defaults.sb similarity index 100% rename from src/libstore/build/sandbox-defaults.sb rename to src/libstore/unix/build/sandbox-defaults.sb diff --git a/src/libstore/build/sandbox-minimal.sb b/src/libstore/unix/build/sandbox-minimal.sb similarity index 100% rename from src/libstore/build/sandbox-minimal.sb rename to src/libstore/unix/build/sandbox-minimal.sb diff --git a/src/libstore/build/sandbox-network.sb b/src/libstore/unix/build/sandbox-network.sb similarity index 100% rename from src/libstore/build/sandbox-network.sb rename to src/libstore/unix/build/sandbox-network.sb diff --git a/src/libstore/build/substitution-goal.cc b/src/libstore/unix/build/substitution-goal.cc similarity index 100% rename from src/libstore/build/substitution-goal.cc rename to src/libstore/unix/build/substitution-goal.cc diff --git a/src/libstore/build/substitution-goal.hh b/src/libstore/unix/build/substitution-goal.hh similarity index 100% rename from src/libstore/build/substitution-goal.hh rename to src/libstore/unix/build/substitution-goal.hh diff --git a/src/libstore/build/worker.cc b/src/libstore/unix/build/worker.cc similarity index 100% rename from src/libstore/build/worker.cc rename to src/libstore/unix/build/worker.cc diff --git a/src/libstore/build/worker.hh b/src/libstore/unix/build/worker.hh similarity index 100% rename from src/libstore/build/worker.hh rename to src/libstore/unix/build/worker.hh diff --git a/src/libstore/builtins/fetchurl.cc b/src/libstore/unix/builtins/fetchurl.cc similarity index 100% rename from src/libstore/builtins/fetchurl.cc rename to src/libstore/unix/builtins/fetchurl.cc diff --git a/src/libstore/builtins/unpack-channel.cc b/src/libstore/unix/builtins/unpack-channel.cc similarity index 100% rename from src/libstore/builtins/unpack-channel.cc rename to src/libstore/unix/builtins/unpack-channel.cc diff --git a/src/libstore/ca-specific-schema.sql b/src/libstore/unix/ca-specific-schema.sql similarity index 100% rename from src/libstore/ca-specific-schema.sql rename to src/libstore/unix/ca-specific-schema.sql diff --git a/src/libstore/gc.cc b/src/libstore/unix/gc.cc similarity index 100% rename from src/libstore/gc.cc rename to src/libstore/unix/gc.cc diff --git a/src/libstore/local-overlay-store.cc b/src/libstore/unix/local-overlay-store.cc similarity index 100% rename from src/libstore/local-overlay-store.cc rename to src/libstore/unix/local-overlay-store.cc diff --git a/src/libstore/local-overlay-store.hh b/src/libstore/unix/local-overlay-store.hh similarity index 100% rename from src/libstore/local-overlay-store.hh rename to src/libstore/unix/local-overlay-store.hh diff --git a/src/libstore/local-overlay-store.md b/src/libstore/unix/local-overlay-store.md similarity index 100% rename from src/libstore/local-overlay-store.md rename to src/libstore/unix/local-overlay-store.md diff --git a/src/libstore/local-store.cc b/src/libstore/unix/local-store.cc similarity index 100% rename from src/libstore/local-store.cc rename to src/libstore/unix/local-store.cc diff --git a/src/libstore/local-store.hh b/src/libstore/unix/local-store.hh similarity index 100% rename from src/libstore/local-store.hh rename to src/libstore/unix/local-store.hh diff --git a/src/libstore/local-store.md b/src/libstore/unix/local-store.md similarity index 100% rename from src/libstore/local-store.md rename to src/libstore/unix/local-store.md diff --git a/src/libstore/lock.cc b/src/libstore/unix/lock.cc similarity index 100% rename from src/libstore/lock.cc rename to src/libstore/unix/lock.cc diff --git a/src/libstore/lock.hh b/src/libstore/unix/lock.hh similarity index 100% rename from src/libstore/lock.hh rename to src/libstore/unix/lock.hh diff --git a/src/libstore/optimise-store.cc b/src/libstore/unix/optimise-store.cc similarity index 100% rename from src/libstore/optimise-store.cc rename to src/libstore/unix/optimise-store.cc diff --git a/src/libstore/posix-fs-canonicalise.cc b/src/libstore/unix/posix-fs-canonicalise.cc similarity index 100% rename from src/libstore/posix-fs-canonicalise.cc rename to src/libstore/unix/posix-fs-canonicalise.cc diff --git a/src/libstore/posix-fs-canonicalise.hh b/src/libstore/unix/posix-fs-canonicalise.hh similarity index 100% rename from src/libstore/posix-fs-canonicalise.hh rename to src/libstore/unix/posix-fs-canonicalise.hh diff --git a/src/libstore/schema.sql b/src/libstore/unix/schema.sql similarity index 100% rename from src/libstore/schema.sql rename to src/libstore/unix/schema.sql diff --git a/src/libstore/uds-remote-store.cc b/src/libstore/unix/uds-remote-store.cc similarity index 100% rename from src/libstore/uds-remote-store.cc rename to src/libstore/unix/uds-remote-store.cc diff --git a/src/libstore/uds-remote-store.hh b/src/libstore/unix/uds-remote-store.hh similarity index 100% rename from src/libstore/uds-remote-store.hh rename to src/libstore/unix/uds-remote-store.hh diff --git a/src/libstore/uds-remote-store.md b/src/libstore/unix/uds-remote-store.md similarity index 100% rename from src/libstore/uds-remote-store.md rename to src/libstore/unix/uds-remote-store.md diff --git a/src/libstore/windows/build.cc b/src/libstore/windows/build.cc new file mode 100644 index 000000000..3eadc5bda --- /dev/null +++ b/src/libstore/windows/build.cc @@ -0,0 +1,37 @@ +#include "store-api.hh" +#include "build-result.hh" + +namespace nix { + +void Store::buildPaths(const std::vector & reqs, BuildMode buildMode, std::shared_ptr evalStore) +{ + unsupported("buildPaths"); +} + +std::vector Store::buildPathsWithResults( + const std::vector & reqs, + BuildMode buildMode, + std::shared_ptr 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"); +} + +} diff --git a/src/libutil/args.cc b/src/libutil/args.cc index 834fc7314..243e3a5a6 100644 --- a/src/libutil/args.cc +++ b/src/libutil/args.cc @@ -9,7 +9,9 @@ #include #include #include -#include +#ifndef _WIN32 +# include +#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) diff --git a/src/libutil/current-process.cc b/src/libutil/current-process.cc index d33f7163a..c88013b3c 100644 --- a/src/libutil/current-process.cc +++ b/src/libutil/current-process.cc @@ -19,7 +19,9 @@ # include "namespaces.hh" #endif -#include +#ifndef _WIN32 +# include +#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 } diff --git a/src/libutil/current-process.hh b/src/libutil/current-process.hh index 444c717d1..a5adb70cf 100644 --- a/src/libutil/current-process.hh +++ b/src/libutil/current-process.hh @@ -2,7 +2,10 @@ ///@file #include -#include + +#ifndef _WIN32 +# include +#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); diff --git a/src/libutil/environment-variables.hh b/src/libutil/environment-variables.hh index 21c2356a4..e0649adac 100644 --- a/src/libutil/environment-variables.hh +++ b/src/libutil/environment-variables.hh @@ -28,6 +28,13 @@ std::optional getEnvNonEmpty(const std::string & key); */ std::map getEnv(); +#ifdef _WIN32 +/** + * Implementation of missing POSIX function. + */ +int unsetenv(const char * name); +#endif + /** * Like POSIX `setenv`, but always overrides. * diff --git a/src/libutil/error.hh b/src/libutil/error.hh index 445b1e19c..0419f36d6 100644 --- a/src/libutil/error.hh +++ b/src/libutil/error.hh @@ -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()'. diff --git a/src/libutil/file-descriptor.cc b/src/libutil/file-descriptor.cc index 95cbb8537..3bbfc50ee 100644 --- a/src/libutil/file-descriptor.cc +++ b/src/libutil/file-descriptor.cc @@ -5,6 +5,11 @@ #include #include +#ifdef _WIN32 +# include +# include +# 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); } } diff --git a/src/libutil/file-descriptor.hh b/src/libutil/file-descriptor.hh index 719e1e444..50201d846 100644 --- a/src/libutil/file-descriptor.hh +++ b/src/libutil/file-descriptor.hh @@ -4,6 +4,11 @@ #include "types.hh" #include "error.hh" +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +#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 & 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); } diff --git a/src/libutil/file-path.hh b/src/libutil/file-path.hh new file mode 100644 index 000000000..6fb100125 --- /dev/null +++ b/src/libutil/file-path.hh @@ -0,0 +1,52 @@ +#pragma once +///@file + +#include +#include + +#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 PathsNG; +typedef std::set 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 +{ + using string_view = std::basic_string_view; + + using string_view::string_view; + + PathViewNG(const PathNG & path) + : std::basic_string_view(path.native()) + { } + + PathViewNG(const PathNG::string_type & path) + : std::basic_string_view(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 maybePathNG(PathView path); + +PathNG pathNG(PathView path); + +} diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc index 89d309731..b03bb767b 100644 --- a/src/libutil/file-system.cc +++ b/src/libutil/file-system.cc @@ -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 #include +#ifdef _WIN32 +# include +#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 maybeLstat(const Path & path) { std::optional 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 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 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 })); } } } diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh index 06a993829..0c4e7cfdd 100644 --- a/src/libutil/file-system.hh +++ b/src/libutil/file-system.hh @@ -14,6 +14,9 @@ #include #include #include +#ifdef _WIN32 +# include +#endif #include #include @@ -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; diff --git a/src/libutil/fs-sink.cc b/src/libutil/fs-sink.cc index 35ce0ac36..91070ea89 100644 --- a/src/libutil/fs-sink.cc +++ b/src/libutil/fs-sink.cc @@ -1,8 +1,15 @@ #include +#include "error.hh" #include "config.hh" #include "fs-sink.hh" +#if _WIN32 +# include +# 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 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); } diff --git a/src/libutil/posix-source-accessor.cc b/src/libutil/posix-source-accessor.cc index 8039d4b80..a589bfd3d 100644 --- a/src/libutil/posix-source-accessor.cc +++ b/src/libutil/posix-source-accessor.cc @@ -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::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(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 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 PosixSourceAccessor::cachedLstat(const CanonPath & path) @@ -89,7 +93,7 @@ std::optional 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; 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 PosixSourceAccessor::getPhysicalPath(const CanonPath & path) diff --git a/src/libutil/unix/processes.hh b/src/libutil/processes.hh similarity index 95% rename from src/libutil/unix/processes.hh rename to src/libutil/processes.hh index 978c37105..a7e85b5be 100644 --- a/src/libutil/unix/processes.hh +++ b/src/libutil/processes.hh @@ -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 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; std::optional gid; +#endif std::optional chdir; std::optional> environment; std::optional 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 + } diff --git a/src/libutil/serialise.cc b/src/libutil/serialise.cc index 70c16ff0d..5ea27ccbe 100644 --- a/src/libutil/serialise.cc +++ b/src/libutil/serialise.cc @@ -7,6 +7,11 @@ #include +#ifdef _WIN32 +# include +# 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; } diff --git a/src/libutil/terminal.cc b/src/libutil/terminal.cc index 096252f03..4dc280f8c 100644 --- a/src/libutil/terminal.cc +++ b/src/libutil/terminal.cc @@ -2,7 +2,12 @@ #include "environment-variables.hh" #include "sync.hh" -#include +#if _WIN32 +# include +# define isatty _isatty +#else +# include +#endif #include namespace nix { @@ -92,6 +97,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w static Sync> windowSize{{0, 0}}; +#ifndef _WIN32 void updateWindowSize() { struct winsize ws; @@ -101,6 +107,7 @@ void updateWindowSize() windowSize_->second = ws.ws_col; } } +#endif std::pair getWindowSize() diff --git a/src/libutil/terminal.hh b/src/libutil/terminal.hh index 9d8d0c743..628833283 100644 --- a/src/libutil/terminal.hh +++ b/src/libutil/terminal.hh @@ -21,12 +21,16 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll = false, unsigned int width = std::numeric_limits::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. * diff --git a/src/libutil/thread-pool.cc b/src/libutil/thread-pool.cc index 805f31d80..0f6349642 100644 --- a/src/libutil/thread-pool.cc +++ b/src/libutil/thread-pool.cc @@ -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; diff --git a/src/libutil/unix/file-path.cc b/src/libutil/unix/file-path.cc new file mode 100644 index 000000000..54a1cc278 --- /dev/null +++ b/src/libutil/unix/file-path.cc @@ -0,0 +1,31 @@ +#include +#include +#include +#include + +#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 maybePathNG(PathView path) +{ + return { path }; +} + +PathNG pathNG(PathView path) +{ + return path; +} + +} diff --git a/src/libutil/users.hh b/src/libutil/users.hh index 449e5bbe9..153cc73fd 100644 --- a/src/libutil/users.hh +++ b/src/libutil/users.hh @@ -3,16 +3,20 @@ #include "types.hh" -#include +#ifndef _WIN32 +# include +#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(); diff --git a/src/libutil/windows/environment-variables.cc b/src/libutil/windows/environment-variables.cc new file mode 100644 index 000000000..25ab9d63a --- /dev/null +++ b/src/libutil/windows/environment-variables.cc @@ -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); +} + +} diff --git a/src/libutil/windows/file-descriptor.cc b/src/libutil/windows/file-descriptor.cc new file mode 100644 index 000000000..26f769b66 --- /dev/null +++ b/src/libutil/windows/file-descriptor.cc @@ -0,0 +1,148 @@ +#include "file-system.hh" +#include "signals.hh" +#include "finally.hh" +#include "serialise.hh" +#include "windows-error.hh" +#include "file-path.hh" + +#include +#include +#include +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string readFile(HANDLE handle) +{ + LARGE_INTEGER li; + if (!GetFileSizeEx(handle, &li)) + throw WinError("%s:%d statting file", __FILE__, __LINE__); + + return drainFD(handle, true, li.QuadPart); +} + + +void readFull(HANDLE handle, char * buf, size_t count) +{ + while (count) { + checkInterrupt(); + DWORD res; + if (!ReadFile(handle, (char *) buf, count, &res, NULL)) + throw WinError("%s:%d reading from file", __FILE__, __LINE__); + if (res == 0) throw EndOfFile("unexpected end-of-file"); + count -= res; + buf += res; + } +} + + +void writeFull(HANDLE handle, std::string_view s, bool allowInterrupts) +{ + while (!s.empty()) { + if (allowInterrupts) checkInterrupt(); + DWORD res; +#if _WIN32_WINNT >= 0x0600 + auto path = handleToPath(handle); // debug; do it before becuase handleToPath changes lasterror + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%:%2%", handle, path); + } +#else + if (!WriteFile(handle, s.data(), s.size(), &res, NULL)) { + throw WinError("writing to file %1%", handle); + } +#endif + if (res > 0) + s.remove_prefix(res); + } +} + + +std::string readLine(HANDLE handle) +{ + std::string s; + while (1) { + checkInterrupt(); + char ch; + // FIXME: inefficient + DWORD rd; + if (!ReadFile(handle, &ch, 1, &rd, NULL)) { + throw WinError("reading a line"); + } else if (rd == 0) + throw EndOfFile("unexpected EOF reading a line"); + else { + if (ch == '\n') return s; + s += ch; + } + } +} + + +void drainFD(HANDLE handle, Sink & sink/*, bool block*/) +{ + std::vector buf(64 * 1024); + while (1) { + checkInterrupt(); + DWORD rd; + if (!ReadFile(handle, buf.data(), buf.size(), &rd, NULL)) { + WinError winError("%s:%d reading from handle %p", __FILE__, __LINE__, handle); + if (winError.lastError == ERROR_BROKEN_PIPE) + break; + throw winError; + } + else if (rd == 0) break; + sink({(char *) buf.data(), (size_t) rd}); + } +} + + +////////////////////////////////////////////////////////////////////// + + +void Pipe::create() +{ + SECURITY_ATTRIBUTES saAttr = {0}; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.lpSecurityDescriptor = NULL; + saAttr.bInheritHandle = TRUE; + + HANDLE hReadPipe, hWritePipe; + if (!CreatePipe(&hReadPipe, &hWritePipe, &saAttr, 0)) + throw WinError("CreatePipe"); + + readSide = hReadPipe; + writeSide = hWritePipe; +} + + +////////////////////////////////////////////////////////////////////// + +#if _WIN32_WINNT >= 0x0600 + +std::wstring handleToFileName(HANDLE handle) { + std::vector buf(0x100); + DWORD dw = GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED); + if (dw == 0) { + if (handle == GetStdHandle(STD_INPUT_HANDLE )) return L""; + if (handle == GetStdHandle(STD_OUTPUT_HANDLE)) return L""; + if (handle == GetStdHandle(STD_ERROR_HANDLE )) return L""; + return (boost::wformat(L"") % handle).str(); + } + if (dw > buf.size()) { + buf.resize(dw); + if (GetFinalPathNameByHandleW(handle, buf.data(), buf.size(), FILE_NAME_OPENED) != dw-1) + throw WinError("GetFinalPathNameByHandleW"); + dw -= 1; + } + return std::wstring(buf.data(), dw); +} + + +Path handleToPath(HANDLE handle) { + return os_string_to_string(handleToFileName(handle)); +} + +#endif + +} diff --git a/src/libutil/windows/file-path.cc b/src/libutil/windows/file-path.cc new file mode 100644 index 000000000..d2f385f50 --- /dev/null +++ b/src/libutil/windows/file-path.cc @@ -0,0 +1,52 @@ +#include +#include +#include +#include + +#include "file-path.hh" +#include "file-path-impl.hh" +#include "util.hh" + +namespace nix { + +std::string os_string_to_string(PathViewNG::string_view path) +{ + std::wstring_convert> converter; + return converter.to_bytes(PathNG::string_type { path }); +} + +PathNG::string_type string_to_os_string(std::string_view s) +{ + std::wstring_convert> converter; + return converter.from_bytes(std::string { s }); +} + +std::optional maybePathNG(PathView path) +{ + if (path.length() >= 3 && (('A' <= path[0] && path[0] <= 'Z') || ('a' <= path[0] && path[0] <= 'z')) && path[1] == ':' && WindowsPathTrait::isPathSep(path[2])) { + PathNG::string_type sw = string_to_os_string( + std::string { "\\\\?\\" } + path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + if (path.length() >= 7 && path[0] == '\\' && path[1] == '\\' && (path[2] == '.' || path[2] == '?') && path[3] == '\\' && + ('A' <= path[4] && path[4] <= 'Z') && path[5] == ':' && WindowsPathTrait::isPathSep(path[6])) { + PathNG::string_type sw = string_to_os_string(path); + std::replace(sw.begin(), sw.end(), '/', '\\'); + return sw; + } + return std::optional(); +} + +PathNG pathNG(PathView path) +{ + std::optional sw = maybePathNG(path); + if (!sw) { + // FIXME why are we not using the regular error handling? + std::cerr << "invalid path for WinAPI call ["< +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef __APPLE__ +# include +#endif + +#ifdef __linux__ +# include +# include +#endif + + +namespace nix { + +std::string runProgram(Path program, bool searchPath, const Strings & args, + const std::optional & input, bool isInteractive) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +// Output = error code + "standard out" output stream +std::pair runProgram(RunOptions && options) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +void runProgram2(const RunOptions & options) +{ + throw UnimplementedError("Cannot shell out to git on Windows yet"); +} + +} diff --git a/src/libutil/windows/signals-impl.hh b/src/libutil/windows/signals-impl.hh new file mode 100644 index 000000000..26d2600bf --- /dev/null +++ b/src/libutil/windows/signals-impl.hh @@ -0,0 +1,41 @@ +#pragma once +///@file + +#include "types.hh" + +namespace nix { + +/* User interruption. */ + +static inline void setInterrupted(bool isInterrupted) +{ + /* Do nothing for now */ +} + +static inline bool getInterrupted() +{ + return false; +} + +inline void setInterruptThrown() +{ + /* Do nothing for now */ +} + +void inline checkInterrupt() +{ + /* Do nothing for now */ +} + +/** + * Does nothing, unlike Unix counterpart, but allows avoiding C++ + */ +struct ReceiveInterrupts +{ + /** + * Explicit destructor avoids dead code warnings. + */ + ~ReceiveInterrupts() {} +}; + +} diff --git a/src/libutil/windows/users.cc b/src/libutil/windows/users.cc new file mode 100644 index 000000000..1792ff1a1 --- /dev/null +++ b/src/libutil/windows/users.cc @@ -0,0 +1,50 @@ +#include "util.hh" +#include "users.hh" +#include "environment-variables.hh" +#include "file-system.hh" +#include "windows-error.hh" + +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string getUserName() +{ + // Get the required buffer size + DWORD size = 0; + if (!GetUserNameA(nullptr, &size)) { + auto lastError = GetLastError(); + if (lastError != ERROR_INSUFFICIENT_BUFFER) + throw WinError(lastError, "cannot figure out size of user name"); + } + + std::string name; + // Allocate a buffer of sufficient size + // + // - 1 because no need for null byte + name.resize(size - 1); + + // Retrieve the username + if (!GetUserNameA(&name[0], &size)) + throw WinError("cannot figure out user name"); + + return name; +} + +Path getHome() +{ + static Path homeDir = []() + { + Path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default"); + assert(!homeDir.empty()); + return canonPath(homeDir); + }(); + return homeDir; +} + +bool isRootUser() { + return false; +} + +} diff --git a/src/libutil/windows/windows-error.cc b/src/libutil/windows/windows-error.cc new file mode 100644 index 000000000..26faaae6d --- /dev/null +++ b/src/libutil/windows/windows-error.cc @@ -0,0 +1,31 @@ +#include "windows-error.hh" + +#include +#define WIN32_LEAN_AND_MEAN +#include + +namespace nix { + +std::string WinError::renderError(DWORD lastError) +{ + LPSTR errorText = NULL; + + FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM // use system message tables to retrieve error text + |FORMAT_MESSAGE_ALLOCATE_BUFFER // allocate buffer on local heap for error text + |FORMAT_MESSAGE_IGNORE_INSERTS, // Important! will fail otherwise, since we're not (and CANNOT) pass insertion parameters + NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM + lastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&errorText, // output + 0, // minimum size for output buffer + NULL); // arguments - see note + + if (NULL != errorText ) { + std::string s2 { errorText }; + LocalFree(errorText); + return s2; + } + return fmt("CODE=%d", lastError); +} + +} diff --git a/src/libutil/windows/windows-error.hh b/src/libutil/windows/windows-error.hh new file mode 100644 index 000000000..fdfd0f52c --- /dev/null +++ b/src/libutil/windows/windows-error.hh @@ -0,0 +1,51 @@ +#pragma once +///@file + +#include + +#include "error.hh" + +namespace nix { + +/** + * Windows Error type. + * + * Unless you need to catch a specific error number, don't catch this in + * portable code. Catch `SystemError` instead. + */ +class WinError : public SystemError +{ +public: + DWORD lastError; + + /** + * Construct using the explicitly-provided error number. + * `FormatMessageA` will be used to try to add additional + * information to the message. + */ + template + WinError(DWORD lastError, const Args & ... args) + : SystemError(""), lastError(lastError) + { + auto hf = HintFmt(args...); + err.msg = HintFmt("%1%: %2%", Uncolored(hf.str()), renderError(lastError)); + } + + /** + * Construct using `GetLastError()` and the ambient "last error". + * + * Be sure to not perform another last-error-modifying operation + * before calling this constructor! + */ + template + WinError(const Args & ... args) + : WinError(GetLastError(), args ...) + { + } + +private: + + std::string renderError(DWORD lastError); +}; + +} diff --git a/src/nix-env/nix-env.cc b/src/nix-env/nix-env.cc index cf0569c9f..eeca01833 100644 --- a/src/nix-env/nix-env.cc +++ b/src/nix-env/nix-env.cc @@ -1342,8 +1342,16 @@ static void opListGenerations(Globals & globals, Strings opFlags, Strings opArgs RunPager pager; for (auto & i : gens) { +#ifdef _WIN32 // TODO portable wrapper in libutil + tm * tp = localtime(&i.creationTime); + if (!tp) + throw Error("cannot convert time"); + auto & t = *tp; +#else tm t; - if (!localtime_r(&i.creationTime, &t)) throw Error("cannot convert time"); + if (!localtime_r(&i.creationTime, &t)) + throw Error("cannot convert time"); +#endif logger->cout("%|4| %|4|-%|02|-%|02| %|02|:%|02|:%|02| %||", i.number, t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, diff --git a/src/nix-instantiate/nix-instantiate.cc b/src/nix-instantiate/nix-instantiate.cc index 86e6f008d..1e1728225 100644 --- a/src/nix-instantiate/nix-instantiate.cc +++ b/src/nix-instantiate/nix-instantiate.cc @@ -168,7 +168,7 @@ static int main_nix_instantiate(int argc, char * * argv) for (auto & i : files) { auto p = state->findFile(i); if (auto fn = p.getPhysicalPath()) - std::cout << fn->native() << std::endl; + std::cout << fn->string() << std::endl; else throw Error("'%s' has no physical path", p); } diff --git a/src/nix-store/nix-store.cc b/src/nix-store/nix-store.cc index 7c8905da6..719675cba 100644 --- a/src/nix-store/nix-store.cc +++ b/src/nix-store/nix-store.cc @@ -4,10 +4,8 @@ #include "globals.hh" #include "build-result.hh" #include "store-cast.hh" -#include "gc-store.hh" +#include "local-fs-store.hh" #include "log-store.hh" -#include "local-store.hh" -#include "monitor-fd.hh" #include "serve-protocol.hh" #include "serve-protocol-impl.hh" #include "shared.hh" @@ -15,7 +13,12 @@ #include "legacy.hh" #include "posix-source-accessor.hh" #include "path-with-outputs.hh" -#include "posix-fs-canonicalise.hh" + +#ifndef _WIN32 // TODO implement on Windows or provide allowed-to-noop interface +# include "local-store.hh" +# include "monitor-fd.hh" +# include "posix-fs-canonicalise.hh" +#endif #include #include @@ -43,12 +46,14 @@ static bool noOutput = false; static std::shared_ptr store; +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ref ensureLocalStore() { auto store2 = std::dynamic_pointer_cast(store); if (!store2) throw Error("you don't have sufficient rights to use this command"); return ref(store2); } +#endif static StorePath useDeriver(const StorePath & path) @@ -550,7 +555,11 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) if (!store->isValidPath(info->path) || reregister) { /* !!! races */ if (canonicalise) +#ifdef _WIN32 // TODO implement on Windows + throw UnimplementedError("file attribute canonicalisation Is not implemented on Windows"); +#else canonicalisePathMetaData(store->printStorePath(info->path), {}); +#endif if (!hashGiven) { HashResult hash = hashPath( *store->getFSAccessor(false), CanonPath { store->printStorePath(info->path) }, @@ -563,7 +572,9 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) } } +#ifndef _WIN32 // TODO reenable on Windows once we have `LocalStore` there ensureLocalStore()->registerValidPaths(infos); +#endif } @@ -684,7 +695,7 @@ static void opDump(Strings opFlags, Strings opArgs) if (!opFlags.empty()) throw UsageError("unknown flag"); if (opArgs.size() != 1) throw UsageError("only one argument allowed"); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); std::string path = *opArgs.begin(); dumpPath(path, sink); sink.flush(); @@ -712,7 +723,7 @@ static void opExport(Strings opFlags, Strings opArgs) for (auto & i : opArgs) paths.insert(store->followLinksToStorePath(i)); - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->exportPaths(paths, sink); sink.flush(); } @@ -825,7 +836,7 @@ static void opServe(Strings opFlags, Strings opArgs) if (!opArgs.empty()) throw UsageError("no arguments expected"); FdSource in(STDIN_FILENO); - FdSink out(STDOUT_FILENO); + FdSink out(getStandardOut()); /* Exchange the greeting. */ ServeProto::Version clientVersion = @@ -946,7 +957,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); try { +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif store->buildPaths(toDerivedPaths(paths)); out << 0; } catch (Error & e) { @@ -966,7 +979,9 @@ static void opServe(Strings opFlags, Strings opArgs) getBuildSettings(); +#ifndef _WIN32 // TODO figure out if Windows needs something similar MonitorFdHup monitor(in.fd); +#endif auto status = store->buildDerivation(drvPath, drv); ServeProto::write(*store, wconn, status); diff --git a/src/nix/cat.cc b/src/nix/cat.cc index 4df086d4f..ee904b0c5 100644 --- a/src/nix/cat.cc +++ b/src/nix/cat.cc @@ -15,7 +15,8 @@ struct MixCat : virtual Args if (st.type != SourceAccessor::Type::tRegular) throw Error("path '%1%' is not a regular file", path); stopProgressBar(); - writeFull(STDOUT_FILENO, accessor->readFile(CanonPath(path))); + + writeFull(getStandardOut(), accessor->readFile(CanonPath(path))); } }; diff --git a/src/nix/develop.cc b/src/nix/develop.cc index b654dc52f..35d3da912 100644 --- a/src/nix/develop.cc +++ b/src/nix/develop.cc @@ -7,7 +7,10 @@ #include "outputs-spec.hh" #include "derivations.hh" #include "progress-bar.hh" -#include "run.hh" + +#ifndef _WIN32 // TODO re-enable on Windows +# include "run.hh" +#endif #include #include @@ -662,6 +665,9 @@ struct CmdDevelop : Common, MixEnvironment // This is to make sure the system shell doesn't leak into the build environment. setEnv("SHELL", shell.c_str()); +#ifdef _WIN32 // TODO re-enable on Windows + throw UnimplementedError("Cannot yet spawn processes on Windows"); +#else // If running a phase or single command, don't want an interactive shell running after // Ctrl-C, so don't pass --rcfile auto args = phase || !command.empty() ? Strings{std::string(baseNameOf(shell)), rcFilePath} @@ -682,6 +688,7 @@ struct CmdDevelop : Common, MixEnvironment } runProgramInStore(store, UseSearchPath::Use, shell, args, buildEnvironment.getSystem()); +#endif } }; diff --git a/src/nix/dump-path.cc b/src/nix/dump-path.cc index 0850d4c1c..953d77d31 100644 --- a/src/nix/dump-path.cc +++ b/src/nix/dump-path.cc @@ -20,7 +20,7 @@ struct CmdDumpPath : StorePathCommand void run(ref store, const StorePath & storePath) override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); store->narFromPath(storePath, sink); sink.flush(); } @@ -55,7 +55,7 @@ struct CmdDumpPath2 : Command void run() override { - FdSink sink(STDOUT_FILENO); + FdSink sink(getStandardOut()); dumpPath(path, sink); sink.flush(); } diff --git a/src/nix/eval.cc b/src/nix/eval.cc index 7b9bf7cb1..494735516 100644 --- a/src/nix/eval.cc +++ b/src/nix/eval.cc @@ -87,7 +87,11 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption // FIXME: disallow strings with contexts? writeFile(path, v.string_view()); else if (v.type() == nAttrs) { - if (mkdir(path.c_str(), 0777) == -1) + if (mkdir(path.c_str() +#ifndef _WIN32 // TODO abstract mkdir perms for Windows + , 0777 +#endif + ) == -1) throw SysError("creating directory '%s'", path); for (auto & attr : *v.attrs()) { std::string_view name = state->symbols[attr.name]; @@ -112,7 +116,7 @@ struct CmdEval : MixJSON, InstallableValueCommand, MixReadOnlyOption else if (raw) { stopProgressBar(); - writeFull(STDOUT_FILENO, *state->coerceToString(noPos, *v, context, "while generating the eval command output")); + writeFull(getStandardOut(), *state->coerceToString(noPos, *v, context, "while generating the eval command output")); } else if (json) { diff --git a/src/nix/local.mk b/src/nix/local.mk index 9f6f31b3a..305b0e9df 100644 --- a/src/nix/local.mk +++ b/src/nix/local.mk @@ -4,19 +4,19 @@ nix_DIR := $(d) nix_SOURCES := \ $(wildcard $(d)/*.cc) \ - $(wildcard src/build-remote/*.cc) \ $(wildcard src/nix-build/*.cc) \ - $(wildcard src/nix-channel/*.cc) \ - $(wildcard src/nix-collect-garbage/*.cc) \ - $(wildcard src/nix-copy-closure/*.cc) \ - $(wildcard src/nix-daemon/*.cc) \ $(wildcard src/nix-env/*.cc) \ $(wildcard src/nix-instantiate/*.cc) \ $(wildcard src/nix-store/*.cc) ifdef HOST_UNIX nix_SOURCES += \ - $(wildcard $(d)/unix/*.cc) + $(wildcard $(d)/unix/*.cc) \ + $(wildcard src/build-remote/*.cc) \ + $(wildcard src/nix-channel/*.cc) \ + $(wildcard src/nix-collect-garbage/*.cc) \ + $(wildcard src/nix-copy-closure/*.cc) \ + $(wildcard src/nix-daemon/*.cc) endif INCLUDE_nix := -I $(d) diff --git a/src/nix/log.cc b/src/nix/log.cc index 9a9bd30f9..7f590c708 100644 --- a/src/nix/log.cc +++ b/src/nix/log.cc @@ -57,7 +57,7 @@ struct CmdLog : InstallableCommand if (!log) continue; stopProgressBar(); printInfo("got build log for '%s' from '%s'", installable->what(), logSub.getUri()); - writeFull(STDOUT_FILENO, *log); + writeFull(getStandardOut(), *log); return; } diff --git a/src/nix/main.cc b/src/nix/main.cc index af6498218..7b0478a9f 100644 --- a/src/nix/main.cc +++ b/src/nix/main.cc @@ -16,26 +16,34 @@ #include "markdown.hh" #include "memory-input-accessor.hh" #include "terminal.hh" +#include "users.hh" #include -#include -#include -#include -#include #include - #include +#ifndef _WIN32 +# include +# include +# include +# include +#endif + #if __linux__ # include "namespaces.hh" #endif +#ifndef _WIN32 extern std::string chrootHelperName; void chrootHelper(int argc, char * * argv); +#endif namespace nix { +#ifdef _WIN32 +[[maybe_unused]] +#endif static bool haveProxyEnvironmentVariables() { static const std::vector proxyVariables = { @@ -57,6 +65,7 @@ static bool haveProxyEnvironmentVariables() /* Check if we have a non-loopback/link-local network interface. */ static bool haveInternet() { +#ifndef _WIN32 struct ifaddrs * addrs; if (getifaddrs(&addrs)) @@ -80,6 +89,10 @@ static bool haveInternet() if (haveProxyEnvironmentVariables()) return true; return false; +#else + // TODO implement on Windows + return true; +#endif } std::string programPath; @@ -342,10 +355,12 @@ void mainWrapped(int argc, char * * argv) /* The chroot helper needs to be run before any threads have been started. */ +#ifndef _WIN32 if (argc > 0 && argv[0] == chrootHelperName) { chrootHelper(argc, argv); return; } +#endif initNix(); initGC(); @@ -364,6 +379,9 @@ void mainWrapped(int argc, char * * argv) programPath = argv[0]; auto programName = std::string(baseNameOf(programPath)); + auto extensionPos = programName.find_last_of("."); + if (extensionPos != std::string::npos) + programName.erase(extensionPos); if (argc > 1 && std::string_view(argv[1]) == "__build-remote") { programName = "build-remote"; @@ -524,9 +542,11 @@ void mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { +#ifndef _WIN32 // TODO implement on Windows // Increase the default stack size for the evaluator and for // libstdc++'s std::regex. nix::setStackSize(64 * 1024 * 1024); +#endif return nix::handleExceptions(argv[0], [&]() { nix::mainWrapped(argc, argv); diff --git a/src/nix/prefetch.cc b/src/nix/prefetch.cc index d30e9d397..8e6a2e805 100644 --- a/src/nix/prefetch.cc +++ b/src/nix/prefetch.cc @@ -95,7 +95,7 @@ std::tuple prefetchFile( if (executable) mode = 0700; - AutoCloseFD fd = open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode); + AutoCloseFD fd = toDescriptor(open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)); if (!fd) throw SysError("creating temporary file '%s'", tmpFile); FdSink sink(fd.get()); diff --git a/src/nix/sigs.cc b/src/nix/sigs.cc index dfef44869..1e277cbbe 100644 --- a/src/nix/sigs.cc +++ b/src/nix/sigs.cc @@ -177,7 +177,7 @@ struct CmdKeyGenerateSecret : Command throw UsageError("required argument '--key-name' is missing"); stopProgressBar(); - writeFull(STDOUT_FILENO, SecretKey::generate(*keyName).to_string()); + writeFull(getStandardOut(), SecretKey::generate(*keyName).to_string()); } }; @@ -199,7 +199,7 @@ struct CmdKeyConvertSecretToPublic : Command { SecretKey secretKey(drainFD(STDIN_FILENO)); stopProgressBar(); - writeFull(STDOUT_FILENO, secretKey.toPublicKey().to_string()); + writeFull(getStandardOut(), secretKey.toPublicKey().to_string()); } }; diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc index d7e9edf0a..b66872a6e 100644 --- a/tests/unit/libutil/tests.cc +++ b/tests/unit/libutil/tests.cc @@ -421,6 +421,7 @@ namespace nix { ASSERT_EQ(string2Int("-100"), -100); } +#ifndef _WIN32 // TODO re-enable on Windows, once we can start processes /* ---------------------------------------------------------------------------- * statusOk * --------------------------------------------------------------------------*/ @@ -429,6 +430,7 @@ namespace nix { ASSERT_EQ(statusOk(0), true); ASSERT_EQ(statusOk(1), false); } +#endif /* ----------------------------------------------------------------------------