[KERNEL32]: While working on the CMAKE branch, Amine and myself discovered a rather...
[reactos.git] / reactos / dll / win32 / kernel32 / file / copy.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/copy.c
6 * PURPOSE: Copying files
7 * PROGRAMMER: Ariadne (ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * 01/11/98 Created
10 * 07/02/99 Moved to seperate file
11 */
12
13 /* INCLUDES ****************************************************************/
14
15 #include <k32.h>
16 #define NDEBUG
17 #include <debug.h>
18
19 /* FUNCTIONS ****************************************************************/
20
21
22 static NTSTATUS
23 CopyLoop (
24 HANDLE FileHandleSource,
25 HANDLE FileHandleDest,
26 LARGE_INTEGER SourceFileSize,
27 LPPROGRESS_ROUTINE lpProgressRoutine,
28 LPVOID lpData,
29 BOOL *pbCancel,
30 BOOL *KeepDest
31 )
32 {
33 NTSTATUS errCode;
34 IO_STATUS_BLOCK IoStatusBlock;
35 UCHAR *lpBuffer = NULL;
36 SIZE_T RegionSize = 0x10000;
37 LARGE_INTEGER BytesCopied;
38 DWORD CallbackReason;
39 DWORD ProgressResult;
40 BOOL EndOfFileFound;
41
42 *KeepDest = FALSE;
43 errCode = NtAllocateVirtualMemory(NtCurrentProcess(),
44 (PVOID *)&lpBuffer,
45 2,
46 &RegionSize,
47 MEM_RESERVE | MEM_COMMIT,
48 PAGE_READWRITE);
49
50 if (NT_SUCCESS(errCode))
51 {
52 BytesCopied.QuadPart = 0;
53 EndOfFileFound = FALSE;
54 CallbackReason = CALLBACK_STREAM_SWITCH;
55 while (! EndOfFileFound &&
56 NT_SUCCESS(errCode) &&
57 (NULL == pbCancel || ! *pbCancel))
58 {
59 if (NULL != lpProgressRoutine)
60 {
61 ProgressResult = (*lpProgressRoutine)(SourceFileSize,
62 BytesCopied,
63 SourceFileSize,
64 BytesCopied,
65 0,
66 CallbackReason,
67 FileHandleSource,
68 FileHandleDest,
69 lpData);
70 switch (ProgressResult)
71 {
72 case PROGRESS_CANCEL:
73 TRACE("Progress callback requested cancel\n");
74 errCode = STATUS_REQUEST_ABORTED;
75 break;
76 case PROGRESS_STOP:
77 TRACE("Progress callback requested stop\n");
78 errCode = STATUS_REQUEST_ABORTED;
79 *KeepDest = TRUE;
80 break;
81 case PROGRESS_QUIET:
82 lpProgressRoutine = NULL;
83 break;
84 case PROGRESS_CONTINUE:
85 default:
86 break;
87 }
88 CallbackReason = CALLBACK_CHUNK_FINISHED;
89 }
90 if (NT_SUCCESS(errCode))
91 {
92 errCode = NtReadFile(FileHandleSource,
93 NULL,
94 NULL,
95 NULL,
96 (PIO_STATUS_BLOCK)&IoStatusBlock,
97 lpBuffer,
98 RegionSize,
99 NULL,
100 NULL);
101 if (NT_SUCCESS(errCode) && (NULL == pbCancel || ! *pbCancel))
102 {
103 errCode = NtWriteFile(FileHandleDest,
104 NULL,
105 NULL,
106 NULL,
107 (PIO_STATUS_BLOCK)&IoStatusBlock,
108 lpBuffer,
109 IoStatusBlock.Information,
110 NULL,
111 NULL);
112 if (NT_SUCCESS(errCode))
113 {
114 BytesCopied.QuadPart += IoStatusBlock.Information;
115 }
116 else
117 {
118 WARN("Error 0x%08x reading writing to dest\n", errCode);
119 }
120 }
121 else if (!NT_SUCCESS(errCode))
122 {
123 if (STATUS_END_OF_FILE == errCode)
124 {
125 EndOfFileFound = TRUE;
126 errCode = STATUS_SUCCESS;
127 }
128 else
129 {
130 WARN("Error 0x%08x reading from source\n", errCode);
131 }
132 }
133 }
134 }
135
136 if (! EndOfFileFound && (NULL != pbCancel && *pbCancel))
137 {
138 TRACE("User requested cancel\n");
139 errCode = STATUS_REQUEST_ABORTED;
140 }
141
142 NtFreeVirtualMemory(NtCurrentProcess(),
143 (PVOID *)&lpBuffer,
144 &RegionSize,
145 MEM_RELEASE);
146 }
147 else
148 {
149 TRACE("Error 0x%08x allocating buffer of %d bytes\n", errCode, RegionSize);
150 }
151
152 return errCode;
153 }
154
155 static NTSTATUS
156 SetLastWriteTime(
157 HANDLE FileHandle,
158 LARGE_INTEGER LastWriteTime
159 )
160 {
161 NTSTATUS errCode = STATUS_SUCCESS;
162 IO_STATUS_BLOCK IoStatusBlock;
163 FILE_BASIC_INFORMATION FileBasic;
164
165 errCode = NtQueryInformationFile (FileHandle,
166 &IoStatusBlock,
167 &FileBasic,
168 sizeof(FILE_BASIC_INFORMATION),
169 FileBasicInformation);
170 if (!NT_SUCCESS(errCode))
171 {
172 WARN("Error 0x%08x obtaining FileBasicInformation\n", errCode);
173 }
174 else
175 {
176 FileBasic.LastWriteTime.QuadPart = LastWriteTime.QuadPart;
177 errCode = NtSetInformationFile (FileHandle,
178 &IoStatusBlock,
179 &FileBasic,
180 sizeof(FILE_BASIC_INFORMATION),
181 FileBasicInformation);
182 if (!NT_SUCCESS(errCode))
183 {
184 WARN("Error 0x%0x setting LastWriteTime\n", errCode);
185 }
186 }
187
188 return errCode;
189 }
190
191
192 /*
193 * @implemented
194 */
195 BOOL
196 WINAPI
197 CopyFileExW (
198 LPCWSTR lpExistingFileName,
199 LPCWSTR lpNewFileName,
200 LPPROGRESS_ROUTINE lpProgressRoutine,
201 LPVOID lpData,
202 BOOL *pbCancel,
203 DWORD dwCopyFlags
204 )
205 {
206 NTSTATUS errCode;
207 HANDLE FileHandleSource, FileHandleDest;
208 IO_STATUS_BLOCK IoStatusBlock;
209 FILE_STANDARD_INFORMATION FileStandard;
210 FILE_BASIC_INFORMATION FileBasic;
211 BOOL RC = FALSE;
212 BOOL KeepDestOnError = FALSE;
213 DWORD SystemError;
214
215 FileHandleSource = CreateFileW(lpExistingFileName,
216 GENERIC_READ,
217 FILE_SHARE_READ | FILE_SHARE_WRITE,
218 NULL,
219 OPEN_EXISTING,
220 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_NO_BUFFERING,
221 NULL);
222 if (INVALID_HANDLE_VALUE != FileHandleSource)
223 {
224 errCode = NtQueryInformationFile(FileHandleSource,
225 &IoStatusBlock,
226 &FileStandard,
227 sizeof(FILE_STANDARD_INFORMATION),
228 FileStandardInformation);
229 if (!NT_SUCCESS(errCode))
230 {
231 TRACE("Status 0x%08x obtaining FileStandardInformation for source\n", errCode);
232 SetLastErrorByStatus(errCode);
233 }
234 else
235 {
236 errCode = NtQueryInformationFile(FileHandleSource,
237 &IoStatusBlock,&FileBasic,
238 sizeof(FILE_BASIC_INFORMATION),
239 FileBasicInformation);
240 if (!NT_SUCCESS(errCode))
241 {
242 TRACE("Status 0x%08x obtaining FileBasicInformation for source\n", errCode);
243 SetLastErrorByStatus(errCode);
244 }
245 else
246 {
247 FileHandleDest = CreateFileW(lpNewFileName,
248 GENERIC_WRITE,
249 FILE_SHARE_WRITE,
250 NULL,
251 dwCopyFlags ? CREATE_NEW : CREATE_ALWAYS,
252 FileBasic.FileAttributes,
253 NULL);
254 if (INVALID_HANDLE_VALUE != FileHandleDest)
255 {
256 errCode = CopyLoop(FileHandleSource,
257 FileHandleDest,
258 FileStandard.EndOfFile,
259 lpProgressRoutine,
260 lpData,
261 pbCancel,
262 &KeepDestOnError);
263 if (!NT_SUCCESS(errCode))
264 {
265 SetLastErrorByStatus(errCode);
266 }
267 else
268 {
269 LARGE_INTEGER t;
270
271 t.QuadPart = FileBasic.LastWriteTime.QuadPart;
272 errCode = SetLastWriteTime(FileHandleDest, t);
273 if (!NT_SUCCESS(errCode))
274 {
275 SetLastErrorByStatus(errCode);
276 }
277 else
278 {
279 RC = TRUE;
280 }
281 }
282 NtClose(FileHandleDest);
283 if (! RC && ! KeepDestOnError)
284 {
285 SystemError = GetLastError();
286 SetFileAttributesW(lpNewFileName, FILE_ATTRIBUTE_NORMAL);
287 DeleteFileW(lpNewFileName);
288 SetLastError(SystemError);
289 }
290 }
291 else
292 {
293 WARN("Error %d during opening of dest file\n", GetLastError());
294 }
295 }
296 }
297 NtClose(FileHandleSource);
298 }
299 else
300 {
301 WARN("Error %d during opening of source file\n", GetLastError());
302 }
303
304 return RC;
305 }
306
307
308 /*
309 * @implemented
310 */
311 BOOL
312 WINAPI
313 CopyFileExA (
314 LPCSTR lpExistingFileName,
315 LPCSTR lpNewFileName,
316 LPPROGRESS_ROUTINE lpProgressRoutine,
317 LPVOID lpData,
318 BOOL *pbCancel,
319 DWORD dwCopyFlags
320 )
321 {
322 PWCHAR ExistingFileNameW;
323 PWCHAR NewFileNameW;
324 BOOL Result;
325
326 if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
327 return FALSE;
328
329 if (!(NewFileNameW = FilenameA2W(lpNewFileName, TRUE)))
330 return FALSE;
331
332 Result = CopyFileExW (ExistingFileNameW ,
333 NewFileNameW ,
334 lpProgressRoutine,
335 lpData,
336 pbCancel,
337 dwCopyFlags);
338
339 RtlFreeHeap (RtlGetProcessHeap (),
340 0,
341 NewFileNameW);
342
343 return Result;
344 }
345
346
347 /*
348 * @implemented
349 */
350 BOOL
351 WINAPI
352 CopyFileA (
353 LPCSTR lpExistingFileName,
354 LPCSTR lpNewFileName,
355 BOOL bFailIfExists
356 )
357 {
358 return CopyFileExA (lpExistingFileName,
359 lpNewFileName,
360 NULL,
361 NULL,
362 NULL,
363 bFailIfExists);
364 }
365
366
367 /*
368 * @implemented
369 */
370 BOOL
371 WINAPI
372 CopyFileW (
373 LPCWSTR lpExistingFileName,
374 LPCWSTR lpNewFileName,
375 BOOL bFailIfExists
376 )
377 {
378 return CopyFileExW (lpExistingFileName,
379 lpNewFileName,
380 NULL,
381 NULL,
382 NULL,
383 bFailIfExists);
384 }
385
386
387 /*
388 * @implemented
389 */
390 BOOL
391 WINAPI
392 PrivCopyFileExW (
393 LPCWSTR lpExistingFileName,
394 LPCWSTR lpNewFileName,
395 LPPROGRESS_ROUTINE lpProgressRoutine,
396 LPVOID lpData,
397 BOOL *pbCancel,
398 DWORD dwCopyFlags
399 )
400 {
401 UNIMPLEMENTED;
402 return FALSE;
403 }
404
405 /* EOF */