|
1
|
1 #include "StdAfx.h"
|
|
|
2
|
|
|
3 #ifdef _WIN32
|
|
|
4
|
|
|
5 #include "file_win32_wrapper.h"
|
|
|
6
|
|
|
7 namespace file_win32_helpers {
|
|
|
8 t_filesize get_size(HANDLE p_handle) {
|
|
|
9 LARGE_INTEGER v = {};
|
|
|
10 WIN32_IO_OP(GetFileSizeEx(p_handle, &v));
|
|
|
11 return make_uint64(v);
|
|
|
12 }
|
|
|
13 void seek(HANDLE p_handle,t_sfilesize p_position,file::t_seek_mode p_mode) {
|
|
|
14 union {
|
|
|
15 t_int64 temp64;
|
|
|
16 struct {
|
|
|
17 DWORD temp_lo;
|
|
|
18 LONG temp_hi;
|
|
|
19 };
|
|
|
20 };
|
|
|
21
|
|
|
22 temp64 = p_position;
|
|
|
23 SetLastError(ERROR_SUCCESS);
|
|
|
24 temp_lo = SetFilePointer(p_handle,temp_lo,&temp_hi,(DWORD)p_mode);
|
|
|
25 if (GetLastError() != ERROR_SUCCESS) exception_io_from_win32(GetLastError());
|
|
|
26 }
|
|
|
27
|
|
|
28 void fillOverlapped(OVERLAPPED & ol, HANDLE myEvent, t_filesize s) {
|
|
|
29 ol.hEvent = myEvent;
|
|
|
30 ol.Offset = (DWORD)( s & 0xFFFFFFFF );
|
|
|
31 ol.OffsetHigh = (DWORD)(s >> 32);
|
|
|
32 }
|
|
|
33
|
|
|
34 void writeOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, const void * in,DWORD inBytes, abort_callback & abort) {
|
|
|
35 abort.check();
|
|
|
36 if (inBytes == 0) return;
|
|
|
37 OVERLAPPED ol = {};
|
|
|
38 fillOverlapped(ol, myEvent, position);
|
|
|
39 ResetEvent(myEvent);
|
|
|
40 DWORD bytesWritten;
|
|
|
41 SetLastError(NO_ERROR);
|
|
|
42 if (WriteFile( handle, in, inBytes, &bytesWritten, &ol)) {
|
|
|
43 // succeeded already?
|
|
|
44 if (bytesWritten != inBytes) throw exception_io();
|
|
|
45 return;
|
|
|
46 }
|
|
|
47
|
|
|
48 {
|
|
|
49 const DWORD code = GetLastError();
|
|
|
50 if (code != ERROR_IO_PENDING) exception_io_from_win32(code);
|
|
|
51 }
|
|
|
52 const HANDLE handles[] = {myEvent, abort.get_abort_event()};
|
|
|
53 SetLastError(NO_ERROR);
|
|
|
54 DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE);
|
|
|
55 if (state == WAIT_OBJECT_0) {
|
|
|
56 try {
|
|
|
57 WIN32_IO_OP( GetOverlappedResult(handle,&ol,&bytesWritten,TRUE) );
|
|
|
58 } catch(...) {
|
|
|
59 CancelIo(handle);
|
|
|
60 throw;
|
|
|
61 }
|
|
|
62 if (bytesWritten != inBytes) throw exception_io();
|
|
|
63 return;
|
|
|
64 }
|
|
|
65 CancelIo(handle);
|
|
|
66 throw exception_aborted();
|
|
|
67 }
|
|
|
68
|
|
|
69 void writeOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, const void * in, size_t inBytes, abort_callback & abort) {
|
|
|
70 enum {writeMAX = 16*1024*1024};
|
|
|
71 size_t done = 0;
|
|
|
72 while(done < inBytes) {
|
|
|
73 size_t delta = inBytes - done;
|
|
|
74 if (delta > writeMAX) delta = writeMAX;
|
|
|
75 writeOverlappedPass(handle, myEvent, position, (const BYTE*)in + done, (DWORD) delta, abort);
|
|
|
76 done += delta;
|
|
|
77 position += delta;
|
|
|
78 }
|
|
|
79 }
|
|
|
80 void writeStreamOverlapped(HANDLE handle, HANDLE myEvent, const void * in, size_t inBytes, abort_callback & abort) {
|
|
|
81 enum {writeMAX = 16*1024*1024};
|
|
|
82 size_t done = 0;
|
|
|
83 while(done < inBytes) {
|
|
|
84 size_t delta = inBytes - done;
|
|
|
85 if (delta > writeMAX) delta = writeMAX;
|
|
|
86 writeOverlappedPass(handle, myEvent, 0, (const BYTE*)in + done, (DWORD) delta, abort);
|
|
|
87 done += delta;
|
|
|
88 }
|
|
|
89 }
|
|
|
90
|
|
|
91 DWORD readOverlappedPass(HANDLE handle, HANDLE myEvent, t_filesize position, void * out, DWORD outBytes, abort_callback & abort) {
|
|
|
92 abort.check();
|
|
|
93 if (outBytes == 0) return 0;
|
|
|
94 OVERLAPPED ol = {};
|
|
|
95 fillOverlapped(ol, myEvent, position);
|
|
|
96 ResetEvent(myEvent);
|
|
|
97 DWORD bytesDone;
|
|
|
98 SetLastError(NO_ERROR);
|
|
|
99 if (ReadFile( handle, out, outBytes, &bytesDone, &ol)) {
|
|
|
100 // succeeded already?
|
|
|
101 return bytesDone;
|
|
|
102 }
|
|
|
103
|
|
|
104 {
|
|
|
105 const DWORD code = GetLastError();
|
|
|
106 switch(code) {
|
|
|
107 case ERROR_HANDLE_EOF:
|
|
|
108 case ERROR_BROKEN_PIPE:
|
|
|
109 return 0;
|
|
|
110 case ERROR_IO_PENDING:
|
|
|
111 break; // continue
|
|
|
112 default:
|
|
|
113 exception_io_from_win32(code);
|
|
|
114 };
|
|
|
115 }
|
|
|
116
|
|
|
117 const HANDLE handles[] = {myEvent, abort.get_abort_event()};
|
|
|
118 SetLastError(NO_ERROR);
|
|
|
119 DWORD state = WaitForMultipleObjects(_countof(handles), handles, FALSE, INFINITE);
|
|
|
120 if (state == WAIT_OBJECT_0) {
|
|
|
121 SetLastError(NO_ERROR);
|
|
|
122 if (!GetOverlappedResult(handle,&ol,&bytesDone,TRUE)) {
|
|
|
123 const DWORD code = GetLastError();
|
|
|
124 if (code == ERROR_HANDLE_EOF || code == ERROR_BROKEN_PIPE) bytesDone = 0;
|
|
|
125 else {
|
|
|
126 CancelIo(handle);
|
|
|
127 exception_io_from_win32(code);
|
|
|
128 }
|
|
|
129 }
|
|
|
130 return bytesDone;
|
|
|
131 }
|
|
|
132 CancelIo(handle);
|
|
|
133 throw exception_aborted();
|
|
|
134 }
|
|
|
135 size_t readOverlapped(HANDLE handle, HANDLE myEvent, t_filesize & position, void * out, size_t outBytes, abort_callback & abort) {
|
|
|
136 enum {readMAX = 16*1024*1024};
|
|
|
137 size_t done = 0;
|
|
|
138 while(done < outBytes) {
|
|
|
139 size_t delta = outBytes - done;
|
|
|
140 if (delta > readMAX) delta = readMAX;
|
|
|
141 delta = readOverlappedPass(handle, myEvent, position, (BYTE*) out + done, (DWORD) delta, abort);
|
|
|
142 if (delta == 0) break;
|
|
|
143 done += delta;
|
|
|
144 position += delta;
|
|
|
145 }
|
|
|
146 return done;
|
|
|
147 }
|
|
|
148
|
|
|
149 size_t readStreamOverlapped(HANDLE handle, HANDLE myEvent, void * out, size_t outBytes, abort_callback & abort) {
|
|
|
150 enum {readMAX = 16*1024*1024};
|
|
|
151 size_t done = 0;
|
|
|
152 while(done < outBytes) {
|
|
|
153 size_t delta = outBytes - done;
|
|
|
154 if (delta > readMAX) delta = readMAX;
|
|
|
155 delta = readOverlappedPass(handle, myEvent, 0, (BYTE*) out + done, (DWORD) delta, abort);
|
|
|
156 if (delta == 0) break;
|
|
|
157 done += delta;
|
|
|
158 }
|
|
|
159 return done;
|
|
|
160 }
|
|
|
161
|
|
|
162 typedef BOOL (WINAPI * pCancelSynchronousIo_t)(HANDLE hThread);
|
|
|
163
|
|
|
164
|
|
|
165 struct createFileData_t {
|
|
|
166 LPCTSTR lpFileName;
|
|
|
167 DWORD dwDesiredAccess;
|
|
|
168 DWORD dwShareMode;
|
|
|
169 LPSECURITY_ATTRIBUTES lpSecurityAttributes;
|
|
|
170 DWORD dwCreationDisposition;
|
|
|
171 DWORD dwFlagsAndAttributes;
|
|
|
172 HANDLE hTemplateFile;
|
|
|
173 HANDLE hResult;
|
|
|
174 DWORD dwErrorCode;
|
|
|
175 };
|
|
|
176
|
|
|
177 HANDLE createFile(LPCTSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile, abort_callback & abort) {
|
|
|
178 abort.check();
|
|
|
179
|
|
|
180 return CreateFile(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
|
|
|
181 }
|
|
|
182
|
|
|
183 size_t lowLevelIO(HANDLE hFile, const GUID & guid, size_t arg1, void * arg2, size_t arg2size, bool canWrite, abort_callback & abort) {
|
|
|
184 if ( guid == file_lowLevelIO::guid_flushFileBuffers ) {
|
|
|
185 if (!canWrite) {
|
|
|
186 PFC_ASSERT(!"File opened for reading, not writing");
|
|
|
187 throw exception_io_denied();
|
|
|
188 }
|
|
|
189 WIN32_IO_OP( ::FlushFileBuffers(hFile) );
|
|
|
190 return 1;
|
|
|
191 } else if ( guid == file_lowLevelIO::guid_getFileTimes ) {
|
|
|
192 if ( arg2size == sizeof(file_lowLevelIO::filetimes_t) ) {
|
|
|
193 if (canWrite) WIN32_IO_OP(::FlushFileBuffers(hFile));
|
|
|
194 auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2);
|
|
|
195 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity");
|
|
|
196 WIN32_IO_OP( GetFileTime( hFile, (FILETIME*)&ft->creation, (FILETIME*)&ft->lastAccess, (FILETIME*)&ft->lastWrite) );
|
|
|
197 return 1;
|
|
|
198 }
|
|
|
199 } else if ( guid == file_lowLevelIO::guid_setFileTimes ) {
|
|
|
200 if (arg2size == sizeof(file_lowLevelIO::filetimes_t)) {
|
|
|
201 if (!canWrite) {
|
|
|
202 PFC_ASSERT(!"File opened for reading, not writing");
|
|
|
203 throw exception_io_denied();
|
|
|
204 }
|
|
|
205 WIN32_IO_OP(::FlushFileBuffers(hFile));
|
|
|
206 auto ft = reinterpret_cast<file_lowLevelIO::filetimes_t *>(arg2);
|
|
|
207 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity");
|
|
|
208 const FILETIME * pCreation = nullptr;
|
|
|
209 const FILETIME * pLastAccess = nullptr;
|
|
|
210 const FILETIME * pLastWrite = nullptr;
|
|
|
211 if ( ft->creation != filetimestamp_invalid ) pCreation = (const FILETIME*)&ft->creation;
|
|
|
212 if ( ft->lastAccess != filetimestamp_invalid ) pLastAccess = (const FILETIME*)&ft->lastAccess;
|
|
|
213 if ( ft->lastWrite != filetimestamp_invalid ) pLastWrite = (const FILETIME*)&ft->lastWrite;
|
|
|
214 WIN32_IO_OP( SetFileTime(hFile, pCreation, pLastAccess, pLastWrite) );
|
|
|
215 return 1;
|
|
|
216 }
|
|
|
217 }
|
|
|
218 return 0;
|
|
|
219 }
|
|
|
220
|
|
|
221 t_filestats2 stats2_from_handle(HANDLE h, const wchar_t * fallbackPath, uint32_t flags, abort_callback& a) {
|
|
|
222 a.check();
|
|
|
223 // Sadly GetFileInformationByHandle() is UNRELIABLE with certain net shares
|
|
|
224 BY_HANDLE_FILE_INFORMATION info = {};
|
|
|
225 if (GetFileInformationByHandle(h, &info)) {
|
|
|
226 return file_win32_helpers::translate_stats2(info);
|
|
|
227 }
|
|
|
228
|
|
|
229 a.check();
|
|
|
230 t_filestats2 ret;
|
|
|
231
|
|
|
232 // ALWAYS get size, fail if bad handle
|
|
|
233 ret.m_size = get_size(h);
|
|
|
234
|
|
|
235 if (flags & (stats2_timestamp | stats2_timestampCreate)) {
|
|
|
236 static_assert(sizeof(t_filetimestamp) == sizeof(FILETIME), "struct sanity");
|
|
|
237 FILETIME ftCreate = {}, ftWrite = {};
|
|
|
238 if (GetFileTime(h, &ftCreate, nullptr, &ftWrite)) {
|
|
|
239 ret.m_timestamp = make_uint64(ftWrite); ret.m_timestampCreate = make_uint64(ftCreate);
|
|
|
240 }
|
|
|
241 }
|
|
|
242 if (flags & stats2_flags) {
|
|
|
243 // No other way to get this from handle?
|
|
|
244 if (fallbackPath != nullptr && *fallbackPath != 0) {
|
|
|
245 DWORD attr = GetFileAttributes(fallbackPath);
|
|
|
246 if (attr != INVALID_FILE_ATTRIBUTES) {
|
|
|
247 attribs_from_win32(ret, attr);
|
|
|
248 }
|
|
|
249 }
|
|
|
250 }
|
|
|
251 return ret;
|
|
|
252 }
|
|
|
253 void attribs_from_win32(t_filestats2& out, DWORD in) {
|
|
|
254 out.set_readonly((in & FILE_ATTRIBUTE_READONLY) != 0);
|
|
|
255 out.set_folder((in & FILE_ATTRIBUTE_DIRECTORY) != 0);
|
|
|
256 out.set_hidden((in & FILE_ATTRIBUTE_HIDDEN) != 0);
|
|
|
257 out.set_system((in & FILE_ATTRIBUTE_SYSTEM) != 0);
|
|
|
258 out.set_remote(false);
|
|
|
259 }
|
|
|
260
|
|
|
261
|
|
|
262 // Seek penalty query, effectively: is this an SSD?
|
|
|
263 // Credit:
|
|
|
264 // https://devblogs.microsoft.com/oldnewthing/20201023-00/?p=104395
|
|
|
265 static bool queryVolumeSeekPenalty(HANDLE hVolume, bool& out) {
|
|
|
266 STORAGE_PROPERTY_QUERY query = {};
|
|
|
267 query.PropertyId = StorageDeviceSeekPenaltyProperty;
|
|
|
268 query.QueryType = PropertyStandardQuery;
|
|
|
269 DWORD count = 1;
|
|
|
270 DEVICE_SEEK_PENALTY_DESCRIPTOR result = {};
|
|
|
271 if (!DeviceIoControl(hVolume, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(query), &result, sizeof(result), &count, nullptr)) {
|
|
|
272 return false;
|
|
|
273 }
|
|
|
274 out = result.IncursSeekPenalty;
|
|
|
275 return true;
|
|
|
276 }
|
|
|
277
|
|
|
278 static HANDLE GetVolumeHandleForFile(PCWSTR filePath) {
|
|
|
279 wchar_t volumePath[MAX_PATH] = {};
|
|
|
280 WIN32_OP_D(GetVolumePathName(filePath, volumePath, ARRAYSIZE(volumePath)));
|
|
|
281
|
|
|
282 wchar_t volumeName[MAX_PATH] = {};
|
|
|
283 WIN32_OP_D(GetVolumeNameForVolumeMountPoint(volumePath, volumeName, ARRAYSIZE(volumeName)));
|
|
|
284
|
|
|
285 auto length = wcslen(volumeName);
|
|
|
286 if ( length == 0 ) {
|
|
|
287 PFC_ASSERT(!"???");
|
|
|
288 return NULL;
|
|
|
289 }
|
|
|
290 if (length && volumeName[length - 1] == L'\\') {
|
|
|
291 volumeName[length - 1] = L'\0';
|
|
|
292 }
|
|
|
293
|
|
|
294 HANDLE ret;
|
|
|
295 WIN32_OP_D( ret = CreateFile(volumeName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr) );
|
|
|
296 return ret;
|
|
|
297 }
|
|
|
298 bool querySeekPenalty(const wchar_t* nativePath, bool& out) {
|
|
|
299 CHandle h;
|
|
|
300 h.Attach( GetVolumeHandleForFile( nativePath ) );
|
|
|
301 if (!h) return false;
|
|
|
302 return queryVolumeSeekPenalty(h, out);
|
|
|
303 }
|
|
|
304 bool querySeekPenalty(const char* fb2k_path, bool& out) {
|
|
|
305 const char * path = fb2k_path;
|
|
|
306 if ( matchProtocol(path, "file")) path = afterProtocol(path);
|
|
|
307 return querySeekPenalty(pfc::wideFromUTF8(path), out);
|
|
|
308 }
|
|
|
309 }
|
|
|
310
|
|
|
311 #endif // _WIN32
|
|
|
312
|