treewide: lint syscalls for c string safety
Change-Id: I3caf476e59dcb7899ac5a3d83dfa3fb7ceaaabf0
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "lix/libstore/path.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -255,7 +256,7 @@ try {
|
||||
bool canBuildLocally = amWilling && couldBuildLocally;
|
||||
|
||||
/* Error ignored here, will be caught later */
|
||||
mkdir(currentLoad.c_str(), 0777);
|
||||
(void) sys::mkdir(currentLoad, 0777);
|
||||
|
||||
while (true) {
|
||||
bestSlotLock.reset();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/local-fs-store.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libmain/shared.hh"
|
||||
@@ -225,8 +226,9 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
left = {"default.nix"};
|
||||
}
|
||||
|
||||
if (runEnv)
|
||||
setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1);
|
||||
if (runEnv) {
|
||||
(void) sys::setenv("IN_NIX_SHELL", pure ? "pure" : "impure", 1);
|
||||
}
|
||||
|
||||
DrvInfos drvs;
|
||||
|
||||
@@ -540,15 +542,13 @@ static void main_nix_build(AsyncIoRoot & aio, std::string programName, Strings a
|
||||
|
||||
environ = envPtrs.data();
|
||||
|
||||
auto argPtrs = stringsToCharPtrs(args);
|
||||
|
||||
restoreProcessContext();
|
||||
|
||||
logger->pause();
|
||||
|
||||
printMsg(lvlChatty, "running shell: %s", concatMapStringsSep(" ", args, shellEscape));
|
||||
|
||||
execvp(shell->c_str(), argPtrs.data());
|
||||
sys::execvp(*shell, args);
|
||||
|
||||
throw SysError("executing shell '%s'", *shell);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "lix/libexpr/eval-settings.hh" // for defexpr
|
||||
#include "lix/libstore/temporary-dir.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/regex.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
@@ -44,7 +45,7 @@ static void readChannels()
|
||||
// Writes the list of channels.
|
||||
static void writeChannels()
|
||||
{
|
||||
auto channelsFD = AutoCloseFD{open(channelsList.c_str(), O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644)};
|
||||
auto channelsFD = sys::open(channelsList, O_WRONLY | O_CLOEXEC | O_CREAT | O_TRUNC, 0644);
|
||||
if (!channelsFD)
|
||||
throw SysError("opening '%1%' for writing", channelsList);
|
||||
for (const auto & channel : channels)
|
||||
@@ -174,11 +175,12 @@ static void update(AsyncIoRoot & aio, const StringSet & channelNames)
|
||||
|
||||
// Make the channels appear in nix-env.
|
||||
struct stat st;
|
||||
if (lstat(nixDefExpr.c_str(), &st) == 0) {
|
||||
if (sys::lstat(nixDefExpr, &st) == 0) {
|
||||
if (S_ISLNK(st.st_mode))
|
||||
// old-skool ~/.nix-defexpr
|
||||
if (unlink(nixDefExpr.c_str()) == -1)
|
||||
if (sys::unlink(nixDefExpr) == -1) {
|
||||
throw SysError("unlinking %1%", nixDefExpr);
|
||||
}
|
||||
} else if (errno != ENOENT) {
|
||||
throw SysError("getting status of %1%", nixDefExpr);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/store-cast.hh"
|
||||
@@ -24,9 +25,11 @@ bool dryRun = false;
|
||||
|
||||
static void removeOldGenerations(std::string dir, NeverAsync = {})
|
||||
{
|
||||
if (access(dir.c_str(), R_OK) != 0) return;
|
||||
if (sys::access(dir, R_OK) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool canWrite = access(dir.c_str(), W_OK) == 0;
|
||||
bool canWrite = sys::access(dir, W_OK) == 0;
|
||||
|
||||
for (auto & i : readDirectory(dir)) {
|
||||
checkInterrupt();
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "lix/libstore/profiles.hh"
|
||||
#include "lix/libcmd/repl.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
|
||||
extern char * * environ __attribute__((weak));
|
||||
|
||||
@@ -303,8 +305,10 @@ void MixEnvironment::setEnviron() {
|
||||
throw UsageError("--unset does not make sense with --ignore-environment");
|
||||
|
||||
for (const auto & var : keep) {
|
||||
auto val = getenv(var.c_str());
|
||||
if (val) stringsEnv.emplace_back(fmt("%s=%s", var.c_str(), val));
|
||||
auto val = sys::getenv(var);
|
||||
if (val) {
|
||||
stringsEnv.emplace_back(fmt("%s=%s", var, val));
|
||||
}
|
||||
}
|
||||
|
||||
vectorEnv = stringsToCharPtrs(stringsEnv);
|
||||
@@ -314,7 +318,7 @@ void MixEnvironment::setEnviron() {
|
||||
throw UsageError("--keep does not make sense without --ignore-environment");
|
||||
|
||||
for (const auto & var : unset)
|
||||
unsetenv(var.c_str());
|
||||
(void) sys::unsetenv(var);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -110,7 +111,7 @@ static el_status_t doCompletion() {
|
||||
if (possible.size() == 1) {
|
||||
const auto completion = *possible.cbegin();
|
||||
if (completion.size() > s.size()) {
|
||||
rl_insert_text(completion.c_str() + s.size());
|
||||
rl_insert_text(requireCString(completion.substr(s.size())));
|
||||
return redisplay();
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ static el_status_t doCompletion() {
|
||||
}
|
||||
if (len > 0) {
|
||||
auto commonPrefix = possible.begin()->substr(start, len);
|
||||
rl_insert_text(commonPrefix.c_str());
|
||||
rl_insert_text(requireCString(commonPrefix));
|
||||
el_ring_bell();
|
||||
return redisplay();
|
||||
}
|
||||
@@ -154,7 +155,7 @@ ReadlineLikeInteracter::Guard ReadlineLikeInteracter::init(detail::ReplCompleter
|
||||
logWarning(e.info());
|
||||
}
|
||||
el_hist_size = 1000;
|
||||
read_history(historyFile.c_str());
|
||||
read_history(requireCString(historyFile));
|
||||
auto oldRepl = curRepl;
|
||||
curRepl = repl;
|
||||
Guard restoreRepl([oldRepl] { curRepl = oldRepl; });
|
||||
@@ -202,7 +203,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
||||
};
|
||||
|
||||
setupSignals();
|
||||
char * s = readline(promptForType(promptType));
|
||||
char * s = readline(promptForType(promptType)); // NOLINT(lix-unsafe-c-calls)
|
||||
Finally doFree([&]() { free(s); });
|
||||
restoreSignals();
|
||||
|
||||
@@ -223,7 +224,7 @@ bool ReadlineLikeInteracter::getLine(std::string & input, ReplPromptType promptT
|
||||
|
||||
void ReadlineLikeInteracter::writeHistory()
|
||||
{
|
||||
int ret = write_history(historyFile.c_str());
|
||||
int ret = write_history(requireCString(historyFile));
|
||||
int writeHistErr = errno;
|
||||
|
||||
if (ret == 0) {
|
||||
|
||||
@@ -56,6 +56,7 @@ using NdString = std::unique_ptr<const char, decltype(&lixdoc_free_string)>;
|
||||
*/
|
||||
NdString lambdaDocsForPos(SourcePath const path, nix::Pos const &pos) {
|
||||
std::string const file = path.to_string();
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): paths are safe
|
||||
return NdString{lixdoc_get_function_docs(file.c_str(), pos.line, pos.column), &lixdoc_free_string};
|
||||
}
|
||||
|
||||
|
||||
@@ -2603,6 +2603,7 @@ bool EvalState::eqValues(Value & v1, Value & v2, const PosIdx pos, std::string_v
|
||||
return v1.str() == v2.str();
|
||||
|
||||
case nPath:
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
return strcmp(v1.string().content, v2.string().content) == 0;
|
||||
|
||||
case nNull:
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "lix/libexpr/value-to-xml.hh"
|
||||
#include "lix/libexpr/primops.hh"
|
||||
#include "lix/libfetchers/fetch-to-store.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/regex.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "value.hh"
|
||||
@@ -273,12 +274,12 @@ void prim_importNative(EvalState & state, Value * * args, Value & v)
|
||||
|
||||
std::string sym(state.forceStringNoCtx(*args[1], noPos, "while evaluating the second argument passed to builtins.importNative"));
|
||||
|
||||
void *handle = dlopen(path.canonical().c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
void * handle = dlopen(requireCString(path.canonical().abs()), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle)
|
||||
state.ctx.errors.make<EvalError>("could not open '%1%': %2%", path, dlerror()).debugThrow();
|
||||
|
||||
dlerror();
|
||||
ValueInitializer func = reinterpret_cast<ValueInitializer>(dlsym(handle, sym.c_str()));
|
||||
ValueInitializer func = reinterpret_cast<ValueInitializer>(dlsym(handle, requireCString(sym)));
|
||||
if(!func) {
|
||||
char *message = dlerror();
|
||||
if (message)
|
||||
@@ -484,6 +485,7 @@ struct CompareValues : NeverAsync
|
||||
case nString:
|
||||
return v1.str() < v2.str();
|
||||
case nPath:
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
return strcmp(v1.string().content, v2.string().content) < 0;
|
||||
case nList:
|
||||
// Lexicographic comparison
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libfetchers/fetchers.hh"
|
||||
#include "lix/libfetchers/cache.hh"
|
||||
@@ -51,7 +52,7 @@ bool touchCacheFile(const Path & path, time_t touch_time)
|
||||
times[1].tv_sec = touch_time;
|
||||
times[1].tv_usec = 0;
|
||||
|
||||
return lutimes(path.c_str(), times) == 0;
|
||||
return sys::lutimes(path, times) == 0;
|
||||
}
|
||||
|
||||
Path getCachePath(std::string_view key)
|
||||
@@ -128,7 +129,7 @@ try {
|
||||
time_t now = time(0);
|
||||
struct stat st;
|
||||
std::optional<std::string> cachedRef;
|
||||
if (stat(headRefFile.c_str(), &st) == 0) {
|
||||
if (sys::stat(headRefFile, &st) == 0) {
|
||||
cachedRef = TRY_AWAIT(readHead(cacheDir));
|
||||
if (cachedRef != std::nullopt &&
|
||||
*cachedRef != gitInitialBranch &&
|
||||
@@ -737,8 +738,7 @@ struct GitInputScheme : InputScheme
|
||||
/* If the local ref is older than ‘tarball-ttl’ seconds, do a
|
||||
git fetch to update the local ref to the remote ref. */
|
||||
struct stat st;
|
||||
return stat(path.c_str(), &st) == 0 &&
|
||||
isCacheFileWithinTtl(now, st);
|
||||
return sys::stat(path, &st) == 0 && isCacheFileWithinTtl(now, st);
|
||||
};
|
||||
if (auto result = resolveRefToCachePath(
|
||||
input,
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "lix/libmain/shared.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/gc-store.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/signals.hh"
|
||||
#include "lix/libmain/loggers.hh"
|
||||
@@ -307,8 +308,8 @@ void printVersion(const std::string & programName)
|
||||
void showManPage(const std::string & name)
|
||||
{
|
||||
restoreProcessContext();
|
||||
setenv("MANPATH", settings.nixManDir.c_str(), 1);
|
||||
execlp("man", "man", name.c_str(), nullptr);
|
||||
(void) sys::setenv("MANPATH", settings.nixManDir, 1);
|
||||
execlp("man", "man", requireCString(name).asCStr(), nullptr);
|
||||
throw SysError("command 'man %1%' failed", name.c_str());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
|
||||
@@ -22,12 +23,13 @@ void commonExecveingChildInit()
|
||||
throw SysError("cannot dup stderr into stdout");
|
||||
|
||||
/* Reroute stdin to /dev/null. */
|
||||
int fdDevNull = open(pathNullDevice.c_str(), O_RDWR);
|
||||
if (fdDevNull == -1)
|
||||
auto fdDevNull = sys::open(pathNullDevice, O_RDWR);
|
||||
if (!fdDevNull) {
|
||||
throw SysError("cannot open '%1%'", pathNullDevice);
|
||||
if (dup2(fdDevNull, STDIN_FILENO) == -1)
|
||||
}
|
||||
if (dup2(fdDevNull.get(), STDIN_FILENO) == -1) {
|
||||
throw SysError("cannot dup null device into stdin");
|
||||
close(fdDevNull);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libstore/build/derivation-goal.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libstore/build/hook-instance.hh"
|
||||
@@ -1182,7 +1183,7 @@ Path DerivationGoal::openLogFile()
|
||||
Path logFileName = fmt("%s/%s%s", dir, baseName.substr(2),
|
||||
settings.compressLog ? ".bz2" : "");
|
||||
|
||||
fdLogFile = AutoCloseFD{open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666)};
|
||||
fdLogFile = sys::open(logFileName, O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0666);
|
||||
if (!fdLogFile) throw SysError("creating log file '%1%'", logFileName);
|
||||
|
||||
logFileSink = std::make_shared<FdSink>(fdLogFile.get());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libstore/build/child.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
@@ -56,7 +57,7 @@ try {
|
||||
throw SysError("dupping to-hook read side");
|
||||
}
|
||||
|
||||
execv(buildHook.c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execv(buildHook, args);
|
||||
|
||||
throw SysError("executing '%s'", buildHook);
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "lix/libstore/path-references.hh"
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libstore/daemon.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/fmt.hh"
|
||||
#include "lix/libutil/regex.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
@@ -370,13 +371,14 @@ bool LocalDerivationGoal::cleanupDecideWhetherDiskFull()
|
||||
auto & localStore = getLocalStore();
|
||||
uint64_t required = 8ULL * 1024 * 1024; // FIXME: make configurable
|
||||
struct statvfs st;
|
||||
if (statvfs(localStore.config().realStoreDir.get().c_str(), &st) == 0 &&
|
||||
(uint64_t) st.f_bavail * st.f_bsize < required)
|
||||
diskFull = true;
|
||||
if (statvfs(tmpDirRoot.c_str(), &st) == 0 && (uint64_t) st.f_bavail * st.f_bsize < required)
|
||||
if (sys::statvfs(localStore.config().realStoreDir, &st) == 0
|
||||
&& (uint64_t) st.f_bavail * st.f_bsize < required)
|
||||
{
|
||||
diskFull = true;
|
||||
}
|
||||
if (sys::statvfs(tmpDirRoot, &st) == 0 && (uint64_t) st.f_bavail * st.f_bsize < required) {
|
||||
diskFull = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -491,7 +493,7 @@ try {
|
||||
}
|
||||
/* The TOCTOU between the previous mkdir call and this open call is unavoidable due to
|
||||
* POSIX semantics.*/
|
||||
tmpDirRootFd = AutoCloseFD{open(tmpDirRoot.c_str(), O_RDONLY | O_NOFOLLOW | O_DIRECTORY)};
|
||||
tmpDirRootFd = sys::open(tmpDirRoot, O_RDONLY | O_NOFOLLOW | O_DIRECTORY);
|
||||
if (!tmpDirRootFd) {
|
||||
throw SysError("failed to open the build temporary directory descriptor '%1%'", tmpDirRoot);
|
||||
}
|
||||
@@ -785,11 +787,13 @@ try {
|
||||
std::string slaveName = ptsname(builderOutPTY.get());
|
||||
|
||||
if (buildUser) {
|
||||
if (chmod(slaveName.c_str(), 0600))
|
||||
if (sys::chmod(slaveName, 0600)) {
|
||||
throw SysError("changing mode of pseudoterminal slave");
|
||||
}
|
||||
|
||||
if (chown(slaveName.c_str(), buildUser->getUID(), 0))
|
||||
if (sys::chown(slaveName, buildUser->getUID(), 0)) {
|
||||
throw SysError("changing owner of pseudoterminal slave");
|
||||
}
|
||||
}
|
||||
#if __APPLE__
|
||||
else {
|
||||
@@ -802,9 +806,8 @@ try {
|
||||
throw SysError("unlocking pseudoterminal");
|
||||
|
||||
/* Open the slave side of the pseudoterminal and use it as stderr. */
|
||||
auto openSlave = [&]()
|
||||
{
|
||||
AutoCloseFD builderOut{open(slaveName.c_str(), O_RDWR | O_NOCTTY)};
|
||||
auto openSlave = [&]() {
|
||||
AutoCloseFD builderOut{sys::open(slaveName, O_RDWR | O_NOCTTY)};
|
||||
if (!builderOut)
|
||||
throw SysError("opening pseudoterminal slave");
|
||||
|
||||
@@ -896,7 +899,12 @@ void LocalDerivationGoal::initTmpDir() {
|
||||
std::string fn = ".attr-" + hash.to_string(Base::Base32, false);
|
||||
Path p = tmpDir + "/" + fn;
|
||||
/* TODO(jade): we should have BorrowedFD instead of OwnedFD. */
|
||||
AutoCloseFD passAsFileFd{openat(tmpDirFd.get(), fn.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC | O_EXCL | O_NOFOLLOW, 0666)};
|
||||
AutoCloseFD passAsFileFd{sys::openat(
|
||||
tmpDirFd.get(),
|
||||
fn,
|
||||
O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC | O_EXCL | O_NOFOLLOW,
|
||||
0666
|
||||
)};
|
||||
if (!passAsFileFd) {
|
||||
throw SysError("opening `passAsFile` file in the sandbox '%1%'", p);
|
||||
}
|
||||
@@ -1083,8 +1091,9 @@ try {
|
||||
void LocalDerivationGoal::chownToBuilder(const Path & path)
|
||||
{
|
||||
if (!buildUser) return;
|
||||
if (chown(path.c_str(), buildUser->getUID(), buildUser->getGID()) == -1)
|
||||
if (sys::chown(path, buildUser->getUID(), buildUser->getGID()) == -1) {
|
||||
throw SysError("cannot change ownership of '%1%'", path);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalDerivationGoal::chownToBuilder(const AutoCloseFD & fd)
|
||||
@@ -1173,8 +1182,9 @@ void LocalDerivationGoal::runChild()
|
||||
|
||||
/* Bind-mount chroot directory to itself, to treat it as a
|
||||
different filesystem from /, as needed for pivot_root. */
|
||||
if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
|
||||
if (sys::mount(chrootRootDir, chrootRootDir, "", MS_BIND, 0) == -1) {
|
||||
throw SysError("unable to bind mount '%1%'", chrootRootDir);
|
||||
}
|
||||
|
||||
/* Bind-mount the sandbox's Nix store onto itself so that
|
||||
we can mark it as a "shared" subtree, allowing bind
|
||||
@@ -1186,11 +1196,13 @@ void LocalDerivationGoal::runChild()
|
||||
to fail with EINVAL. Don't know why. */
|
||||
Path chrootStoreDir = chrootRootDir + worker.store.config().storeDir;
|
||||
|
||||
if (mount(chrootStoreDir.c_str(), chrootStoreDir.c_str(), 0, MS_BIND, 0) == -1)
|
||||
if (sys::mount(chrootStoreDir, chrootStoreDir, "", MS_BIND, 0) == -1) {
|
||||
throw SysError("unable to bind mount the Nix store", chrootStoreDir);
|
||||
}
|
||||
|
||||
if (mount(0, chrootStoreDir.c_str(), 0, MS_SHARED, 0) == -1)
|
||||
if (sys::mount("", chrootStoreDir, "", MS_SHARED, 0) == -1) {
|
||||
throw SysError("unable to make '%s' shared", chrootStoreDir);
|
||||
}
|
||||
|
||||
/* Set up a nearly empty /dev, unless the user asked to
|
||||
bind-mount the host /dev. */
|
||||
@@ -1295,21 +1307,31 @@ void LocalDerivationGoal::runChild()
|
||||
|
||||
/* Bind a new instance of procfs on /proc. */
|
||||
createDirs(chrootRootDir + "/proc");
|
||||
if (mount("none", (chrootRootDir + "/proc").c_str(), "proc", 0, 0) == -1)
|
||||
if (sys::mount("none", chrootRootDir + "/proc", "proc", 0, 0) == -1) {
|
||||
throw SysError("mounting /proc");
|
||||
}
|
||||
|
||||
/* Mount sysfs on /sys. */
|
||||
if (buildUser && buildUser->getUIDCount() != 1) {
|
||||
createDirs(chrootRootDir + "/sys");
|
||||
if (mount("none", (chrootRootDir + "/sys").c_str(), "sysfs", 0, 0) == -1)
|
||||
if (sys::mount("none", chrootRootDir + "/sys", "sysfs", 0, 0) == -1) {
|
||||
throw SysError("mounting /sys");
|
||||
}
|
||||
}
|
||||
|
||||
/* Mount a new tmpfs on /dev/shm to ensure that whatever
|
||||
the builder puts in /dev/shm is cleaned up automatically. */
|
||||
if (pathExists("/dev/shm") && mount("none", (chrootRootDir + "/dev/shm").c_str(), "tmpfs", 0,
|
||||
fmt("size=%s", settings.sandboxShmSize).c_str()) == -1)
|
||||
if (pathExists("/dev/shm")
|
||||
&& sys::mount(
|
||||
"none",
|
||||
chrootRootDir + "/dev/shm",
|
||||
"tmpfs",
|
||||
0,
|
||||
fmt("size=%s", settings.sandboxShmSize).c_str()
|
||||
) == -1)
|
||||
{
|
||||
throw SysError("mounting /dev/shm");
|
||||
}
|
||||
|
||||
/* Mount a new devpts on /dev/pts. Note that this
|
||||
requires the kernel to be compiled with
|
||||
@@ -1319,7 +1341,10 @@ void LocalDerivationGoal::runChild()
|
||||
!pathExists(chrootRootDir + "/dev/ptmx")
|
||||
&& !pathsInChroot.count("/dev/pts"))
|
||||
{
|
||||
if (mount("none", (chrootRootDir + "/dev/pts").c_str(), "devpts", 0, "newinstance,mode=0620") == 0)
|
||||
if (sys::mount(
|
||||
"none", (chrootRootDir + "/dev/pts"), "devpts", 0, "newinstance,mode=0620"
|
||||
)
|
||||
== 0)
|
||||
{
|
||||
createSymlink("/dev/pts/ptmx", chrootRootDir + "/dev/ptmx");
|
||||
|
||||
@@ -1364,8 +1389,9 @@ void LocalDerivationGoal::runChild()
|
||||
throw SysError("unsharing cgroup namespace");
|
||||
|
||||
/* Do the chroot(). */
|
||||
if (chdir(chrootRootDir.c_str()) == -1)
|
||||
if (sys::chdir(chrootRootDir) == -1) {
|
||||
throw SysError("cannot change directory to '%1%'", chrootRootDir);
|
||||
}
|
||||
|
||||
if (mkdir("real-root", 0) == -1)
|
||||
throw SysError("cannot create real-root directory");
|
||||
@@ -1424,8 +1450,9 @@ void LocalDerivationGoal::runChild()
|
||||
}
|
||||
#endif
|
||||
|
||||
if (chdir(tmpDirInSandbox.c_str()) == -1)
|
||||
if (sys::chdir(tmpDirInSandbox) == -1) {
|
||||
throw SysError("changing into '%1%'", tmpDir);
|
||||
}
|
||||
|
||||
/* Close all other file descriptors. */
|
||||
closeExtraFDs();
|
||||
@@ -1658,7 +1685,7 @@ void LocalDerivationGoal::runChild()
|
||||
|
||||
void LocalDerivationGoal::execBuilder(std::string builder, Strings args, Strings envStrs)
|
||||
{
|
||||
execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
|
||||
sys::execve(builder, args, envStrs);
|
||||
}
|
||||
|
||||
|
||||
@@ -1738,7 +1765,7 @@ try {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto optSt = maybeLstat(actualPath.c_str());
|
||||
auto optSt = maybeLstat(actualPath);
|
||||
if (!optSt)
|
||||
throw BuildError(
|
||||
"builder for '%s' failed to produce output path for output '%s' at '%s'",
|
||||
@@ -2216,9 +2243,11 @@ try {
|
||||
msg << HintFmt("derivation '%s' may not be deterministic: outputs differ", drvPath.to_string());
|
||||
for (auto [oldPath, newPath]: nondeterministic) {
|
||||
if (newPath) {
|
||||
msg << HintFmt("\n output differs: output '%s' differs from '%s'", oldPath.c_str(), *newPath);
|
||||
msg << HintFmt(
|
||||
"\n output differs: output '%s' differs from '%s'", oldPath, *newPath
|
||||
);
|
||||
} else {
|
||||
msg << HintFmt("\n output '%s' differs", oldPath.c_str());
|
||||
msg << HintFmt("\n output '%s' differs", oldPath);
|
||||
}
|
||||
}
|
||||
throw NotDeterministic(msg.str());
|
||||
@@ -2569,36 +2598,39 @@ try {
|
||||
static void makeVisible(int parentFd, const char * entry, uid_t user, gid_t group)
|
||||
{
|
||||
struct stat st;
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): entry is a dentry name
|
||||
if (fstatat(parentFd, entry, &st, AT_SYMLINK_NOFOLLOW)) {
|
||||
throw SysError("fstat(%s)", guessOrInventPathFromFD(parentFd));
|
||||
}
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
int dirfd = openat(parentFd, entry, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (dirfd < 0) {
|
||||
auto dirfd = sys::openat(parentFd, entry, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (!dirfd) {
|
||||
throw SysError("openat(%s/%s)", guessOrInventPathFromFD(parentFd), entry);
|
||||
}
|
||||
AutoCloseDir dir(fdopendir(dirfd));
|
||||
AutoCloseDir dir(fdopendir(dirfd.get()));
|
||||
if (!dir) {
|
||||
close(dirfd);
|
||||
throw SysError("fdopendir(%s/%s)", guessOrInventPathFromFD(parentFd), entry);
|
||||
}
|
||||
dirfd.release();
|
||||
|
||||
struct dirent * dirent;
|
||||
while (errno = 0, dirent = readdir(dir.get())) {
|
||||
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
makeVisible(dirfd, dirent->d_name, user, group);
|
||||
makeVisible(::dirfd(dir.get()), dirent->d_name, user, group);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore permissions errors for symlinks. linux can't chmod them.
|
||||
// clear special permission bits while we're here, just to be safe
|
||||
if (fchmodat(parentFd, entry, st.st_mode & 0777, AT_SYMLINK_NOFOLLOW) && !S_ISLNK(st.st_mode)) {
|
||||
if (sys::fchmodat(parentFd, entry, st.st_mode & 0777, AT_SYMLINK_NOFOLLOW)
|
||||
&& !S_ISLNK(st.st_mode))
|
||||
{
|
||||
throw SysError("fchmod(%s)", guessOrInventPathFromFD(parentFd));
|
||||
}
|
||||
if (user != uid_t(-1) && group != gid_t(-1)
|
||||
&& fchownat(parentFd, entry, user, group, AT_SYMLINK_NOFOLLOW))
|
||||
&& sys::fchownat(parentFd, entry, user, group, AT_SYMLINK_NOFOLLOW))
|
||||
{
|
||||
throw SysError("fchown(%s)", guessOrInventPathFromFD(parentFd));
|
||||
}
|
||||
@@ -2621,7 +2653,7 @@ void LocalDerivationGoal::finalizeTmpDir(bool force, bool duringDestruction)
|
||||
} catch (SysError & e) {
|
||||
printError("error making '%s' accessible: %s", tmpDir, e.what());
|
||||
}
|
||||
chmod(tmpDirRoot.c_str(), 0755);
|
||||
(void) sys::chmod(tmpDirRoot, 0755);
|
||||
}
|
||||
else if (duringDestruction)
|
||||
deletePathUninterruptible(tmpDirRoot);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libstore/builtins/buildenv.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
#include <sys/stat.h>
|
||||
@@ -40,8 +41,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
|
||||
struct stat srcSt;
|
||||
try {
|
||||
if (stat(srcFile.c_str(), &srcSt) == -1)
|
||||
if (sys::stat(srcFile, &srcSt) == -1) {
|
||||
throw SysError("getting status of '%1%'", srcFile);
|
||||
}
|
||||
} catch (SysError & e) {
|
||||
if (e.errNo == ENOENT || e.errNo == ENOTDIR) {
|
||||
printTaggedWarning("skipping dangling symlink '%s'", dstFile);
|
||||
@@ -66,7 +68,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
continue;
|
||||
|
||||
else if (S_ISDIR(srcSt.st_mode)) {
|
||||
auto dstStOpt = maybeLstat(dstFile.c_str());
|
||||
auto dstStOpt = maybeLstat(dstFile);
|
||||
if (dstStOpt) {
|
||||
auto & dstSt = *dstStOpt;
|
||||
if (S_ISDIR(dstSt.st_mode)) {
|
||||
@@ -76,10 +78,12 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
auto target = canonPath(dstFile, true);
|
||||
if (!S_ISDIR(lstat(target).st_mode))
|
||||
throw Error("collision between '%1%' and non-directory '%2%'", srcFile, target);
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
if (sys::unlink(dstFile) == -1) {
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
if (mkdir(dstFile.c_str(), 0755) == -1)
|
||||
}
|
||||
if (sys::mkdir(dstFile, 0755) == -1) {
|
||||
throw SysError("creating directory '%1%'", dstFile);
|
||||
}
|
||||
createLinks(state, target, dstFile, state.priorities[dstFile]);
|
||||
createLinks(state, srcFile, dstFile, priority);
|
||||
continue;
|
||||
@@ -88,7 +92,7 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
}
|
||||
|
||||
else {
|
||||
auto dstStOpt = maybeLstat(dstFile.c_str());
|
||||
auto dstStOpt = maybeLstat(dstFile);
|
||||
if (dstStOpt) {
|
||||
auto & dstSt = *dstStOpt;
|
||||
if (S_ISLNK(dstSt.st_mode)) {
|
||||
@@ -101,8 +105,9 @@ static void createLinks(State & state, const Path & srcDir, const Path & dstDir,
|
||||
);
|
||||
if (prevPriority < priority)
|
||||
continue;
|
||||
if (unlink(dstFile.c_str()) == -1)
|
||||
if (sys::unlink(dstFile) == -1) {
|
||||
throw SysError("unlinking '%1%'", dstFile);
|
||||
}
|
||||
} else if (S_ISDIR(dstSt.st_mode))
|
||||
throw Error("collision between non-directory '%1%' and directory '%2%'", srcFile, dstFile);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/compression.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
@@ -51,8 +52,9 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData,
|
||||
|
||||
auto executable = drv.env.find("executable");
|
||||
if (executable != drv.env.end() && executable->second == "1") {
|
||||
if (chmod(storePath.c_str(), 0755) == -1)
|
||||
if (sys::chmod(storePath, 0755) == -1) {
|
||||
throw SysError("making '%1%' executable", storePath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libstore/filetransfer.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/namespaces.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
@@ -163,7 +164,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
}
|
||||
for (auto it = options.headers.begin(); it != options.headers.end(); ++it){
|
||||
if (auto next = curl_slist_append(
|
||||
requestHeaders.get(), fmt("%s: %s", it->first, it->second).c_str()
|
||||
requestHeaders.get(), requireCString(fmt("%s: %s", it->first, it->second))
|
||||
);
|
||||
next != nullptr)
|
||||
{
|
||||
@@ -788,7 +789,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
KJ_DEFER(curl_url_cleanup(url));
|
||||
curl_url_set(url, CURLUPART_URL, uri.c_str(), 0);
|
||||
curl_url_set(url, CURLUPART_URL, requireCString(uri), 0);
|
||||
char * path = nullptr;
|
||||
curl_url_get(url, CURLUPART_PATH, &path, 0);
|
||||
auto decoded = kj::decodeUriComponent(kj::arrayPtr(path, path + strlen(path)));
|
||||
@@ -796,7 +797,7 @@ struct curlFileTransfer : public FileTransfer
|
||||
Path fsPath(decoded.cStr(), decoded.size());
|
||||
FileTransferResult metadata{.effectiveUri = std::string("file://") + path};
|
||||
struct stat st;
|
||||
AutoCloseFD fd(open(fsPath.c_str(), O_RDONLY));
|
||||
AutoCloseFD fd(sys::open(fsPath, O_RDONLY));
|
||||
if (!fd || fstat(fd.get(), &st) != 0) {
|
||||
throw FileTransferError(
|
||||
NotFound, std::nullopt, "%s: file not found (%s)", fsPath, strerror(errno)
|
||||
|
||||
+18
-14
@@ -2,6 +2,7 @@
|
||||
#include "lix/libstore/local-store.hh"
|
||||
#include "lix/libstore/pathlocks.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/processes.hh"
|
||||
@@ -42,7 +43,7 @@ static void makeSymlink(const Path & link, const Path & target)
|
||||
|
||||
/* Create the new symlink. */
|
||||
Path tempLink = makeTempPath(link);
|
||||
unlink(tempLink.c_str()); // just in case; ignore errors
|
||||
(void) sys::unlink(tempLink); // just in case; ignore errors
|
||||
createSymlink(target, tempLink);
|
||||
|
||||
/* Atomically replace the old one. */
|
||||
@@ -98,21 +99,21 @@ void LocalStore::createTempRootsFile()
|
||||
/* Create the temporary roots file for this process. */
|
||||
while (true) {
|
||||
auto tmp = makeTempPath(fnTempRoots, ".tmp");
|
||||
AutoCloseFD fd{open(tmp.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600)};
|
||||
AutoCloseFD fd{sys::open(tmp, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600)};
|
||||
if (!fd && errno != EEXIST) {
|
||||
throw SysError("opening lock file '%1%'", tmp);
|
||||
}
|
||||
// if we can't lock it then GC must've found and deleted it, so we try again.
|
||||
// if we *can* lock it GC may have still deleted it, and rename will tell us.
|
||||
if (!tryLockFile(fd.get(), ltWrite)) {
|
||||
unlink(tmp.c_str()); // just to be sure it's gone
|
||||
(void) sys::unlink(tmp); // just to be sure it's gone
|
||||
continue;
|
||||
} else if (auto fdTempRoots(_fdTempRoots.lock()); *fdTempRoots) {
|
||||
if (unlink(tmp.c_str()) == -1) {
|
||||
if (sys::unlink(tmp) == -1) {
|
||||
throw SysError("deleting lock file '%1%'", tmp);
|
||||
}
|
||||
break;
|
||||
} else if (rename(tmp.c_str(), fnTempRoots.c_str()) == -1) {
|
||||
} else if (sys::rename(tmp, fnTempRoots) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
throw SysError("moving lock file '%1%'", tmp);
|
||||
}
|
||||
@@ -224,7 +225,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
|
||||
pid_t pid = std::stoi(i.name);
|
||||
|
||||
debug("reading temporary root file '%1%'", path);
|
||||
AutoCloseFD fd(open(path.c_str(), O_CLOEXEC | O_RDWR, 0666));
|
||||
AutoCloseFD fd(sys::open(path, O_CLOEXEC | O_RDWR, 0666));
|
||||
if (!fd) {
|
||||
/* It's okay if the file has disappeared. */
|
||||
if (errno == ENOENT) continue;
|
||||
@@ -236,7 +237,7 @@ void LocalStore::findTempRoots(Roots & tempRoots, bool censor)
|
||||
we don't care about its temporary roots. */
|
||||
if (tryLockFile(fd.get(), ltWrite)) {
|
||||
printInfo("removing stale temporary roots file '%1%'", path);
|
||||
unlink(path.c_str());
|
||||
(void) sys::unlink(path);
|
||||
writeFull(fd.get(), "d");
|
||||
continue;
|
||||
}
|
||||
@@ -296,7 +297,7 @@ try {
|
||||
if (!pathExists(target)) {
|
||||
if (isInDir(path, config().stateDir + "/" + gcRootsDir + "/auto")) {
|
||||
printInfo("removing stale link from '%1%' to '%2%'", path, target);
|
||||
unlink(path.c_str());
|
||||
(void) sys::unlink(path);
|
||||
}
|
||||
} else {
|
||||
struct stat st2 = lstat(target);
|
||||
@@ -673,7 +674,7 @@ try {
|
||||
by another process. We need to be sure that we can acquire an
|
||||
exclusive lock before deleting them. */
|
||||
if (baseName.find("tmp-", 0) == 0) {
|
||||
AutoCloseFD tmpDirFd{open(realPath.c_str(), O_RDONLY | O_DIRECTORY)};
|
||||
AutoCloseFD tmpDirFd{sys::open(realPath, O_RDONLY | O_DIRECTORY)};
|
||||
if (tmpDirFd.get() == -1 || !tryLockFile(tmpDirFd.get(), ltWrite)) {
|
||||
debug("skipping locked tempdir '%s'", realPath);
|
||||
return;
|
||||
@@ -847,7 +848,7 @@ try {
|
||||
printInfo("determining live/dead paths...");
|
||||
|
||||
try {
|
||||
AutoCloseDir dir(opendir(config().realStoreDir.get().c_str()));
|
||||
AutoCloseDir dir(sys::opendir(config().realStoreDir));
|
||||
if (!dir) throw SysError("opening directory '%1%'", config().realStoreDir);
|
||||
|
||||
/* Read the store and delete all paths that are invalid or
|
||||
@@ -891,7 +892,7 @@ try {
|
||||
if (options.action == GCOptions::gcDeleteDead || deleteSpecific) {
|
||||
printInfo("deleting unused links...");
|
||||
|
||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||
AutoCloseDir dir(sys::opendir(linksDir));
|
||||
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||
|
||||
int64_t actualSize = 0, unsharedSize = 0;
|
||||
@@ -913,16 +914,18 @@ try {
|
||||
|
||||
printMsg(lvlTalkative, "deleting unused link '%1%'", path);
|
||||
|
||||
if (unlink(path.c_str()) == -1)
|
||||
if (sys::unlink(path) == -1) {
|
||||
throw SysError("deleting '%1%'", path);
|
||||
}
|
||||
|
||||
/* Do not accound for deleted file here. Rely on deletePath()
|
||||
accounting. */
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(linksDir.c_str(), &st) == -1)
|
||||
if (sys::stat(linksDir, &st) == -1) {
|
||||
throw SysError("statting '%1%'", linksDir);
|
||||
}
|
||||
int64_t overhead = st.st_blocks * 512ULL;
|
||||
|
||||
printInfo("note: currently hard linking saves %.2f MiB",
|
||||
@@ -962,8 +965,9 @@ try {
|
||||
return std::stoll(readFile(*fakeFreeSpaceFile));
|
||||
|
||||
struct statvfs st;
|
||||
if (statvfs(config().realStoreDir.get().c_str(), &st))
|
||||
if (sys::statvfs(config().realStoreDir, &st)) {
|
||||
throw SysError("getting filesystem info about '%s'", config().realStoreDir);
|
||||
}
|
||||
|
||||
return (uint64_t) st.f_bavail * st.f_frsize;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "lix/libutil/compute-levels.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
@@ -403,8 +404,7 @@ void initPlugins()
|
||||
for (const auto & file : pluginFiles) {
|
||||
/* handle is purposefully leaked as there may be state in the
|
||||
DSO needed by the action of the plugin. */
|
||||
void *handle =
|
||||
dlopen(file.c_str(), RTLD_LAZY | RTLD_LOCAL);
|
||||
void * handle = dlopen(requireCString(file), RTLD_LAZY | RTLD_LOCAL);
|
||||
if (!handle) {
|
||||
printTaggedWarning(
|
||||
"could not dynamically open plugin file '%s', skipping it: %s", file, dlerror()
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/local-fs-store.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/compression.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
@@ -27,7 +28,7 @@ try {
|
||||
auto realPath = TRY_AWAIT(toRealPath(path));
|
||||
|
||||
struct stat st;
|
||||
if (lstat(realPath.c_str(), &st)) {
|
||||
if (sys::lstat(realPath, &st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR) {
|
||||
co_return {Type::tMissing, 0, false};
|
||||
}
|
||||
|
||||
+39
-29
@@ -8,6 +8,7 @@
|
||||
#include "lix/libstore/nar-info.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/references.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/serialise.hh"
|
||||
@@ -125,8 +126,9 @@ LocalStore::LocalStore(LocalStoreConfig config)
|
||||
for (auto & perUserDir : {profilesDir + "/per-user", gcRootsDir + "/per-user"}) {
|
||||
createDirs(perUserDir);
|
||||
if (!config_.readOnly) {
|
||||
if (chmod(perUserDir.c_str(), 0755) == -1)
|
||||
if (sys::chmod(perUserDir, 0755) == -1) {
|
||||
throw SysError("could not set permissions on '%s' to 755", perUserDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,19 +137,22 @@ LocalStore::LocalStore(LocalStoreConfig config)
|
||||
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
||||
mode_t perm = 01775;
|
||||
|
||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||
struct group * gr = sys::getgrnam(settings.buildUsersGroup);
|
||||
if (!gr)
|
||||
printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
|
||||
else {
|
||||
struct stat st;
|
||||
if (stat(config_.realStoreDir.get().c_str(), &st))
|
||||
if (sys::stat(config_.realStoreDir, &st)) {
|
||||
throw SysError("getting attributes of path '%1%'", config_.realStoreDir);
|
||||
}
|
||||
|
||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||
if (chown(config_.realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
|
||||
if (sys::chown(config_.realStoreDir, 0, gr->gr_gid) == -1) {
|
||||
throw SysError("changing ownership of path '%1%'", config_.realStoreDir);
|
||||
if (chmod(config_.realStoreDir.get().c_str(), perm) == -1)
|
||||
}
|
||||
if (sys::chmod(config_.realStoreDir, perm) == -1) {
|
||||
throw SysError("changing permissions on path '%1%'", config_.realStoreDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,10 +178,8 @@ LocalStore::LocalStore(LocalStoreConfig config)
|
||||
before doing a garbage collection. */
|
||||
try {
|
||||
struct stat st;
|
||||
if (stat(reservedSpacePath.c_str(), &st) == -1 ||
|
||||
st.st_size != settings.reservedSize)
|
||||
{
|
||||
AutoCloseFD fd{open(reservedSpacePath.c_str(), O_WRONLY | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (sys::stat(reservedSpacePath, &st) == -1 || st.st_size != settings.reservedSize) {
|
||||
AutoCloseFD fd{sys::open(reservedSpacePath, O_WRONLY | O_CREAT | O_CLOEXEC, 0600)};
|
||||
int res = -1;
|
||||
#if HAVE_POSIX_FALLOCATE
|
||||
res = posix_fallocate(fd.get(), 0, settings.reservedSize);
|
||||
@@ -193,7 +196,7 @@ LocalStore::LocalStore(LocalStoreConfig config)
|
||||
schema upgrade is in progress. */
|
||||
if (!config_.readOnly) {
|
||||
Path globalLockPath = dbDir + "/big-lock";
|
||||
globalLock = openLockFile(globalLockPath.c_str(), true);
|
||||
globalLock = openLockFile(globalLockPath, true);
|
||||
}
|
||||
|
||||
if (!config_.readOnly && !tryLockFile(globalLock.get(), ltRead)) {
|
||||
@@ -328,7 +331,7 @@ LocalStore::LocalStore(std::string scheme, std::string path, LocalStoreConfig co
|
||||
AutoCloseFD LocalStore::openGCLock()
|
||||
{
|
||||
Path fnGCLock = config_.stateDir + "/gc.lock";
|
||||
AutoCloseFD fdGCLock{open(fnGCLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
AutoCloseFD fdGCLock{sys::open(fnGCLock, O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fdGCLock)
|
||||
throw SysError("opening global GC lock '%1%'", fnGCLock);
|
||||
return fdGCLock;
|
||||
@@ -354,7 +357,7 @@ LocalStore::~LocalStore()
|
||||
auto fdTempRoots(_fdTempRoots.lock());
|
||||
if (*fdTempRoots) {
|
||||
fdTempRoots->reset();
|
||||
unlink(fnTempRoots.c_str());
|
||||
(void) sys::unlink(fnTempRoots);
|
||||
}
|
||||
} catch (...) {
|
||||
ignoreExceptionInDestructor();
|
||||
@@ -377,8 +380,9 @@ void LocalStore::openDB(DBState & state, bool create)
|
||||
throw Error("cannot create database while in read-only mode");
|
||||
}
|
||||
|
||||
if (access(dbDir.c_str(), R_OK | (config_.readOnly ? 0 : W_OK)))
|
||||
if (sys::access(dbDir, R_OK | (config_.readOnly ? 0 : W_OK))) {
|
||||
throw SysError("Nix database directory '%1%' is not writable", dbDir);
|
||||
}
|
||||
|
||||
/* Open the Nix database. */
|
||||
std::string dbPath = dbDir + "/db.sqlite";
|
||||
@@ -445,12 +449,14 @@ void LocalStore::makeStoreWritable()
|
||||
if (getuid() != 0) return;
|
||||
/* Check if /nix/store is on a read-only mount. */
|
||||
struct statvfs stat;
|
||||
if (statvfs(config_.realStoreDir.get().c_str(), &stat) != 0)
|
||||
if (sys::statvfs(config_.realStoreDir, &stat) != 0) {
|
||||
throw SysError("getting info about the Nix store mount point");
|
||||
}
|
||||
|
||||
if (stat.f_flag & ST_RDONLY) {
|
||||
if (mount(0, config_.realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
if (sys::mount("", config_.realStoreDir, "none", MS_REMOUNT | MS_BIND, 0) == -1) {
|
||||
throw SysError("remounting %1% writable", config_.realStoreDir);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -470,8 +476,9 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
mode = (st.st_mode & S_IFMT)
|
||||
| 0444
|
||||
| (st.st_mode & S_IXUSR ? 0111 : 0);
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
if (sys::chmod(path, mode) == -1) {
|
||||
throw SysError("changing mode of '%1%' to %2$o", path, mode);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -483,13 +490,13 @@ static void canonicaliseTimestampAndPermissions(const Path & path, const struct
|
||||
times[1].tv_sec = mtimeStore;
|
||||
times[1].tv_usec = 0;
|
||||
#if HAVE_LUTIMES
|
||||
if (lutimes(path.c_str(), times) == -1)
|
||||
if (errno != ENOSYS ||
|
||||
(!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1))
|
||||
if (sys::lutimes(path, times) == -1) {
|
||||
if (errno != ENOSYS || (!S_ISLNK(st.st_mode) && sys::utimes(path, times) == -1))
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) && utimes(path.c_str(), times) == -1)
|
||||
#endif
|
||||
throw SysError("changing modification time of '%1%'", path);
|
||||
throw SysError("changing modification time of '%1%'", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -539,7 +546,7 @@ static void canonicalisePathMetaData_(
|
||||
|
||||
#if __linux__
|
||||
/* Remove extended attributes / ACLs. */
|
||||
ssize_t eaSize = llistxattr(path.c_str(), nullptr, 0);
|
||||
ssize_t eaSize = sys::llistxattr(path, nullptr, 0);
|
||||
|
||||
if (eaSize < 0) {
|
||||
if (errno != ENOTSUP && errno != ENODATA)
|
||||
@@ -547,21 +554,23 @@ static void canonicalisePathMetaData_(
|
||||
} else if (eaSize > 0) {
|
||||
std::vector<char> eaBuf(eaSize);
|
||||
|
||||
if ((eaSize = llistxattr(path.c_str(), eaBuf.data(), eaBuf.size())) < 0)
|
||||
if ((eaSize = sys::llistxattr(path, eaBuf.data(), eaBuf.size())) < 0) {
|
||||
throw SysError("querying extended attributes of '%s'", path);
|
||||
}
|
||||
|
||||
bool resetMode = false;
|
||||
if ((S_ISREG(st.st_mode) || S_ISDIR(st.st_mode)) && !(st.st_mode & S_IWUSR)) {
|
||||
resetMode = true;
|
||||
chmod(path.c_str(), st.st_mode | S_IWUSR);
|
||||
(void) sys::chmod(path, st.st_mode | S_IWUSR);
|
||||
}
|
||||
for (auto & eaName: tokenizeString<Strings>(std::string(eaBuf.data(), eaSize), std::string("\000", 1))) {
|
||||
if (settings.ignoredAcls.get().count(eaName)) continue;
|
||||
if (lremovexattr(path.c_str(), eaName.c_str()) == -1)
|
||||
if (sys::lremovexattr(path, eaName) == -1) {
|
||||
throw SysError("removing extended attribute '%s' from '%s'", eaName, path);
|
||||
}
|
||||
}
|
||||
if (resetMode) {
|
||||
chmod(path.c_str(), st.st_mode);
|
||||
(void) sys::chmod(path, st.st_mode);
|
||||
resetMode = false;
|
||||
}
|
||||
}
|
||||
@@ -580,7 +589,7 @@ static void canonicalisePathMetaData_(
|
||||
users group); we check for this case below. */
|
||||
if (st.st_uid != geteuid()) {
|
||||
#if HAVE_LCHOWN
|
||||
if (lchown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
if (sys::lchown(path, geteuid(), getegid()) == -1)
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), getegid()) == -1)
|
||||
@@ -1407,7 +1416,7 @@ std::pair<Path, AutoCloseFD> LocalStore::createTempDirInStore()
|
||||
the GC between createTempDir() and when we acquire a lock on it.
|
||||
We'll repeat until 'tmpDir' exists and we've locked it. */
|
||||
tmpDirFn = createTempDir(config_.realStoreDir, "tmp");
|
||||
tmpDirFd = AutoCloseFD{open(tmpDirFn.c_str(), O_RDONLY | O_DIRECTORY)};
|
||||
tmpDirFd = sys::open(tmpDirFn, O_RDONLY | O_DIRECTORY);
|
||||
if (tmpDirFd.get() < 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -1501,10 +1510,11 @@ try {
|
||||
printError("link '%s' was modified! expected hash '%s', got '%s'",
|
||||
linkPath, link.name, hash);
|
||||
if (repair) {
|
||||
if (unlink(linkPath.c_str()) == 0)
|
||||
if (sys::unlink(linkPath) == 0) {
|
||||
printInfo("removed link '%s'", linkPath);
|
||||
else
|
||||
} else {
|
||||
throw SysError("removing corrupt link '%s'", linkPath);
|
||||
}
|
||||
} else {
|
||||
errors = true;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libstore/lock.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
@@ -11,14 +12,14 @@ namespace nix {
|
||||
|
||||
#if __linux__
|
||||
|
||||
static std::vector<gid_t> get_group_list(const char *username, gid_t group_id)
|
||||
static std::vector<gid_t> get_group_list(const std::string & username, gid_t group_id)
|
||||
{
|
||||
std::vector<gid_t> gids;
|
||||
gids.resize(32); // Initial guess
|
||||
|
||||
auto getgroupl_failed {[&] {
|
||||
int ngroups = gids.size();
|
||||
int err = getgrouplist(username, group_id, gids.data(), &ngroups);
|
||||
int err = getgrouplist(requireCString(username), group_id, gids.data(), &ngroups);
|
||||
gids.resize(ngroups);
|
||||
return err == -1;
|
||||
}};
|
||||
@@ -53,7 +54,7 @@ struct SimpleUserLock : UserLock
|
||||
createDirs(settings.nixStateDir + "/userpool");
|
||||
|
||||
/* Get the members of the build-users-group. */
|
||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||
struct group * gr = sys::getgrnam(settings.buildUsersGroup);
|
||||
if (!gr)
|
||||
throw Error("the group '%s' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
|
||||
|
||||
@@ -72,7 +73,7 @@ struct SimpleUserLock : UserLock
|
||||
for (auto & i : users) {
|
||||
debug("trying user '%s'", i);
|
||||
|
||||
struct passwd * pw = getpwnam(i.c_str());
|
||||
auto pw = sys::getpwnam(i);
|
||||
if (!pw) {
|
||||
#ifdef __APPLE__
|
||||
#define APPLE_HINT "\n\nhint: this may be caused by an update to macOS Sequoia breaking existing Lix installations.\n" \
|
||||
@@ -86,7 +87,7 @@ struct SimpleUserLock : UserLock
|
||||
|
||||
auto fnUserLock = fmt("%s/userpool/%s", settings.nixStateDir,pw->pw_uid);
|
||||
|
||||
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
AutoCloseFD fd{sys::open(fnUserLock, O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fd)
|
||||
throw SysError("opening user lock '%s'", fnUserLock);
|
||||
|
||||
@@ -158,7 +159,7 @@ struct AutoUserLock : UserLock
|
||||
|
||||
auto fnUserLock = fmt("%s/userpool2/slot-%d", settings.nixStateDir, i);
|
||||
|
||||
AutoCloseFD fd{open(fnUserLock.c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
AutoCloseFD fd{sys::open(fnUserLock, O_RDWR | O_CREAT | O_CLOEXEC, 0600)};
|
||||
if (!fd)
|
||||
throw SysError("opening user lock '%s'", fnUserLock);
|
||||
|
||||
@@ -176,7 +177,7 @@ struct AutoUserLock : UserLock
|
||||
if (useUserNamespace)
|
||||
lock->firstGid = firstUid;
|
||||
else {
|
||||
struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str());
|
||||
struct group * gr = sys::getgrnam(settings.buildUsersGroup);
|
||||
if (!gr)
|
||||
throw Error("the group '%s' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
|
||||
lock->firstGid = gr->gr_gid;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libstore/local-store.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/signals.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
@@ -21,8 +22,9 @@ namespace nix {
|
||||
static void makeWritable(const Path & path)
|
||||
{
|
||||
auto st = lstat(path);
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
if (sys::chmod(path, st.st_mode | S_IWUSR) == -1) {
|
||||
throw SysError("changing writability of '%1%'", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +49,7 @@ LocalStore::InodeHash LocalStore::loadInodeHash()
|
||||
debug("loading hash inodes in memory");
|
||||
InodeHash inodeHash;
|
||||
|
||||
AutoCloseDir dir(opendir(linksDir.c_str()));
|
||||
AutoCloseDir dir(sys::opendir(linksDir));
|
||||
if (!dir) throw SysError("opening directory '%1%'", linksDir);
|
||||
|
||||
struct dirent * dirent;
|
||||
@@ -68,7 +70,7 @@ Strings LocalStore::readDirectoryIgnoringInodes(const Path & path, const InodeHa
|
||||
{
|
||||
Strings names;
|
||||
|
||||
AutoCloseDir dir(opendir(path.c_str()));
|
||||
AutoCloseDir dir(sys::opendir(path));
|
||||
if (!dir) throw SysError("opening directory '%1%'", path);
|
||||
|
||||
struct dirent * dirent;
|
||||
@@ -167,15 +169,16 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
"There may be more corrupted paths."
|
||||
"\nYou should run `nix-store --verify --check-contents --repair` to fix them all"
|
||||
);
|
||||
if (unlink(linkPath.c_str()) == -1 && errno != ENOENT)
|
||||
if (sys::unlink(linkPath) == -1 && errno != ENOENT) {
|
||||
throw SysError("cannot unlink '%1%'", linkPath);
|
||||
}
|
||||
stLinkOpt.reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (!stLinkOpt) {
|
||||
/* Nope, create a hard link in the links directory. */
|
||||
if (link(path.c_str(), linkPath.c_str()) == 0) {
|
||||
if (sys::link(path, linkPath) == 0) {
|
||||
inodeHash.insert(st.st_ino);
|
||||
return;
|
||||
}
|
||||
@@ -221,9 +224,9 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : "");
|
||||
|
||||
Path tempLink = makeTempPath(config().realStoreDir, "/.tmp-link");
|
||||
unlink(tempLink.c_str()); // just in case; ignore errors
|
||||
(void) sys::unlink(tempLink); // just in case; ignore errors
|
||||
|
||||
if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
|
||||
if (sys::link(linkPath, tempLink) == -1) {
|
||||
if (errno == EMLINK) {
|
||||
/* Too many links to the same file (>= 32000 on most file
|
||||
systems). This is likely to happen with empty files.
|
||||
@@ -239,8 +242,9 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats,
|
||||
try {
|
||||
renameFile(tempLink, path);
|
||||
} catch (SysError & e) {
|
||||
if (unlink(tempLink.c_str()) == -1)
|
||||
if (sys::unlink(tempLink) == -1) {
|
||||
printError("unable to unlink '%1%': %2%", tempLink, strerror(errno));
|
||||
}
|
||||
if (errno == EMLINK) {
|
||||
/* Some filesystems generate too many links on the rename,
|
||||
rather than on the original link. (Probably it
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lix/libstore/pathlocks.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/signals.hh"
|
||||
@@ -20,7 +21,7 @@ namespace nix {
|
||||
|
||||
AutoCloseFD openLockFile(const Path & path, bool create)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600)};
|
||||
AutoCloseFD fd{sys::open(path, O_CLOEXEC | O_RDWR | (create ? O_CREAT : 0), 0600)};
|
||||
if (!fd && (create || errno != ENOENT))
|
||||
throw SysError("opening lock file '%1%'", path);
|
||||
|
||||
@@ -235,7 +236,7 @@ void PathLock::unlock()
|
||||
// this file it will figure out that the file is stale once it calls stat()
|
||||
// and inspects the link count. if unlink fails we merely leave around some
|
||||
// stale lock file paths that can be reused or cleaned up by other threads.
|
||||
unlink(path.c_str());
|
||||
(void) sys::unlink(path);
|
||||
// clobber file contents for compatibility wither other nix implementations
|
||||
writeFull(fd.get(), "d");
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libstore/build/worker.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/cgroup.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
@@ -46,7 +47,7 @@ static void readProcLink(const std::string & file, UncheckedRoots & roots)
|
||||
{
|
||||
constexpr auto bufsiz = PATH_MAX;
|
||||
char buf[bufsiz];
|
||||
auto res = readlink(file.c_str(), buf, bufsiz);
|
||||
auto res = sys::readlink(file, buf, bufsiz);
|
||||
if (res == -1) {
|
||||
if (errno == ENOENT || errno == EACCES || errno == ESRCH) {
|
||||
return;
|
||||
@@ -96,7 +97,7 @@ try {
|
||||
readProcLink(fmt("/proc/%s/cwd", ent->d_name), unchecked);
|
||||
|
||||
auto fdStr = fmt("/proc/%s/fd", ent->d_name);
|
||||
auto fdDir = AutoCloseDir(opendir(fdStr.c_str()));
|
||||
auto fdDir = sys::opendir(fdStr);
|
||||
if (!fdDir) {
|
||||
if (errno == ENOENT || errno == EACCES) {
|
||||
continue;
|
||||
@@ -787,11 +788,19 @@ void LinuxLocalDerivationGoal::prepareSandbox()
|
||||
printMsg(lvlChatty, "setting up chroot environment in '%1%'", chrootRootDir);
|
||||
|
||||
// FIXME: make this 0700
|
||||
if (mkdir(chrootRootDir.c_str(), buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1)
|
||||
if (sys::mkdir(chrootRootDir, buildUser && buildUser->getUIDCount() != 1 ? 0755 : 0750) == -1) {
|
||||
throw SysError("cannot create '%1%'", chrootRootDir);
|
||||
}
|
||||
|
||||
if (buildUser && chown(chrootRootDir.c_str(), buildUser->getUIDCount() != 1 ? buildUser->getUID() : 0, buildUser->getGID()) == -1)
|
||||
if (buildUser
|
||||
&& sys::chown(
|
||||
chrootRootDir,
|
||||
buildUser->getUIDCount() != 1 ? buildUser->getUID() : 0,
|
||||
buildUser->getGID()
|
||||
) == -1)
|
||||
{
|
||||
throw SysError("cannot change ownership of '%1%'", chrootRootDir);
|
||||
}
|
||||
|
||||
/* Create a writable /tmp in the chroot. Many builders need
|
||||
this. (Of course they should really respect $TMPDIR
|
||||
@@ -827,8 +836,9 @@ void LinuxLocalDerivationGoal::prepareSandbox()
|
||||
createDirs(chrootStoreDir);
|
||||
chmodPath(chrootStoreDir, 01775);
|
||||
|
||||
if (buildUser && chown(chrootStoreDir.c_str(), 0, buildUser->getGID()) == -1)
|
||||
if (buildUser && sys::chown(chrootStoreDir, 0, buildUser->getGID()) == -1) {
|
||||
throw SysError("cannot change ownership of '%1%'", chrootStoreDir);
|
||||
}
|
||||
|
||||
for (auto & i : inputPaths) {
|
||||
auto p = worker.store.printStorePath(i);
|
||||
@@ -1077,7 +1087,7 @@ Pid LinuxLocalDerivationGoal::startChild(std::function<void()> openSlave)
|
||||
// clang-format on
|
||||
};
|
||||
|
||||
AutoCloseFD netns(open(fmt("/proc/%i/ns/net", pid.get()).c_str(), O_RDONLY | O_CLOEXEC));
|
||||
AutoCloseFD netns(sys::open(fmt("/proc/%i/ns/net", pid.get()), O_RDONLY | O_CLOEXEC));
|
||||
if (!netns) {
|
||||
throw SysError("failed to open netns");
|
||||
}
|
||||
@@ -1085,7 +1095,7 @@ Pid LinuxLocalDerivationGoal::startChild(std::function<void()> openSlave)
|
||||
AutoCloseFD userns;
|
||||
if (usingUserNamespace) {
|
||||
userns =
|
||||
AutoCloseFD(open(fmt("/proc/%i/ns/user", pid.get()).c_str(), O_RDONLY | O_CLOEXEC));
|
||||
AutoCloseFD(sys::open(fmt("/proc/%i/ns/user", pid.get()), O_RDONLY | O_CLOEXEC));
|
||||
if (!userns) {
|
||||
throw SysError("failed to open userns");
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "lix/libstore/profiles.hh"
|
||||
#include "lix/libstore/local-fs-store.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
@@ -114,8 +115,9 @@ try {
|
||||
|
||||
static void removeFile(const Path & path)
|
||||
{
|
||||
if (remove(path.c_str()) == -1)
|
||||
if (sys::remove(path) == -1) {
|
||||
throw SysError("cannot unlink '%1%'", path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lix/libstore/remote-fs-accessor.hh"
|
||||
#include "lix/libstore/nar-accessor.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -72,10 +73,9 @@ try {
|
||||
try {
|
||||
listing = nix::readFile(makeCacheFile(storePath.hashPart(), "ls"));
|
||||
|
||||
auto narAccessor = makeLazyNarAccessor(listing,
|
||||
[cacheFile](uint64_t offset, uint64_t length) {
|
||||
|
||||
AutoCloseFD fd{open(cacheFile.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
auto narAccessor =
|
||||
makeLazyNarAccessor(listing, [cacheFile](uint64_t offset, uint64_t length) {
|
||||
AutoCloseFD fd{sys::open(cacheFile, O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd)
|
||||
throw SysError("opening NAR cache file '%s'", cacheFile);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/charptr-cast.hh"
|
||||
#include "lix/libstore/sqlite.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
@@ -71,7 +72,8 @@ SQLite::SQLite(const Path & path, SQLiteOpenMode mode)
|
||||
if (mode == SQLiteOpenMode::Normal) flags |= SQLITE_OPEN_CREATE;
|
||||
auto uri = "file:" + percentEncode(path) + "?immutable=" + (immutable ? "1" : "0");
|
||||
sqlite3 * db;
|
||||
int ret = sqlite3_open_v2(uri.c_str(), &db, SQLITE_OPEN_URI | flags, vfs);
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): vfs is safe
|
||||
int ret = sqlite3_open_v2(requireCString(uri), &db, SQLITE_OPEN_URI | flags, vfs);
|
||||
if (ret != SQLITE_OK) {
|
||||
const char * err = sqlite3_errstr(ret);
|
||||
throw Error("cannot open SQLite database '%s': %s", path, err);
|
||||
@@ -108,8 +110,9 @@ void SQLite::isCache()
|
||||
void SQLite::exec(const std::string & stmt, NeverAsync)
|
||||
{
|
||||
retrySQLite([&]() {
|
||||
if (sqlite3_exec(db.get(), stmt.c_str(), 0, 0, 0) != SQLITE_OK)
|
||||
if (sqlite3_exec(db.get(), requireCString(stmt), 0, 0, 0) != SQLITE_OK) {
|
||||
SQLiteError::throw_(db.get(), "executing SQLite statement '%s'", stmt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -145,8 +148,9 @@ SQLiteStmt::SQLiteStmt(sqlite3 * db, const std::string & sql)
|
||||
{
|
||||
checkInterrupt();
|
||||
sqlite3_stmt * stmt;
|
||||
if (sqlite3_prepare_v2(db, sql.c_str(), -1, &stmt, 0) != SQLITE_OK)
|
||||
if (sqlite3_prepare_v2(db, requireCString(sql), -1, &stmt, 0) != SQLITE_OK) {
|
||||
SQLiteError::throw_(db, "creating statement '%s'", sql);
|
||||
}
|
||||
this->stmt = {stmt, {this}};
|
||||
this->db = db;
|
||||
this->sql = sql;
|
||||
@@ -179,6 +183,7 @@ SQLiteStmt::Use::~Use()
|
||||
SQLiteStmt::Use & SQLiteStmt::Use::operator () (std::string_view value, bool notNull)
|
||||
{
|
||||
if (notNull) {
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): nul bytes are allowed, hence the length arg
|
||||
if (sqlite3_bind_text(
|
||||
stmt.stmt.get(), curArg++, value.data(), value.length(), SQLITE_TRANSIENT
|
||||
)
|
||||
@@ -272,6 +277,7 @@ SQLiteTxn::SQLiteTxn(sqlite3 * db, SQLiteTxnType type)
|
||||
sql = "begin exclusive;";
|
||||
break;
|
||||
}
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): only immediate strings here
|
||||
if (sqlite3_exec(db, sql, 0, 0, 0) != SQLITE_OK)
|
||||
SQLiteError::throw_(db, "starting transaction");
|
||||
this->db.reset(db);
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libstore/ssh.hh"
|
||||
@@ -92,7 +93,7 @@ std::unique_ptr<SSH::Connection> SSH::startCommand(const std::string & command)
|
||||
throw SysError("duping over stderr");
|
||||
}
|
||||
|
||||
execvp(args.begin()->c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execvp(*args.begin(), args);
|
||||
|
||||
// could not exec ssh/bash
|
||||
throw SysError("unable to execute '%s'", args.front());
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/hash.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -1366,7 +1367,7 @@ openFromNonUri(const std::string & uri, const StoreConfig::Params & params, Allo
|
||||
auto stateDir = getOr(params, "state", settings.nixStateDir);
|
||||
if (allowDaemon == AllowDaemon::Allow && pathExists(settings.nixDaemonSocketFile)) {
|
||||
return make_ref<UDSRemoteStore>(params);
|
||||
} else if (access(stateDir.c_str(), R_OK | W_OK) == 0) {
|
||||
} else if (sys::access(stateDir, R_OK | W_OK) == 0) {
|
||||
return LocalStore::makeLocalStore(params);
|
||||
}
|
||||
#if __linux__
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "lix/libstore/temporary-dir.hh"
|
||||
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
|
||||
@@ -15,7 +16,7 @@ std::pair<AutoCloseFD, Path> createTempFile(const Path & prefix)
|
||||
{
|
||||
Path tmpl(defaultTempDir() + "/" + prefix + ".XXXXXX");
|
||||
// FIXME: use O_TMPFILE.
|
||||
AutoCloseFD fd(mkstemp(tmpl.data()));
|
||||
AutoCloseFD fd(sys::mkstemp(tmpl));
|
||||
if (!fd)
|
||||
throw SysError("creating temporary file '%s'", tmpl);
|
||||
closeOnExec(fd.get());
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/async-io.hh"
|
||||
#include "lix/libutil/box_ptr.hh"
|
||||
#include "lix/libutil/config.hh"
|
||||
@@ -40,7 +41,7 @@ PathFilter defaultPathFilter = [](const Path &) { return true; };
|
||||
|
||||
static WireFormatGenerator dumpContents(Path path, off_t size)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
AutoCloseFD fd{sys::open(path, O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd) throw SysError("opening file '%1%'", path);
|
||||
|
||||
std::vector<char> buf(65536);
|
||||
@@ -316,6 +317,7 @@ struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator() (const std::string & a, const std::string & b) const
|
||||
{
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): valid pathnames never contain nuls
|
||||
return strcasecmp(a.c_str(), b.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
@@ -1032,8 +1034,9 @@ public:
|
||||
{
|
||||
auto name = maybeCaseHackFilename(name_);
|
||||
Path p = dstPath + name;
|
||||
if (mkdir(p.c_str(), 0777) == -1)
|
||||
if (sys::mkdir(p, 0777) == -1) {
|
||||
throw SysError("creating directory '%1%'", p);
|
||||
}
|
||||
return make_box_ptr<NARRestoreVisitor>(p + "/", useCaseHack);
|
||||
};
|
||||
|
||||
@@ -1041,7 +1044,7 @@ public:
|
||||
{
|
||||
auto name = maybeCaseHackFilename(name_);
|
||||
Path p = dstPath + name;
|
||||
AutoCloseFD fd = AutoCloseFD{open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666)};
|
||||
AutoCloseFD fd = sys::open(p, O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 0666);
|
||||
if (!fd) throw SysError("creating file '%1%'", p);
|
||||
|
||||
return make_box_ptr<MyFileHandle>(std::move(fd), size, executable);
|
||||
|
||||
+3
-1
@@ -1,4 +1,5 @@
|
||||
#include "lix/libutil/args.hh"
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/args/root.hh"
|
||||
#include "lix/libutil/hash.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
@@ -366,6 +367,7 @@ static void _completePath(AddCompletions & completions, std::string_view prefix,
|
||||
flags |= GLOB_ONLYDIR;
|
||||
#endif
|
||||
// using expandTilde here instead of GLOB_TILDE(_CHECK) so that ~<Tab> expands to /home/user/
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
if (glob((expandTilde(prefix) + "*").c_str(), flags, nullptr, &globbuf) == 0) {
|
||||
for (size_t i = 0; i < globbuf.gl_pathc; ++i) {
|
||||
if (onlyDirs) {
|
||||
@@ -603,7 +605,7 @@ void ExternalCommand::run() {
|
||||
"running external command: %s",
|
||||
concatMapStringsSep(" ", externalArgv, shellEscape)
|
||||
);
|
||||
execv(absoluteBinaryPath.c_str(), stringsToCharPtrs(externalArgv).data());
|
||||
sys::execv(absoluteBinaryPath, externalArgv);
|
||||
|
||||
throw SysError(errno, "failed to execute external command '%1%'", absoluteBinaryPath);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
#include <cerrno>
|
||||
#include <grp.h>
|
||||
#include <optional>
|
||||
#include <pwd.h>
|
||||
#include <string>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "c-calls.hh"
|
||||
#include "error.hh"
|
||||
#include "strings.hh"
|
||||
|
||||
namespace nix {
|
||||
CString requireCString(const std::string & s)
|
||||
{
|
||||
if (s.contains('\0')) {
|
||||
std::string p{s};
|
||||
for (auto pos = p.find('\0'); pos != p.npos; pos = p.find('\0')) {
|
||||
p.replace(pos, 1, "␀");
|
||||
}
|
||||
throw Error(
|
||||
"string %s that contains NUL bytes was used in a place that doesn't allow this", p
|
||||
);
|
||||
}
|
||||
return CString{s};
|
||||
}
|
||||
}
|
||||
|
||||
namespace nix::sys {
|
||||
|
||||
AutoCloseFD open(const std::string & path, int flags)
|
||||
{
|
||||
return AutoCloseFD{::open(requireCString(path), flags)};
|
||||
}
|
||||
|
||||
AutoCloseFD open(const std::string & path, int flags, mode_t mode)
|
||||
{
|
||||
return AutoCloseFD{::open(requireCString(path), flags, mode)};
|
||||
}
|
||||
|
||||
AutoCloseFD openat(int dir, const std::string & path, int flags)
|
||||
{
|
||||
return AutoCloseFD{::openat(dir, requireCString(path), flags)};
|
||||
}
|
||||
|
||||
AutoCloseFD openat(int dir, const std::string & path, int flags, mode_t mode)
|
||||
{
|
||||
return AutoCloseFD{::openat(dir, requireCString(path), flags, mode)};
|
||||
}
|
||||
|
||||
AutoCloseDir opendir(const std::string & path)
|
||||
{
|
||||
return AutoCloseDir{::opendir(requireCString(path))};
|
||||
}
|
||||
|
||||
int mkdir(const std::string & path, mode_t mode)
|
||||
{
|
||||
return ::mkdir(requireCString(path), mode);
|
||||
}
|
||||
|
||||
int lstat(const std::string & path, struct ::stat * st)
|
||||
{
|
||||
return ::lstat(requireCString(path), st);
|
||||
}
|
||||
|
||||
int stat(const std::string & path, struct ::stat * st)
|
||||
{
|
||||
return ::stat(requireCString(path), st);
|
||||
}
|
||||
|
||||
int unlink(const std::string & path)
|
||||
{
|
||||
return ::unlink(requireCString(path));
|
||||
}
|
||||
|
||||
int access(const std::string & path, int mode)
|
||||
{
|
||||
return ::access(requireCString(path), mode);
|
||||
}
|
||||
|
||||
int chmod(const std::string & path, mode_t mode)
|
||||
{
|
||||
return ::chmod(requireCString(path), mode);
|
||||
}
|
||||
|
||||
int chown(const std::string & path, uid_t uid, gid_t gid)
|
||||
{
|
||||
return ::chown(requireCString(path), uid, gid);
|
||||
}
|
||||
|
||||
int fchownat(int dir, const std::string & path, uid_t uid, gid_t gid, int flags)
|
||||
{
|
||||
return ::fchownat(dir, requireCString(path), uid, gid, flags);
|
||||
}
|
||||
|
||||
int lchown(const std::string & path, uid_t uid, gid_t gid)
|
||||
{
|
||||
return ::lchown(requireCString(path), uid, gid);
|
||||
}
|
||||
|
||||
int rename(const std::string & oldPath, const std::string & newPath)
|
||||
{
|
||||
return ::rename(requireCString(oldPath), requireCString(newPath));
|
||||
}
|
||||
|
||||
int utimes(const std::string & path, const struct ::timeval times[2])
|
||||
{
|
||||
return ::utimes(requireCString(path), times);
|
||||
}
|
||||
|
||||
int lutimes(const std::string & path, const struct ::timeval times[2])
|
||||
{
|
||||
return ::lutimes(requireCString(path), times);
|
||||
}
|
||||
|
||||
int link(const std::string & oldPath, const std::string & newPath)
|
||||
{
|
||||
return ::link(requireCString(oldPath), requireCString(newPath));
|
||||
}
|
||||
|
||||
int symlink(const std::string & target, const std::string & linkpath)
|
||||
{
|
||||
return ::symlink(requireCString(target), requireCString(linkpath));
|
||||
}
|
||||
|
||||
int unlinkat(int dir, const std::string & path, int flags)
|
||||
{
|
||||
return ::unlinkat(dir, requireCString(path), flags);
|
||||
}
|
||||
|
||||
int remove(const std::string & path)
|
||||
{
|
||||
return ::remove(requireCString(path));
|
||||
}
|
||||
|
||||
int rmdir(const std::string & path)
|
||||
{
|
||||
return ::rmdir(requireCString(path));
|
||||
}
|
||||
|
||||
int fstatat(int dir, const std::string & path, struct ::stat * st, int flags)
|
||||
{
|
||||
return ::fstatat(dir, requireCString(path), st, flags);
|
||||
}
|
||||
|
||||
int fchmodat(int dir, const std::string & path, mode_t mode, int flags)
|
||||
{
|
||||
return ::fchmodat(dir, requireCString(path), mode, flags);
|
||||
}
|
||||
|
||||
int statvfs(const std::string & path, struct ::statvfs * st)
|
||||
{
|
||||
return ::statvfs(requireCString(path), st);
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
int mount(
|
||||
const std::string & source,
|
||||
const std::string & target,
|
||||
const std::string & filesystemtype,
|
||||
unsigned long mountflags,
|
||||
const void * data
|
||||
)
|
||||
{
|
||||
return ::mount(
|
||||
requireCString(source),
|
||||
requireCString(target),
|
||||
requireCString(filesystemtype),
|
||||
mountflags,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
ssize_t llistxattr(const std::string & path, char * list, size_t size)
|
||||
{
|
||||
return ::llistxattr(requireCString(path), list, size);
|
||||
}
|
||||
|
||||
ssize_t lremovexattr(const std::string & path, const std::string & name)
|
||||
{
|
||||
return ::lremovexattr(requireCString(path), requireCString(name));
|
||||
}
|
||||
|
||||
ssize_t getxattr(const std::string & path, const std::string & name, void * value, size_t size)
|
||||
{
|
||||
return ::getxattr(requireCString(path), requireCString(name), value, size);
|
||||
}
|
||||
#endif
|
||||
|
||||
int chdir(const std::string & path)
|
||||
{
|
||||
return ::chdir(requireCString(path));
|
||||
}
|
||||
|
||||
int chroot(const std::string & path)
|
||||
{
|
||||
return ::chroot(requireCString(path));
|
||||
}
|
||||
|
||||
AutoCloseFD mkstemp(std::string & path)
|
||||
{
|
||||
return AutoCloseFD{::mkstemp(path.data())};
|
||||
}
|
||||
|
||||
ssize_t readlink(const std::string & path, char * buf, size_t bufsiz)
|
||||
{
|
||||
return ::readlink(requireCString(path), buf, bufsiz);
|
||||
}
|
||||
|
||||
int execv(const std::string & path, const std::list<std::string> & argv)
|
||||
{
|
||||
return ::execv(requireCString(path), stringsToCharPtrs(argv).data());
|
||||
}
|
||||
|
||||
int execvp(const std::string & path, const std::list<std::string> & argv)
|
||||
{
|
||||
return ::execvp(requireCString(path), stringsToCharPtrs(argv).data());
|
||||
}
|
||||
|
||||
int execve(
|
||||
const std::string & path,
|
||||
const std::list<std::string> & argv,
|
||||
const std::list<std::string> & envp
|
||||
)
|
||||
{
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
return ::execve(
|
||||
requireCString(path), stringsToCharPtrs(argv).data(), stringsToCharPtrs(envp).data()
|
||||
);
|
||||
}
|
||||
|
||||
char * getenv(const std::string & name)
|
||||
{
|
||||
return ::getenv(requireCString(name));
|
||||
}
|
||||
|
||||
int setenv(const std::string & name, const std::string & value, int overwrite)
|
||||
{
|
||||
return ::setenv(requireCString(name), requireCString(value), overwrite);
|
||||
}
|
||||
|
||||
int unsetenv(const std::string & name)
|
||||
{
|
||||
return ::unsetenv(requireCString(name));
|
||||
}
|
||||
|
||||
struct group * getgrnam(const std::string & name)
|
||||
{
|
||||
return ::getgrnam(requireCString(name));
|
||||
}
|
||||
|
||||
std::optional<Passwd> getpwnam(const std::string & name)
|
||||
{
|
||||
std::vector<char> buf(1024);
|
||||
auto cName = requireCString(name);
|
||||
struct passwd pw, *result;
|
||||
|
||||
while (true) {
|
||||
const auto err = getpwnam_r(cName, &pw, buf.data(), buf.size(), &result);
|
||||
if (err == ERANGE) {
|
||||
buf.resize(buf.size() * 2);
|
||||
continue;
|
||||
} else if (err != 0) {
|
||||
throw SysError(err, "getpwnam");
|
||||
}
|
||||
if (!result) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return {{
|
||||
.pw_name = result->pw_name,
|
||||
.pw_passwd = result->pw_passwd,
|
||||
.pw_uid = result->pw_uid,
|
||||
.pw_gid = result->pw_gid,
|
||||
.pw_gecos = result->pw_gecos,
|
||||
.pw_dir = result->pw_dir,
|
||||
.pw_shell = result->pw_shell,
|
||||
}};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
/**
|
||||
* @file
|
||||
*
|
||||
* NUL-safe wrappers for C functions.
|
||||
*/
|
||||
|
||||
#include "file-descriptor.hh"
|
||||
#include "file-system.hh"
|
||||
#include <grp.h>
|
||||
#include <sys/statvfs.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
class CString
|
||||
{
|
||||
friend CString requireCString(const std::string & s);
|
||||
|
||||
const std::string * s;
|
||||
|
||||
explicit CString(const std::string & s) : s(&s) {}
|
||||
|
||||
public:
|
||||
operator const char *() const
|
||||
{
|
||||
return s->c_str();
|
||||
}
|
||||
|
||||
const char * asCStr() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
CString requireCString(const std::string & s);
|
||||
}
|
||||
|
||||
namespace nix::sys {
|
||||
|
||||
AutoCloseFD open(const std::string & path, int flags);
|
||||
AutoCloseFD open(const std::string & path, int flags, mode_t mode);
|
||||
|
||||
AutoCloseFD openat(int dir, const std::string & path, int flags);
|
||||
AutoCloseFD openat(int dir, const std::string & path, int flags, mode_t mode);
|
||||
|
||||
AutoCloseDir opendir(const std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
int mkdir(const std::string & path, mode_t mode);
|
||||
|
||||
[[nodiscard]]
|
||||
int lstat(const std::string & path, struct ::stat * st);
|
||||
|
||||
[[nodiscard]]
|
||||
int stat(const std::string & path, struct ::stat * st);
|
||||
|
||||
[[nodiscard]]
|
||||
int unlink(const std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
int access(const std::string & path, int mode);
|
||||
|
||||
[[nodiscard]]
|
||||
int chmod(const std::string & path, mode_t mode);
|
||||
|
||||
[[nodiscard]]
|
||||
int chown(const std::string & path, uid_t uid, gid_t gid);
|
||||
|
||||
[[nodiscard]]
|
||||
int fchownat(int dir, const std::string & path, uid_t uid, gid_t gid, int flags);
|
||||
|
||||
[[nodiscard]]
|
||||
int lchown(const std::string & path, uid_t uid, gid_t gid);
|
||||
|
||||
[[nodiscard]]
|
||||
int rename(const std::string & oldPath, const std::string & newPath);
|
||||
|
||||
[[nodiscard]]
|
||||
int utimes(const std::string & path, const struct ::timeval times[2]);
|
||||
[[nodiscard]]
|
||||
int lutimes(const std::string & path, const struct ::timeval times[2]);
|
||||
|
||||
[[nodiscard]]
|
||||
int link(const std::string & oldPath, const std::string & newPath);
|
||||
|
||||
[[nodiscard]]
|
||||
int symlink(const std::string & target, const std::string & linkpath);
|
||||
|
||||
[[nodiscard]]
|
||||
int unlinkat(int dir, const std::string & path, int flags);
|
||||
|
||||
[[nodiscard]]
|
||||
int remove(const std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
int rmdir(const std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
int fstatat(int dir, const std::string & path, struct ::stat * st, int flags);
|
||||
|
||||
[[nodiscard]]
|
||||
int fchmodat(int dir, const std::string & path, mode_t mode, int flags);
|
||||
|
||||
[[nodiscard]]
|
||||
int statvfs(const std::string & path, struct ::statvfs * st);
|
||||
|
||||
#if __linux__
|
||||
[[nodiscard]]
|
||||
int mount(
|
||||
const std::string & source,
|
||||
const std::string & target,
|
||||
const std::string & filesystemtype,
|
||||
unsigned long mountflags,
|
||||
const void * data
|
||||
);
|
||||
|
||||
[[nodiscard]]
|
||||
ssize_t llistxattr(const std::string & path, char * list, size_t size);
|
||||
|
||||
[[nodiscard]]
|
||||
ssize_t lremovexattr(const std::string & path, const std::string & name);
|
||||
|
||||
[[nodiscard]]
|
||||
ssize_t getxattr(const std::string & path, const std::string & name, void * value, size_t size);
|
||||
#endif
|
||||
|
||||
[[nodiscard]]
|
||||
int chdir(const std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
int chroot(const std::string & path);
|
||||
|
||||
AutoCloseFD mkstemp(std::string & path);
|
||||
|
||||
[[nodiscard]]
|
||||
ssize_t readlink(const std::string & path, char * buf, size_t bufsiz);
|
||||
|
||||
int execv(const std::string & path, const std::list<std::string> & argv);
|
||||
int execvp(const std::string & path, const std::list<std::string> & argv);
|
||||
int execve(
|
||||
const std::string & path,
|
||||
const std::list<std::string> & argv,
|
||||
const std::list<std::string> & envp
|
||||
);
|
||||
|
||||
char * getenv(const std::string & name);
|
||||
|
||||
[[nodiscard]]
|
||||
int setenv(const std::string & name, const std::string & value, int overwrite);
|
||||
|
||||
[[nodiscard]]
|
||||
int unsetenv(const std::string & name);
|
||||
|
||||
struct group * getgrnam(const std::string & name);
|
||||
|
||||
struct Passwd
|
||||
{
|
||||
std::string pw_name;
|
||||
std::string pw_passwd;
|
||||
uid_t pw_uid;
|
||||
gid_t pw_gid;
|
||||
std::string pw_gecos;
|
||||
std::string pw_dir;
|
||||
std::string pw_shell;
|
||||
};
|
||||
|
||||
std::optional<Passwd> getpwnam(const std::string & name);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include "error.hh"
|
||||
#include "file-descriptor.hh"
|
||||
#include "logging.hh"
|
||||
@@ -26,7 +27,7 @@ static bool isCgroupDelegated(const Path & path)
|
||||
{
|
||||
char delegate_xattr;
|
||||
|
||||
if (getxattr(path.c_str(), "user.delegate", &delegate_xattr, sizeof(delegate_xattr)) >= 1) {
|
||||
if (sys::getxattr(path, "user.delegate", &delegate_xattr, sizeof(delegate_xattr)) >= 1) {
|
||||
if (delegate_xattr != '1') {
|
||||
throw Error("Unexpected `user.delegate` xattr: '%c'", delegate_xattr);
|
||||
}
|
||||
@@ -130,7 +131,7 @@ destroyCgroup(const std::string & name, const std::filesystem::path & aliveCgrou
|
||||
// has been excised. until then a timeout on group death will have to do.
|
||||
{
|
||||
auto eventsFile = aliveCgroup / "cgroup.events";
|
||||
AutoCloseFD events(open(eventsFile.c_str(), O_RDONLY));
|
||||
AutoCloseFD events(sys::open(eventsFile, O_RDONLY));
|
||||
if (!events) {
|
||||
throw SysError("failed to open %s", eventsFile);
|
||||
}
|
||||
@@ -169,7 +170,7 @@ destroyCgroup(const std::string & name, const std::filesystem::path & aliveCgrou
|
||||
|
||||
Result<CgroupStats> stats = readStatistics(aliveCgroup);
|
||||
|
||||
if (rmdir(aliveCgroup.c_str()) == -1) {
|
||||
if (sys::rmdir(aliveCgroup) == -1) {
|
||||
throw SysError("deleting cgroup '%s' at '%s'", name, aliveCgroup);
|
||||
}
|
||||
|
||||
@@ -316,13 +317,13 @@ AutoDestroyCgroup::AutoDestroyCgroup(
|
||||
{
|
||||
auto path = std::get<std::filesystem::path>(cgroup_);
|
||||
|
||||
if (mkdir(path.c_str(), 0755) == -1) {
|
||||
if (sys::mkdir(path, 0755) == -1) {
|
||||
throw SysError(
|
||||
"cannot create the top-level directory at '%s' for cgroup '%s'", path, name_
|
||||
);
|
||||
}
|
||||
|
||||
if (chown(path.c_str(), uid, gid) == -1) {
|
||||
if (sys::chown(path, uid, gid) == -1) {
|
||||
throw SysError(
|
||||
"cannot delegate the top-level directory '%s' from cgroup '%s' to user uid=%d,gid=%d",
|
||||
path,
|
||||
@@ -332,9 +333,9 @@ AutoDestroyCgroup::AutoDestroyCgroup(
|
||||
);
|
||||
}
|
||||
|
||||
AutoCloseFD cgroupFd{open(path.c_str(), O_PATH | O_NOFOLLOW)};
|
||||
AutoCloseFD cgroupFd{sys::open(path, O_PATH | O_NOFOLLOW)};
|
||||
for (auto node : {"procs", "threads", "subtree_control"}) {
|
||||
if (fchownat(cgroupFd.get(), fmt("cgroup.%s", node).c_str(), uid, gid, 0) == -1) {
|
||||
if (sys::fchownat(cgroupFd.get(), fmt("cgroup.%s", node), uid, gid, 0) == -1) {
|
||||
throw SysError(
|
||||
"cannot delegate '%s' from cgroup '%s' to user uid=%d,gid=%d", node, name_, uid, gid
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "error.hh"
|
||||
#include "file-descriptor.hh"
|
||||
#include "io-buffer.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/charptr-cast.hh"
|
||||
#include "lix/libutil/compression.hh"
|
||||
#include "lix/libutil/tarfile.hh"
|
||||
@@ -85,14 +86,21 @@ struct ArchiveCompressionSink : CompressionSink
|
||||
|
||||
ArchiveCompressionSink(Sink & nextSink, std::string format, bool parallel, int level = COMPRESSION_LEVEL_DEFAULT) : nextSink(nextSink)
|
||||
{
|
||||
auto cFormat = requireCString(format);
|
||||
archive = archive_write_new();
|
||||
if (!archive) throw Error("failed to initialize libarchive");
|
||||
check(archive_write_add_filter_by_name(archive, format.c_str()), "couldn't initialize compression (%s)");
|
||||
check(
|
||||
archive_write_add_filter_by_name(archive, cFormat),
|
||||
"couldn't initialize compression (%s)"
|
||||
);
|
||||
check(archive_write_set_format_raw(archive));
|
||||
if (parallel)
|
||||
check(archive_write_set_filter_option(archive, format.c_str(), "threads", "0"));
|
||||
if (parallel) {
|
||||
check(archive_write_set_filter_option(archive, cFormat, "threads", "0"));
|
||||
}
|
||||
if (level != COMPRESSION_LEVEL_DEFAULT)
|
||||
check(archive_write_set_filter_option(archive, format.c_str(), "compression-level", std::to_string(level).c_str()));
|
||||
check(archive_write_set_filter_option(
|
||||
archive, cFormat, "compression-level", requireCString(std::to_string(level))
|
||||
));
|
||||
// disable internal buffering
|
||||
check(archive_write_set_bytes_per_block(archive, 0));
|
||||
// disable output padding
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@@ -11,7 +12,7 @@ namespace nix {
|
||||
|
||||
std::optional<std::string> getEnv(const std::string & key)
|
||||
{
|
||||
char * value = getenv(key.c_str());
|
||||
char * value = sys::getenv(key);
|
||||
if (!value) return {};
|
||||
return std::string(value);
|
||||
}
|
||||
@@ -40,14 +41,14 @@ std::map<std::string, std::string> getEnv()
|
||||
void clearEnv()
|
||||
{
|
||||
for (auto & name : getEnv())
|
||||
unsetenv(name.first.c_str());
|
||||
(void) sys::unsetenv(name.first);
|
||||
}
|
||||
|
||||
void replaceEnv(const std::map<std::string, std::string> & newEnv)
|
||||
{
|
||||
clearEnv();
|
||||
for (auto & newEnvVar : newEnv)
|
||||
setenv(newEnvVar.first.c_str(), newEnvVar.second.c_str(), 1);
|
||||
for (auto & newEnvVar : newEnv) {
|
||||
(void) sys::setenv(newEnvVar.first, newEnvVar.second, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ std::string guessOrInventPathFromFD(int fd)
|
||||
* But we can read /proc/ */
|
||||
#if __linux__
|
||||
try {
|
||||
return readLink(fmt("/proc/self/fd/%1%", fd).c_str());
|
||||
return readLink(fmt("/proc/self/fd/%1%", fd));
|
||||
} catch (...) {
|
||||
}
|
||||
#elif defined (HAVE_F_GETPATH) && HAVE_F_GETPATH
|
||||
|
||||
+47
-32
@@ -1,9 +1,12 @@
|
||||
#include <sys/mount.h>
|
||||
#include <sys/time.h>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <atomic>
|
||||
#include <random>
|
||||
#include <sys/xattr.h>
|
||||
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
@@ -106,7 +109,7 @@ Path canonPath(PathView path, bool resolveSymlinks)
|
||||
Path realPath(Path const & path)
|
||||
{
|
||||
// With nullptr, realpath() malloc's and returns a new c-string.
|
||||
char * resolved = realpath(path.c_str(), nullptr);
|
||||
char * resolved = realpath(requireCString(path), nullptr);
|
||||
int saved = errno;
|
||||
if (resolved == nullptr) {
|
||||
throw SysError(saved, "cannot get realpath for '%s'", path);
|
||||
@@ -138,8 +141,9 @@ Path tildePath(Path const & path, const std::optional<Path> & home)
|
||||
|
||||
void chmodPath(const Path & path, mode_t mode)
|
||||
{
|
||||
if (chmod(path.c_str(), mode) == -1)
|
||||
if (sys::chmod(path, mode) == -1) {
|
||||
throw SysError("setting permissions on '%s'", path);
|
||||
}
|
||||
}
|
||||
|
||||
Path dirOf(const PathView path)
|
||||
@@ -199,8 +203,9 @@ bool isDirOrInDir(std::string_view path, std::string_view dir)
|
||||
struct stat stat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (stat(path.c_str(), &st))
|
||||
if (sys::stat(path, &st)) {
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
@@ -208,16 +213,16 @@ struct stat stat(const Path & path)
|
||||
struct stat lstat(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
if (sys::lstat(path, &st)) {
|
||||
throw SysError("getting status of '%1%'", path);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
std::optional<struct stat> maybeStat(const Path & path)
|
||||
{
|
||||
std::optional<struct stat> st{std::in_place};
|
||||
if (stat(path.c_str(), &*st))
|
||||
{
|
||||
if (sys::stat(path, &*st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
st.reset();
|
||||
else
|
||||
@@ -229,8 +234,7 @@ std::optional<struct stat> maybeStat(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 (sys::lstat(path, &*st)) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
st.reset();
|
||||
else
|
||||
@@ -271,7 +275,7 @@ Path readLink(const Path & path)
|
||||
std::vector<char> buf;
|
||||
for (ssize_t bufSize = PATH_MAX/4; true; bufSize += bufSize/2) {
|
||||
buf.resize(bufSize);
|
||||
ssize_t rlSize = readlink(path.c_str(), buf.data(), bufSize);
|
||||
ssize_t rlSize = sys::readlink(path, buf.data(), bufSize);
|
||||
if (rlSize == -1)
|
||||
if (errno == EINVAL)
|
||||
throw Error("'%1%' is not a symlink", path);
|
||||
@@ -317,7 +321,7 @@ static DirEntries readDirectory(DIR *dir, const Path & path, bool interruptible)
|
||||
|
||||
static DirEntries readDirectory(const Path & path, bool interruptible)
|
||||
{
|
||||
AutoCloseDir dir(opendir(path.c_str()));
|
||||
AutoCloseDir dir(sys::opendir(path));
|
||||
if (!dir) throw SysError("opening directory '%1%'", path);
|
||||
|
||||
return readDirectory(dir.get(), path, interruptible);
|
||||
@@ -341,7 +345,7 @@ unsigned char getFileType(const Path & path)
|
||||
|
||||
std::string readFile(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
AutoCloseFD fd{sys::open(path, O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return readFile(fd.get());
|
||||
@@ -350,7 +354,7 @@ std::string readFile(const Path & path)
|
||||
|
||||
Generator<Bytes> readFileSource(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
|
||||
AutoCloseFD fd{sys::open(path, O_RDONLY | O_CLOEXEC)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%s'", path);
|
||||
return [](AutoCloseFD fd) -> Generator<Bytes> {
|
||||
@@ -360,7 +364,7 @@ Generator<Bytes> readFileSource(const Path & path)
|
||||
|
||||
void writeFile(const Path & path, std::string_view s, mode_t mode, bool allowInterrupts)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
AutoCloseFD fd{sys::open(path, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
|
||||
@@ -389,7 +393,7 @@ void writeFileUninterruptible(const Path & path, std::string_view s, mode_t mode
|
||||
void writeFileAndSync(const Path & path, std::string_view s, mode_t mode)
|
||||
{
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
AutoCloseFD fd{sys::open(path, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
if (!fd) {
|
||||
throw SysError("opening file '%1%'", path);
|
||||
}
|
||||
@@ -405,7 +409,7 @@ void writeFileAndSync(const Path & path, std::string_view s, mode_t mode)
|
||||
|
||||
static AutoCloseFD openForWrite(const Path & path, mode_t mode)
|
||||
{
|
||||
AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
AutoCloseFD fd{sys::open(path, O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
return fd;
|
||||
@@ -467,7 +471,7 @@ try {
|
||||
|
||||
void syncParent(const Path & path)
|
||||
{
|
||||
AutoCloseFD fd{open(dirOf(path).c_str(), O_RDONLY, 0)};
|
||||
AutoCloseFD fd{sys::open(dirOf(path), O_RDONLY, 0)};
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
fd.fsync();
|
||||
@@ -493,7 +497,7 @@ static void _deletePath(int parentfd, const std::string & name, uint64_t & bytes
|
||||
*/
|
||||
|
||||
struct stat st;
|
||||
if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (sys::fstatat(parentfd, name, &st, AT_SYMLINK_NOFOLLOW) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("getting status of '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
|
||||
}
|
||||
@@ -526,23 +530,29 @@ static void _deletePath(int parentfd, const std::string & name, uint64_t & bytes
|
||||
/* Make the directory accessible. */
|
||||
const auto PERM_MASK = S_IRUSR | S_IWUSR | S_IXUSR;
|
||||
if ((st.st_mode & PERM_MASK) != PERM_MASK) {
|
||||
if (fchmodat(parentfd, name.c_str(), st.st_mode | PERM_MASK, 0) == -1) {
|
||||
if (sys::fchmodat(parentfd, name, st.st_mode | PERM_MASK, 0) == -1) {
|
||||
throw SysError("chmod '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
|
||||
}
|
||||
}
|
||||
|
||||
int fd = openat(parentfd, name.c_str(), O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (fd == -1)
|
||||
throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
|
||||
AutoCloseDir dir(fdopendir(fd));
|
||||
auto fd = sys::openat(parentfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
|
||||
if (!fd) {
|
||||
throw SysError(
|
||||
"opening directory '%1%' in directory '%2%'",
|
||||
name,
|
||||
guessOrInventPathFromFD(parentfd)
|
||||
);
|
||||
}
|
||||
AutoCloseDir dir(fdopendir(fd.get()));
|
||||
if (!dir)
|
||||
throw SysError("opening directory '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
|
||||
fd.release();
|
||||
for (auto & i : readDirectory(dir.get(), name, interruptible))
|
||||
_deletePath(dirfd(dir.get()), i.name, bytesFreed, interruptible);
|
||||
}
|
||||
|
||||
int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
|
||||
if (unlinkat(parentfd, name.c_str(), flags) == -1) {
|
||||
if (sys::unlinkat(parentfd, name, flags) == -1) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("cannot unlink '%1%' in directory '%2%'", name, guessOrInventPathFromFD(parentfd));
|
||||
}
|
||||
@@ -554,7 +564,7 @@ static void _deletePath(const Path & path, uint64_t & bytesFreed, bool interrupt
|
||||
if (dir == "")
|
||||
dir = "/";
|
||||
|
||||
AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)};
|
||||
AutoCloseFD dirfd{sys::open(dir, O_RDONLY)};
|
||||
if (!dirfd) {
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError("opening directory '%1%'", path);
|
||||
@@ -590,16 +600,18 @@ Paths createDirs(const Path & path)
|
||||
if (path == "/") return created;
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st) == -1) {
|
||||
if (sys::lstat(path, &st) == -1) {
|
||||
created = createDirs(dirOf(path));
|
||||
if (mkdir(path.c_str(), 0777) == -1 && errno != EEXIST)
|
||||
if (sys::mkdir(path, 0777) == -1 && errno != EEXIST) {
|
||||
throw SysError("creating directory '%1%'", path);
|
||||
}
|
||||
st = lstat(path);
|
||||
created.push_back(path);
|
||||
}
|
||||
|
||||
if (S_ISLNK(st.st_mode) && stat(path.c_str(), &st) == -1)
|
||||
if (S_ISLNK(st.st_mode) && sys::stat(path, &st) == -1) {
|
||||
throw SysError("statting symlink '%1%'", path);
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path);
|
||||
|
||||
@@ -624,8 +636,9 @@ AutoDelete::~AutoDelete()
|
||||
if (recursive)
|
||||
deletePath(path);
|
||||
else {
|
||||
if (remove(path.c_str()) == -1)
|
||||
if (sys::remove(path) == -1) {
|
||||
throw SysError("cannot unlink '%1%'", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
@@ -666,7 +679,7 @@ Path createTempSubdir(const Path & parent, const Path & prefix,
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = tempName(parent, prefix, includePid, counter);
|
||||
if (mkdir(tmpDir.c_str(), mode) == 0) {
|
||||
if (sys::mkdir(tmpDir, mode) == 0) {
|
||||
#if __FreeBSD__
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
@@ -695,8 +708,9 @@ Path makeTempPath(const Path & root, const Path & suffix)
|
||||
|
||||
void createSymlink(const Path & target, const Path & link)
|
||||
{
|
||||
if (symlink(target.c_str(), link.c_str()))
|
||||
if (sys::symlink(target, link)) {
|
||||
throw SysError("creating symlink from '%1%' to '%2%'", link, target);
|
||||
}
|
||||
}
|
||||
|
||||
void replaceSymlink(const Path & target, const Path & link)
|
||||
@@ -728,14 +742,15 @@ void setWriteTime(const fs::path & p, const struct stat & st)
|
||||
.tv_sec = st.st_mtime,
|
||||
.tv_usec = 0,
|
||||
};
|
||||
if (lutimes(p.c_str(), times) != 0)
|
||||
if (sys::lutimes(p, times) != 0) {
|
||||
throw SysError("changing modification time of '%s'", p);
|
||||
}
|
||||
}
|
||||
|
||||
void copy(const fs::directory_entry & from, const fs::path & to, CopyFileFlags flags)
|
||||
{
|
||||
// TODO: Rewrite the `is_*` to use `symlink_status()`
|
||||
auto statOfFrom = lstat(from.path().c_str());
|
||||
auto statOfFrom = lstat(from.path());
|
||||
auto fromStatus = from.symlink_status();
|
||||
|
||||
// Mark the directory as writable so that we can delete its children
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -366,7 +367,7 @@ void logFatal(std::string const & s)
|
||||
{
|
||||
writeLogsToStderr(s + "\n");
|
||||
// std::string for guaranteed null termination
|
||||
syslog(LOG_CRIT, "%s", s.c_str());
|
||||
syslog(LOG_CRIT, "%s", requireCString(s).asCStr());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ libutil_sources = files(
|
||||
'archive.cc',
|
||||
'args.cc',
|
||||
'async-io.cc',
|
||||
'c-calls.cc',
|
||||
'canon-path.cc',
|
||||
'cgroup.cc',
|
||||
'compression.cc',
|
||||
@@ -66,6 +67,7 @@ libutil_headers = files(
|
||||
'async.hh',
|
||||
'backed-string-view.hh',
|
||||
'box_ptr.hh',
|
||||
'c-calls.hh',
|
||||
'canon-path.hh',
|
||||
'cgroup.hh',
|
||||
'charptr-cast.hh',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "lix/libutil/mount.hh"
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -12,8 +13,9 @@ void bindPath(const Path & source, const Path & target, bool optional, CopyFileF
|
||||
debug("bind mounting '%1%' to '%2%'", source, target);
|
||||
|
||||
auto bindMount = [&]() {
|
||||
if (mount(source.c_str(), target.c_str(), "", MS_BIND | MS_REC, 0) == -1)
|
||||
if (sys::mount(source, target, "", MS_BIND | MS_REC, 0) == -1) {
|
||||
throw SysError("bind mount from '%1%' to '%2%' failed", source, target);
|
||||
}
|
||||
};
|
||||
|
||||
auto maybeSt = maybeLstat(source);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/file-descriptor.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -49,8 +50,9 @@ void restoreMountNamespace()
|
||||
throw SysError("chroot into saved root");
|
||||
}
|
||||
|
||||
if (chdir(savedCwd.c_str()) == -1)
|
||||
if (sys::chdir(savedCwd) == -1) {
|
||||
throw SysError("restoring cwd");
|
||||
}
|
||||
} catch (Error & e) {
|
||||
debug("%1%", Uncolored(e.msg()));
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "async-io.hh"
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libutil/finally.hh"
|
||||
@@ -353,8 +354,9 @@ RunningProgram runProgram2(const RunOptions & options)
|
||||
}
|
||||
}
|
||||
|
||||
if (options.chdir && chdir((*options.chdir).c_str()) == -1)
|
||||
if (options.chdir && sys::chdir(*options.chdir) == -1) {
|
||||
throw SysError("chdir failed");
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
if (!options.caps.empty() && prctl(PR_SET_KEEPCAPS, 1) < 0) {
|
||||
@@ -414,12 +416,12 @@ RunningProgram runProgram2(const RunOptions & options)
|
||||
|
||||
restoreProcessContext();
|
||||
|
||||
if (options.searchPath)
|
||||
execvp(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||
if (options.searchPath) {
|
||||
sys::execvp(options.program, args_);
|
||||
// This allows you to refer to a program with a pathname relative
|
||||
// to the PATH variable.
|
||||
else
|
||||
execv(options.program.c_str(), stringsToCharPtrs(args_).data());
|
||||
} else
|
||||
sys::execv(options.program, args_);
|
||||
|
||||
throw SysError("executing '%1%'", options.program);
|
||||
}, processOptions)};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
#include "lix/libutil/references.hh"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
@@ -9,7 +10,9 @@ std::vector<char *> stringsToCharPtrs(const Strings & ss)
|
||||
{
|
||||
std::vector<char *> res;
|
||||
// This is const cast since this exists for OS APIs that want char *
|
||||
for (auto & s : ss) res.push_back(const_cast<char *>(s.data()));
|
||||
for (auto & s : ss) {
|
||||
res.push_back(const_cast<char *>(requireCString(s).asCStr()));
|
||||
}
|
||||
res.push_back(0);
|
||||
return res;
|
||||
}
|
||||
|
||||
+10
-1
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "async-io.hh"
|
||||
#include "file-descriptor.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/charptr-cast.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -28,6 +29,7 @@ static ssize_t callback_read(struct archive * archive, void * _self, const void
|
||||
} catch (EndOfFile &) {
|
||||
return 0;
|
||||
} catch (std::exception & err) { // NOLINT(lix-foreign-exceptions)
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): what() is a c string
|
||||
archive_set_error(archive, EIO, "Source threw exception: %s", err.what());
|
||||
return -1;
|
||||
}
|
||||
@@ -71,7 +73,10 @@ TarArchive::TarArchive(const Path & path)
|
||||
archive_read_support_filter_all(archive);
|
||||
archive_read_support_format_all(archive);
|
||||
archive_read_set_option(archive, nullptr, "mac-ext", nullptr);
|
||||
check(archive_read_open_filename(archive, path.c_str(), 16384), "failed to open archive: %s");
|
||||
check(
|
||||
archive_read_open_filename(archive, requireCString(path), 16384),
|
||||
"failed to open archive: %s"
|
||||
);
|
||||
}
|
||||
|
||||
void TarArchive::close()
|
||||
@@ -86,6 +91,8 @@ TarArchive::~TarArchive()
|
||||
|
||||
static void extract_archive(TarArchive & archive, const Path & destDir)
|
||||
{
|
||||
requireCString(destDir);
|
||||
|
||||
int flags = ARCHIVE_EXTRACT_TIME
|
||||
| ARCHIVE_EXTRACT_SECURE_SYMLINKS
|
||||
| ARCHIVE_EXTRACT_SECURE_NODOTDOT;
|
||||
@@ -102,6 +109,7 @@ static void extract_archive(TarArchive & archive, const Path & destDir)
|
||||
else
|
||||
archive.check(r);
|
||||
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): destDir is checked, name is a c string
|
||||
archive_entry_copy_pathname(entry,
|
||||
(destDir + "/" + name).c_str());
|
||||
|
||||
@@ -112,6 +120,7 @@ static void extract_archive(TarArchive & archive, const Path & destDir)
|
||||
// Patch hardlink path
|
||||
const char *original_hardlink = archive_entry_hardlink(entry);
|
||||
if (original_hardlink) {
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls): destDir is checked, name is a c string
|
||||
archive_entry_copy_hardlink(entry,
|
||||
(destDir + "/" + original_hardlink).c_str());
|
||||
}
|
||||
|
||||
@@ -9,10 +9,13 @@ void setCurrentThreadName(const char * name)
|
||||
{
|
||||
// https://stackoverflow.com/questions/2369738/how-to-set-the-name-of-a-thread-in-linux-pthreads/7989973
|
||||
#if defined(__linux__)
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
pthread_setname_np(pthread_self(), name);
|
||||
#elif defined(__APPLE__)
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
pthread_setname_np(name);
|
||||
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
// NOLINTNEXTLINE(lix-unsafe-c-calls)
|
||||
pthread_set_name_np(pthread_self(), name);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/processes.hh"
|
||||
#include "lix/libutil/unix-domain-socket.hh"
|
||||
@@ -29,7 +30,7 @@ AutoCloseFD createUnixDomainSocket(const Path & path, mode_t mode)
|
||||
|
||||
bind(fdSocket.get(), path);
|
||||
|
||||
chmodPath(path.c_str(), mode);
|
||||
chmodPath(path, mode);
|
||||
|
||||
if (listen(fdSocket.get(), 100) == -1)
|
||||
throw SysError("cannot listen on socket '%1%'", path);
|
||||
@@ -69,8 +70,9 @@ static void bindConnectProcHelper(
|
||||
try {
|
||||
pipe.readSide.close();
|
||||
Path dir = dirOf(path);
|
||||
if (chdir(dir.c_str()) == -1)
|
||||
if (sys::chdir(dir) == -1) {
|
||||
throw SysError("chdir to '%s' failed", dir);
|
||||
}
|
||||
std::string base(baseNameOf(path));
|
||||
if (base.size() + 1 >= sizeof(addr.sun_path))
|
||||
throw Error("socket path '%s' is too long", base);
|
||||
@@ -102,7 +104,7 @@ static void bindConnectProcHelper(
|
||||
|
||||
void bind(int fd, const std::string & path)
|
||||
{
|
||||
unlink(path.c_str());
|
||||
(void) sys::unlink(path);
|
||||
|
||||
bindConnectProcHelper("bind", ::bind, fd, path);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "c-calls.hh"
|
||||
#include "lix/libutil/environment-variables.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
@@ -38,7 +39,7 @@ Path getHome()
|
||||
if (homeDir) {
|
||||
// Only use `$HOME` if it exists and is owned by the current user.
|
||||
struct stat st;
|
||||
int result = stat(homeDir->c_str(), &st);
|
||||
int result = sys::stat(*homeDir, &st);
|
||||
if (result != 0) {
|
||||
if (errno != ENOENT) {
|
||||
printTaggedWarning(
|
||||
|
||||
+6
-2
@@ -16,6 +16,7 @@
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/serialise.hh"
|
||||
#include "lix/libutil/archive.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libstore/globals.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libutil/finally.hh"
|
||||
@@ -153,8 +154,11 @@ static bool matchUser(const std::string & user, const std::string & group, const
|
||||
|
||||
for (auto & i : users)
|
||||
if (i.substr(0, 1) == "@") {
|
||||
if (group == i.substr(1)) return true;
|
||||
struct group * gr = getgrnam(i.c_str() + 1);
|
||||
auto rest = i.substr(1);
|
||||
if (group == rest) {
|
||||
return true;
|
||||
}
|
||||
struct group * gr = sys::getgrnam(rest);
|
||||
if (!gr) continue;
|
||||
if (matchUser(user, *gr)) return true;
|
||||
}
|
||||
|
||||
+4
-3
@@ -8,6 +8,7 @@
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libstore/parsed-derivations.hh"
|
||||
#include "lix/libutil/async.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "run.hh"
|
||||
#include "lix/libstore/temporary-dir.hh"
|
||||
@@ -618,7 +619,7 @@ struct CmdDevelop : Common, MixEnvironment
|
||||
|
||||
setEnviron();
|
||||
// prevent garbage collection until shell exits
|
||||
setenv("NIX_GCROOT", gcroot.c_str(), 1);
|
||||
(void) sys::setenv("NIX_GCROOT", gcroot, 1);
|
||||
|
||||
Path shell = "bash";
|
||||
|
||||
@@ -662,7 +663,7 @@ struct CmdDevelop : Common, MixEnvironment
|
||||
|
||||
// Override SHELL with the one chosen for this environment.
|
||||
// This is to make sure the system shell doesn't leak into the build environment.
|
||||
setenv("SHELL", shell.c_str(), 1);
|
||||
(void) sys::setenv("SHELL", shell, 1);
|
||||
|
||||
// If running a phase or single command, don't want an interactive shell running after
|
||||
// Ctrl-C, so don't pass --rcfile
|
||||
@@ -676,7 +677,7 @@ struct CmdDevelop : Common, MixEnvironment
|
||||
if (installableFlake) {
|
||||
auto sourcePath = installableFlake->getLockedFlake(*state)->flake.resolvedRef.input.getSourcePath();
|
||||
if (sourcePath) {
|
||||
if (chdir(sourcePath->c_str()) == -1) {
|
||||
if (sys::chdir(*sourcePath) == -1) {
|
||||
throw SysError("chdir to '%s' failed", *sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -3,6 +3,7 @@
|
||||
#include "lix/libexpr/eval.hh"
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libcmd/editor-for.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/current-process.hh"
|
||||
#include "edit.hh"
|
||||
|
||||
@@ -51,7 +52,7 @@ struct CmdEdit : InstallableCommand
|
||||
|
||||
printMsg(lvlChatty, "running editor: %s", concatMapStringsSep(" ", args, shellEscape));
|
||||
|
||||
execvp(args.front().c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execvp(args.front(), args);
|
||||
|
||||
std::string command;
|
||||
for (const auto &arg : args) command += " '" + arg + "'";
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@
|
||||
#include "lix/libmain/shared.hh"
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/filetransfer.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/tarfile.hh"
|
||||
#include "lix/libexpr/attr-path.hh"
|
||||
#include "lix/libexpr/eval-inline.hh" // IWYU pragma: keep
|
||||
@@ -99,7 +100,7 @@ std::tuple<StorePath, Hash> prefetchFile(
|
||||
if (executable)
|
||||
mode = 0700;
|
||||
|
||||
AutoCloseFD fd{open(tmpFile.c_str(), O_WRONLY | O_CREAT | O_EXCL, mode)};
|
||||
AutoCloseFD fd{sys::open(tmpFile, O_WRONLY | O_CREAT | O_EXCL, mode)};
|
||||
if (!fd) throw SysError("creating temporary file '%s'", tmpFile);
|
||||
|
||||
FdSink sink(fd.get());
|
||||
|
||||
+20
-14
@@ -6,6 +6,7 @@
|
||||
#include "lix/libstore/store-api.hh"
|
||||
#include "lix/libstore/derivations.hh"
|
||||
#include "lix/libstore/local-store.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/libstore/fs-accessor.hh"
|
||||
#include "lix/libexpr/eval.hh"
|
||||
@@ -59,7 +60,7 @@ void runProgramInStore(ref<Store> store,
|
||||
};
|
||||
for (auto & arg : args) helperArgs.push_back(arg);
|
||||
|
||||
execv(getSelfExe().value_or("nix").c_str(), stringsToCharPtrs(helperArgs).data());
|
||||
sys::execv(getSelfExe().value_or("nix"), helperArgs);
|
||||
|
||||
throw SysError("could not execute chroot helper");
|
||||
}
|
||||
@@ -68,9 +69,9 @@ void runProgramInStore(ref<Store> store,
|
||||
setPersonality(*system);
|
||||
|
||||
if (useSearchPath == UseSearchPath::Use)
|
||||
execvp(program.c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execvp(program, args);
|
||||
else
|
||||
execv(program.c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execv(program, args);
|
||||
|
||||
throw SysError("unable to execute '%s'", program);
|
||||
}
|
||||
@@ -147,8 +148,8 @@ struct CmdShell : InstallablesCommand, MixEnvironment
|
||||
auto unixPath = tokenizeString<Strings>(getEnv("PATH").value_or(""), ":");
|
||||
unixPath.insert(unixPath.begin(), pathAdditions.begin(), pathAdditions.end());
|
||||
auto unixPathString = concatStringsSep(":", unixPath);
|
||||
setenv("PATH", unixPathString.c_str(), 1);
|
||||
setenv("IN_NIX_SHELL", ignoreEnvironment ? "pure" : "impure", 1);
|
||||
(void) sys::setenv("PATH", unixPathString, 1);
|
||||
(void) sys::setenv("IN_NIX_SHELL", ignoreEnvironment ? "pure" : "impure", 1);
|
||||
|
||||
Strings args;
|
||||
for (auto & arg : command) args.push_back(arg);
|
||||
@@ -262,8 +263,9 @@ void chrootHelper(int argc, char * * argv)
|
||||
|
||||
createDirs(tmpDir + storeDir);
|
||||
|
||||
if (mount(realStoreDir.c_str(), (tmpDir + storeDir).c_str(), "", MS_BIND, 0) == -1)
|
||||
if (sys::mount(realStoreDir, (tmpDir + storeDir), "", MS_BIND, 0) == -1) {
|
||||
throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
|
||||
}
|
||||
|
||||
for (auto entry : readDirectory("/")) {
|
||||
auto src = "/" + entry.name;
|
||||
@@ -271,10 +273,12 @@ void chrootHelper(int argc, char * * argv)
|
||||
if (pathExists(dst)) continue;
|
||||
auto st = lstat(src);
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (mkdir(dst.c_str(), 0700) == -1)
|
||||
if (sys::mkdir(dst, 0700) == -1) {
|
||||
throw SysError("creating directory '%s'", dst);
|
||||
if (mount(src.c_str(), dst.c_str(), "", MS_BIND | MS_REC, 0) == -1)
|
||||
}
|
||||
if (sys::mount(src, dst, "", MS_BIND | MS_REC, 0) == -1) {
|
||||
throw SysError("mounting '%s' on '%s'", src, dst);
|
||||
}
|
||||
} else if (S_ISLNK(st.st_mode))
|
||||
createSymlink(readLink(src), dst);
|
||||
}
|
||||
@@ -283,14 +287,16 @@ void chrootHelper(int argc, char * * argv)
|
||||
if (!cwd) throw SysError("getting current directory");
|
||||
Finally freeCwd([&]() { free(cwd); });
|
||||
|
||||
if (chroot(tmpDir.c_str()) == -1)
|
||||
if (sys::chroot(tmpDir) == -1) {
|
||||
throw SysError("chrooting into '%s'", tmpDir);
|
||||
}
|
||||
|
||||
if (chdir(cwd) == -1)
|
||||
if (sys::chdir(cwd) == -1) {
|
||||
throw SysError("chdir to '%s' in chroot", cwd);
|
||||
} else
|
||||
if (mount(realStoreDir.c_str(), storeDir.c_str(), "", MS_BIND, 0) == -1)
|
||||
throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
|
||||
}
|
||||
} else if (sys::mount(realStoreDir, storeDir, "", MS_BIND, 0) == -1) {
|
||||
throw SysError("mounting '%s' on '%s'", realStoreDir, storeDir);
|
||||
}
|
||||
|
||||
writeFile("/proc/self/setgroups", "deny");
|
||||
writeFile("/proc/self/uid_map", fmt("%d %d %d", uid, uid, 1));
|
||||
@@ -299,7 +305,7 @@ void chrootHelper(int argc, char * * argv)
|
||||
if (system != "")
|
||||
setPersonality(system);
|
||||
|
||||
execvp(cmd.c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execvp(cmd, args);
|
||||
|
||||
throw SysError("unable to exec '%s'", cmd);
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include <clang-tidy/ClangTidyModule.h>
|
||||
#include <clang-tidy/ClangTidyModuleRegistry.h>
|
||||
#include "CharPtrCast.hh"
|
||||
#include "DisallowedDecls.hh"
|
||||
#include "ForeignExceptions.hh"
|
||||
#include "HasPrefixSuffix.hh"
|
||||
#include "CharPtrCast.hh"
|
||||
#include "NeverAsync.hh"
|
||||
#include "UnsafeCCalls.hh"
|
||||
#include <clang-tidy/ClangTidyModule.h>
|
||||
#include <clang-tidy/ClangTidyModuleRegistry.h>
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
using namespace clang;
|
||||
@@ -18,6 +19,7 @@ class NixClangTidyChecks : public ClangTidyModule {
|
||||
CheckFactories.registerCheck<NeverAsync>("lix-never-async");
|
||||
CheckFactories.registerCheck<DisallowedDeclsCheck>("lix-disallowed-decls");
|
||||
CheckFactories.registerCheck<ForeignExceptions>("lix-foreign-exceptions");
|
||||
CheckFactories.registerCheck<UnsafeCCalls>("lix-unsafe-c-calls");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "UnsafeCCalls.hh"
|
||||
#include <clang/AST/Expr.h>
|
||||
#include <clang/AST/ExprCXX.h>
|
||||
#include <clang/ASTMatchers/ASTMatchers.h>
|
||||
#include <clang/Basic/Diagnostic.h>
|
||||
#include <clang/Tooling/Transformer/SourceCode.h>
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
using namespace clang::ast_matchers;
|
||||
using namespace clang;
|
||||
|
||||
void UnsafeCCalls::registerMatchers(ast_matchers::MatchFinder *Finder) {
|
||||
auto cStringType = pointerType(pointee(isAnyCharacter(), isConstQualified()));
|
||||
|
||||
Finder->addMatcher(
|
||||
traverse(
|
||||
clang::TK_IgnoreUnlessSpelledInSource,
|
||||
callExpr(callee(functionDecl(
|
||||
hasAnyParameter(hasType(cStringType)),
|
||||
unless(anyOf(hasName("strlen"), hasName("strdup"),
|
||||
hasName("strcpy"))),
|
||||
unless(hasAncestor(namespaceDecl())))),
|
||||
hasAnyArgument(allOf(
|
||||
hasType(asString("const char *")),
|
||||
unless(callExpr(callee(cxxMethodDecl(
|
||||
hasName("asCStr"), hasParent(cxxRecordDecl(hasName(
|
||||
"nix::CString")))))))))))
|
||||
.bind("call"),
|
||||
this);
|
||||
}
|
||||
|
||||
void UnsafeCCalls::check(
|
||||
const ast_matchers::MatchFinder::MatchResult &Result) {
|
||||
const auto ReinterpretCastExpr = Result.Nodes.getNodeAs<CallExpr>("call");
|
||||
auto Diag =
|
||||
diag(ReinterpretCastExpr->getExprLoc(),
|
||||
"potentially unsafe call to C function (maybe use a sys::* wrapper instead)");
|
||||
Diag << ReinterpretCastExpr->getSourceRange();
|
||||
}
|
||||
|
||||
} // namespace nix::clang_tidy
|
||||
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <clang-tidy/ClangTidyCheck.h>
|
||||
#include <clang-tidy/utils/IncludeInserter.h>
|
||||
#include <clang/ASTMatchers/ASTMatchFinder.h>
|
||||
#include <llvm/ADT/StringRef.h>
|
||||
|
||||
namespace nix::clang_tidy {
|
||||
|
||||
using namespace clang;
|
||||
using namespace clang::tidy;
|
||||
|
||||
class UnsafeCCalls : public ClangTidyCheck {
|
||||
public:
|
||||
UnsafeCCalls(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context) {}
|
||||
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
|
||||
};
|
||||
} // namespace nix::clang_tidy
|
||||
@@ -12,6 +12,7 @@ sources = files(
|
||||
'HasPrefixSuffix.cc',
|
||||
'LixClangTidyChecks.cc',
|
||||
'NeverAsync.cc',
|
||||
'UnsafeCCalls.cc',
|
||||
)
|
||||
|
||||
lix_clang_tidy = shared_module('lix-clang-tidy', sources,
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "test-session.hh"
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/escape-char.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/processes.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
|
||||
@@ -34,7 +36,7 @@ RunningProcess RunningProcess::start(std::string executable, Strings args)
|
||||
if (dup2(STDOUT_FILENO, STDERR_FILENO) == -1) {
|
||||
throw SysError("dupping stderr");
|
||||
}
|
||||
execv(executable.c_str(), stringsToCharPtrs(args).data());
|
||||
sys::execv(executable, args);
|
||||
throw SysError("exec did not happen");
|
||||
});
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "lix/libutil/c-calls.hh"
|
||||
#include "lix/libutil/file-system.hh"
|
||||
#include "lix/libutil/processes.hh"
|
||||
#include "lix/libutil/strings.hh"
|
||||
@@ -232,14 +233,14 @@ namespace nix {
|
||||
Path filePath = getUnitTestDataPath("guess-or-invent/test.txt");
|
||||
createDirs(dirOf(filePath));
|
||||
writeFile(filePath, "some text");
|
||||
AutoCloseFD file{open(filePath.c_str(), O_RDONLY, 0666)};
|
||||
AutoCloseFD file{sys::open(filePath, O_RDONLY, 0666)};
|
||||
testGuessOrInventPathPrePostDeletion(file, filePath);
|
||||
}
|
||||
|
||||
TEST(guessOrInventPath, directories) {
|
||||
Path dirPath = getUnitTestDataPath("guess-or-invent/test-dir");
|
||||
createDirs(dirPath);
|
||||
AutoCloseFD directory{open(dirPath.c_str(), O_DIRECTORY, 0666)};
|
||||
AutoCloseFD directory{sys::open(dirPath, O_DIRECTORY, 0666)};
|
||||
testGuessOrInventPathPrePostDeletion(directory, dirPath);
|
||||
}
|
||||
|
||||
@@ -249,15 +250,15 @@ namespace nix {
|
||||
Path targetPath = getUnitTestDataPath("guess-or-invent/nowhere");
|
||||
createDirs(dirOf(symlinkPath));
|
||||
createSymlink(targetPath, symlinkPath);
|
||||
AutoCloseFD symlink{open(symlinkPath.c_str(), O_PATH | O_NOFOLLOW, 0666)};
|
||||
AutoCloseFD symlink{sys::open(symlinkPath, O_PATH | O_NOFOLLOW, 0666)};
|
||||
testGuessOrInventPathPrePostDeletion(symlink, symlinkPath);
|
||||
}
|
||||
|
||||
TEST(guessOrInventPath, fifos) {
|
||||
Path fifoPath = getUnitTestDataPath("guess-or-invent/fifo");
|
||||
createDirs(dirOf(fifoPath));
|
||||
ASSERT_TRUE(mkfifo(fifoPath.c_str(), 0666) == 0);
|
||||
AutoCloseFD fifo{open(fifoPath.c_str(), O_PATH | O_NOFOLLOW, 0666)};
|
||||
ASSERT_TRUE(mkfifo(fifoPath.c_str(), 0666) == 0); // NOLINT(lix-unsafe-c-calls)
|
||||
AutoCloseFD fifo{sys::open(fifoPath, O_PATH | O_NOFOLLOW, 0666)};
|
||||
testGuessOrInventPathPrePostDeletion(fifo, fifoPath);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user