diff options
Diffstat (limited to '')
-rw-r--r-- | src/fs/io/FileOutputStream.cxx | 70 | ||||
-rw-r--r-- | src/fs/io/FileOutputStream.hxx | 80 |
2 files changed, 101 insertions, 49 deletions
diff --git a/src/fs/io/FileOutputStream.cxx b/src/fs/io/FileOutputStream.cxx index 85eb300e7..f587874a3 100644 --- a/src/fs/io/FileOutputStream.cxx +++ b/src/fs/io/FileOutputStream.cxx @@ -37,37 +37,33 @@ FileOutputStream::Create(Path path, Error &error) #ifdef WIN32 FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path), - handle(CreateFile(path.c_str(), GENERIC_WRITE, 0, nullptr, - CREATE_ALWAYS, - FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, - nullptr)) + :BaseFileOutputStream(_path) { - if (handle == INVALID_HANDLE_VALUE) { - const auto path_utf8 = path.ToUTF8(); + SetHandle(CreateFile(_path.c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, + nullptr)); + if (!IsDefined()) error.FormatLastError("Failed to create %s", - path_utf8.c_str()); - } + GetPath().ToUTF8().c_str()); } bool -FileOutputStream::Write(const void *data, size_t size, Error &error) +BaseFileOutputStream::Write(const void *data, size_t size, Error &error) { assert(IsDefined()); DWORD nbytes; if (!WriteFile(handle, data, size, &nbytes, nullptr)) { - const auto path_utf8 = path.ToUTF8(); error.FormatLastError("Failed to write to %s", - path_utf8.c_str()); + path.ToUTF8().c_str()); return false; } if (size_t(nbytes) != size) { - const auto path_utf8 = path.ToUTF8(); error.FormatLastError(ERROR_DISK_FULL, "Failed to write to %s", - path_utf8.c_str()); + path.ToUTF8().c_str()); return false; } @@ -79,8 +75,7 @@ FileOutputStream::Commit(gcc_unused Error &error) { assert(IsDefined()); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; + Close(); return true; } @@ -89,9 +84,8 @@ FileOutputStream::Cancel() { assert(IsDefined()); - CloseHandle(handle); - handle = INVALID_HANDLE_VALUE; - RemoveFile(path); + Close(); + RemoveFile(GetPath()); } #else @@ -124,35 +118,36 @@ OpenTempFile(FileDescriptor &fd, Path path) #endif /* HAVE_LINKAT */ FileOutputStream::FileOutputStream(Path _path, Error &error) - :path(_path) + :BaseFileOutputStream(_path) { #ifdef HAVE_LINKAT /* try Linux's O_TMPFILE first */ - is_tmpfile = OpenTempFile(fd, path); + is_tmpfile = OpenTempFile(SetFD(), GetPath()); if (!is_tmpfile) { #endif /* fall back to plain POSIX */ - if (!fd.Open(path.c_str(), - O_WRONLY|O_CREAT|O_TRUNC, - 0666)) - error.FormatErrno("Failed to create %s", path.c_str()); + if (!SetFD().Open(GetPath().c_str(), + O_WRONLY|O_CREAT|O_TRUNC, + 0666)) + error.FormatErrno("Failed to create %s", + GetPath().c_str()); #ifdef HAVE_LINKAT } #endif } bool -FileOutputStream::Write(const void *data, size_t size, Error &error) +BaseFileOutputStream::Write(const void *data, size_t size, Error &error) { assert(IsDefined()); ssize_t nbytes = fd.Write(data, size); if (nbytes < 0) { - error.FormatErrno("Failed to write to %s", path.c_str()); + error.FormatErrno("Failed to write to %s", GetPath().c_str()); return false; } else if ((size_t)nbytes < size) { error.FormatErrno(ENOSPC, - "Failed to write to %s", path.c_str()); + "Failed to write to %s", GetPath().c_str()); return false; } @@ -166,24 +161,25 @@ FileOutputStream::Commit(Error &error) #if HAVE_LINKAT if (is_tmpfile) { - RemoveFile(path); + RemoveFile(GetPath()); /* hard-link the temporary file to the final path */ char fd_path[64]; snprintf(fd_path, sizeof(fd_path), "/proc/self/fd/%d", - fd.Get()); - if (linkat(AT_FDCWD, fd_path, AT_FDCWD, path.c_str(), + GetFD().Get()); + if (linkat(AT_FDCWD, fd_path, AT_FDCWD, GetPath().c_str(), AT_SYMLINK_FOLLOW) < 0) { - error.FormatErrno("Failed to commit %s", path.c_str()); - fd.Close(); + error.FormatErrno("Failed to commit %s", + GetPath().c_str()); + Close(); return false; } } #endif - bool success = fd.Close(); + bool success = Close(); if (!success) - error.FormatErrno("Failed to commit %s", path.c_str()); + error.FormatErrno("Failed to commit %s", GetPath().c_str()); return success; } @@ -193,12 +189,12 @@ FileOutputStream::Cancel() { assert(IsDefined()); - fd.Close(); + Close(); #ifdef HAVE_LINKAT if (!is_tmpfile) #endif - RemoveFile(path); + RemoveFile(GetPath()); } #endif diff --git a/src/fs/io/FileOutputStream.hxx b/src/fs/io/FileOutputStream.hxx index 7d30d95bb..ce53427e2 100644 --- a/src/fs/io/FileOutputStream.hxx +++ b/src/fs/io/FileOutputStream.hxx @@ -37,7 +37,7 @@ class Path; -class FileOutputStream final : public OutputStream { +class BaseFileOutputStream : public OutputStream { const AllocatedPath path; #ifdef WIN32 @@ -46,6 +46,73 @@ class FileOutputStream final : public OutputStream { FileDescriptor fd; #endif +protected: +#ifdef WIN32 + template<typename P> + BaseFileOutputStream(P &&_path) + :path(std::forward<P>(_path)), + handle(INVALID_HANDLE_VALUE) {} +#else + template<typename P> + BaseFileOutputStream(P &&_path) + :path(std::forward<P>(_path)), + fd(FileDescriptor::Undefined()) {} +#endif + + ~BaseFileOutputStream() { + assert(!IsDefined()); + } + +#ifdef WIN32 + void SetHandle(HANDLE _handle) { + assert(!IsDefined()); + + handle = _handle; + + assert(IsDefined()); + } +#else + FileDescriptor &SetFD() { + assert(!IsDefined()); + + return fd; + } + + const FileDescriptor &GetFD() const { + return fd; + } +#endif + + bool Close() { + assert(IsDefined()); + +#ifdef WIN32 + CloseHandle(handle); + handle = INVALID_HANDLE_VALUE; + return true; +#else + return fd.Close(); +#endif + } + +public: + bool IsDefined() const { +#ifdef WIN32 + return handle != INVALID_HANDLE_VALUE; +#else + return fd.IsDefined(); +#endif + } + + Path GetPath() const { + return path; + } + + /* virtual methods from class OutputStream */ + bool Write(const void *data, size_t size, Error &error) override; +}; + +class FileOutputStream final : public BaseFileOutputStream { #ifdef HAVE_LINKAT /** * Was O_TMPFILE used? If yes, then linkat() must be used to @@ -64,19 +131,8 @@ public: static FileOutputStream *Create(Path path, Error &error); - bool IsDefined() const { -#ifdef WIN32 - return handle != INVALID_HANDLE_VALUE; -#else - return fd.IsDefined(); -#endif - } - bool Commit(Error &error); void Cancel(); - - /* virtual methods from class OutputStream */ - bool Write(const void *data, size_t size, Error &error) override; }; #endif |