Sync with trunk r63637.
[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: lib/kernel32/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 seperate 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 if (NT_SUCCESS(errCode) && (NULL == pbCancel || ! *pbCancel))
105 {
106 errCode = NtWriteFile(FileHandleDest,
107 NULL,
108 NULL,
109 NULL,
110 (PIO_STATUS_BLOCK)&IoStatusBlock,
111 lpBuffer,
112 IoStatusBlock.Information,
113 NULL,
114 NULL);
115 if (NT_SUCCESS(errCode))
116 {
117 BytesCopied.QuadPart += IoStatusBlock.Information;
118 }
119 else
120 {
121 WARN("Error 0x%08x reading writing to dest\n", errCode);
122 }
123 }
124 else if (!NT_SUCCESS(errCode))
125 {
126 if (STATUS_END_OF_FILE == errCode)
127 {
128 EndOfFileFound = TRUE;
129 errCode = STATUS_SUCCESS;
130 }
131 else
132 {
133 WARN("Error 0x%08x reading from source\n", errCode);
134 }
135 }
136 }
137 }
138
139 if (! EndOfFileFound && (NULL != pbCancel && *pbCancel))
140 {
141 TRACE("User requested cancel\n");
142 errCode = STATUS_REQUEST_ABORTED;
143 }
144
145 NtFreeVirtualMemory(NtCurrentProcess(),
146 (PVOID *)&lpBuffer,
147 &RegionSize,
148 MEM_RELEASE);
149 }
150 else
151 {
152 TRACE("Error 0x%08x allocating buffer of %lu bytes\n", errCode, RegionSize);
153 }
154
155 return errCode;
156 }
157
158 static NTSTATUS
159 SetLastWriteTime(
160 HANDLE FileHandle,
161 LARGE_INTEGER LastWriteTime
162 )
163 {
164 NTSTATUS errCode = STATUS_SUCCESS;
165 IO_STATUS_BLOCK IoStatusBlock;
166 FILE_BASIC_INFORMATION FileBasic;
167
168 errCode = NtQueryInformationFile (FileHandle,
169 &IoStatusBlock,
170 &FileBasic,
171 sizeof(FILE_BASIC_INFORMATION),
172 FileBasicInformation);
173 if (!NT_SUCCESS(errCode))
174 {
175 WARN("Error 0x%08x obtaining FileBasicInformation\n", errCode);
176 }
177 else
178 {
179 FileBasic.LastWriteTime.QuadPart = LastWriteTime.QuadPart;
180 errCode = NtSetInformationFile (FileHandle,
181 &IoStatusBlock,
182 &FileBasic,
183 sizeof(FILE_BASIC_INFORMATION),
184 FileBasicInformation);
185 if (!NT_SUCCESS(errCode))
186 {
187 WARN("Error 0x%0x setting LastWriteTime\n", errCode);
188 }
189 }
190
191 return errCode;
192 }
193
194 BOOL
195 BasepCopyFileExW(IN LPCWSTR lpExistingFileName,
196 IN LPCWSTR lpNewFileName,
197 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
198 IN LPVOID lpData OPTIONAL,
199 IN LPBOOL pbCancel OPTIONAL,
200 IN DWORD dwCopyFlags,
201 IN DWORD dwBasepFlags,
202 OUT LPHANDLE lpExistingHandle,
203 OUT LPHANDLE lpNewHandle)
204 {
205 NTSTATUS errCode;
206 HANDLE FileHandleSource, FileHandleDest;
207 IO_STATUS_BLOCK IoStatusBlock;
208 FILE_STANDARD_INFORMATION FileStandard;
209 FILE_BASIC_INFORMATION FileBasic;
210 BOOL RC = FALSE;
211 BOOL KeepDestOnError = FALSE;
212 DWORD SystemError;
213
214 FileHandleSource = CreateFileW(lpExistingFileName,
215 GENERIC_READ,
216 FILE_SHARE_READ | FILE_SHARE_WRITE,
217 NULL,
218 OPEN_EXISTING,
219 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
220 NULL);
221 if (INVALID_HANDLE_VALUE != FileHandleSource)
222 {
223 errCode = NtQueryInformationFile(FileHandleSource,
224 &IoStatusBlock,
225 &FileStandard,
226 sizeof(FILE_STANDARD_INFORMATION),
227 FileStandardInformation);
228 if (!NT_SUCCESS(errCode))
229 {
230 TRACE("Status 0x%08x obtaining FileStandardInformation for source\n", errCode);
231 BaseSetLastNTError(errCode);
232 }
233 else
234 {
235 errCode = NtQueryInformationFile(FileHandleSource,
236 &IoStatusBlock,&FileBasic,
237 sizeof(FILE_BASIC_INFORMATION),
238 FileBasicInformation);
239 if (!NT_SUCCESS(errCode))
240 {
241 TRACE("Status 0x%08x obtaining FileBasicInformation for source\n", errCode);
242 BaseSetLastNTError(errCode);
243 }
244 else
245 {
246 FileHandleDest = CreateFileW(lpNewFileName,
247 GENERIC_WRITE,
248 FILE_SHARE_WRITE,
249 NULL,
250 dwCopyFlags ? CREATE_NEW : CREATE_ALWAYS,
251 FileBasic.FileAttributes,
252 NULL);
253 if (INVALID_HANDLE_VALUE != FileHandleDest)
254 {
255 errCode = CopyLoop(FileHandleSource,
256 FileHandleDest,
257 FileStandard.EndOfFile,
258 lpProgressRoutine,
259 lpData,
260 pbCancel,
261 &KeepDestOnError);
262 if (!NT_SUCCESS(errCode))
263 {
264 BaseSetLastNTError(errCode);
265 }
266 else
267 {
268 LARGE_INTEGER t;
269
270 t.QuadPart = FileBasic.LastWriteTime.QuadPart;
271 errCode = SetLastWriteTime(FileHandleDest, t);
272 if (!NT_SUCCESS(errCode))
273 {
274 BaseSetLastNTError(errCode);
275 }
276 else
277 {
278 RC = TRUE;
279 }
280 }
281 NtClose(FileHandleDest);
282 if (! RC && ! KeepDestOnError)
283 {
284 SystemError = GetLastError();
285 SetFileAttributesW(lpNewFileName, FILE_ATTRIBUTE_NORMAL);
286 DeleteFileW(lpNewFileName);
287 SetLastError(SystemError);
288 }
289 }
290 else
291 {
292 WARN("Error %lu during opening of dest file\n", GetLastError());
293 }
294 }
295 }
296 NtClose(FileHandleSource);
297 }
298 else
299 {
300 WARN("Error %lu during opening of source file\n", GetLastError());
301 }
302
303 return RC;
304 }
305
306 /*
307 * @implemented
308 */
309 BOOL
310 WINAPI
311 CopyFileExW(IN LPCWSTR lpExistingFileName,
312 IN LPCWSTR lpNewFileName,
313 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
314 IN LPVOID lpData OPTIONAL,
315 IN LPBOOL pbCancel OPTIONAL,
316 IN DWORD dwCopyFlags)
317 {
318 BOOL Ret;
319 HANDLE ExistingHandle, NewHandle;
320
321 ExistingHandle = INVALID_HANDLE_VALUE;
322 NewHandle = INVALID_HANDLE_VALUE;
323
324 _SEH2_TRY
325 {
326 Ret = BasepCopyFileExW(lpExistingFileName,
327 lpNewFileName,
328 lpProgressRoutine,
329 lpData,
330 pbCancel,
331 dwCopyFlags,
332 0,
333 &ExistingHandle,
334 &NewHandle);
335 }
336 _SEH2_FINALLY
337 {
338 if (ExistingHandle != INVALID_HANDLE_VALUE)
339 {
340 CloseHandle(ExistingHandle);
341 }
342
343 if (NewHandle != INVALID_HANDLE_VALUE)
344 {
345 CloseHandle(NewHandle);
346 }
347 }
348 _SEH2_END;
349
350 return Ret;
351 }
352
353
354 /*
355 * @implemented
356 */
357 BOOL
358 WINAPI
359 CopyFileExA(IN LPCSTR lpExistingFileName,
360 IN LPCSTR lpNewFileName,
361 IN LPPROGRESS_ROUTINE lpProgressRoutine OPTIONAL,
362 IN LPVOID lpData OPTIONAL,
363 IN LPBOOL pbCancel OPTIONAL,
364 IN DWORD dwCopyFlags)
365 {
366 BOOL Result = FALSE;
367 UNICODE_STRING lpNewFileNameW;
368 PUNICODE_STRING lpExistingFileNameW;
369
370 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName);
371 if (!lpExistingFileNameW)
372 {
373 return FALSE;
374 }
375
376 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName))
377 {
378 Result = CopyFileExW(lpExistingFileNameW->Buffer,
379 lpNewFileNameW.Buffer,
380 lpProgressRoutine,
381 lpData,
382 pbCancel,
383 dwCopyFlags);
384
385 RtlFreeUnicodeString(&lpNewFileNameW);
386 }
387
388 return Result;
389 }
390
391
392 /*
393 * @implemented
394 */
395 BOOL
396 WINAPI
397 CopyFileA(IN LPCSTR lpExistingFileName,
398 IN LPCSTR lpNewFileName,
399 IN BOOL bFailIfExists)
400 {
401 BOOL Result = FALSE;
402 UNICODE_STRING lpNewFileNameW;
403 PUNICODE_STRING lpExistingFileNameW;
404
405 lpExistingFileNameW = Basep8BitStringToStaticUnicodeString(lpExistingFileName);
406 if (!lpExistingFileNameW)
407 {
408 return FALSE;
409 }
410
411 if (Basep8BitStringToDynamicUnicodeString(&lpNewFileNameW, lpNewFileName))
412 {
413 Result = CopyFileExW(lpExistingFileNameW->Buffer,
414 lpNewFileNameW.Buffer,
415 NULL,
416 NULL,
417 NULL,
418 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0));
419
420 RtlFreeUnicodeString(&lpNewFileNameW);
421 }
422
423 return Result;
424 }
425
426
427 /*
428 * @implemented
429 */
430 BOOL
431 WINAPI
432 CopyFileW(IN LPCWSTR lpExistingFileName,
433 IN LPCWSTR lpNewFileName,
434 IN BOOL bFailIfExists)
435 {
436 return CopyFileExW(lpExistingFileName,
437 lpNewFileName,
438 NULL,
439 NULL,
440 NULL,
441 (bFailIfExists ? COPY_FILE_FAIL_IF_EXISTS : 0));
442 }
443
444
445 /*
446 * @implemented
447 */
448 BOOL
449 WINAPI
450 PrivCopyFileExW(IN LPCWSTR lpExistingFileName,
451 IN LPCWSTR lpNewFileName,
452 IN LPPROGRESS_ROUTINE lpProgressRoutine,
453 IN LPVOID lpData,
454 IN LPBOOL pbCancel,
455 IN DWORD dwCopyFlags)
456 {
457 BOOL Ret;
458 HANDLE ExistingHandle, NewHandle;
459
460 ExistingHandle = INVALID_HANDLE_VALUE;
461 NewHandle = INVALID_HANDLE_VALUE;
462
463 /* Check for incompatible flags */
464 if (dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS && dwCopyFlags & BASEP_COPY_REPLACE)
465 {
466 SetLastError(ERROR_INVALID_PARAMETER);
467 return FALSE;
468 }
469
470 _SEH2_TRY
471 {
472 Ret = BasepCopyFileExW(lpExistingFileName,
473 lpNewFileName,
474 lpProgressRoutine,
475 lpData,
476 pbCancel,
477 dwCopyFlags & BASEP_COPY_PUBLIC_MASK,
478 dwCopyFlags & BASEP_COPY_BASEP_MASK,
479 &ExistingHandle,
480 &NewHandle);
481 }
482 _SEH2_FINALLY
483 {
484 if (ExistingHandle != INVALID_HANDLE_VALUE)
485 {
486 CloseHandle(ExistingHandle);
487 }
488
489 if (NewHandle != INVALID_HANDLE_VALUE)
490 {
491 CloseHandle(NewHandle);
492 }
493 }
494 _SEH2_END;
495
496 return Ret;
497 }
498
499 /* EOF */