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