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:
parent
2248a3f545
commit
8433027e35
111 changed files with 1162 additions and 140 deletions
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue