blob: 716048fd8e451ab52b566f18639c420c833b7c45 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
#ifndef FILE_UPDATE_HANDLE_H_INCLUDED
#define FILE_UPDATE_HANDLE_H_INCLUDED
#include "win.h" //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 <class CreateFileCmd>
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
{
//zen::ScopeGuard guardErrorCode = zen::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
|