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/gc.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/gc.cc')
-rw-r--r-- | src/libstore/gc.cc | 111 |
1 files changed, 7 insertions, 104 deletions
diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 20519c1a2..535bbd251 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -321,105 +321,8 @@ Roots LocalStore::findRoots(bool censor) return roots; } -typedef std::unordered_map<Path, std::unordered_set<std::string>> UncheckedRoots; - -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"(\$&)"); -} - -#if __linux__ -static void readFileRoots(const char * path, UncheckedRoots & roots) +void LocalStore::findPlatformRoots(UncheckedRoots & unchecked) { - try { - roots[readFile(path)].emplace(path); - } catch (SysError & e) { - if (e.errNo != ENOENT && e.errNo != EACCES) - throw; - } -} -#endif - -void LocalStore::findRuntimeRoots(Roots & roots, bool censor) -{ - 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"); - } - -#if !defined(__linux__) // lsof is really slow on OS X. This actually causes the gc-concurrent.sh test to fail. // See: https://github.com/NixOS/nix/issues/3011 // Because of this we disable lsof when running the tests. @@ -437,13 +340,13 @@ void LocalStore::findRuntimeRoots(Roots & roots, bool censor) /* lsof not installed, lsof failed */ } } -#endif +} + +void LocalStore::findRuntimeRoots(Roots & roots, bool censor) +{ + UncheckedRoots unchecked; -#if __linux__ - readFileRoots("/proc/sys/kernel/modprobe", unchecked); - readFileRoots("/proc/sys/kernel/fbsplash", unchecked); - readFileRoots("/proc/sys/kernel/poweroff_cmd", unchecked); -#endif + findPlatformRoots(unchecked); for (auto & [target, links] : unchecked) { if (!isInStore(target)) continue; |