#include
#include
#include
#include
#include
#include
#ifndef _WIN32
#include
#include
#include
#endif
namespace vix::cli::util
{
namespace fs = std::filesystem;
namespace {
std::atomic sequence{0};
std::optional read_bytes(const fs::path &p) { std::ifstream in(p, std::ios::binary); if (!in) return std::nullopt; return std::string((std::istreambuf_iterator(in)), {}); }
bool write_bytes(const fs::path &p, const std::string &bytes) { std::ofstream out(p, std::ios::binary | std::ios::trunc); return out && static_cast(out > kind >> existed >> index >> std::quoted(relative)) {
const fs::path target=root/fs::path(relative);
if (!under_root(root, target)) { error="unsafe transaction journal path"; return false; }
if (existed) { const auto bytes=read_bytes(journal/("original-"+std::to_string(index))); if (!bytes || !write_bytes(target, *bytes)) { error="cannot restore unfinished transaction"; return false; } }
else { fs::remove(target, ec); if (ec) { error="cannot remove unfinished transaction output: "+ec.message(); return false; } }
}
if (!in.eof()) { error="invalid transaction journal"; return false; }
fs::remove_all(journal, ec); if (ec) { error="cannot remove recovered transaction journal: "+ec.message(); return false; }
}
return true;
}
}
ProjectMutationLock::ProjectMutationLock(const fs::path &root)
{
#ifdef _WIN32
error_ = "Project mutation lock is not implemented on this platform.";
#else
std::error_code ec; fs::create_directories(root / ".vix" / "locks", ec); if (ec) { error_="cannot create mutation lock directory: "+ec.message(); return; }
const auto path = root / ".vix" / "locks" / "project-mutation.lock";
fd_ = ::open(path.c_str(), O_CREAT | O_RDWR, 0600); if (fd_ < 0) { error_="cannot open project mutation lock"; return; }
if (::flock(fd_, LOCK_EX | LOCK_NB) != 0) { ::close(fd_); fd_=-1; error_="another Vix project mutation is active"; return; }
if (!recover_transactions(root, error_)) { ::flock(fd_, LOCK_UN); ::close(fd_); fd_=-1; }
#endif
}
ProjectMutationLock::~ProjectMutationLock()
{
#ifndef _WIN32
if (fd_ >= 0) { ::flock(fd_, LOCK_UN); ::close(fd_); }
#endif
}
ProjectMutationTransaction::ProjectMutationTransaction(const fs::path &root) : root_(root) {}
ProjectMutationTransaction::~ProjectMutationTransaction() { if (!finished_) rollback(); }
bool ProjectMutationTransaction::stage_write(const fs::path &path, const std::string &bytes, std::string &error)
{
if (finished_) { error="transaction is already finished"; return false; }
std::error_code ec; fs::create_directories(path.parent_path(),ec); if (ec) { error="staging failed: "+ec.message(); return false; }
Item item; item.path=path; item.original=read_bytes(path); item.temporary=path.string()+".vix-txn-"+std::to_string(++sequence);
{ std::ofstream out(item.temporary,std::ios::binary|std::ios::trunc); if (!out || !(out