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 * Dmitry Philippov (shedon@mail.ru)
13 * Fix some bugs in the add_boot_rename_entry function
16 /* INCLUDES *****************************************************************/
22 DEBUG_CHANNEL(kernel32file
);
24 /* GLOBALS *****************************************************************/
26 /* FUNCTIONS ****************************************************************/
28 RemoveReadOnlyAttributeW(IN LPCWSTR lpFileName
)
31 Attributes
= GetFileAttributesW(lpFileName
);
32 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
34 return SetFileAttributesW(lpFileName
,Attributes
-
35 (Attributes
& ~FILE_ATTRIBUTE_READONLY
));
42 /***********************************************************************
43 * add_boot_rename_entry
45 * Adds an entry to the registry that is loaded when windows boots and
46 * checks if there are some files to be removed or renamed/moved.
47 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
48 * non-NULL then the file is moved, otherwise it is deleted. The
49 * entry of the registrykey is always appended with two zero
50 * terminated strings. If <fn2> is NULL then the second entry is
51 * simply a single 0-byte. Otherwise the second filename goes
52 * there. The entries are prepended with \??\ before the path and the
53 * second filename gets also a '!' as the first character if
54 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
55 * 0-byte follows to indicate the end of the strings.
57 * \??\D:\test\file1[0]
58 * !\??\D:\test\file1_renamed[0]
59 * \??\D:\Test|delete[0]
60 * [0] <- file is to be deleted, second string empty
61 * \??\D:\test\file2[0]
62 * !\??\D:\test\file2_renamed[0]
63 * [0] <- indicates end of strings
66 * \??\D:\test\file1[0]
67 * !\??\D:\test\file1_renamed[0]
68 * \??\D:\Test|delete[0]
69 * [0] <- file is to be deleted, second string empty
70 * [0] <- indicates end of strings
73 static BOOL
add_boot_rename_entry( LPCWSTR source
, LPCWSTR dest
, DWORD flags
)
75 static const WCHAR ValueName
[] = {'P','e','n','d','i','n','g',
76 'F','i','l','e','R','e','n','a','m','e',
77 'O','p','e','r','a','t','i','o','n','s',0};
79 UNICODE_STRING KeyName
= RTL_CONSTANT_STRING(L
"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Control\\Session Manager");
81 static const int info_size
= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
83 OBJECT_ATTRIBUTES ObjectAttributes
;
84 UNICODE_STRING nameW
, source_name
, dest_name
;
85 KEY_VALUE_PARTIAL_INFORMATION
*info
;
95 TRACE("add_boot_rename_entry( %S, %S, %d ) \n", source
, dest
, flags
);
98 DestLen
= wcslen(dest
);
100 if (!RtlDosPathNameToNtPathName_U( source
, &source_name
, NULL
, NULL
))
102 SetLastError( ERROR_PATH_NOT_FOUND
);
105 dest_name
.Buffer
= NULL
;
106 if (DestLen
&& !RtlDosPathNameToNtPathName_U( dest
, &dest_name
, NULL
, NULL
))
108 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name
.Buffer
);
109 SetLastError( ERROR_PATH_NOT_FOUND
);
113 InitializeObjectAttributes(&ObjectAttributes
,
115 OBJ_OPENIF
| OBJ_CASE_INSENSITIVE
,
119 Status
= NtCreateKey(&Reboot
,
120 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
124 REG_OPTION_NON_VOLATILE
,
127 if (Status
== STATUS_ACCESS_DENIED
)
129 Status
= NtCreateKey(
131 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
135 REG_OPTION_BACKUP_RESTORE
,
139 if (!NT_SUCCESS(Status
))
141 WARN("NtCreateKey() failed (Status 0x%lx)\n", Status
);
142 if (source_name
.Buffer
)
143 RtlFreeHeap(RtlGetProcessHeap(), 0, source_name
.Buffer
);
144 if (dest_name
.Buffer
)
145 RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name
.Buffer
);
149 len1
= source_name
.Length
+ sizeof(WCHAR
);
152 len2
= dest_name
.Length
+ sizeof(WCHAR
);
153 if (flags
& MOVEFILE_REPLACE_EXISTING
)
154 len2
+= sizeof(WCHAR
); /* Plus 1 because of the leading '!' */
158 len2
= sizeof(WCHAR
); /* minimum is the 0 characters for the empty second string */
161 RtlInitUnicodeString( &nameW
, ValueName
);
163 /* First we check if the key exists and if so how many bytes it already contains. */
164 Status
= NtQueryValueKey(
167 KeyValuePartialInformation
,
171 if ((Status
== STATUS_BUFFER_OVERFLOW
) ||
172 (Status
== STATUS_BUFFER_TOO_SMALL
))
174 if (!(Buffer
= HeapAlloc(GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
))))
176 Status
= NtQueryValueKey(Reboot
, &nameW
, KeyValuePartialInformation
,
177 Buffer
, DataSize
, &DataSize
);
178 if(!NT_SUCCESS(Status
))
180 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
181 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
182 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
186 DataSize
= info_size
;
187 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
191 memcpy( Buffer
+ DataSize
, source_name
.Buffer
, len1
);
193 p
= (WCHAR
*)(Buffer
+ DataSize
);
196 if (flags
& MOVEFILE_REPLACE_EXISTING
)
198 memcpy( p
, dest_name
.Buffer
, len2
);
204 DataSize
+= sizeof(WCHAR
);
208 p
= (WCHAR
*)(Buffer
+ DataSize
);
210 DataSize
+= sizeof(WCHAR
);
212 rc
= NT_SUCCESS(NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
));
215 RtlFreeHeap(RtlGetProcessHeap(), 0, source_name
.Buffer
);
216 if (dest_name
.Buffer
)
217 RtlFreeHeap(RtlGetProcessHeap(), 0, dest_name
.Buffer
);
220 HeapFree(GetProcessHeap(), 0, Buffer
);
230 MoveFileWithProgressW (
231 LPCWSTR lpExistingFileName
,
232 LPCWSTR lpNewFileName
,
233 LPPROGRESS_ROUTINE lpProgressRoutine
,
238 HANDLE hFile
= NULL
, hNewFile
= NULL
;
239 IO_STATUS_BLOCK IoStatusBlock
;
240 OBJECT_ATTRIBUTES ObjectAttributes
;
241 PFILE_RENAME_INFORMATION FileRename
;
244 UNICODE_STRING DstPathU
;
247 TRACE("MoveFileWithProgressW()\n");
249 if (dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
)
250 return add_boot_rename_entry( lpExistingFileName
, lpNewFileName
, dwFlags
);
252 // if (dwFlags & MOVEFILE_WRITE_THROUGH)
253 // FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n");
256 return DeleteFileW(lpExistingFileName
);
258 /* validate & translate the filename */
259 if (!RtlDosPathNameToNtPathName_U (lpNewFileName
,
264 WARN("Invalid destination path\n");
265 SetLastError(ERROR_PATH_NOT_FOUND
);
269 InitializeObjectAttributes(&ObjectAttributes
,
271 OBJ_CASE_INSENSITIVE
,
275 errCode
= NtOpenFile( &hNewFile
,
276 GENERIC_READ
| GENERIC_WRITE
| SYNCHRONIZE
,
280 FILE_NON_DIRECTORY_FILE
| FILE_SYNCHRONOUS_IO_NONALERT
|
281 ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_WRITE_THROUGH
: 0) );
283 if (NT_SUCCESS(errCode
)) /* Destination exists */
287 if (!(dwFlags
& MOVEFILE_REPLACE_EXISTING
))
289 SetLastError(ERROR_ALREADY_EXISTS
);
292 else if (GetFileAttributesW(lpNewFileName
) & FILE_ATTRIBUTE_DIRECTORY
)
294 SetLastError(ERROR_ACCESS_DENIED
);
299 hFile
= CreateFileW (lpExistingFileName
,
301 FILE_SHARE_WRITE
|FILE_SHARE_READ
,
304 FILE_FLAG_BACKUP_SEMANTICS
|
305 ((dwFlags
& MOVEFILE_WRITE_THROUGH
) ? FILE_FLAG_WRITE_THROUGH
: 0),
308 if (hFile
== INVALID_HANDLE_VALUE
)
313 FileRename
= RtlAllocateHeap(
316 sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
);
319 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
322 if( dwFlags
& MOVEFILE_REPLACE_EXISTING
) {
323 FileRename
->ReplaceIfExists
= TRUE
;
326 FileRename
->ReplaceIfExists
= FALSE
;
330 memcpy(FileRename
->FileName
, DstPathU
.Buffer
, DstPathU
.Length
);
331 RtlFreeHeap (RtlGetProcessHeap (),
335 FileRename
->FileNameLength
= DstPathU
.Length
;
336 errCode
= NtSetInformationFile (hFile
,
339 sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
,
340 FileRenameInformation
);
342 RtlFreeHeap(RtlGetProcessHeap(), 0, FileRename
);
344 if (GetFileAttributesW(lpExistingFileName
) & FILE_ATTRIBUTE_DIRECTORY
)
352 * Fail now move the folder
353 * Before we fail at CreateFileW
357 if (NT_SUCCESS(errCode
))
365 Result
= CopyFileExW (lpExistingFileName
,
370 (dwFlags
& MOVEFILE_REPLACE_EXISTING
) ? 0 : COPY_FILE_FAIL_IF_EXISTS
);
373 /* Cleanup the source file */
374 Result
= DeleteFileW (lpExistingFileName
);
379 /* move folder code start */
380 WIN32_FIND_DATAW findBuffer
;
381 LPWSTR lpExistingFileName2
= NULL
;
382 LPWSTR lpNewFileName2
= NULL
;
383 LPWSTR lpDeleteFile
= NULL
;
388 INT max_size
= MAX_PATH
;
391 /* Build the string */
392 size
= wcslen(lpExistingFileName
);
393 if (size
+6> max_size
)
396 lpDeleteFile
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
397 if (lpDeleteFile
== NULL
)
400 lpNewFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
401 if (lpNewFileName2
== NULL
)
403 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
407 lpExistingFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
408 if (lpExistingFileName2
== NULL
)
410 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
411 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
415 wcscpy( (WCHAR
*)lpExistingFileName2
,lpExistingFileName
);
416 wcscpy( (WCHAR
*)&lpExistingFileName2
[size
],L
"\\*.*\0");
418 /* Get the file name */
419 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
420 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
421 if (hFile
== INVALID_HANDLE_VALUE
)
424 if (findBuffer
.cFileName
[0] == L
'\0')
429 * remove readonly flag from source folder and do not set the readonly flag to dest folder
431 RemoveReadOnlyAttributeW(lpExistingFileName
);
432 RemoveReadOnlyAttributeW(lpNewFileName
);
433 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
434 CreateDirectoryW(lpNewFileName
, NULL
);
436 /* search the files/folders and move them */
441 if ((!wcscmp(findBuffer
.cFileName
,L
"..")) || (!wcscmp(findBuffer
.cFileName
,L
".")))
443 loop
= FindNextFileW(hFile
, &findBuffer
);
447 size
= wcslen(lpExistingFileName2
)-4;
449 hFile
= INVALID_HANDLE_VALUE
;
451 wcscpy( &lpExistingFileName2
[size
],L
"\0");
453 if (wcsncmp(lpExistingFileName
,lpExistingFileName2
,size
))
458 TRACE("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile
);
460 /* remove system folder flag other wise we can not delete the folder */
461 Attributes
= GetFileAttributesW(lpExistingFileName2
);
462 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
464 SetFileAttributesW(lpExistingFileName2
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
467 RemoveReadOnlyAttributeW(lpExistingFileName2
);
469 Result
= RemoveDirectoryW(lpExistingFileName2
);
474 size
= wcslen(lpExistingFileName
);
478 if (lpNewFileName2
!= NULL
)
479 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
481 if (lpExistingFileName2
!= NULL
)
482 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
484 if (lpDeleteFile
!= NULL
)
485 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
490 wcscpy( lpExistingFileName2
,lpExistingFileName
);
491 wcscpy( &lpExistingFileName2
[size
],L
"\\*.*\0");
493 /* Get the file name */
494 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
495 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
501 if (findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
504 /* Build the new src string */
505 size
= wcslen(findBuffer
.cFileName
);
506 size2
= wcslen(lpExistingFileName2
);
508 if (size2
+size
+6>max_size
)
512 if (lpNewFileName2
!= NULL
)
513 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
515 if (lpExistingFileName2
!= NULL
)
516 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
518 if (lpDeleteFile
!= NULL
)
519 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
524 wcscpy( &lpExistingFileName2
[size2
-3],findBuffer
.cFileName
);
525 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\0");
529 wcscpy( lpDeleteFile
,lpExistingFileName2
);
530 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\\*.*\0");
533 /* Build the new dst string */
534 size
= wcslen(lpExistingFileName2
) + wcslen(lpNewFileName
);
535 size2
= wcslen(lpExistingFileName
);
541 if (lpNewFileName2
!= NULL
)
542 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
544 if (lpExistingFileName2
!= NULL
)
545 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
547 if (lpDeleteFile
!= NULL
)
548 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
553 wcscpy( lpNewFileName2
,lpNewFileName
);
554 size
= wcslen(lpNewFileName
);
555 wcscpy( &lpNewFileName2
[size
], &lpExistingFileName2
[size2
]);
556 size
= wcslen(lpNewFileName2
);
557 wcscpy( &lpNewFileName2
[size
-4],L
"\0");
562 * remove readonly flag from source folder and do not set the readonly flag to dest folder
564 RemoveReadOnlyAttributeW(lpDeleteFile
);
565 RemoveReadOnlyAttributeW(lpNewFileName2
);
567 CreateDirectoryW(lpNewFileName2
,NULL
);
568 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
571 /* set new search path from src string */
573 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
574 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
579 /* Build the new string */
580 size
= wcslen(findBuffer
.cFileName
);
581 size2
= wcslen(lpExistingFileName2
);
582 wcscpy( lpDeleteFile
,lpExistingFileName2
);
583 wcscpy( &lpDeleteFile
[size2
-3],findBuffer
.cFileName
);
585 /* Build dest string */
586 size
= wcslen(lpDeleteFile
) + wcslen(lpNewFileName
);
587 size2
= wcslen(lpExistingFileName
);
593 if (lpNewFileName2
!= NULL
)
594 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
596 if (lpExistingFileName2
!= NULL
)
597 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
599 if (lpDeleteFile
!= NULL
)
600 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
605 wcscpy( lpNewFileName2
,lpNewFileName
);
606 size
= wcslen(lpNewFileName
);
607 wcscpy(&lpNewFileName2
[size
],&lpDeleteFile
[size2
]);
610 /* overrite existsen file, if the file got the flag have readonly
611 * we need reomve that flag
616 TRACE("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile
, lpNewFileName2
);
617 RemoveReadOnlyAttributeW(lpDeleteFile
);
618 RemoveReadOnlyAttributeW(lpNewFileName2
);
620 Result
= CopyFileExW (lpDeleteFile
,
631 TRACE("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2
);
632 Result
= RemoveReadOnlyAttributeW(lpDeleteFile
);
636 TRACE("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile
);
637 Result
= DeleteFileW(lpDeleteFile
);
642 loop
= FindNextFileW(hFile
, &findBuffer
);
646 /* Remove last folder */
647 if ((loop
== FALSE
) && (Result
!= FALSE
))
651 Attributes
= GetFileAttributesW(lpDeleteFile
);
652 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
654 SetFileAttributesW(lpDeleteFile
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
657 Result
= RemoveDirectoryW(lpExistingFileName
);
663 if (lpNewFileName2
!= NULL
)
665 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
666 lpNewFileName2
= NULL
;
669 if (lpExistingFileName2
!= NULL
)
671 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
672 lpExistingFileName2
= NULL
;
675 if (lpDeleteFile
!= NULL
)
677 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
683 // end move folder code
697 MoveFileWithProgressA (
698 LPCSTR lpExistingFileName
,
699 LPCSTR lpNewFileName
,
700 LPPROGRESS_ROUTINE lpProgressRoutine
,
705 PWCHAR ExistingFileNameW
;
709 if (!(ExistingFileNameW
= FilenameA2W(lpExistingFileName
, FALSE
)))
712 if (!(NewFileNameW
= FilenameA2W(lpNewFileName
, TRUE
)))
715 ret
= MoveFileWithProgressW (ExistingFileNameW
,
721 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW
);
733 LPCWSTR lpExistingFileName
,
734 LPCWSTR lpNewFileName
737 return MoveFileExW (lpExistingFileName
,
739 MOVEFILE_COPY_ALLOWED
);
749 LPCWSTR lpExistingFileName
,
750 LPCWSTR lpNewFileName
,
754 return MoveFileWithProgressW (lpExistingFileName
,
768 LPCSTR lpExistingFileName
,
772 return MoveFileExA (lpExistingFileName
,
774 MOVEFILE_COPY_ALLOWED
);
784 LPCSTR lpExistingFileName
,
785 LPCSTR lpNewFileName
,
789 return MoveFileWithProgressA (lpExistingFileName
,