sorry createDirectoryExW fails to create folder why ? change back to CreateDirectoryW...
[reactos.git] / reactos / lib / kernel32 / file / move.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/file/file.c
6 * PURPOSE: Directory functions
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * Gerhard W. Gruber (sparhawk_at_gmx.at)
9 * UPDATE HISTORY:
10 * Created 01/11/98
11 */
12
13 /* INCLUDES *****************************************************************/
14
15 #include <k32.h>
16 #include <malloc.h>
17
18 #define NDEBUG
19 #include "../include/debug.h"
20
21 /* GLOBALS *****************************************************************/
22
23 /* FUNCTIONS ****************************************************************/
24 static BOOL
25 RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName)
26 {
27 DWORD Attributes;
28 Attributes = GetFileAttributesW(lpFileName);
29 if (Attributes != INVALID_FILE_ATTRIBUTES)
30 {
31 return SetFileAttributesW(lpFileName,Attributes -
32 (Attributes & ~FILE_ATTRIBUTE_READONLY));
33 }
34
35 return FALSE;
36 }
37
38
39 /***********************************************************************
40 * add_boot_rename_entry
41 *
42 * Adds an entry to the registry that is loaded when windows boots and
43 * checks if there are some files to be removed or renamed/moved.
44 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
45 * non-NULL then the file is moved, otherwise it is deleted. The
46 * entry of the registrykey is always appended with two zero
47 * terminated strings. If <fn2> is NULL then the second entry is
48 * simply a single 0-byte. Otherwise the second filename goes
49 * there. The entries are prepended with \??\ before the path and the
50 * second filename gets also a '!' as the first character if
51 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
52 * 0-byte follows to indicate the end of the strings.
53 * i.e.:
54 * \??\D:\test\file1[0]
55 * !\??\D:\test\file1_renamed[0]
56 * \??\D:\Test|delete[0]
57 * [0] <- file is to be deleted, second string empty
58 * \??\D:\test\file2[0]
59 * !\??\D:\test\file2_renamed[0]
60 * [0] <- indicates end of strings
61 *
62 * or:
63 * \??\D:\test\file1[0]
64 * !\??\D:\test\file1_renamed[0]
65 * \??\D:\Test|delete[0]
66 * [0] <- file is to be deleted, second string empty
67 * [0] <- indicates end of strings
68 *
69 */
70 static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
71 {
72 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
73 'F','i','l','e','R','e','n','a','m','e',
74 'O','p','e','r','a','t','i','o','n','s',0};
75 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
76 'S','y','s','t','e','m','\\',
77 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
78 'C','o','n','t','r','o','l','\\',
79 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
80 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
81
82 OBJECT_ATTRIBUTES attr;
83 UNICODE_STRING nameW, source_name, dest_name;
84 KEY_VALUE_PARTIAL_INFORMATION *info;
85 BOOL rc = FALSE;
86 HANDLE Reboot = 0;
87 DWORD len1, len2;
88 DWORD DataSize = 0;
89 BYTE *Buffer = NULL;
90 WCHAR *p;
91
92 DPRINT("Add support to smss for keys created by MOVEFILE_DELAY_UNTIL_REBOOT\n");
93
94 if (!RtlDosPathNameToNtPathName_U( (LPWSTR)source, &source_name, NULL, NULL ))
95 {
96 SetLastError( ERROR_PATH_NOT_FOUND );
97 return FALSE;
98 }
99 dest_name.Buffer = NULL;
100 if (dest && !RtlDosPathNameToNtPathName_U( (LPWSTR)dest, &dest_name, NULL, NULL ))
101 {
102 RtlFreeUnicodeString( &source_name );
103 SetLastError( ERROR_PATH_NOT_FOUND );
104 return FALSE;
105 }
106
107 attr.Length = sizeof(attr);
108 attr.RootDirectory = 0;
109 attr.ObjectName = &nameW;
110 attr.Attributes = 0;
111 attr.SecurityDescriptor = NULL;
112 attr.SecurityQualityOfService = NULL;
113 RtlInitUnicodeString( &nameW, SessionW );
114
115 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
116 {
117 DPRINT1("Error creating key for reboot managment [%s]\n",
118 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
119 RtlFreeUnicodeString( &source_name );
120 RtlFreeUnicodeString( &dest_name );
121 return FALSE;
122 }
123
124 len1 = source_name.Length + sizeof(WCHAR);
125 if (dest)
126 {
127 len2 = dest_name.Length + sizeof(WCHAR);
128 if (flags & MOVEFILE_REPLACE_EXISTING)
129 len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
130 }
131 else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
132
133 RtlInitUnicodeString( &nameW, ValueName );
134
135 /* First we check if the key exists and if so how many bytes it already contains. */
136 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
137 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
138 {
139 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
140 goto Quit;
141 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
142 Buffer, DataSize, &DataSize )) goto Quit;
143 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
144 if (info->Type != REG_MULTI_SZ) goto Quit;
145 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
146 }
147 else
148 {
149 DataSize = info_size;
150 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
151 goto Quit;
152 }
153
154 memcpy( Buffer + DataSize, source_name.Buffer, len1 );
155 DataSize += len1;
156 p = (WCHAR *)(Buffer + DataSize);
157 if (dest)
158 {
159 if (flags & MOVEFILE_REPLACE_EXISTING)
160 *p++ = '!';
161 memcpy( p, dest_name.Buffer, len2 );
162 DataSize += len2;
163 }
164 else
165 {
166 *p = 0;
167 DataSize += sizeof(WCHAR);
168 }
169
170 /* add final null */
171 p = (WCHAR *)(Buffer + DataSize);
172 *p = 0;
173 DataSize += sizeof(WCHAR);
174
175 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
176
177 Quit:
178 RtlFreeUnicodeString( &source_name );
179 RtlFreeUnicodeString( &dest_name );
180 if (Reboot) NtClose(Reboot);
181 HeapFree( GetProcessHeap(), 0, Buffer );
182 return(rc);
183 }
184
185
186 /*
187 * @implemented
188 */
189 BOOL
190 STDCALL
191 MoveFileWithProgressW (
192 LPCWSTR lpExistingFileName,
193 LPCWSTR lpNewFileName,
194 LPPROGRESS_ROUTINE lpProgressRoutine,
195 LPVOID lpData,
196 DWORD dwFlags
197 )
198 {
199 HANDLE hFile = NULL;
200 IO_STATUS_BLOCK IoStatusBlock;
201 PFILE_RENAME_INFORMATION FileRename;
202 NTSTATUS errCode;
203 BOOL Result;
204 UNICODE_STRING DstPathU;
205 BOOL folder = FALSE;
206
207 DPRINT("MoveFileWithProgressW()\n");
208
209 if (dwFlags & MOVEFILE_DELAY_UNTIL_REBOOT)
210 return add_boot_rename_entry( lpExistingFileName, lpNewFileName, dwFlags );
211
212 hFile = CreateFileW (lpExistingFileName,
213 GENERIC_ALL,
214 FILE_SHARE_WRITE|FILE_SHARE_READ,
215 NULL,
216 OPEN_EXISTING,
217 FILE_FLAG_BACKUP_SEMANTICS,
218 NULL);
219
220 if (hFile == INVALID_HANDLE_VALUE)
221 {
222 return FALSE;
223 }
224
225
226 /* validate & translate the filename */
227 if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpNewFileName,
228 &DstPathU,
229 NULL,
230 NULL))
231 {
232 DPRINT("Invalid destination path\n");
233 CloseHandle(hFile);
234 SetLastError(ERROR_PATH_NOT_FOUND);
235 return FALSE;
236 }
237
238 FileRename = alloca(sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
239 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == MOVEFILE_REPLACE_EXISTING)
240 FileRename->ReplaceIfExists = TRUE;
241 else
242 FileRename->ReplaceIfExists = FALSE;
243
244 memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
245 RtlFreeHeap (RtlGetProcessHeap (),
246 0,
247 DstPathU.Buffer);
248 /*
249 * FIXME:
250 * Is the length the count of characters or the length of the buffer?
251 */
252 FileRename->FileNameLength = DstPathU.Length / sizeof(WCHAR);
253 errCode = NtSetInformationFile (hFile,
254 &IoStatusBlock,
255 FileRename,
256 sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
257 FileRenameInformation);
258 CloseHandle(hFile);
259
260 if (GetFileAttributesW(lpExistingFileName) & FILE_ATTRIBUTE_DIRECTORY)
261 {
262 folder = TRUE;
263 }
264
265
266 /*
267 * FIXME:
268 * Fail now move the folder
269 * Before we fail at CreateFileW
270 */
271
272
273 if (NT_SUCCESS(errCode))
274 {
275 Result = TRUE;
276 }
277 else
278 {
279 if (folder==FALSE)
280 {
281 Result = CopyFileExW (lpExistingFileName,
282 lpNewFileName,
283 lpProgressRoutine,
284 lpData,
285 NULL,
286 FileRename->ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS);
287 if (Result)
288 {
289 /* Cleanup the source file */
290 Result = DeleteFileW (lpExistingFileName);
291 }
292 }
293 else
294 {
295 /* move folder code start */
296 WIN32_FIND_DATAW findBuffer;
297 LPWSTR lpExistingFileName2 = NULL;
298 LPWSTR lpNewFileName2 = NULL;
299 LPWSTR lpDeleteFile = NULL;
300 INT size;
301 INT size2;
302 BOOL loop = TRUE;
303 BOOL Result = FALSE;
304 INT max_size = MAX_PATH;
305
306
307 /* Build the string */
308 size = wcslen(lpExistingFileName);
309 if (size+6> max_size)
310 max_size = size + 6;
311
312 lpDeleteFile = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
313 if (lpDeleteFile == NULL)
314 return FALSE;
315
316 lpNewFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
317 if (lpNewFileName2 == NULL)
318 {
319 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
320 return FALSE;
321 }
322
323 lpExistingFileName2 = (LPWSTR) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,max_size * sizeof(WCHAR));
324 if (lpNewFileName2 == NULL)
325 {
326 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
327 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
328 return FALSE;
329 }
330
331 wcscpy( (WCHAR *)lpExistingFileName2,lpExistingFileName);
332 wcscpy( (WCHAR *)&lpExistingFileName2[size],L"\\*.*\0");
333
334 /* Get the file name */
335 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
336 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
337 if (hFile == NULL)
338 loop=FALSE;
339
340 if (findBuffer.cFileName[0] == L'\0')
341 loop=FALSE;
342
343
344 /* FIXME
345 * remove readonly flag from source folder and do not set the readonly flag to dest folder
346 */
347 RemoveReadOnlyAttributeW(lpExistingFileName);
348 RemoveReadOnlyAttributeW(lpNewFileName);
349 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
350 CreateDirectoryW(lpNewFileName, NULL);
351
352 /* search the files/folders and move them */
353 while (loop==TRUE)
354 {
355 Result = TRUE;
356
357 if ((!wcscmp(findBuffer.cFileName,L"..")) || (!wcscmp(findBuffer.cFileName,L".")))
358 {
359 loop = FindNextFileW(hFile, &findBuffer);
360
361 if (!loop)
362 {
363 size = wcslen(lpExistingFileName2)-4;
364 FindClose(hFile);
365 wcscpy( &lpExistingFileName2[size],L"\0");
366
367 if (wcsncmp(lpExistingFileName,lpExistingFileName2,size))
368 {
369 DWORD Attributes;
370
371 FindClose(hFile);
372
373 /* delete folder */
374 DPRINT("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile);
375
376 /* remove system folder flag other wise we can not delete the folder */
377 Attributes = GetFileAttributesW(lpExistingFileName2);
378 if (Attributes != INVALID_FILE_ATTRIBUTES)
379 {
380 SetFileAttributesW(lpExistingFileName2,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
381 }
382
383 RemoveReadOnlyAttributeW(lpExistingFileName2);
384
385 Result = RemoveDirectoryW(lpExistingFileName2);
386 if (Result == FALSE)
387 break;
388
389 loop=TRUE;
390 size = wcslen(lpExistingFileName);
391
392 if (size+6>max_size)
393 {
394 if (lpNewFileName2 != NULL)
395 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
396
397 if (lpExistingFileName2 != NULL)
398 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
399
400 if (lpDeleteFile != NULL)
401 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
402
403 return FALSE;
404 }
405
406 wcscpy( lpExistingFileName2,lpExistingFileName);
407 wcscpy( &lpExistingFileName2[size],L"\\*.*\0");
408
409 /* Get the file name */
410 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
411 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
412 }
413 }
414 continue;
415 }
416
417 if (findBuffer.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
418 {
419
420 /* Build the new src string */
421 size = wcslen(findBuffer.cFileName);
422 size2= wcslen(lpExistingFileName2);
423
424 if (size2+size+6>max_size)
425 {
426 FindClose(hFile);
427
428 if (lpNewFileName2 != NULL)
429 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
430
431 if (lpExistingFileName2 != NULL)
432 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
433
434 if (lpDeleteFile != NULL)
435 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
436
437 return FALSE;
438 }
439
440 wcscpy( &lpExistingFileName2[size2-3],findBuffer.cFileName);
441 wcscpy( &lpExistingFileName2[size2+size-3],L"\0");
442
443
444 /* Continue */
445 wcscpy( lpDeleteFile,lpExistingFileName2);
446 wcscpy( &lpExistingFileName2[size2+size-3],L"\\*.*\0");
447
448
449 /* Build the new dst string */
450 size = wcslen(lpExistingFileName2) + wcslen(lpNewFileName);
451 size2 = wcslen(lpExistingFileName);
452
453 if (size>max_size)
454 {
455 FindClose(hFile);
456
457 if (lpNewFileName2 != NULL)
458 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
459
460 if (lpExistingFileName2 != NULL)
461 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
462
463 if (lpDeleteFile != NULL)
464 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
465
466 return FALSE;
467 }
468
469 wcscpy( lpNewFileName2,lpNewFileName);
470 size = wcslen(lpNewFileName);
471 wcscpy( &lpNewFileName2[size], &lpExistingFileName2[size2]);
472 size = wcslen(lpNewFileName2);
473 wcscpy( &lpNewFileName2[size-4],L"\0");
474
475 /* Create Folder */
476
477 /* FIXME
478 * remove readonly flag from source folder and do not set the readonly flag to dest folder
479 */
480 RemoveReadOnlyAttributeW(lpDeleteFile);
481 RemoveReadOnlyAttributeW(lpNewFileName2);
482
483 CreateDirectoryW(lpNewFileName2,NULL);
484 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
485
486
487 /* set new search path from src string */
488 FindClose(hFile);
489 memset(&findBuffer,0,sizeof(WIN32_FIND_DATAW));
490 hFile = FindFirstFileW(lpExistingFileName2, &findBuffer);
491 }
492 else
493 {
494
495 /* Build the new string */
496 size = wcslen(findBuffer.cFileName);
497 size2= wcslen(lpExistingFileName2);
498 wcscpy( lpDeleteFile,lpExistingFileName2);
499 wcscpy( &lpDeleteFile[size2-3],findBuffer.cFileName);
500
501 /* Build dest string */
502 size = wcslen(lpDeleteFile) + wcslen(lpNewFileName);
503 size2 = wcslen(lpExistingFileName);
504
505 if (size>max_size)
506 {
507 FindClose(hFile);
508
509 if (lpNewFileName2 != NULL)
510 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
511
512 if (lpExistingFileName2 != NULL)
513 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
514
515 if (lpDeleteFile != NULL)
516 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
517
518 return FALSE;
519 }
520
521 wcscpy( lpNewFileName2,lpNewFileName);
522 size = wcslen(lpNewFileName);
523 wcscpy(&lpNewFileName2[size],&lpDeleteFile[size2]);
524
525
526 /* overrite existsen file, if the file got the flag have readonly
527 * we need reomve that flag
528 */
529
530 /* copy file */
531
532 DPRINT("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile, lpNewFileName2);
533 RemoveReadOnlyAttributeW(lpDeleteFile);
534 RemoveReadOnlyAttributeW(lpNewFileName2);
535
536 Result = CopyFileExW (lpDeleteFile,
537 lpNewFileName2,
538 lpProgressRoutine,
539 lpData,
540 NULL,
541 0);
542
543 if (Result == FALSE)
544 break;
545
546 /* delete file */
547 DPRINT("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2);
548 Result = RemoveReadOnlyAttributeW(lpDeleteFile);
549 if (Result == FALSE)
550 break;
551
552 DPRINT("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile);
553 Result = DeleteFileW(lpDeleteFile);
554 if (Result == FALSE)
555 break;
556
557 }
558 loop = FindNextFileW(hFile, &findBuffer);
559 }
560
561
562 /* Remove last folder */
563 if ((loop == FALSE) && (Result != FALSE))
564 {
565 DWORD Attributes;
566
567 FindClose(hFile);
568 Attributes = GetFileAttributesW(lpDeleteFile);
569 if (Attributes != INVALID_FILE_ATTRIBUTES)
570 {
571 SetFileAttributesW(lpDeleteFile,(Attributes & ~FILE_ATTRIBUTE_SYSTEM));
572 }
573
574 Result = RemoveDirectoryW(lpExistingFileName);
575 }
576
577 /* Cleanup */
578 FindClose(hFile);
579
580 if (lpNewFileName2 != NULL)
581 {
582 HeapFree(GetProcessHeap(),0,(VOID *) lpNewFileName2);
583 lpNewFileName2 = NULL;
584 }
585
586 if (lpExistingFileName2 != NULL)
587 {
588 HeapFree(GetProcessHeap(),0,(VOID *) lpExistingFileName2);
589 lpExistingFileName2 = NULL;
590 }
591
592 if (lpDeleteFile != NULL)
593 {
594 HeapFree(GetProcessHeap(),0,(VOID *) lpDeleteFile);
595 lpDeleteFile = NULL;
596 }
597
598 return Result;
599
600 // end move folder code
601 }
602 }
603
604
605 return Result;
606 }
607
608
609 /*
610 * @implemented
611 */
612 BOOL
613 STDCALL
614 MoveFileWithProgressA (
615 LPCSTR lpExistingFileName,
616 LPCSTR lpNewFileName,
617 LPPROGRESS_ROUTINE lpProgressRoutine,
618 LPVOID lpData,
619 DWORD dwFlags
620 )
621 {
622 PWCHAR ExistingFileNameW;
623 PWCHAR NewFileNameW;
624 BOOL ret;
625
626 if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
627 return FALSE;
628
629 if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
630 return FALSE;
631
632 ret = MoveFileWithProgressW (ExistingFileNameW ,
633 NewFileNameW,
634 lpProgressRoutine,
635 lpData,
636 dwFlags);
637
638 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
639
640 return ret;
641 }
642
643
644 /*
645 * @implemented
646 */
647 BOOL
648 STDCALL
649 MoveFileW (
650 LPCWSTR lpExistingFileName,
651 LPCWSTR lpNewFileName
652 )
653 {
654 return MoveFileExW (lpExistingFileName,
655 lpNewFileName,
656 MOVEFILE_COPY_ALLOWED);
657 }
658
659
660 /*
661 * @implemented
662 */
663 BOOL
664 STDCALL
665 MoveFileExW (
666 LPCWSTR lpExistingFileName,
667 LPCWSTR lpNewFileName,
668 DWORD dwFlags
669 )
670 {
671 return MoveFileWithProgressW (lpExistingFileName,
672 lpNewFileName,
673 NULL,
674 NULL,
675 dwFlags);
676 }
677
678
679 /*
680 * @implemented
681 */
682 BOOL
683 STDCALL
684 MoveFileA (
685 LPCSTR lpExistingFileName,
686 LPCSTR lpNewFileName
687 )
688 {
689 return MoveFileExA (lpExistingFileName,
690 lpNewFileName,
691 MOVEFILE_COPY_ALLOWED);
692 }
693
694
695 /*
696 * @implemented
697 */
698 BOOL
699 STDCALL
700 MoveFileExA (
701 LPCSTR lpExistingFileName,
702 LPCSTR lpNewFileName,
703 DWORD dwFlags
704 )
705 {
706 return MoveFileWithProgressA (lpExistingFileName,
707 lpNewFileName,
708 NULL,
709 NULL,
710 dwFlags);
711 }
712
713 /* EOF */