treewide: lint syscalls for c string safety

Change-Id: I3caf476e59dcb7899ac5a3d83dfa3fb7ceaaabf0
This commit is contained in:
eldritch horrors
2025-10-10 16:13:54 +00:00
parent 3ef7e0711b
commit 2f16cf6014
62 changed files with 944 additions and 255 deletions
+2 -1
View File
@@ -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();
+5 -5
View File
@@ -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);
}
+5 -3
View File
@@ -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);
}
+5 -2
View File
@@ -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();
+7 -3
View File
@@ -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);
}
}
+6 -5
View File
@@ -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) {
+1
View File
@@ -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};
}
+1
View File
@@ -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:
+4 -2
View File
@@ -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
+4 -4
View File
@@ -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 -2
View File
@@ -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());
}
+6 -4
View File
@@ -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);
}
}
}
+2 -1
View File
@@ -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());
+2 -1
View File
@@ -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);
});
+66 -34
View File
@@ -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);
+11 -6
View File
@@ -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 -1
View File
@@ -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);
}
}
};
+4 -3
View File
@@ -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
View File
@@ -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;
};
+2 -2
View File
@@ -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()
+2 -1
View File
@@ -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
View File
@@ -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;
}
+8 -7
View File
@@ -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;
+12 -8
View File
@@ -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
+3 -2
View File
@@ -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");
+17 -7
View File
@@ -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");
}
+3 -1
View File
@@ -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);
}
}
+4 -4
View File
@@ -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);
+9 -3
View File
@@ -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
View File
@@ -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());
+2 -1
View File
@@ -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__
+2 -1
View File
@@ -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());
+6 -3
View File
@@ -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
View File
@@ -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);
}
+280
View File
@@ -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,
}};
}
}
}
+168
View File
@@ -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);
}
+8 -7
View File
@@ -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
);
+12 -4
View File
@@ -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
+6 -5
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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());
}
}
+2
View File
@@ -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',
+3 -1
View File
@@ -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);
+3 -1
View File
@@ -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()));
}
+7 -5
View File
@@ -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)};
+4 -1
View File
@@ -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
View File
@@ -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());
}
+3
View File
@@ -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
}
+5 -3
View File
@@ -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);
}
+2 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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");
});
+6 -5
View File
@@ -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