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