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 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name
.Buffer
);
102 SetLastError( ERROR_PATH_NOT_FOUND
);
106 InitializeObjectAttributes(&ObjectAttributes
,
108 OBJ_CASE_INSENSITIVE
,
112 Status
= NtCreateKey(&Reboot
,
113 KEY_QUERY_VALUE
| KEY_SET_VALUE
,
117 REG_OPTION_NON_VOLATILE
,
120 if (!NT_SUCCESS(Status
))
122 DPRINT1("NtCreateKey() failed (Status 0x%lx)\n", Status
);
123 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name
.Buffer
);
124 RtlFreeHeap( RtlGetProcessHeap(), 0, dest_name
.Buffer
);
128 len1
= source_name
.Length
+ sizeof(WCHAR
);
131 len2
= dest_name
.Length
+ sizeof(WCHAR
);
132 if (flags
& MOVEFILE_REPLACE_EXISTING
)
133 len2
+= sizeof(WCHAR
); /* Plus 1 because of the leading '!' */
135 else len2
= sizeof(WCHAR
); /* minimum is the 0 characters for the empty second string */
137 RtlInitUnicodeString( &nameW
, ValueName
);
139 /* First we check if the key exists and if so how many bytes it already contains. */
140 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
141 NULL
, 0, &DataSize
) == STATUS_BUFFER_OVERFLOW
)
143 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
145 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
146 Buffer
, DataSize
, &DataSize
)) goto Quit
;
147 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
148 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
149 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
153 DataSize
= info_size
;
154 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
158 memcpy( Buffer
+ DataSize
, source_name
.Buffer
, len1
);
160 p
= (WCHAR
*)(Buffer
+ DataSize
);
163 if (flags
& MOVEFILE_REPLACE_EXISTING
)
165 memcpy( p
, dest_name
.Buffer
, len2
);
171 DataSize
+= sizeof(WCHAR
);
175 p
= (WCHAR
*)(Buffer
+ DataSize
);
177 DataSize
+= sizeof(WCHAR
);
179 rc
= NT_SUCCESS(NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
));
182 RtlFreeHeap( RtlGetProcessHeap(), 0, source_name
.Buffer
);
183 RtlFreeHeap( RtlGetProcessHeap(), 0, dest_name
.Buffer
);
184 if (Reboot
) NtClose(Reboot
);
185 HeapFree( GetProcessHeap(), 0, Buffer
);
195 MoveFileWithProgressW (
196 LPCWSTR lpExistingFileName
,
197 LPCWSTR lpNewFileName
,
198 LPPROGRESS_ROUTINE lpProgressRoutine
,
204 IO_STATUS_BLOCK IoStatusBlock
;
205 PFILE_RENAME_INFORMATION FileRename
;
208 UNICODE_STRING DstPathU
;
211 DPRINT("MoveFileWithProgressW()\n");
213 if (dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
)
214 return add_boot_rename_entry( lpExistingFileName
, lpNewFileName
, dwFlags
);
216 hFile
= CreateFileW (lpExistingFileName
,
218 FILE_SHARE_WRITE
|FILE_SHARE_READ
,
221 FILE_FLAG_BACKUP_SEMANTICS
,
224 if (hFile
== INVALID_HANDLE_VALUE
)
230 /* validate & translate the filename */
231 if (!RtlDosPathNameToNtPathName_U (lpNewFileName
,
236 DPRINT("Invalid destination path\n");
238 SetLastError(ERROR_PATH_NOT_FOUND
);
242 FileRename
= alloca(sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
);
243 if ((dwFlags
& MOVEFILE_REPLACE_EXISTING
) == MOVEFILE_REPLACE_EXISTING
)
244 FileRename
->ReplaceIfExists
= TRUE
;
246 FileRename
->ReplaceIfExists
= FALSE
;
248 memcpy(FileRename
->FileName
, DstPathU
.Buffer
, DstPathU
.Length
);
249 RtlFreeHeap (RtlGetProcessHeap (),
254 * Is the length the count of characters or the length of the buffer?
256 FileRename
->FileNameLength
= DstPathU
.Length
/ sizeof(WCHAR
);
257 errCode
= NtSetInformationFile (hFile
,
260 sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
,
261 FileRenameInformation
);
264 if (GetFileAttributesW(lpExistingFileName
) & FILE_ATTRIBUTE_DIRECTORY
)
272 * Fail now move the folder
273 * Before we fail at CreateFileW
277 if (NT_SUCCESS(errCode
))
285 Result
= CopyFileExW (lpExistingFileName
,
290 FileRename
->ReplaceIfExists
? 0 : COPY_FILE_FAIL_IF_EXISTS
);
293 /* Cleanup the source file */
294 Result
= DeleteFileW (lpExistingFileName
);
299 /* move folder code start */
300 WIN32_FIND_DATAW findBuffer
;
301 LPWSTR lpExistingFileName2
= NULL
;
302 LPWSTR lpNewFileName2
= NULL
;
303 LPWSTR lpDeleteFile
= NULL
;
308 INT max_size
= MAX_PATH
;
311 /* Build the string */
312 size
= wcslen(lpExistingFileName
);
313 if (size
+6> max_size
)
316 lpDeleteFile
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
317 if (lpDeleteFile
== NULL
)
320 lpNewFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
321 if (lpNewFileName2
== NULL
)
323 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
327 lpExistingFileName2
= (LPWSTR
) HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY
,max_size
* sizeof(WCHAR
));
328 if (lpNewFileName2
== NULL
)
330 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
331 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
335 wcscpy( (WCHAR
*)lpExistingFileName2
,lpExistingFileName
);
336 wcscpy( (WCHAR
*)&lpExistingFileName2
[size
],L
"\\*.*\0");
338 /* Get the file name */
339 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
340 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
344 if (findBuffer
.cFileName
[0] == L
'\0')
349 * remove readonly flag from source folder and do not set the readonly flag to dest folder
351 RemoveReadOnlyAttributeW(lpExistingFileName
);
352 RemoveReadOnlyAttributeW(lpNewFileName
);
353 //CreateDirectoryExW(lpExistingFileName,lpNewFileName,NULL);
354 CreateDirectoryW(lpNewFileName
, NULL
);
356 /* search the files/folders and move them */
361 if ((!wcscmp(findBuffer
.cFileName
,L
"..")) || (!wcscmp(findBuffer
.cFileName
,L
".")))
363 loop
= FindNextFileW(hFile
, &findBuffer
);
367 size
= wcslen(lpExistingFileName2
)-4;
369 wcscpy( &lpExistingFileName2
[size
],L
"\0");
371 if (wcsncmp(lpExistingFileName
,lpExistingFileName2
,size
))
378 DPRINT("MoveFileWithProgressW : Delete folder : %S\n",lpDeleteFile
);
380 /* remove system folder flag other wise we can not delete the folder */
381 Attributes
= GetFileAttributesW(lpExistingFileName2
);
382 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
384 SetFileAttributesW(lpExistingFileName2
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
387 RemoveReadOnlyAttributeW(lpExistingFileName2
);
389 Result
= RemoveDirectoryW(lpExistingFileName2
);
394 size
= wcslen(lpExistingFileName
);
398 if (lpNewFileName2
!= NULL
)
399 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
401 if (lpExistingFileName2
!= NULL
)
402 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
404 if (lpDeleteFile
!= NULL
)
405 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
410 wcscpy( lpExistingFileName2
,lpExistingFileName
);
411 wcscpy( &lpExistingFileName2
[size
],L
"\\*.*\0");
413 /* Get the file name */
414 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
415 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
421 if (findBuffer
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
424 /* Build the new src string */
425 size
= wcslen(findBuffer
.cFileName
);
426 size2
= wcslen(lpExistingFileName2
);
428 if (size2
+size
+6>max_size
)
432 if (lpNewFileName2
!= NULL
)
433 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
435 if (lpExistingFileName2
!= NULL
)
436 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
438 if (lpDeleteFile
!= NULL
)
439 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
444 wcscpy( &lpExistingFileName2
[size2
-3],findBuffer
.cFileName
);
445 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\0");
449 wcscpy( lpDeleteFile
,lpExistingFileName2
);
450 wcscpy( &lpExistingFileName2
[size2
+size
-3],L
"\\*.*\0");
453 /* Build the new dst string */
454 size
= wcslen(lpExistingFileName2
) + wcslen(lpNewFileName
);
455 size2
= wcslen(lpExistingFileName
);
461 if (lpNewFileName2
!= NULL
)
462 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
464 if (lpExistingFileName2
!= NULL
)
465 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
467 if (lpDeleteFile
!= NULL
)
468 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
473 wcscpy( lpNewFileName2
,lpNewFileName
);
474 size
= wcslen(lpNewFileName
);
475 wcscpy( &lpNewFileName2
[size
], &lpExistingFileName2
[size2
]);
476 size
= wcslen(lpNewFileName2
);
477 wcscpy( &lpNewFileName2
[size
-4],L
"\0");
482 * remove readonly flag from source folder and do not set the readonly flag to dest folder
484 RemoveReadOnlyAttributeW(lpDeleteFile
);
485 RemoveReadOnlyAttributeW(lpNewFileName2
);
487 CreateDirectoryW(lpNewFileName2
,NULL
);
488 //CreateDirectoryExW(lpDeleteFile, lpNewFileName2,NULL);
491 /* set new search path from src string */
493 memset(&findBuffer
,0,sizeof(WIN32_FIND_DATAW
));
494 hFile
= FindFirstFileW(lpExistingFileName2
, &findBuffer
);
499 /* Build the new string */
500 size
= wcslen(findBuffer
.cFileName
);
501 size2
= wcslen(lpExistingFileName2
);
502 wcscpy( lpDeleteFile
,lpExistingFileName2
);
503 wcscpy( &lpDeleteFile
[size2
-3],findBuffer
.cFileName
);
505 /* Build dest string */
506 size
= wcslen(lpDeleteFile
) + wcslen(lpNewFileName
);
507 size2
= wcslen(lpExistingFileName
);
513 if (lpNewFileName2
!= NULL
)
514 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
516 if (lpExistingFileName2
!= NULL
)
517 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
519 if (lpDeleteFile
!= NULL
)
520 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
525 wcscpy( lpNewFileName2
,lpNewFileName
);
526 size
= wcslen(lpNewFileName
);
527 wcscpy(&lpNewFileName2
[size
],&lpDeleteFile
[size2
]);
530 /* overrite existsen file, if the file got the flag have readonly
531 * we need reomve that flag
536 DPRINT("MoveFileWithProgressW : Copy file : %S to %S\n",lpDeleteFile
, lpNewFileName2
);
537 RemoveReadOnlyAttributeW(lpDeleteFile
);
538 RemoveReadOnlyAttributeW(lpNewFileName2
);
540 Result
= CopyFileExW (lpDeleteFile
,
551 DPRINT("MoveFileWithProgressW : remove readonly flag from file : %S\n",lpNewFileName2
);
552 Result
= RemoveReadOnlyAttributeW(lpDeleteFile
);
556 DPRINT("MoveFileWithProgressW : Delete file : %S\n",lpDeleteFile
);
557 Result
= DeleteFileW(lpDeleteFile
);
562 loop
= FindNextFileW(hFile
, &findBuffer
);
566 /* Remove last folder */
567 if ((loop
== FALSE
) && (Result
!= FALSE
))
572 Attributes
= GetFileAttributesW(lpDeleteFile
);
573 if (Attributes
!= INVALID_FILE_ATTRIBUTES
)
575 SetFileAttributesW(lpDeleteFile
,(Attributes
& ~FILE_ATTRIBUTE_SYSTEM
));
578 Result
= RemoveDirectoryW(lpExistingFileName
);
584 if (lpNewFileName2
!= NULL
)
586 HeapFree(GetProcessHeap(),0,(VOID
*) lpNewFileName2
);
587 lpNewFileName2
= NULL
;
590 if (lpExistingFileName2
!= NULL
)
592 HeapFree(GetProcessHeap(),0,(VOID
*) lpExistingFileName2
);
593 lpExistingFileName2
= NULL
;
596 if (lpDeleteFile
!= NULL
)
598 HeapFree(GetProcessHeap(),0,(VOID
*) lpDeleteFile
);
604 // end move folder code
618 MoveFileWithProgressA (
619 LPCSTR lpExistingFileName
,
620 LPCSTR lpNewFileName
,
621 LPPROGRESS_ROUTINE lpProgressRoutine
,
626 PWCHAR ExistingFileNameW
;
630 if (!(ExistingFileNameW
= FilenameA2W(lpExistingFileName
, FALSE
)))
633 if (!(NewFileNameW
= FilenameA2W(lpNewFileName
, TRUE
)))
636 ret
= MoveFileWithProgressW (ExistingFileNameW
,
642 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW
);
654 LPCWSTR lpExistingFileName
,
655 LPCWSTR lpNewFileName
658 return MoveFileExW (lpExistingFileName
,
660 MOVEFILE_COPY_ALLOWED
);
670 LPCWSTR lpExistingFileName
,
671 LPCWSTR lpNewFileName
,
675 return MoveFileWithProgressW (lpExistingFileName
,
689 LPCSTR lpExistingFileName
,
693 return MoveFileExA (lpExistingFileName
,
695 MOVEFILE_COPY_ALLOWED
);
705 LPCSTR lpExistingFileName
,
706 LPCSTR lpNewFileName
,
710 return MoveFileWithProgressA (lpExistingFileName
,