mirror of
https://github.com/NixOS/nix.git
synced 2025-12-17 06:21:06 +01:00
Merge branch 'master' into path-setting
This commit is contained in:
commit
37cf990b41
145 changed files with 2949 additions and 625 deletions
|
|
@ -371,13 +371,13 @@ void RootArgs::parseCmdline(const Strings & _cmdline, bool allowShebang)
|
|||
d.completer(*completions, d.n, d.prefix);
|
||||
}
|
||||
|
||||
Path Args::getCommandBaseDir() const
|
||||
std::filesystem::path Args::getCommandBaseDir() const
|
||||
{
|
||||
assert(parent);
|
||||
return parent->getCommandBaseDir();
|
||||
}
|
||||
|
||||
Path RootArgs::getCommandBaseDir() const
|
||||
std::filesystem::path RootArgs::getCommandBaseDir() const
|
||||
{
|
||||
return commandBaseDir;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include "nix/util/file-system.hh"
|
||||
#include "nix/util/processes.hh"
|
||||
#include "nix/util/signals.hh"
|
||||
#include "nix/util/environment-variables.hh"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
|
@ -65,13 +66,27 @@ void setStackSize(size_t stackSize)
|
|||
struct rlimit limit;
|
||||
if (getrlimit(RLIMIT_STACK, &limit) == 0 && static_cast<size_t>(limit.rlim_cur) < stackSize) {
|
||||
savedStackSize = limit.rlim_cur;
|
||||
limit.rlim_cur = std::min(static_cast<rlim_t>(stackSize), limit.rlim_max);
|
||||
if (limit.rlim_max < static_cast<rlim_t>(stackSize)) {
|
||||
if (getEnv("_NIX_TEST_NO_ENVIRONMENT_WARNINGS") != "1") {
|
||||
logger->log(
|
||||
lvlWarn,
|
||||
HintFmt(
|
||||
"Stack size hard limit is %1%, which is less than the desired %2%. If possible, increase the hard limit, e.g. with 'ulimit -Hs %3%'.",
|
||||
limit.rlim_max,
|
||||
stackSize,
|
||||
stackSize / 1024)
|
||||
.str());
|
||||
}
|
||||
}
|
||||
auto requestedSize = std::min(static_cast<rlim_t>(stackSize), limit.rlim_max);
|
||||
limit.rlim_cur = requestedSize;
|
||||
if (setrlimit(RLIMIT_STACK, &limit) != 0) {
|
||||
logger->log(
|
||||
lvlError,
|
||||
HintFmt(
|
||||
"Failed to increase stack size from %1% to %2% (maximum allowed stack size: %3%): %4%",
|
||||
"Failed to increase stack size from %1% to %2% (desired: %3%, maximum allowed: %4%): %5%",
|
||||
savedStackSize,
|
||||
requestedSize,
|
||||
stackSize,
|
||||
limit.rlim_max,
|
||||
std::strerror(errno))
|
||||
|
|
@ -109,7 +124,7 @@ std::optional<Path> getSelfExe()
|
|||
{
|
||||
static auto cached = []() -> std::optional<Path> {
|
||||
#if defined(__linux__) || defined(__GNU__)
|
||||
return readLink("/proc/self/exe");
|
||||
return readLink(std::filesystem::path{"/proc/self/exe"});
|
||||
#elif defined(__APPLE__)
|
||||
char buf[1024];
|
||||
uint32_t size = sizeof(buf);
|
||||
|
|
|
|||
|
|
@ -101,9 +101,11 @@ Path absPath(PathView path, std::optional<PathView> dir, bool resolveSymlinks)
|
|||
return canonPath(path, resolveSymlinks);
|
||||
}
|
||||
|
||||
std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks)
|
||||
std::filesystem::path
|
||||
absPath(const std::filesystem::path & path, const std::filesystem::path * dir_, bool resolveSymlinks)
|
||||
{
|
||||
return absPath(path.string(), std::nullopt, resolveSymlinks);
|
||||
std::optional<std::string> dir = dir_ ? std::optional<std::string>{dir_->string()} : std::nullopt;
|
||||
return absPath(PathView{path.string()}, dir.transform([](auto & p) { return PathView(p); }), resolveSymlinks);
|
||||
}
|
||||
|
||||
Path canonPath(PathView path, bool resolveSymlinks)
|
||||
|
|
@ -242,10 +244,15 @@ bool pathAccessible(const std::filesystem::path & path)
|
|||
}
|
||||
}
|
||||
|
||||
Path readLink(const Path & path)
|
||||
std::filesystem::path readLink(const std::filesystem::path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
return std::filesystem::read_symlink(path).string();
|
||||
return std::filesystem::read_symlink(path);
|
||||
}
|
||||
|
||||
Path readLink(const Path & path)
|
||||
{
|
||||
return readLink(std::filesystem::path{path}).string();
|
||||
}
|
||||
|
||||
std::string readFile(const Path & path)
|
||||
|
|
@ -669,16 +676,16 @@ void AutoUnmount::cancel()
|
|||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
std::string defaultTempDir()
|
||||
std::filesystem::path defaultTempDir()
|
||||
{
|
||||
return getEnvNonEmpty("TMPDIR").value_or("/tmp");
|
||||
}
|
||||
|
||||
Path createTempDir(const Path & tmpRoot, const Path & prefix, mode_t mode)
|
||||
std::filesystem::path createTempDir(const std::filesystem::path & tmpRoot, const std::string & prefix, mode_t mode)
|
||||
{
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = makeTempPath(tmpRoot, prefix);
|
||||
std::filesystem::path tmpDir = makeTempPath(tmpRoot, prefix);
|
||||
if (mkdir(
|
||||
tmpDir.c_str()
|
||||
#ifndef _WIN32 // TODO abstract mkdir perms for Windows
|
||||
|
|
@ -720,11 +727,11 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
|||
return {std::move(fd), tmpl};
|
||||
}
|
||||
|
||||
Path makeTempPath(const Path & root, const Path & suffix)
|
||||
std::filesystem::path makeTempPath(const std::filesystem::path & root, const std::string & suffix)
|
||||
{
|
||||
// start the counter at a random value to minimize issues with preexisting temp paths
|
||||
static std::atomic<uint32_t> counter(std::random_device{}());
|
||||
auto tmpRoot = canonPath(root.empty() ? defaultTempDir() : root, true);
|
||||
auto tmpRoot = canonPath(root.empty() ? defaultTempDir().string() : root.string(), true);
|
||||
return fmt("%1%/%2%-%3%-%4%", tmpRoot, suffix, getpid(), counter.fetch_add(1, std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,7 +84,8 @@ void RestoreSink::createDirectory(const CanonPath & path, DirectoryCreatedCallba
|
|||
|
||||
RestoreSink dirSink{startFsync};
|
||||
dirSink.dstPath = append(dstPath, path);
|
||||
dirSink.dirFd = ::openat(dirFd.get(), path.rel_c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
dirSink.dirFd =
|
||||
unix::openFileEnsureBeneathNoSymlinks(dirFd.get(), path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
|
||||
|
||||
if (!dirSink.dirFd)
|
||||
throw SysError("opening directory '%s'", dirSink.dstPath.string());
|
||||
|
|
@ -169,7 +170,7 @@ void RestoreSink::createRegularFile(const CanonPath & path, std::function<void(C
|
|||
constexpr int flags = O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC;
|
||||
if (!dirFd)
|
||||
return ::open(p.c_str(), flags, 0666);
|
||||
return ::openat(dirFd.get(), path.rel_c_str(), flags, 0666);
|
||||
return unix::openFileEnsureBeneathNoSymlinks(dirFd.get(), path, flags, 0666);
|
||||
}();
|
||||
#endif
|
||||
;
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ public:
|
|||
*
|
||||
* This only returns the correct value after parseCmdline() has run.
|
||||
*/
|
||||
virtual Path getCommandBaseDir() const;
|
||||
virtual std::filesystem::path getCommandBaseDir() const;
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ protected:
|
|||
*
|
||||
* @see getCommandBaseDir()
|
||||
*/
|
||||
Path commandBaseDir = ".";
|
||||
std::filesystem::path commandBaseDir = ".";
|
||||
|
||||
public:
|
||||
/** Parse the command line, throwing a UsageError if something goes
|
||||
|
|
@ -48,7 +48,7 @@ public:
|
|||
|
||||
std::shared_ptr<Completions> completions;
|
||||
|
||||
Path getCommandBaseDir() const override;
|
||||
std::filesystem::path getCommandBaseDir() const override;
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include "nix/util/canon-path.hh"
|
||||
#include "nix/util/types.hh"
|
||||
#include "nix/util/error.hh"
|
||||
|
||||
|
|
@ -203,6 +204,26 @@ void closeOnExec(Descriptor fd);
|
|||
} // namespace unix
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
namespace linux {
|
||||
|
||||
/**
|
||||
* Wrapper around Linux's openat2 syscall introduced in Linux 5.6.
|
||||
*
|
||||
* @see https://man7.org/linux/man-pages/man2/openat2.2.html
|
||||
* @see https://man7.org/linux/man-pages/man2/open_how.2type.html
|
||||
v*
|
||||
* @param flags O_* flags
|
||||
* @param mode Mode for O_{CREAT,TMPFILE}
|
||||
* @param resolve RESOLVE_* flags
|
||||
*
|
||||
* @return nullopt if openat2 is not supported by the kernel.
|
||||
*/
|
||||
std::optional<Descriptor> openat2(Descriptor dirFd, const char * path, uint64_t flags, uint64_t mode, uint64_t resolve);
|
||||
|
||||
} // namespace linux
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && _WIN32_WINNT >= 0x0600
|
||||
namespace windows {
|
||||
|
||||
|
|
@ -212,6 +233,45 @@ std::wstring handleToFileName(Descriptor handle);
|
|||
} // namespace windows
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
namespace unix {
|
||||
|
||||
struct SymlinkNotAllowed : public Error
|
||||
{
|
||||
CanonPath path;
|
||||
|
||||
SymlinkNotAllowed(CanonPath path)
|
||||
/* Can't provide better error message, since the parent directory is only known to the caller. */
|
||||
: Error("relative path '%s' points to a symlink, which is not allowed", path.rel())
|
||||
, path(std::move(path))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Safe(r) function to open \param path file relative to \param dirFd, while
|
||||
* disallowing escaping from a directory and resolving any symlinks in the
|
||||
* process.
|
||||
*
|
||||
* @note When not on Linux or when openat2 is not available this is implemented
|
||||
* via openat single path component traversal. Uses RESOLVE_BENEATH with openat2
|
||||
* or O_RESOLVE_BENEATH.
|
||||
*
|
||||
* @note Since this is Unix-only path is specified as CanonPath, which models
|
||||
* Unix-style paths and ensures that there are no .. or . components.
|
||||
*
|
||||
* @param flags O_* flags
|
||||
* @param mode Mode for O_{CREAT,TMPFILE}
|
||||
*
|
||||
* @pre path.isRoot() is false
|
||||
*
|
||||
* @throws SymlinkNotAllowed if any path components
|
||||
*/
|
||||
Descriptor openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPath & path, int flags, mode_t mode = 0);
|
||||
|
||||
} // namespace unix
|
||||
#endif
|
||||
|
||||
MakeError(EndOfFile, Error);
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ inline Path absPath(const Path & path, std::optional<PathView> dir = {}, bool re
|
|||
return absPath(PathView{path}, dir, resolveSymlinks);
|
||||
}
|
||||
|
||||
std::filesystem::path absPath(const std::filesystem::path & path, bool resolveSymlinks = false);
|
||||
std::filesystem::path
|
||||
absPath(const std::filesystem::path & path, const std::filesystem::path * dir = nullptr, bool resolveSymlinks = false);
|
||||
|
||||
/**
|
||||
* Canonicalise a path by removing all `.` or `..` components and
|
||||
|
|
@ -152,6 +153,12 @@ bool pathAccessible(const std::filesystem::path & path);
|
|||
*/
|
||||
Path readLink(const Path & path);
|
||||
|
||||
/**
|
||||
* Read the contents (target) of a symbolic link. The result is not
|
||||
* in any way canonicalised.
|
||||
*/
|
||||
std::filesystem::path readLink(const std::filesystem::path & path);
|
||||
|
||||
/**
|
||||
* Open a `Descriptor` with read-only access to the given directory.
|
||||
*/
|
||||
|
|
@ -327,7 +334,8 @@ typedef std::unique_ptr<DIR, DIRDeleter> AutoCloseDir;
|
|||
/**
|
||||
* Create a temporary directory.
|
||||
*/
|
||||
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", mode_t mode = 0755);
|
||||
std::filesystem::path
|
||||
createTempDir(const std::filesystem::path & tmpRoot = "", const std::string & prefix = "nix", mode_t mode = 0755);
|
||||
|
||||
/**
|
||||
* Create a temporary file, returning a file handle and its path.
|
||||
|
|
@ -337,7 +345,7 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix = "nix");
|
|||
/**
|
||||
* Return `TMPDIR`, or the default temporary directory if unset or empty.
|
||||
*/
|
||||
Path defaultTempDir();
|
||||
std::filesystem::path defaultTempDir();
|
||||
|
||||
/**
|
||||
* Interpret `exe` as a location in the ambient file system and return
|
||||
|
|
@ -351,7 +359,7 @@ bool isExecutableFileAmbient(const std::filesystem::path & exe);
|
|||
* The constructed path looks like `<root><suffix>-<pid>-<unique>`. To create a
|
||||
* path nested in a directory, provide a suffix starting with `/`.
|
||||
*/
|
||||
Path makeTempPath(const Path & root, const Path & suffix = ".tmp");
|
||||
std::filesystem::path makeTempPath(const std::filesystem::path & root, const std::string & suffix = ".tmp");
|
||||
|
||||
/**
|
||||
* Used in various places.
|
||||
|
|
|
|||
|
|
@ -6,18 +6,30 @@
|
|||
#include "nix/util/experimental-features.hh"
|
||||
|
||||
// Following https://github.com/nlohmann/json#how-can-i-use-get-for-non-default-constructiblenon-copyable-types
|
||||
#define JSON_IMPL_INNER_TO(TYPE) \
|
||||
struct adl_serializer<TYPE> \
|
||||
{ \
|
||||
static void to_json(json & json, const TYPE & t); \
|
||||
}
|
||||
|
||||
#define JSON_IMPL_INNER_FROM(TYPE) \
|
||||
struct adl_serializer<TYPE> \
|
||||
{ \
|
||||
static TYPE from_json(const json & json); \
|
||||
}
|
||||
|
||||
#define JSON_IMPL_INNER(TYPE) \
|
||||
struct adl_serializer<TYPE> \
|
||||
{ \
|
||||
static TYPE from_json(const json & json); \
|
||||
static void to_json(json & json, const TYPE & t); \
|
||||
};
|
||||
}
|
||||
|
||||
#define JSON_IMPL(TYPE) \
|
||||
namespace nlohmann { \
|
||||
using namespace nix; \
|
||||
template<> \
|
||||
JSON_IMPL_INNER(TYPE) \
|
||||
#define JSON_IMPL(TYPE) \
|
||||
namespace nlohmann { \
|
||||
using namespace nix; \
|
||||
template<> \
|
||||
JSON_IMPL_INNER(TYPE); \
|
||||
}
|
||||
|
||||
#define JSON_IMPL_WITH_XP_FEATURES(TYPE) \
|
||||
|
|
|
|||
|
|
@ -188,23 +188,23 @@ using namespace nix;
|
|||
|
||||
#define ARG fso::Regular<RegularContents>
|
||||
template<typename RegularContents>
|
||||
JSON_IMPL_INNER(ARG)
|
||||
JSON_IMPL_INNER(ARG);
|
||||
#undef ARG
|
||||
|
||||
#define ARG fso::DirectoryT<Child>
|
||||
template<typename Child>
|
||||
JSON_IMPL_INNER(ARG)
|
||||
JSON_IMPL_INNER(ARG);
|
||||
#undef ARG
|
||||
|
||||
template<>
|
||||
JSON_IMPL_INNER(fso::Symlink)
|
||||
JSON_IMPL_INNER(fso::Symlink);
|
||||
|
||||
template<>
|
||||
JSON_IMPL_INNER(fso::Opaque)
|
||||
JSON_IMPL_INNER(fso::Opaque);
|
||||
|
||||
#define ARG fso::VariantT<RegularContents, recur>
|
||||
template<typename RegularContents, bool recur>
|
||||
JSON_IMPL_INNER(ARG)
|
||||
JSON_IMPL_INNER(ARG);
|
||||
#undef ARG
|
||||
|
||||
} // namespace nlohmann
|
||||
|
|
|
|||
|
|
@ -447,18 +447,27 @@ struct LengthSource : Source
|
|||
*/
|
||||
struct LambdaSink : Sink
|
||||
{
|
||||
typedef std::function<void(std::string_view data)> lambda_t;
|
||||
typedef std::function<void(std::string_view data)> data_t;
|
||||
typedef std::function<void()> cleanup_t;
|
||||
|
||||
lambda_t lambda;
|
||||
data_t dataFun;
|
||||
cleanup_t cleanupFun;
|
||||
|
||||
LambdaSink(const lambda_t & lambda)
|
||||
: lambda(lambda)
|
||||
LambdaSink(
|
||||
const data_t & dataFun, const cleanup_t & cleanupFun = []() {})
|
||||
: dataFun(dataFun)
|
||||
, cleanupFun(cleanupFun)
|
||||
{
|
||||
}
|
||||
|
||||
~LambdaSink()
|
||||
{
|
||||
cleanupFun();
|
||||
}
|
||||
|
||||
void operator()(std::string_view data) override
|
||||
{
|
||||
lambda(data);
|
||||
dataFun(data);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "nix/util/types.hh"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
|
@ -15,43 +17,43 @@ std::string getUserName();
|
|||
/**
|
||||
* @return the given user's home directory from /etc/passwd.
|
||||
*/
|
||||
Path getHomeOf(uid_t userId);
|
||||
std::filesystem::path getHomeOf(uid_t userId);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return $HOME or the user's home directory from /etc/passwd.
|
||||
*/
|
||||
Path getHome();
|
||||
std::filesystem::path getHome();
|
||||
|
||||
/**
|
||||
* @return $NIX_CACHE_HOME or $XDG_CACHE_HOME/nix or $HOME/.cache/nix.
|
||||
*/
|
||||
Path getCacheDir();
|
||||
std::filesystem::path getCacheDir();
|
||||
|
||||
/**
|
||||
* @return $NIX_CONFIG_HOME or $XDG_CONFIG_HOME/nix or $HOME/.config/nix.
|
||||
*/
|
||||
Path getConfigDir();
|
||||
std::filesystem::path getConfigDir();
|
||||
|
||||
/**
|
||||
* @return the directories to search for user configuration files
|
||||
*/
|
||||
std::vector<Path> getConfigDirs();
|
||||
std::vector<std::filesystem::path> getConfigDirs();
|
||||
|
||||
/**
|
||||
* @return $NIX_DATA_HOME or $XDG_DATA_HOME/nix or $HOME/.local/share/nix.
|
||||
*/
|
||||
Path getDataDir();
|
||||
std::filesystem::path getDataDir();
|
||||
|
||||
/**
|
||||
* @return $NIX_STATE_HOME or $XDG_STATE_HOME/nix or $HOME/.local/state/nix.
|
||||
*/
|
||||
Path getStateDir();
|
||||
std::filesystem::path getStateDir();
|
||||
|
||||
/**
|
||||
* Create the Nix state directory and return the path to it.
|
||||
*/
|
||||
Path createNixStateDir();
|
||||
std::filesystem::path createNixStateDir();
|
||||
|
||||
/**
|
||||
* Perform tilde expansion on a path, replacing tilde with the user's
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "nix/util/logging.hh"
|
||||
#include "nix/util/strings.hh"
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
|
|
@ -58,6 +59,12 @@ Strings quoteStrings(const C & c, char quote = '\'')
|
|||
return res;
|
||||
}
|
||||
|
||||
inline Strings quoteFSPaths(const std::set<std::filesystem::path> & paths, char quote = '\'')
|
||||
{
|
||||
return paths | std::views::transform([&](const auto & p) { return quoteString(p.string(), quote); })
|
||||
| std::ranges::to<Strings>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove trailing whitespace from a string.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include "nix/util/canon-path.hh"
|
||||
#include "nix/util/file-system.hh"
|
||||
#include "nix/util/signals.hh"
|
||||
#include "nix/util/finally.hh"
|
||||
|
|
@ -7,6 +8,14 @@
|
|||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
|
||||
#if defined(__linux__) && defined(__NR_openat2)
|
||||
# define HAVE_OPENAT2 1
|
||||
# include <sys/syscall.h>
|
||||
# include <linux/openat2.h>
|
||||
#else
|
||||
# define HAVE_OPENAT2 0
|
||||
#endif
|
||||
|
||||
#include "util-config-private.hh"
|
||||
#include "util-unix-config-private.hh"
|
||||
|
||||
|
|
@ -223,4 +232,107 @@ void unix::closeOnExec(int fd)
|
|||
throw SysError("setting close-on-exec flag");
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
namespace linux {
|
||||
|
||||
std::optional<Descriptor> openat2(Descriptor dirFd, const char * path, uint64_t flags, uint64_t mode, uint64_t resolve)
|
||||
{
|
||||
# if HAVE_OPENAT2
|
||||
/* Cache the result of whether openat2 is not supported. */
|
||||
static std::atomic_flag unsupported{};
|
||||
|
||||
if (!unsupported.test()) {
|
||||
/* No glibc wrapper yet, but there's a patch:
|
||||
* https://patchwork.sourceware.org/project/glibc/patch/20251029200519.3203914-1-adhemerval.zanella@linaro.org/
|
||||
*/
|
||||
auto how = ::open_how{.flags = flags, .mode = mode, .resolve = resolve};
|
||||
auto res = ::syscall(__NR_openat2, dirFd, path, &how, sizeof(how));
|
||||
/* Cache that the syscall is not supported. */
|
||||
if (res < 0 && errno == ENOSYS) {
|
||||
unsupported.test_and_set();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
# endif
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace linux
|
||||
|
||||
#endif
|
||||
|
||||
static Descriptor
|
||||
openFileEnsureBeneathNoSymlinksIterative(Descriptor dirFd, const CanonPath & path, int flags, mode_t mode)
|
||||
{
|
||||
AutoCloseFD parentFd;
|
||||
auto nrComponents = std::ranges::distance(path);
|
||||
assert(nrComponents >= 1);
|
||||
auto components = std::views::take(path, nrComponents - 1); /* Everything but last component */
|
||||
auto getParentFd = [&]() { return parentFd ? parentFd.get() : dirFd; };
|
||||
|
||||
/* This rather convoluted loop is necessary to avoid TOCTOU when validating that
|
||||
no inner path component is a symlink. */
|
||||
for (auto it = components.begin(); it != components.end(); ++it) {
|
||||
auto component = std::string(*it); /* Copy into a string to make NUL terminated. */
|
||||
assert(component != ".." && !component.starts_with('/')); /* In case invariant is broken somehow.. */
|
||||
|
||||
AutoCloseFD parentFd2 = ::openat(
|
||||
getParentFd(), /* First iteration uses dirFd. */
|
||||
component.c_str(),
|
||||
O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC
|
||||
#ifdef __linux__
|
||||
| O_PATH /* Linux-specific optimization. Files are open only for path resolution purposes. */
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
| O_RESOLVE_BENEATH /* Further guard against any possible SNAFUs. */
|
||||
#endif
|
||||
);
|
||||
|
||||
if (!parentFd2) {
|
||||
/* Construct the CanonPath for error message. */
|
||||
auto path2 = std::ranges::fold_left(components.begin(), ++it, CanonPath::root, [](auto lhs, auto rhs) {
|
||||
lhs.push(rhs);
|
||||
return lhs;
|
||||
});
|
||||
|
||||
if (errno == ENOTDIR) /* Path component might be a symlink. */ {
|
||||
struct ::stat st;
|
||||
if (::fstatat(getParentFd(), component.c_str(), &st, AT_SYMLINK_NOFOLLOW) == 0 && S_ISLNK(st.st_mode))
|
||||
throw unix::SymlinkNotAllowed(path2);
|
||||
errno = ENOTDIR; /* Restore the errno. */
|
||||
} else if (errno == ELOOP) {
|
||||
throw unix::SymlinkNotAllowed(path2);
|
||||
}
|
||||
|
||||
return INVALID_DESCRIPTOR;
|
||||
}
|
||||
|
||||
parentFd = std::move(parentFd2);
|
||||
}
|
||||
|
||||
auto res = ::openat(getParentFd(), std::string(path.baseName().value()).c_str(), flags | O_NOFOLLOW, mode);
|
||||
if (res < 0 && errno == ELOOP)
|
||||
throw unix::SymlinkNotAllowed(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
Descriptor unix::openFileEnsureBeneathNoSymlinks(Descriptor dirFd, const CanonPath & path, int flags, mode_t mode)
|
||||
{
|
||||
assert(!path.rel().starts_with('/')); /* Just in case the invariant is somehow broken. */
|
||||
assert(!path.isRoot());
|
||||
#ifdef __linux__
|
||||
auto maybeFd = linux::openat2(
|
||||
dirFd, path.rel_c_str(), flags, static_cast<uint64_t>(mode), RESOLVE_BENEATH | RESOLVE_NO_SYMLINKS);
|
||||
if (maybeFd) {
|
||||
if (*maybeFd < 0 && errno == ELOOP)
|
||||
throw unix::SymlinkNotAllowed(path);
|
||||
return *maybeFd;
|
||||
}
|
||||
#endif
|
||||
return openFileEnsureBeneathNoSymlinksIterative(dirFd, path, flags, mode);
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ std::string getUserName()
|
|||
return name;
|
||||
}
|
||||
|
||||
Path getHomeOf(uid_t userId)
|
||||
std::filesystem::path getHomeOf(uid_t userId)
|
||||
{
|
||||
std::vector<char> buf(16384);
|
||||
struct passwd pwbuf;
|
||||
|
|
@ -28,9 +28,9 @@ Path getHomeOf(uid_t userId)
|
|||
return pw->pw_dir;
|
||||
}
|
||||
|
||||
Path getHome()
|
||||
std::filesystem::path getHome()
|
||||
{
|
||||
static Path homeDir = []() {
|
||||
static std::filesystem::path homeDir = []() {
|
||||
std::optional<std::string> unownedUserHomeDir = {};
|
||||
auto homeDir = getEnv("HOME");
|
||||
if (homeDir) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
Path getCacheDir()
|
||||
std::filesystem::path getCacheDir()
|
||||
{
|
||||
auto dir = getEnv("NIX_CACHE_HOME");
|
||||
if (dir) {
|
||||
|
|
@ -13,14 +13,14 @@ Path getCacheDir()
|
|||
} else {
|
||||
auto xdgDir = getEnv("XDG_CACHE_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
return std::filesystem::path{*xdgDir} / "nix";
|
||||
} else {
|
||||
return getHome() + "/.cache/nix";
|
||||
return getHome() / ".cache" / "nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path getConfigDir()
|
||||
std::filesystem::path getConfigDir()
|
||||
{
|
||||
auto dir = getEnv("NIX_CONFIG_HOME");
|
||||
if (dir) {
|
||||
|
|
@ -28,26 +28,27 @@ Path getConfigDir()
|
|||
} else {
|
||||
auto xdgDir = getEnv("XDG_CONFIG_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
return std::filesystem::path{*xdgDir} / "nix";
|
||||
} else {
|
||||
return getHome() + "/.config/nix";
|
||||
return getHome() / ".config" / "nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Path> getConfigDirs()
|
||||
std::vector<std::filesystem::path> getConfigDirs()
|
||||
{
|
||||
Path configHome = getConfigDir();
|
||||
std::filesystem::path configHome = getConfigDir();
|
||||
auto configDirs = getEnv("XDG_CONFIG_DIRS").value_or("/etc/xdg");
|
||||
std::vector<Path> result = tokenizeString<std::vector<std::string>>(configDirs, ":");
|
||||
for (auto & p : result) {
|
||||
p += "/nix";
|
||||
auto tokens = tokenizeString<std::vector<std::string>>(configDirs, ":");
|
||||
std::vector<std::filesystem::path> result;
|
||||
result.push_back(configHome);
|
||||
for (auto & token : tokens) {
|
||||
result.push_back(std::filesystem::path{token} / "nix");
|
||||
}
|
||||
result.insert(result.begin(), configHome);
|
||||
return result;
|
||||
}
|
||||
|
||||
Path getDataDir()
|
||||
std::filesystem::path getDataDir()
|
||||
{
|
||||
auto dir = getEnv("NIX_DATA_HOME");
|
||||
if (dir) {
|
||||
|
|
@ -55,14 +56,14 @@ Path getDataDir()
|
|||
} else {
|
||||
auto xdgDir = getEnv("XDG_DATA_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
return std::filesystem::path{*xdgDir} / "nix";
|
||||
} else {
|
||||
return getHome() + "/.local/share/nix";
|
||||
return getHome() / ".local" / "share" / "nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path getStateDir()
|
||||
std::filesystem::path getStateDir()
|
||||
{
|
||||
auto dir = getEnv("NIX_STATE_HOME");
|
||||
if (dir) {
|
||||
|
|
@ -70,16 +71,16 @@ Path getStateDir()
|
|||
} else {
|
||||
auto xdgDir = getEnv("XDG_STATE_HOME");
|
||||
if (xdgDir) {
|
||||
return *xdgDir + "/nix";
|
||||
return std::filesystem::path{*xdgDir} / "nix";
|
||||
} else {
|
||||
return getHome() + "/.local/state/nix";
|
||||
return getHome() / ".local" / "state" / "nix";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Path createNixStateDir()
|
||||
std::filesystem::path createNixStateDir()
|
||||
{
|
||||
Path dir = getStateDir();
|
||||
std::filesystem::path dir = getStateDir();
|
||||
createDirs(dir);
|
||||
return dir;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ std::string getUserName()
|
|||
return name;
|
||||
}
|
||||
|
||||
Path getHome()
|
||||
std::filesystem::path getHome()
|
||||
{
|
||||
static Path homeDir = []() {
|
||||
Path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default");
|
||||
static std::filesystem::path homeDir = []() {
|
||||
std::filesystem::path homeDir = getEnv("USERPROFILE").value_or("C:\\Users\\Default");
|
||||
assert(!homeDir.empty());
|
||||
return canonPath(homeDir);
|
||||
}();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue