#pragma once /** * @file * * Utiltities for working with the file sytem and file paths. */ #include "types.hh" #include "error.hh" #include "logging.hh" #include "file-descriptor.hh" #include "file-path.hh" #include #include #include #include #ifdef _WIN32 # include #endif #include #include #include #include #include #include #include /** * Polyfill for MinGW * * Windows does in fact support symlinks, but the C runtime interfaces predate this. * * @todo get rid of this, and stop using `stat` when we want `lstat` too. */ #ifndef S_ISLNK # define S_ISLNK(m) false #endif namespace nix { struct Sink; struct Source; /** * @return An absolutized path, resolving paths relative to the * specified directory, or the current directory otherwise. The path * is also canonicalised. */ Path absPath(PathView path, std::optional dir = {}, bool resolveSymlinks = false); /** * Canonicalise a path by removing all `.` or `..` components and * double or trailing slashes. Optionally resolves all symlink * components such that each component of the resulting path is *not* * a symbolic link. */ Path canonPath(PathView path, bool resolveSymlinks = false); /** * @return The directory part of the given canonical path, i.e., * everything before the final `/`. If the path is the root or an * immediate child thereof (e.g., `/foo`), this means `/` * is returned. */ Path dirOf(const PathView path); /** * @return the base name of the given canonical path, i.e., everything * following the final `/` (trailing slashes are removed). */ std::string_view baseNameOf(std::string_view path); /** * Check whether 'path' is a descendant of 'dir'. Both paths must be * canonicalized. */ bool isInDir(std::string_view path, std::string_view dir); /** * Check whether 'path' is equal to 'dir' or a descendant of * 'dir'. Both paths must be canonicalized. */ bool isDirOrInDir(std::string_view path, std::string_view dir); /** * Get status of `path`. */ struct stat stat(const Path & path); struct stat lstat(const Path & path); /** * `lstat` the given path if it exists. * @return std::nullopt if the path doesn't exist, or an optional containing the result of `lstat` otherwise */ std::optional maybeLstat(const Path & path); /** * @return true iff the given path exists. */ bool pathExists(const Path & path); /** * A version of pathExists that returns false on a permission error. * Useful for inferring default paths across directories that might not * be readable. * @return true iff the given path can be accessed and exists */ bool pathAccessible(const Path & path); /** * Read the contents (target) of a symbolic link. The result is not * in any way canonicalised. */ Path readLink(const Path & path); /** * Open a `Descriptor` with read-only access to the given directory. */ Descriptor openDirectory(const std::filesystem::path & path); /** * Read the contents of a directory. The entries `.` and `..` are * removed. */ std::vector readDirectory(const Path & path); /** * Read the contents of a file into a string. */ std::string readFile(const Path & path); void readFile(const Path & path, Sink & sink); /** * Write a string to a file. */ void writeFile(const Path & path, std::string_view s, mode_t mode = 0666, bool sync = false); void writeFile(const Path & path, Source & source, mode_t mode = 0666, bool sync = false); /** * Flush a file's parent directory to disk */ void syncParent(const Path & path); /** * Delete a path; i.e., in the case of a directory, it is deleted * recursively. It's not an error if the path does not exist. The * second variant returns the number of bytes and blocks freed. */ void deletePath(const std::filesystem::path & path); void deletePath(const std::filesystem::path & path, uint64_t & bytesFreed); /** * Create a directory and all its parents, if necessary. Returns the * list of created directories, in order of creation. */ Paths createDirs(const Path & path); inline Paths createDirs(PathView path) { return createDirs(Path(path)); } /** * Create a symlink. */ void createSymlink(const Path & target, const Path & link); /** * Atomically create or replace a symlink. */ void replaceSymlink(const Path & target, const Path & link); /** * Similar to 'renameFile', but fallback to a copy+remove if `src` and `dst` * are on a different filesystem. * * Beware that this might not be atomic because of the copy that happens behind * the scenes */ void moveFile(const Path & src, const Path & dst); /** * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but * with the guaranty that the destination will be “fresh”, with no stale inode * or file descriptor pointing to it). */ void copyFile(const std::filesystem::directory_entry & from, const std::filesystem::path & to, bool andDelete); /** * Automatic cleanup of resources. */ class AutoDelete { std::filesystem::path _path; bool del; bool recursive; public: AutoDelete(); AutoDelete(const std::filesystem::path & p, bool recursive = true); ~AutoDelete(); void cancel(); void reset(const std::filesystem::path & p, bool recursive = true); const std::filesystem::path & path() const { return _path; } PathViewNG view() const { return _path; } operator const std::filesystem::path & () const { return _path; } operator PathViewNG () const { return _path; } }; struct DIRDeleter { void operator()(DIR * dir) const { closedir(dir); } }; typedef std::unique_ptr AutoCloseDir; /** * Create a temporary directory. */ Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix", bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755); /** * Create a temporary file, returning a file handle and its path. */ std::pair createTempFile(const Path & prefix = "nix"); /** * Return `TMPDIR`, or the default temporary directory if unset or empty. */ Path defaultTempDir(); /** * Used in various places. */ typedef std::function PathFilter; extern PathFilter defaultPathFilter; }