92 lines
3.4 KiB
C++
92 lines
3.4 KiB
C++
#pragma once
|
|
#include <cerrno>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <functional>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<std::string>& 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<bool(const std::filesystem::path&)>;
|
|
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<std::string>& 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
|