1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-06 05:01:48 +02: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:
John Ericson 2023-09-02 17:35:16 -04:00
parent 2248a3f545
commit 8433027e35
111 changed files with 1162 additions and 140 deletions

View file

@ -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)

View file

@ -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
}

View file

@ -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);

View file

@ -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.
*

View file

@ -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()'.

View file

@ -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);
}
}

View file

@ -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
View 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);
}

View file

@ -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("Cant 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 }));
}
}
}

View file

@ -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;

View file

@ -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)

View file

@ -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

View file

@ -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);
}

View file

@ -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)

View file

@ -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
}

View file

@ -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;
}

View file

@ -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()

View file

@ -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.
*

View file

@ -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;

View 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;
}
}

View file

@ -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();

View 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);
}
}

View 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
}

View 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;
}
}

View 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");
}
}

View 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() {}
};
}

View 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;
}
}

View 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);
}
}

View 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);
};
}