diff options
author | Artemis Tosini <lix@artem.ist> | 2024-04-22 17:32:21 +0000 |
---|---|---|
committer | Artemis Tosini <lix@artem.ist> | 2024-04-23 16:17:05 +0000 |
commit | b247ef72dc7bcc857288c0ddcceb3e42f76a78f1 (patch) | |
tree | 01ec226e6f1e030797bde73bac219c2eb87e7c00 /src/libstore/platform/linux.cc | |
parent | c8c838381d8e76450ffd57b778bb28217b32084d (diff) |
libstore: Create platform LocalStore subclasses
This creates new subclasses of LocalStore for each OS to include
platform-specific functionality. Currently this just includes garbage
collector roots but it could be extended to sandboxing as well.
In order to make sure that the generic LocalStore is not accidentally
constructed, its constructor is protected. A Fallback is provided which
implements no functionality except constructors.
Change-Id: I836a28e90b68309873f75afb83e0f1b2e2c89fb3
Diffstat (limited to 'src/libstore/platform/linux.cc')
-rw-r--r-- | src/libstore/platform/linux.cc | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/libstore/platform/linux.cc b/src/libstore/platform/linux.cc new file mode 100644 index 000000000..9be3e47da --- /dev/null +++ b/src/libstore/platform/linux.cc @@ -0,0 +1,123 @@ +#include "gc-store.hh" +#include "signals.hh" +#include "platform/linux.hh" + +#include <regex> + +namespace nix { +static RegisterStoreImplementation<LinuxLocalStore, LocalStoreConfig> regLocalStore; + +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); + if (res == -1) { + if (errno == ENOENT || errno == EACCES || errno == ESRCH) { + return; + } + throw SysError("reading symlink"); + } + if (res == bufsiz) { + throw Error("overly long symlink starting with '%1%'", std::string_view(buf, bufsiz)); + } + if (res > 0 && buf[0] == '/') { + roots[std::string(static_cast<char *>(buf), res)].emplace(file); + } +} + +static std::string quoteRegexChars(const std::string & raw) +{ + static auto specialRegex = std::regex(R"([.^$\\*+?()\[\]{}|])"); + return std::regex_replace(raw, specialRegex, R"(\$&)"); +} + +static void readFileRoots(const char * path, UncheckedRoots & roots) +{ + try { + roots[readFile(path)].emplace(path); + } catch (SysError & e) { + if (e.errNo != ENOENT && e.errNo != EACCES) { + throw; + } + } +} + +void LinuxLocalStore::findPlatformRoots(UncheckedRoots & unchecked) +{ + auto procDir = AutoCloseDir{opendir("/proc")}; + if (procDir) { + struct dirent * ent; + auto digitsRegex = std::regex(R"(^\d+$)"); + auto mapRegex = std::regex(R"(^\s*\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+(/\S+)\s*$)"); + auto storePathRegex = + std::regex(quoteRegexChars(storeDir) + R"(/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*)"); + while (errno = 0, ent = readdir(procDir.get())) { + checkInterrupt(); + if (std::regex_match(ent->d_name, digitsRegex)) { + try { + readProcLink(fmt("/proc/%s/exe", ent->d_name), unchecked); + 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())); + if (!fdDir) { + if (errno == ENOENT || errno == EACCES) { + continue; + } + throw SysError("opening %1%", fdStr); + } + struct dirent * fd_ent; + while (errno = 0, fd_ent = readdir(fdDir.get())) { + if (fd_ent->d_name[0] != '.') { + readProcLink(fmt("%s/%s", fdStr, fd_ent->d_name), unchecked); + } + } + if (errno) { + if (errno == ESRCH) { + continue; + } + throw SysError("iterating /proc/%1%/fd", ent->d_name); + } + fdDir.reset(); + + auto mapFile = fmt("/proc/%s/maps", ent->d_name); + auto mapLines = + tokenizeString<std::vector<std::string>>(readFile(mapFile), "\n"); + for (const auto & line : mapLines) { + auto match = std::smatch{}; + if (std::regex_match(line, match, mapRegex)) { + unchecked[match[1]].emplace(mapFile); + } + } + + auto envFile = fmt("/proc/%s/environ", ent->d_name); + auto envString = readFile(envFile); + auto env_end = std::sregex_iterator{}; + for (auto i = + std::sregex_iterator{ + envString.begin(), envString.end(), storePathRegex + }; + i != env_end; + ++i) + { + unchecked[i->str()].emplace(envFile); + } + } catch (SysError & e) { + if (errno == ENOENT || errno == EACCES || errno == ESRCH) { + continue; + } + throw; + } + } + } + if (errno) { + throw SysError("iterating /proc"); + } + } + + readFileRoots("/proc/sys/kernel/modprobe", unchecked); + readFileRoots("/proc/sys/kernel/fbsplash", unchecked); + readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked); +} +} |