xref: /reactos/dll/win32/kernel32/client/file/copy.c (revision c2c66aff)
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
CopyLoop(HANDLE FileHandleSource,HANDLE FileHandleDest,LARGE_INTEGER SourceFileSize,LPPROGRESS_ROUTINE lpProgressRoutine,LPVOID lpData,BOOL * pbCancel,BOOL * KeepDest)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
SetLastWriteTime(HANDLE FileHandle,LARGE_INTEGER LastWriteTime)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
BasepCopyFileExW(IN LPCWSTR lpExistingFileName,IN LPCWSTR lpNewFileName,IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,IN LPVOID lpData OPTIONAL,IN LPBOOL pbCancel OPTIONAL,IN DWORD dwCopyFlags,IN DWORD dwBasepFlags,OUT LPHANDLE lpExistingHandle,OUT LPHANDLE lpNewHandle)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
CopyFileExW(IN LPCWSTR lpExistingFileName,IN LPCWSTR lpNewFileName,IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,IN LPVOID lpData OPTIONAL,IN LPBOOL pbCancel OPTIONAL,IN DWORD dwCopyFlags)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
CopyFileExA(IN LPCSTR lpExistingFileName,IN LPCSTR lpNewFileName,IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,IN LPVOID lpData OPTIONAL,IN LPBOOL pbCancel OPTIONAL,IN DWORD dwCopyFlags)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
CopyFileA(IN LPCSTR lpExistingFileName,IN LPCSTR lpNewFileName,IN BOOL bFailIfExists)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
CopyFileW(IN LPCWSTR lpExistingFileName,IN LPCWSTR lpNewFileName,IN BOOL bFailIfExists)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
PrivCopyFileExW(IN LPCWSTR lpExistingFileName,IN LPCWSTR lpNewFileName,IN LPPROGRESS_ROUTINE lpProgressRoutine,IN LPVOID lpData,IN LPBOOL pbCancel,IN DWORD dwCopyFlags)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 */
507