#pragma once #include #include #include #include #include #include #include namespace doos { class SysError : public std::runtime_error { public: explicit SysError(const std::string& what, int err = errno); int code() const { return code_; } private: int code_; }; class Fd { public: Fd() = default; explicit Fd(int fd) : fd_(fd) {} Fd(Fd&& o) noexcept : fd_(o.release()) {} Fd& operator=(Fd&& o) noexcept; Fd(const Fd&) = delete; Fd& operator=(const Fd&) = delete; ~Fd() { reset(); } int get() const { return fd_; } int release(); void reset(int fd = -1); bool valid() const { return fd_ >= 0; } private: int fd_ = -1; }; // flock(2) on a file for the lifetime of the object, bounded wait. class FileLock { public: FileLock(const std::filesystem::path& file, int timeoutMs); ~FileLock() = default; void unlock() { fd_.reset(); } int fd() const { return fd_.get(); } private: Fd fd_; }; // Parses "512m", "2g", "1t" (k/m/g/t, optional trailing b) into bytes; throws on garbage. std::uint64_t parseSize(const std::string& text); std::string formatSize(std::uint64_t bytes); std::string base64Decode(const std::string& in); // lenient on whitespace, throws on bad characters struct ProcResult { int code = -1; std::string out; // stdout, only when captured }; // fork+exec without a shell. Captured output is capped; stderr is inherited unless quiet. ProcResult runProcess(const std::vector& argv, bool capture = false, bool quiet = false); namespace fsutil { struct SeedStats { std::size_t made = 0; std::size_t skipped = 0; // unreadable or unsupported entries, never silent std::uint64_t bytes = 0; }; // Creates every directory of `lower` in `upper` (owner rwx added) so a unmapped-owner lower stays writable. SeedStats mirrorDirs(const std::filesystem::path& lower, const std::filesystem::path& upper); // Copies dirs, regular files and symlinks readable by the caller. Capped by spec.hpp limits. // `skip` (optional) is asked for every entry and returns true to leave it out (counted as skipped). using SkipFn = std::function; SeedStats copyTree(const std::filesystem::path& from, const std::filesystem::path& to, const SkipFn& skip = nullptr); // Copies only the named entries (relative paths, files, links or dirs) of `from` into `to`. SeedStats copyEntries(const std::filesystem::path& from, const std::filesystem::path& to, const std::vector& entries, const SkipFn& skip); // Copies only the regular files directly inside `from` (readable ones) into `to`; existing targets stay. SeedStats copyFiles(const std::filesystem::path& from, const std::filesystem::path& to); // Sum of allocated bytes (st_blocks) below p, lstat only. `skipped` counts unreadable dirs. std::uint64_t diskUsage(const std::filesystem::path& p, std::size_t& skipped); void mkdirs(const std::filesystem::path& p, mode_t mode = 0755); void writeFile(const std::filesystem::path& p, const std::string& content); std::string readFile(const std::filesystem::path& p); // Removes a tree without following symlinks; bounded depth. void removeTree(const std::filesystem::path& p, std::size_t depth = 0); std::size_t countEntries(const std::filesystem::path& p, std::size_t cap); } // namespace fsutil } // namespace doos