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)
13 /* INCLUDES *****************************************************************/
19 #include "../include/debug.h"
21 /* GLOBALS *****************************************************************/
23 /* FUNCTIONS ****************************************************************/
25 RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName
)
28 Attributes
= GetFileAttributesW(lpFileName
);
29 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
31 return SetFileAttributesW(lpFileName
,Attributes
-
32 (Attributes
& ~FILE_ATTRIBUTE_READONLY
));
39 /***********************************************************************
40 * add_boot_rename_entry
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.
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
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
70 static BOOL
add_boot_rename_entry( LPCWSTR source
, LPCWSTR dest
, DWORD flags
)
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};
76 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
78 static const int info_size
= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
80 OBJECT_ATTRIBUTES ObjectAttributes
;
81 UNICODE_STRING nameW
, source_name
, dest_name
;
82 KEY_VALUE_PARTIAL_INFORMATION
*info
;
91 DPRINT("Add support to smss for keys created by MOVEFILE_DELAY_UNTIL_REBOOT\n");
93 if (!RtlDosPathNameToNtPathName_U( source
, &source_name
, NULL
, NULL
))
95 SetLastError( ERROR_PATH_NOT_FOUND
);
98 dest_name
.Buffer
= NULL
;
99 if (dest
&& !RtlDosPathNameToNtPathName_U( dest
, &dest_name
, NULL
, NULL
))
101 RtlFreeUnicodeString( &source_name
);
102 SetLastError( ERROR_PATH_NOT_FOUND
);
106 InitializeObjectAttributes(&ObjectAttributes
,
108 OBJ_CASE_INSENSITIVE
,
112 Status
= NtOpenKey(&Reboot
,
116 if (!NT_SUCCESS(Status
))
118 Status
= NtCreateKey(&Reboot
,
123 REG_OPTION_NON_VOLATILE
,
127 if (!NT_SUCCESS(Status
))
129 DPRINT1("NtCreateKey() failed (Status %lx)\n", Status
);
130 RtlFreeUnicodeString( &source_name
);
131 RtlFreeUnicodeString( &dest_name
);
135 len1
= source_name
.Length
+ sizeof(WCHAR
);
138 len2
= dest_name
.Length
+ sizeof(WCHAR
);
139 if (flags
& MOVEFILE_REPLACE_EXISTING
)
140 len2
+= sizeof(WCHAR
); /* Plus 1 because of the leading '!' */
142 else len2
= sizeof(WCHAR
); /* minimum is the 0 characters for the empty second string */
144 RtlInitUnicodeString( &nameW
, ValueName
);
146 /* First we check if the key exists and if so how many bytes it already contains. */
147 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
148 NULL
, 0, &DataSize
) == STATUS_BUFFER_OVERFLOW
)
150 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
152 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
153 Buffer
, DataSize
, &DataSize
)) goto Quit
;
154 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
155 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
156 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
160 DataSize
= info_size
;
161 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
165 memcpy( Buffer
+ DataSize
, source_name
.Buffer
, len1
);
167 p
= (WCHAR
*)(Buffer
+ DataSize
);
170 if (flags
& MOVEFILE_REPLACE_EXISTING
)
172 memcpy( p
, dest_name
.Buffer
, len2
);
178 DataSize
+= sizeof(WCHAR
);
182 p
= (WCHAR
*)(Buffer
+ DataSize
);
184 DataSize
+= sizeof(WCHAR
);
186 rc
= !NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
);
189 RtlFreeUnicodeString( &source_name
);
190 RtlFreeUnicodeString( &dest_name
);
191 if (Reboot
) NtClose(Reboot
);
192 HeapFree( GetProcessHeap(), 0, Buffer
);
202 MoveFileWithProgressW (
203 LPCWSTR lpExistingFileName
,
204 LPCWSTR lpNewFileName
,
205 LPPROGRESS_ROUTINE lpProgressRoutine
,
211 IO_STATUS_BLOCK IoStatusBlock
;
212 PFILE_RENAME_INFORMATION FileRename
;
215 UNICODE_STRING DstPathU
;
218 DPRINT("MoveFileWithProgressW()\n");
220 if (dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
)
221 return add_boot_rename_entry( lpExistingFileName
, lpNewFileName
, dwFlags
);
223 hFile
= CreateFileW (lpExistingFileName
,
225 FILE_SHARE_WRITE
|FILE_SHARE_READ
,
228 FILE_FLAG_BACKUP_SEMANTICS
,
231 if (hFile
== INVALID_HANDLE_VALUE
)
237 /* validate & translate the filename */
238 if (!RtlDosPathNameToNtPathName_U (lpNewFileName
,
243 DPRINT("Invalid destination path\n");
245 SetLastError(ERROR_PATH_NOT_FOUND
);
249 FileRename
= alloca(sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
);
250 if ((dwFlags
& MOVEFILE_REPLACE_EXISTING
) == MOVEFILE_REPLACE_EXISTING
)
251 FileRename
->ReplaceIfExists
= TRUE
;
253 FileRename
->ReplaceIfExists
= FALSE
;
255 memcpy(FileRename
->FileName
, DstPathU
.Buffer
, DstPathU
.Length
);
256 RtlFreeHeap (RtlGetProcessHeap (),
261 * Is the length the count of characters or the length of the buffer?
263 FileRename
->FileNameLength
= DstPathU
.Length
/ sizeof(WCHAR
);
264 errCode
= NtSetInformationFile (hFile
,
267 sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
,
268 FileRenameInformation
);
271 if (GetFileAttributesW(lpExistingFileName
) & FILE_ATTRIBUTE_DIRECTORY
)
279 * Fail now move the folder
280 * Before we fail at CreateFileW
284 if (NT_SUCCESS(errCode
))
292 Result
= CopyFileExW (lpExistingFileName
,
297 FileRename
->ReplaceIfExists
? 0 : COPY_FILE_FAIL_IF_EXISTS
);
300 /* Cleanup the source file */
301 Result
= DeleteFileW (lpExistingFileName
);
306 /* move folder code start */
307 WIN32_FIND_DATAW findBuffer
;
308 LPWSTR lpExistingFileName2
= NULL
;
309 LPWSTR lpNewFileName2
= NULL
;
310 LPWSTR lpDeleteFile
= NULL
;
315 INT max_size
= MAX_PATH
;
318 /* Build the string */
319 size
= wcslen(lpExistingFileName
);
320 if (size
+6> max_size
)
323 lpDeleteFile
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
324 if (lpDeleteFile
== NULL
)
327 lpNewFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
328 if (lpNewFileName2
== NULL
)
330 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
334 lpExistingFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
335 if (lpNewFileName2
== NULL
)
337 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
338 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
342 wcscpy( (WCHAR
*)lpExistingFileName2
,lpExistingFileName
);
343 wcscpy( (WCHAR
*)&lpExistingFileName2
[size
],L
"\\*.*\0");
345 /* Get the file name */
346 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
347 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
351 if (findBuffer
.cFileName
[0] == L
'\0')
356 * remove readonly flag from source folder and do not set the readonly flag to dest folder
358 RemoveReadOnlyAttributeW(lpExistingFileName
);
359 RemoveReadOnlyAttributeW(lpNewFileName
);
360 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
361 CreateDirectoryW(lpNewFileName
, NULL
);
363 /* search the files/folders and move them */
368 if ((!wcscmp(findBuffer
.cFileName
,L
"..")) || (!wcscmp(findBuffer
.cFileName
,L
".")))
370 loop
= FindNextFileW(hFile
, &findBuffer
);
374 size
= wcslen(lpExistingFileName2
)-4;
376 wcscpy( &lpExistingFileName2
[size
],L
"\0");
378 if (wcsncmp(lpExistingFileName
,lpExistingFileName2
,size
))
385 DPRINT("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile
);
387 /* remove system folder flag other wise we can not delete the folder */
388 Attributes
= GetFileAttributesW(lpExistingFileName2
);
389 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
391 SetFileAttributesW(lpExistingFileName2
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
394 RemoveReadOnlyAttributeW(lpExistingFileName2
);
396 Result
= RemoveDirectoryW(lpExistingFileName2
);
401 size
= wcslen(lpExistingFileName
);
405 if (lpNewFileName2
!= NULL
)
406 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
408 if (lpExistingFileName2
!= NULL
)
409 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
411 if (lpDeleteFile
!= NULL
)
412 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
417 wcscpy( lpExistingFileName2
,lpExistingFileName
);
418 wcscpy( &lpExistingFileName2
[size
],L
"\\*.*\0");
420 /* Get the file name */
421 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
422 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
428 if (findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
431 /* Build the new src string */
432 size
= wcslen(findBuffer
.cFileName
);
433 size2
= wcslen(lpExistingFileName2
);
435 if (size2
+size
+6>max_size
)
439 if (lpNewFileName2
!= NULL
)
440 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
442 if (lpExistingFileName2
!= NULL
)
443 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
445 if (lpDeleteFile
!= NULL
)
446 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
451 wcscpy( &lpExistingFileName2
[size2
-3],findBuffer
.cFileName
);
452 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\0");
456 wcscpy( lpDeleteFile
,lpExistingFileName2
);
457 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\\*.*\0");
460 /* Build the new dst string */
461 size
= wcslen(lpExistingFileName2
) + wcslen(lpNewFileName
);
462 size2
= wcslen(lpExistingFileName
);
468 if (lpNewFileName2
!= NULL
)
469 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
471 if (lpExistingFileName2
!= NULL
)
472 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
474 if (lpDeleteFile
!= NULL
)
475 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
480 wcscpy( lpNewFileName2
,lpNewFileName
);
481 size
= wcslen(lpNewFileName
);
482 wcscpy( &lpNewFileName2
[size
], &lpExistingFileName2
[size2
]);
483 size
= wcslen(lpNewFileName2
);
484 wcscpy( &lpNewFileName2
[size
-4],L
"\0");
489 * remove readonly flag from source folder and do not set the readonly flag to dest folder
491 RemoveReadOnlyAttributeW(lpDeleteFile
);
492 RemoveReadOnlyAttributeW(lpNewFileName2
);
494 CreateDirectoryW(lpNewFileName2
,NULL
);
495 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
498 /* set new search path from src string */
500 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
501 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
506 /* Build the new string */
507 size
= wcslen(findBuffer
.cFileName
);
508 size2
= wcslen(lpExistingFileName2
);
509 wcscpy( lpDeleteFile
,lpExistingFileName2
);
510 wcscpy( &lpDeleteFile
[size2
-3],findBuffer
.cFileName
);
512 /* Build dest string */
513 size
= wcslen(lpDeleteFile
) + wcslen(lpNewFileName
);
514 size2
= wcslen(lpExistingFileName
);
520 if (lpNewFileName2
!= NULL
)
521 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
523 if (lpExistingFileName2
!= NULL
)
524 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
526 if (lpDeleteFile
!= NULL
)
527 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
532 wcscpy( lpNewFileName2
,lpNewFileName
);
533 size
= wcslen(lpNewFileName
);
534 wcscpy(&lpNewFileName2
[size
],&lpDeleteFile
[size2
]);
537 /* overrite existsen file, if the file got the flag have readonly
538 * we need reomve that flag
543 DPRINT("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile
, lpNewFileName2
);
544 RemoveReadOnlyAttributeW(lpDeleteFile
);
545 RemoveReadOnlyAttributeW(lpNewFileName2
);
547 Result
= CopyFileExW (lpDeleteFile
,
558 DPRINT("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2
);
559 Result
= RemoveReadOnlyAttributeW(lpDeleteFile
);
563 DPRINT("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile
);
564 Result
= DeleteFileW(lpDeleteFile
);
569 loop
= FindNextFileW(hFile
, &findBuffer
);
573 /* Remove last folder */
574 if ((loop
== FALSE
) && (Result
!= FALSE
))
579 Attributes
= GetFileAttributesW(lpDeleteFile
);
580 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
582 SetFileAttributesW(lpDeleteFile
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
585 Result
= RemoveDirectoryW(lpExistingFileName
);
591 if (lpNewFileName2
!= NULL
)
593 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
594 lpNewFileName2
= NULL
;
597 if (lpExistingFileName2
!= NULL
)
599 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
600 lpExistingFileName2
= NULL
;
603 if (lpDeleteFile
!= NULL
)
605 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
611 // end move folder code
625 MoveFileWithProgressA (
626 LPCSTR lpExistingFileName
,
627 LPCSTR lpNewFileName
,
628 LPPROGRESS_ROUTINE lpProgressRoutine
,
633 PWCHAR ExistingFileNameW
;
637 if (!(ExistingFileNameW
= FilenameA2W(lpExistingFileName
, FALSE
)))
640 if (!(NewFileNameW
= FilenameA2W(lpNewFileName
, TRUE
)))
643 ret
= MoveFileWithProgressW (ExistingFileNameW
,
649 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW
);
661 LPCWSTR lpExistingFileName
,
662 LPCWSTR lpNewFileName
665 return MoveFileExW (lpExistingFileName
,
667 MOVEFILE_COPY_ALLOWED
);
677 LPCWSTR lpExistingFileName
,
678 LPCWSTR lpNewFileName
,
682 return MoveFileWithProgressW (lpExistingFileName
,
696 LPCSTR lpExistingFileName
,
700 return MoveFileExA (lpExistingFileName
,
702 MOVEFILE_COPY_ALLOWED
);
712 LPCSTR lpExistingFileName
,
713 LPCSTR lpNewFileName
,
717 return MoveFileWithProgressA (lpExistingFileName
,