From 7f23ee90fd545995a29e2175f15e8b97e59ca67a Mon Sep 17 00:00:00 2001 From: Daniel Wilhelm Date: Fri, 18 Apr 2014 17:13:13 +0200 Subject: 3.20 --- shared/file_update_handle.h | 67 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 shared/file_update_handle.h (limited to 'shared/file_update_handle.h') diff --git a/shared/file_update_handle.h b/shared/file_update_handle.h new file mode 100644 index 00000000..1ffb7000 --- /dev/null +++ b/shared/file_update_handle.h @@ -0,0 +1,67 @@ +#ifndef FILE_UPDATE_HANDLE_H_INCLUDED +#define FILE_UPDATE_HANDLE_H_INCLUDED + +#include //includes "windows.h" +#include "long_path_prefix.h" + +namespace +{ +//manage file handle to update existing files (temporarily resetting read-only if necessary) +//CreateFileCmd: lambda directly returning non-owned file handle from ::CreateFile() +class FileUpdateHandle +{ +public: + template + FileUpdateHandle(const Zstring& filename, CreateFileCmd cmd) : + filenameFmt(zen::applyLongPathPrefix(filename)), + hFile(INVALID_HANDLE_VALUE), + attr(INVALID_FILE_ATTRIBUTES) + { + hFile = cmd(); + if (hFile != INVALID_HANDLE_VALUE) + return; + + const DWORD lastError = ::GetLastError(); + if (lastError == ERROR_ACCESS_DENIED) //function fails if file is read-only + { + Loki::ScopeGuard guardErrorCode = Loki::MakeGuard(::SetLastError, lastError); //transactional behavior: ensure cleanup (e.g. network drop) -> cref [!] + + //read-only file attribute may cause trouble: temporarily reset it + const DWORD tmpAttr = ::GetFileAttributes(filenameFmt.c_str()); + if (tmpAttr != INVALID_FILE_ATTRIBUTES && (tmpAttr & FILE_ATTRIBUTE_READONLY)) + { + if (::SetFileAttributes(filenameFmt.c_str(), FILE_ATTRIBUTE_NORMAL)) + { + guardErrorCode.Dismiss(); + attr = tmpAttr; //"create" guard on read-only attribute + + //now try again + hFile = cmd(); + } + } + } + } + + ~FileUpdateHandle() + { + if (hFile != INVALID_HANDLE_VALUE) + ::CloseHandle(hFile); + + if (attr != INVALID_FILE_ATTRIBUTES) + ::SetFileAttributes(filenameFmt.c_str(), attr); + } + + //may return INVALID_FILE_ATTRIBUTES, in which case ::GetLastError() may be called directly after FileUpdateHandle() + HANDLE get() const { return hFile; } + +private: + FileUpdateHandle(const FileUpdateHandle&); + FileUpdateHandle& operator=(const FileUpdateHandle&); + + Zstring filenameFmt; + HANDLE hFile; + DWORD attr; +}; +} + +#endif // FILE_UPDATE_HANDLE_H_INCLUDED -- cgit