diff --git a/lix/libutil/file-system.cc b/lix/libutil/file-system.cc index d2e5523ae..23e36d611 100644 --- a/lix/libutil/file-system.cc +++ b/lix/libutil/file-system.cc @@ -381,11 +381,27 @@ void writeFile(const Path & path, std::string_view s, mode_t mode, bool sync) } -void writeFile(const Path & path, Source & source, mode_t mode, bool sync) +static AutoCloseFD openForWrite(const Path & path, mode_t mode) { AutoCloseFD fd{open(path.c_str(), O_WRONLY | O_TRUNC | O_CREAT | O_CLOEXEC, mode)}; if (!fd) throw SysError("opening file '%1%'", path); + return fd; +} + +static void closeForWrite(const Path & path, AutoCloseFD & fd, bool sync) +{ + 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 = openForWrite(path, mode); std::vector buf(64 * 1024); @@ -400,12 +416,32 @@ void writeFile(const Path & path, Source & source, mode_t mode, bool sync) 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); + closeForWrite(path, fd, sync); +} + +kj::Promise> +writeFile(const Path & path, AsyncInputStream & source, mode_t mode, bool sync) +try { + AutoCloseFD fd = openForWrite(path, mode); + + std::vector buf(64 * 1024); + + try { + while (true) { + if (auto n = TRY_AWAIT(source.read(buf.data(), buf.size()))) { + writeFull(fd.get(), {buf.data(), n}); + } else { + break; + } + } + } catch (Error & e) { + e.addTrace({}, "writing file '%1%'", path); + throw; + } + closeForWrite(path, fd, sync); + co_return result::success(); +} catch (...) { + co_return result::current_exception(); } void syncParent(const Path & path) diff --git a/lix/libutil/file-system.hh b/lix/libutil/file-system.hh index dcac51257..67f7102eb 100644 --- a/lix/libutil/file-system.hh +++ b/lix/libutil/file-system.hh @@ -5,6 +5,7 @@ * Utiltities for working with the file sytem and file paths. */ +#include "lix/libutil/async-io.hh" #include "lix/libutil/box_ptr.hh" #include "lix/libutil/types.hh" #include "lix/libutil/file-descriptor.hh" @@ -191,6 +192,8 @@ Generator readFileSource(const Path & path); 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); +kj::Promise> +writeFile(const Path & path, AsyncInputStream & source, mode_t mode = 0666, bool sync = false); /** * Flush a file's parent directory to disk