[NTOS:KE/x64] Handle NMI vs swapgs race condition
[reactos.git] / dll / win32 / kernel32 / client / file / copy.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/file/copy.c
5 * PURPOSE: Copying files
6 * PROGRAMMER: Ariadne (ariadne@xs4all.nl)
7 * UPDATE HISTORY:
8 * 01/11/98 Created
9 * 07/02/99 Moved to separate file
10 */
11
12 /* INCLUDES ****************************************************************/
13
14 #include <k32.h>
15 #define NDEBUG
16 #include <debug.h>
17
18 #if DBG
19 DEBUG_CHANNEL(kernel32file);
20 #endif
21
22 /* FUNCTIONS ****************************************************************/
23
24
25 static NTSTATUS
26 CopyLoop (
27 HANDLE FileHandleSource,
28 HANDLE FileHandleDest,
29 LARGE_INTEGER SourceFileSize,
30 LPPROGRESS_ROUTINE lpProgressRoutine,
31 LPVOID lpData,
32 BOOL *pbCancel,
33 BOOL *KeepDest
34 )
35 {
36 NTSTATUS errCode;
37 IO_STATUS_BLOCK IoStatusBlock;
38 UCHAR *lpBuffer = NULL;
39 SIZE_T RegionSize = 0x10000;
40 LARGE_INTEGER BytesCopied;
41 DWORD CallbackReason;
42 DWORD ProgressResult;
43 BOOL EndOfFileFound;
44
45 *KeepDest = FALSE;
46 errCode = NtAllocateVirtualMemory(NtCurrentProcess(),
47 (PVOID *)&lpBuffer,
48 0,
49 &RegionSize,
50 MEM_RESERVE | MEM_COMMIT,
51 PAGE_READWRITE);
52
53 if (NT_SUCCESS(errCode))
54 {
55 BytesCopied.QuadPart = 0;
56 EndOfFileFound = FALSE;
57 CallbackReason = CALLBACK_STREAM_SWITCH;
58 while (! EndOfFileFound &&
59 NT_SUCCESS(errCode) &&
60 (NULL == pbCancel || ! *pbCancel))
61 {
62 if (NULL != lpProgressRoutine)
63 {
64 ProgressResult = (*lpProgressRoutine)(SourceFileSize,
65 BytesCopied,
66 SourceFileSize,
67 BytesCopied,
68 0,
69 CallbackReason,
70 FileHandleSource,
71 FileHandleDest,
72 lpData);
73 switch (ProgressResult)
74 {
75 case PROGRESS_CANCEL:
76 TRACE("Progress callback requested cancel\n");
77 errCode = STATUS_REQUEST_ABORTED;
78 break;
79 case PROGRESS_STOP:
80 TRACE("Progress callback requested stop\n");
81 errCode = STATUS_REQUEST_ABORTED;
82 *KeepDest = TRUE;
83 break;
84 case PROGRESS_QUIET:
85 lpProgressRoutine = NULL;
86 break;
87 case PROGRESS_CONTINUE:
88 default:
89 break;
90 }
91 CallbackReason = CALLBACK_CHUNK_FINISHED;
92 }
93 if (NT_SUCCESS(errCode))
94 {
95 errCode = NtReadFile(FileHandleSource,
96 NULL,
97 NULL,
98 NULL,
99 (PIO_STATUS_BLOCK)&IoStatusBlock,
100 lpBuffer,
101 RegionSize,
102 NULL,
103 NULL);
104 /* With sync read, 0 length + status success mean EOF:
105 * https://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
106 */
107 if (NT_SUCCESS(errCode) && IoStatusBlock.Information == 0)
108 {
109 errCode = STATUS_END_OF_FILE;
110 }
111 if (NT_SUCCESS(errCode) && (NULL == pbCancel || ! *pbCancel))
112 {
113 errCode = NtWriteFile(FileHandleDest,
114 NULL,
115 NULL,
116 NULL,
117 (PIO_STATUS_BLOCK)&IoStatusBlock,
118 lpBuffer,
119 IoStatusBlock.Information,
120 NULL,
121 NULL);
122 if (NT_SUCCESS(errCode))
123 {
124 BytesCopied.QuadPart += IoStatusBlock.Information;
125 }
126 else
127 {
128 WARN("Error 0x%08x reading writing to dest\n", errCode);
129 }
130 }
131 else if (!NT_SUCCESS(errCode))
132 {
133 if (STATUS_END_OF_FILE == errCode)
134 {
135 EndOfFileFound = TRUE;
136 errCode = STATUS_SUCCESS;
137 }
138 else
139 {
140 WARN("Error 0x%08x reading from source\n", errCode);
141 }
142 }
143 }
144 }
145
146 if (! EndOfFileFound && (NULL != pbCancel && *pbCancel))
147 {
148 TRACE("User requested cancel\n");
149 errCode = STATUS_REQUEST_ABORTED;
150 }
151
152 NtFreeVirtualMemory(NtCurrentProcess(),
153 (PVOID *)&lpBuffer,
154 &RegionSize,
155 MEM_RELEASE);
156 }
157 else
158 {
159 TRACE("Error 0x%08x allocating buffer of %lu bytes\n", errCode, RegionSize);
160 }
161
162 return errCode;
163 }
164
165 static NTSTATUS
166 SetLastWriteTime(
167 HANDLE FileHandle,
168 LARGE_INTEGER LastWriteTime
169 )
170 {
171 NTSTATUS errCode = STATUS_SUCCESS;
172 IO_STATUS_BLOCK IoStatusBlock;
173 FILE_BASIC_INFORMATION FileBasic;
174
175 errCode = NtQueryInformationFile (FileHandle,
176 &IoStatusBlock,
177 &FileBasic,
178 sizeof(FILE_BASIC_INFORMATION),
179 FileBasicInformation);
180 if (!NT_SUCCESS(errCode))
181 {
182 WARN("Error 0x%08x obtaining FileBasicInformation\n", errCode);
183 }
184 else
185 {
186 FileBasic.LastWriteTime.QuadPart = LastWriteTime.QuadPart;
187 errCode = NtSetInformationFile (FileHandle,
188 &IoStatusBlock,
189 &FileBasic,
190 sizeof(FILE_BASIC_INFORMATION),
191 FileBasicInformation);
192 if (!NT_SUCCESS(errCode))
193 {
194 WARN("Error 0x%0x setting LastWriteTime\n", errCode);
195 }
196 }
197
198 return errCode;
199 }
200
201 BOOL
202 BasepCopyFileExW(IN LPCWSTR lpExistingFileName,
203 IN LPCWSTR lpNewFileName,
204 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
205 IN LPVOID lpData OPTIONAL,
206 IN LPBOOL pbCancel OPTIONAL,
207 IN DWORD dwCopyFlags,
208 IN DWORD dwBasepFlags,
209 OUT LPHANDLE lpExistingHandle,
210 OUT LPHANDLE lpNewHandle)
211 {
212 NTSTATUS errCode;
213 HANDLE FileHandleSource, FileHandleDest;
214 IO_STATUS_BLOCK IoStatusBlock;
215 FILE_STANDARD_INFORMATION FileStandard;
216 FILE_BASIC_INFORMATION FileBasic;
217 BOOL RC = FALSE;
218 BOOL KeepDestOnError = FALSE;
219 DWORD SystemError;
220
221 FileHandleSource = CreateFileW(lpExistingFileName,
222 GENERIC_READ,
223 FILE_SHARE_READ | FILE_SHARE_WRITE,
224 NULL,
225 OPEN_EXISTING,
226 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
227 NULL);
228 if (INVALID_HANDLE_VALUE != FileHandleSource)
229 {
230 errCode = NtQueryInformationFile(FileHandleSource,
231 &IoStatusBlock,
232 &FileStandard,
233 sizeof(FILE_STANDARD_INFORMATION),
234 FileStandardInformation);
235 if (!NT_SUCCESS(errCode))
236 {
237 TRACE("Status 0x%08x obtaining FileStandardInformation for source\n", errCode);
238 BaseSetLastNTError(errCode);
239 }
240 else
241 {
242 errCode = NtQueryInformationFile(FileHandleSource,
243 &IoStatusBlock,&FileBasic,
244 sizeof(FILE_BASIC_INFORMATION),
245 FileBasicInformation);
246 if (!NT_SUCCESS(errCode))
247 {
248 TRACE("Status 0x%08x obtaining FileBasicInformation for source\n", errCode);
249 BaseSetLastNTError(errCode);
250 }
251 else
252 {
253 FileHandleDest = CreateFileW(lpNewFileName,
254 GENERIC_WRITE,
255 FILE_SHARE_WRITE,
256 NULL,
257 dwCopyFlags ? CREATE_NEW : CREATE_ALWAYS,
258 FileBasic.FileAttributes,
259 NULL);
260 if (INVALID_HANDLE_VALUE != FileHandleDest)
261 {
262 errCode = CopyLoop(FileHandleSource,
263 FileHandleDest,
264 FileStandard.EndOfFile,
265 lpProgressRoutine,
266 lpData,
267 pbCancel,
268 &KeepDestOnError);
269 if (!NT_SUCCESS(errCode))
270 {
271 BaseSetLastNTError(errCode);
272 }
273 else
274 {
275 LARGE_INTEGER t;
276
277 t.QuadPart = FileBasic.LastWriteTime.QuadPart;
278 errCode = SetLastWriteTime(FileHandleDest, t);
279 if (!NT_SUCCESS(errCode))
280 {
281 BaseSetLastNTError(errCode);
282 }
283 else
284 {
285 RC = TRUE;
286 }
287 }
288 NtClose(FileHandleDest);
289 if (! RC && ! KeepDestOnError)
290 {
291 SystemError = GetLastError();
292 SetFileAttributesW(lpNewFileName, FILE_ATTRIBUTE_NORMAL);
293 DeleteFileW(lpNewFileName);
294 SetLastError(SystemError);
295 }
296 }
297 else
298 {
299 WARN("Error %lu during opening of dest file\n", GetLastError());
300 }
301 }
302 }
303 NtClose(FileHandleSource);
304 }
305 else
306 {
307 WARN("Error %lu during opening of source file\n", GetLastError());
308 }
309
310 return RC;
311 }
312
313 /*
314 * @implemented
315 */
316 BOOL
317 WINAPI
318 CopyFileExW(IN LPCWSTR lpExistingFileName,
319 IN LPCWSTR lpNewFileName,
320 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
321 IN LPVOID lpData OPTIONAL,
322 IN LPBOOL pbCancel OPTIONAL,
323 IN DWORD dwCopyFlags)
324 {
325 BOOL Ret;
326 HANDLE ExistingHandle, NewHandle;
327
328 ExistingHandle = INVALID_HANDLE_VALUE;
329 NewHandle = INVALID_HANDLE_VALUE;
330
331 _SEH2_TRY
332 {
333 Ret = BasepCopyFileExW(lpExistingFileName,
334 lpNewFileName,
335 lpProgressRoutine,
336 lpData,
337 pbCancel,
338 dwCopyFlags,
339 0,
340 &ExistingHandle,
341 &NewHandle);
342 }
343 _SEH2_FINALLY
344 {
345 if (ExistingHandle != INVALID_HANDLE_VALUE)
346 {
347 CloseHandle(ExistingHandle);
348 }
349
350 if (NewHandle != INVALID_HANDLE_VALUE)
351 {
352 CloseHandle(NewHandle);
353 }
354 }
355 _SEH2_END;
356
357 return Ret;
358 }
359
360
361 /*
362 * @implemented
363 */
364 BOOL
365 WINAPI
366 CopyFileExA(IN LPCSTR lpExistingFileName,
367 IN LPCSTR lpNewFileName,
368 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
369 IN LPVOID lpData OPTIONAL,
370 IN LPBOOL pbCancel OPTIONAL,
371 IN DWORD dwCopyFlags)
372 {
373 BOOL Result = FALSE;
374 UNICODE_STRING lpNewFileNameW;
375 PUNICODE_STRING lpExistingFileNameW;
376
377 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName);
378 if (!lpExistingFileNameW)
379 {
380 return FALSE;
381 }
382
383 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName))
384 {
385 Result = CopyFileExW(lpExistingFileNameW->Buffer,
386 lpNewFileNameW.Buffer,
387 lpProgressRoutine,
388 lpData,
389 pbCancel,
390 dwCopyFlags);
391
392 RtlFreeUnicodeString(&lpNewFileNameW);
393 }
394
395 return Result;
396 }
397
398
399 /*
400 * @implemented
401 */
402 BOOL
403 WINAPI
404 CopyFileA(IN LPCSTR lpExistingFileName,
405 IN LPCSTR lpNewFileName,
406 IN BOOL bFailIfExists)
407 {
408 BOOL Result = FALSE;
409 UNICODE_STRING lpNewFileNameW;
410 PUNICODE_STRING lpExistingFileNameW;
411
412 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName);
413 if (!lpExistingFileNameW)
414 {
415 return FALSE;
416 }
417
418 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName))
419 {
420 Result = CopyFileExW(lpExistingFileNameW->Buffer,
421 lpNewFileNameW.Buffer,
422 NULL,
423 NULL,
424 NULL,
425 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0));
426
427 RtlFreeUnicodeString(&lpNewFileNameW);
428 }
429
430 return Result;
431 }
432
433
434 /*
435 * @implemented
436 */
437 BOOL
438 WINAPI
439 CopyFileW(IN LPCWSTR lpExistingFileName,
440 IN LPCWSTR lpNewFileName,
441 IN BOOL bFailIfExists)
442 {
443 return CopyFileExW(lpExistingFileName,
444 lpNewFileName,
445 NULL,
446 NULL,
447 NULL,
448 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0));
449 }
450
451
452 /*
453 * @implemented
454 */
455 BOOL
456 WINAPI
457 PrivCopyFileExW(IN LPCWSTR lpExistingFileName,
458 IN LPCWSTR lpNewFileName,
459 IN LPPROGRESS_ROUTINE lpProgressRoutine,
460 IN LPVOID lpData,
461 IN LPBOOL pbCancel,
462 IN DWORD dwCopyFlags)
463 {
464 BOOL Ret;
465 HANDLE ExistingHandle, NewHandle;
466
467 ExistingHandle = INVALID_HANDLE_VALUE;
468 NewHandle = INVALID_HANDLE_VALUE;
469
470 /* Check for incompatible flags */
471 if (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS && dwCopyFlags & BASEP_COPY_REPLACE)
472 {
473 SetLastError(ERROR_INVALID_PARAMETER);
474 return FALSE;
475 }
476
477 _SEH2_TRY
478 {
479 Ret = BasepCopyFileExW(lpExistingFileName,
480 lpNewFileName,
481 lpProgressRoutine,
482 lpData,
483 pbCancel,
484 dwCopyFlags & BASEP_COPY_PUBLIC_MASK,
485 dwCopyFlags & BASEP_COPY_BASEP_MASK,
486 &ExistingHandle,
487 &NewHandle);
488 }
489 _SEH2_FINALLY
490 {
491 if (ExistingHandle != INVALID_HANDLE_VALUE)
492 {
493 CloseHandle(ExistingHandle);
494 }
495
496 if (NewHandle != INVALID_HANDLE_VALUE)
497 {
498 CloseHandle(NewHandle);
499 }
500 }
501 _SEH2_END;
502
503 return Ret;
504 }
505
506 /* EOF */