aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/util.cc
diff options
context:
space:
mode:
authorTom Hubrecht <github@mail.hubrecht.ovh>2024-05-28 11:52:13 +0200
committerTom Hubrecht <github@mail.hubrecht.ovh>2024-05-29 09:54:47 +0200
commit6b5078c81554ddb36547f8c41805cc94b7738396 (patch)
tree0e0c3552d9d397920326b6e21cff278c4b272935 /src/libutil/util.cc
parent81bdf8d2d672e135e68745e6975ad5edafadf13a (diff)
util.{hh,cc}: Split out file-system.{hh,cc}
Change-Id: Ifa89a529e7e34e7291eca87d802d2f569cf2493e
Diffstat (limited to 'src/libutil/util.cc')
-rw-r--r--src/libutil/util.cc501
1 files changed, 1 insertions, 500 deletions
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index 63d9e5248..ac3071ba8 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -5,6 +5,7 @@
#include "cgroup.hh"
#include "signals.hh"
#include "environment-variables.hh"
+#include "file-system.hh"
#include <array>
#include <cctype>
@@ -50,352 +51,6 @@
namespace nix {
-Path absPath(Path path, std::optional<PathView> dir, bool resolveSymlinks)
-{
- if (path.empty() || path[0] != '/') {
- if (!dir) {
-#ifdef __GNU__
- /* GNU (aka. GNU/Hurd) doesn't have any limitation on path
- lengths and doesn't define `PATH_MAX'. */
- char *buf = getcwd(NULL, 0);
- if (buf == NULL)
-#else
- char buf[PATH_MAX];
- if (!getcwd(buf, sizeof(buf)))
-#endif
- throw SysError("cannot get cwd");
- path = concatStrings(buf, "/", path);
-#ifdef __GNU__
- free(buf);
-#endif
- } else
- path = concatStrings(*dir, "/", path);
- }
- return canonPath(path, resolveSymlinks);
-}
-
-
-Path canonPath(PathView path, bool resolveSymlinks)
-{
- assert(path != "");
-
- std::string s;
- s.reserve(256);
-
- if (path[0] != '/')
- throw Error("not an absolute path: '%1%'", path);
-
- std::string temp;
-
- /* Count the number of times we follow a symlink and stop at some
- arbitrary (but high) limit to prevent infinite loops. */
- unsigned int followCount = 0, maxFollow = 1024;
-
- while (1) {
-
- /* Skip slashes. */
- while (!path.empty() && path[0] == '/') path.remove_prefix(1);
- if (path.empty()) break;
-
- /* Ignore `.'. */
- if (path == "." || path.substr(0, 2) == "./")
- path.remove_prefix(1);
-
- /* If `..', delete the last component. */
- else if (path == ".." || path.substr(0, 3) == "../")
- {
- if (!s.empty()) s.erase(s.rfind('/'));
- path.remove_prefix(2);
- }
-
- /* Normal component; copy it. */
- else {
- s += '/';
- if (const auto slash = path.find('/'); slash == std::string::npos) {
- s += path;
- path = {};
- } else {
- s += path.substr(0, slash);
- path = path.substr(slash);
- }
-
- /* If s points to a symlink, resolve it and continue from there */
- if (resolveSymlinks && isLink(s)) {
- if (++followCount >= maxFollow)
- throw Error("infinite symlink recursion in path '%1%'", path);
- temp = concatStrings(readLink(s), path);
- path = temp;
- if (!temp.empty() && temp[0] == '/') {
- s.clear(); /* restart for symlinks pointing to absolute path */
- } else {
- s = dirOf(s);
- if (s == "/") { // we don’t want trailing slashes here, which dirOf only produces if s = /
- s.clear();
- }
- }
- }
- }
- }
-
- return s.empty() ? "/" : std::move(s);
-}
-
-void chmodPath(const Path & path, mode_t mode)
-{
- if (chmod(path.c_str(), mode) == -1)
- throw SysError("setting permissions on '%s'", path);
-}
-
-Path dirOf(const PathView path)
-{
- Path::size_type pos = path.rfind('/');
- if (pos == std::string::npos)
- return ".";
- return pos == 0 ? "/" : Path(path, 0, pos);
-}
-
-
-std::string_view baseNameOf(std::string_view path)
-{
- if (path.empty())
- return "";
-
- auto last = path.size() - 1;
- if (path[last] == '/' && last > 0)
- last -= 1;
-
- auto pos = path.rfind('/', last);
- if (pos == std::string::npos)
- pos = 0;
- else
- pos += 1;
-
- return path.substr(pos, last - pos + 1);
-}
-
-
-std::string expandTilde(std::string_view path)
-{
- // TODO: expand ~user ?
- auto tilde = path.substr(0, 2);
- if (tilde == "~/" || tilde == "~")
- return getHome() + std::string(path.substr(1));
- else
- return std::string(path);
-}
-
-
-bool isInDir(std::string_view path, std::string_view dir)
-{
- return path.substr(0, 1) == "/"
- && path.substr(0, dir.size()) == dir
- && path.size() >= dir.size() + 2
- && path[dir.size()] == '/';
-}
-
-
-bool isDirOrInDir(std::string_view path, std::string_view dir)
-{
- return path == dir || isInDir(path, dir);
-}
-
-
-struct stat stat(const Path & path)
-{
- struct stat st;
- if (stat(path.c_str(), &st))
- throw SysError("getting status of '%1%'", path);
- return st;
-}
-
-
-struct stat lstat(const Path & path)
-{
- struct stat st;
- if (lstat(path.c_str(), &st))
- throw SysError("getting status of '%1%'", path);
- return st;
-}
-
-std::optional<struct stat> maybeLstat(const Path & path)
-{
- std::optional<struct stat> st{std::in_place};
- if (lstat(path.c_str(), &*st))
- {
- if (errno == ENOENT || errno == ENOTDIR)
- st.reset();
- else
- throw SysError("getting status of '%s'", path);
- }
- return st;
-}
-
-bool pathExists(const Path & path)
-{
- return maybeLstat(path).has_value();
-}
-
-bool pathAccessible(const Path & path)
-{
- try {
- return pathExists(path);
- } catch (SysError & e) {
- // swallow EPERM
- if (e.errNo == EPERM) return false;
- throw;
- }
-}
-
-
-Path readLink(const Path & path)
-{
- checkInterrupt();
- 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);
- if (rlSize == -1)
- if (errno == EINVAL)
- throw Error("'%1%' is not a symlink", path);
- else
- throw SysError("reading symbolic link '%1%'", path);
- else if (rlSize < bufSize)
- return std::string(buf.data(), rlSize);
- }
-}
-
-
-bool isLink(const Path & path)
-{
- struct stat st = lstat(path);
- return S_ISLNK(st.st_mode);
-}
-
-
-DirEntries readDirectory(DIR *dir, const Path & path)
-{
- DirEntries entries;
- entries.reserve(64);
-
- struct dirent * dirent;
- while (errno = 0, dirent = readdir(dir)) { /* sic */
- checkInterrupt();
- std::string name = dirent->d_name;
- if (name == "." || name == "..") continue;
- entries.emplace_back(name, dirent->d_ino,
-#ifdef HAVE_STRUCT_DIRENT_D_TYPE
- dirent->d_type
-#else
- DT_UNKNOWN
-#endif
- );
- }
- if (errno) throw SysError("reading directory '%1%'", path);
-
- return entries;
-}
-
-DirEntries readDirectory(const Path & path)
-{
- AutoCloseDir dir(opendir(path.c_str()));
- if (!dir) throw SysError("opening directory '%1%'", path);
-
- return readDirectory(dir.get(), path);
-}
-
-
-unsigned char getFileType(const Path & path)
-{
- struct stat st = lstat(path);
- if (S_ISDIR(st.st_mode)) return DT_DIR;
- if (S_ISLNK(st.st_mode)) return DT_LNK;
- if (S_ISREG(st.st_mode)) return DT_REG;
- return DT_UNKNOWN;
-}
-
-
-std::string readFile(int fd)
-{
- struct stat st;
- if (fstat(fd, &st) == -1)
- throw SysError("statting file");
-
- return drainFD(fd, true, st.st_size);
-}
-
-
-std::string readFile(const Path & path)
-{
- AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
- if (!fd)
- throw SysError("opening file '%1%'", path);
- return readFile(fd.get());
-}
-
-
-void readFile(const Path & path, Sink & sink)
-{
- AutoCloseFD fd{open(path.c_str(), O_RDONLY | O_CLOEXEC)};
- if (!fd)
- throw SysError("opening file '%s'", path);
- drainFD(fd.get(), sink);
-}
-
-
-void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync)
-{
- AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
- if (!fd)
- throw SysError("opening file '%1%'", path);
- try {
- writeFull(fd.get(), s);
- } catch (Error & e) {
- e.addTrace({}, "writing file '%1%'", path);
- throw;
- }
- if (sync)
- fd.fsync();
- // Explicitly close to make sure exceptions are propagated.
- fd.close();
- if (sync)
- syncParent(path);
-}
-
-
-void writeFile(const Path & path, Source & source, mode_t mode, bool sync)
-{
- AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)};
- if (!fd)
- throw SysError("opening file '%1%'", path);
-
- std::vector<char> buf(64 * 1024);
-
- try {
- while (true) {
- try {
- auto n = source.read(buf.data(), buf.size());
- writeFull(fd.get(), {buf.data(), n});
- } catch (EndOfFile &) { break; }
- }
- } catch (Error & e) {
- e.addTrace({}, "writing file '%1%'", path);
- throw;
- }
- if (sync)
- fd.fsync();
- // Explicitly close to make sure exceptions are propagated.
- fd.close();
- if (sync)
- syncParent(path);
-}
-
-void syncParent(const Path & path)
-{
- AutoCloseFD fd{open(dirOf(path).c_str(), O_RDONLY, 0)};
- if (!fd)
- throw SysError("opening file '%1%'", path);
- fd.fsync();
-}
std::string readLine(int fd)
{
@@ -425,98 +80,6 @@ void writeLine(int fd, std::string s)
}
-static void _deletePath(int parentfd, const Path & path, uint64_t & bytesFreed)
-{
- checkInterrupt();
-
- std::string name(baseNameOf(path));
-
- struct stat st;
- if (fstatat(parentfd, name.c_str(), &st, AT_SYMLINK_NOFOLLOW) == -1) {
- if (errno == ENOENT) return;
- throw SysError("getting status of '%1%'", path);
- }
-
- if (!S_ISDIR(st.st_mode)) {
- /* We are about to delete a file. Will it likely free space? */
-
- switch (st.st_nlink) {
- /* Yes: last link. */
- case 1:
- bytesFreed += st.st_size;
- break;
- /* Maybe: yes, if 'auto-optimise-store' or manual optimisation
- was performed. Instead of checking for real let's assume
- it's an optimised file and space will be freed.
-
- In worst case we will double count on freed space for files
- with exactly two hardlinks for unoptimised packages.
- */
- case 2:
- bytesFreed += st.st_size;
- break;
- /* No: 3+ links. */
- default:
- break;
- }
- }
-
- if (S_ISDIR(st.st_mode)) {
- /* 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)
- throw SysError("chmod '%1%'", path);
- }
-
- int fd = openat(parentfd, path.c_str(), O_RDONLY);
- if (fd == -1)
- throw SysError("opening directory '%1%'", path);
- AutoCloseDir dir(fdopendir(fd));
- if (!dir)
- throw SysError("opening directory '%1%'", path);
- for (auto & i : readDirectory(dir.get(), path))
- _deletePath(dirfd(dir.get()), path + "/" + i.name, bytesFreed);
- }
-
- int flags = S_ISDIR(st.st_mode) ? AT_REMOVEDIR : 0;
- if (unlinkat(parentfd, name.c_str(), flags) == -1) {
- if (errno == ENOENT) return;
- throw SysError("cannot unlink '%1%'", path);
- }
-}
-
-static void _deletePath(const Path & path, uint64_t & bytesFreed)
-{
- Path dir = dirOf(path);
- if (dir == "")
- dir = "/";
-
- AutoCloseFD dirfd{open(dir.c_str(), O_RDONLY)};
- if (!dirfd) {
- if (errno == ENOENT) return;
- throw SysError("opening directory '%1%'", path);
- }
-
- _deletePath(dirfd.get(), path, bytesFreed);
-}
-
-
-void deletePath(const Path & path)
-{
- uint64_t dummy;
- deletePath(path, dummy);
-}
-
-
-void deletePath(const Path & path, uint64_t & bytesFreed)
-{
- //Activity act(*logger, lvlDebug, "recursively deleting path '%1%'", path);
- bytesFreed = 0;
- _deletePath(path, bytesFreed);
-}
-
-
std::string getUserName()
{
auto pw = getpwuid(geteuid());
@@ -632,28 +195,6 @@ std::optional<Path> getSelfExe()
}
-Paths createDirs(const Path & path)
-{
- Paths created;
- if (path == "/") return created;
-
- struct stat st;
- if (lstat(path.c_str(), &st) == -1) {
- created = createDirs(dirOf(path));
- if (mkdir(path.c_str(), 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)
- throw SysError("statting symlink '%1%'", path);
-
- if (!S_ISDIR(st.st_mode)) throw Error("'%1%' is not a directory", path);
-
- return created;
-}
-
void readFull(int fd, char * buf, size_t count)
{
@@ -762,46 +303,6 @@ unsigned int getMaxCPU()
//////////////////////////////////////////////////////////////////////
-AutoDelete::AutoDelete() : del{false} {}
-
-AutoDelete::AutoDelete(const std::string & p, bool recursive) : path(p)
-{
- del = true;
- this->recursive = recursive;
-}
-
-AutoDelete::~AutoDelete()
-{
- try {
- if (del) {
- if (recursive)
- deletePath(path);
- else {
- if (remove(path.c_str()) == -1)
- throw SysError("cannot unlink '%1%'", path);
- }
- }
- } catch (...) {
- ignoreException();
- }
-}
-
-void AutoDelete::cancel()
-{
- del = false;
-}
-
-void AutoDelete::reset(const Path & p, bool recursive) {
- path = p;
- this->recursive = recursive;
- del = true;
-}
-
-
-
-//////////////////////////////////////////////////////////////////////
-
-
AutoCloseFD::AutoCloseFD() : fd{-1} {}