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 ****************************************************************/
26 AdjustFileAttributes (
27 LPCWSTR ExistingFileName
,
31 IO_STATUS_BLOCK IoStatusBlock
;
32 FILE_BASIC_INFORMATION ExistingInfo
,
39 hFile
= CreateFileW (ExistingFileName
,
40 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
44 FILE_ATTRIBUTE_NORMAL
,
46 if (INVALID_HANDLE_VALUE
!= hFile
)
48 errCode
= NtQueryInformationFile (hFile
,
51 sizeof(FILE_BASIC_INFORMATION
),
52 FileBasicInformation
);
53 if (NT_SUCCESS (errCode
))
55 if (0 != (ExistingInfo
.FileAttributes
& FILE_ATTRIBUTE_READONLY
))
57 Attributes
= ExistingInfo
.FileAttributes
;
58 ExistingInfo
.FileAttributes
&= ~ FILE_ATTRIBUTE_READONLY
;
59 if (0 == (ExistingInfo
.FileAttributes
&
60 (FILE_ATTRIBUTE_HIDDEN
|
61 FILE_ATTRIBUTE_SYSTEM
|
62 FILE_ATTRIBUTE_ARCHIVE
)))
64 ExistingInfo
.FileAttributes
|= FILE_ATTRIBUTE_NORMAL
;
66 errCode
= NtSetInformationFile (hFile
,
69 sizeof(FILE_BASIC_INFORMATION
),
70 FileBasicInformation
);
71 if (!NT_SUCCESS(errCode
))
73 DPRINT("Removing READONLY attribute from source failed with status 0x%08x\n", errCode
);
75 ExistingInfo
.FileAttributes
= Attributes
;
79 if (NT_SUCCESS(errCode
))
81 hFile
= CreateFileW (NewFileName
,
82 FILE_READ_ATTRIBUTES
| FILE_WRITE_ATTRIBUTES
,
86 FILE_ATTRIBUTE_NORMAL
,
88 if (INVALID_HANDLE_VALUE
!= hFile
)
90 errCode
= NtQueryInformationFile(hFile
,
93 sizeof(FILE_BASIC_INFORMATION
),
94 FileBasicInformation
);
95 if (NT_SUCCESS(errCode
))
97 NewInfo
.FileAttributes
= (NewInfo
.FileAttributes
&
98 ~ (FILE_ATTRIBUTE_HIDDEN
|
99 FILE_ATTRIBUTE_SYSTEM
|
100 FILE_ATTRIBUTE_READONLY
|
101 FILE_ATTRIBUTE_NORMAL
)) |
102 (ExistingInfo
.FileAttributes
&
103 (FILE_ATTRIBUTE_HIDDEN
|
104 FILE_ATTRIBUTE_SYSTEM
|
105 FILE_ATTRIBUTE_READONLY
|
106 FILE_ATTRIBUTE_NORMAL
)) |
107 FILE_ATTRIBUTE_ARCHIVE
;
108 NewInfo
.CreationTime
= ExistingInfo
.CreationTime
;
109 NewInfo
.LastAccessTime
= ExistingInfo
.LastAccessTime
;
110 NewInfo
.LastWriteTime
= ExistingInfo
.LastWriteTime
;
111 errCode
= NtSetInformationFile (hFile
,
114 sizeof(FILE_BASIC_INFORMATION
),
115 FileBasicInformation
);
116 if (NT_SUCCESS(errCode
))
122 DPRINT("Setting attributes on dest file failed with status 0x%08x\n", errCode
);
127 DPRINT("Obtaining attributes from dest file failed with status 0x%08x\n", errCode
);
133 DPRINT("Opening dest file to set attributes failed with code %d\n", GetLastError());
139 DPRINT("Obtaining attributes from source file failed with status 0x%08x\n", errCode
);
145 DPRINT("Opening source file to obtain attributes failed with code %d\n", GetLastError());
151 /***********************************************************************
152 * add_boot_rename_entry
154 * Adds an entry to the registry that is loaded when windows boots and
155 * checks if there are some files to be removed or renamed/moved.
156 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
157 * non-NULL then the file is moved, otherwise it is deleted. The
158 * entry of the registrykey is always appended with two zero
159 * terminated strings. If <fn2> is NULL then the second entry is
160 * simply a single 0-byte. Otherwise the second filename goes
161 * there. The entries are prepended with \??\ before the path and the
162 * second filename gets also a '!' as the first character if
163 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
164 * 0-byte follows to indicate the end of the strings.
166 * \??\D:\test\file1[0]
167 * !\??\D:\test\file1_renamed[0]
168 * \??\D:\Test|delete[0]
169 * [0] <- file is to be deleted, second string empty
170 * \??\D:\test\file2[0]
171 * !\??\D:\test\file2_renamed[0]
172 * [0] <- indicates end of strings
175 * \??\D:\test\file1[0]
176 * !\??\D:\test\file1_renamed[0]
177 * \??\D:\Test|delete[0]
178 * [0] <- file is to be deleted, second string empty
179 * [0] <- indicates end of strings
182 static BOOL
add_boot_rename_entry( LPCWSTR source
, LPCWSTR dest
, DWORD flags
)
184 static const WCHAR ValueName
[] = {'P','e','n','d','i','n','g',
185 'F','i','l','e','R','e','n','a','m','e',
186 'O','p','e','r','a','t','i','o','n','s',0};
187 static const WCHAR SessionW
[] = {'M','a','c','h','i','n','e','\\',
188 'S','y','s','t','e','m','\\',
189 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
190 'C','o','n','t','r','o','l','\\',
191 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
192 static const int info_size
= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
194 OBJECT_ATTRIBUTES attr
;
195 UNICODE_STRING nameW
, source_name
, dest_name
;
196 KEY_VALUE_PARTIAL_INFORMATION
*info
;
204 DPRINT("Add support to smss for keys created by MOVEFILE_DELAY_UNTIL_REBOOT\n");
206 if (!RtlDosPathNameToNtPathName_U( (LPWSTR
)source
, &source_name
, NULL
, NULL
))
208 SetLastError( ERROR_PATH_NOT_FOUND
);
211 dest_name
.Buffer
= NULL
;
212 if (dest
&& !RtlDosPathNameToNtPathName_U( (LPWSTR
)dest
, &dest_name
, NULL
, NULL
))
214 RtlFreeUnicodeString( &source_name
);
215 SetLastError( ERROR_PATH_NOT_FOUND
);
219 attr
.Length
= sizeof(attr
);
220 attr
.RootDirectory
= 0;
221 attr
.ObjectName
= &nameW
;
223 attr
.SecurityDescriptor
= NULL
;
224 attr
.SecurityQualityOfService
= NULL
;
225 RtlInitUnicodeString( &nameW
, SessionW
);
227 if (NtCreateKey( &Reboot
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, 0, NULL
) != STATUS_SUCCESS
)
229 DPRINT1("Error creating key for reboot managment [%s]\n",
230 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
231 RtlFreeUnicodeString( &source_name
);
232 RtlFreeUnicodeString( &dest_name
);
236 len1
= source_name
.Length
+ sizeof(WCHAR
);
239 len2
= dest_name
.Length
+ sizeof(WCHAR
);
240 if (flags
& MOVEFILE_REPLACE_EXISTING
)
241 len2
+= sizeof(WCHAR
); /* Plus 1 because of the leading '!' */
243 else len2
= sizeof(WCHAR
); /* minimum is the 0 characters for the empty second string */
245 RtlInitUnicodeString( &nameW
, ValueName
);
247 /* First we check if the key exists and if so how many bytes it already contains. */
248 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
249 NULL
, 0, &DataSize
) == STATUS_BUFFER_OVERFLOW
)
251 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
253 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
254 Buffer
, DataSize
, &DataSize
)) goto Quit
;
255 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
256 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
257 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
261 DataSize
= info_size
;
262 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
266 memcpy( Buffer
+ DataSize
, source_name
.Buffer
, len1
);
268 p
= (WCHAR
*)(Buffer
+ DataSize
);
271 if (flags
& MOVEFILE_REPLACE_EXISTING
)
273 memcpy( p
, dest_name
.Buffer
, len2
);
279 DataSize
+= sizeof(WCHAR
);
283 p
= (WCHAR
*)(Buffer
+ DataSize
);
285 DataSize
+= sizeof(WCHAR
);
287 rc
= !NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
);
290 RtlFreeUnicodeString( &source_name
);
291 RtlFreeUnicodeString( &dest_name
);
292 if (Reboot
) NtClose(Reboot
);
293 HeapFree( GetProcessHeap(), 0, Buffer
);
303 MoveFileWithProgressW (
304 LPCWSTR lpExistingFileName
,
305 LPCWSTR lpNewFileName
,
306 LPPROGRESS_ROUTINE lpProgressRoutine
,
312 IO_STATUS_BLOCK IoStatusBlock
;
313 PFILE_RENAME_INFORMATION FileRename
;
316 UNICODE_STRING DstPathU
;
319 DPRINT("MoveFileWithProgressW()\n");
321 if (dwFlags
& MOVEFILE_DELAY_UNTIL_REBOOT
)
322 return add_boot_rename_entry( lpExistingFileName
, lpNewFileName
, dwFlags
);
324 hFile
= CreateFileW (lpExistingFileName
,
326 FILE_SHARE_WRITE
|FILE_SHARE_READ
,
329 FILE_ATTRIBUTE_NORMAL
,
332 if (hFile
== INVALID_HANDLE_VALUE
)
334 hFile
= CreateFileW (lpExistingFileName
,
336 FILE_SHARE_WRITE
|FILE_SHARE_READ
,
339 FILE_FLAG_BACKUP_SEMANTICS
,
342 if (hFile
== INVALID_HANDLE_VALUE
)
349 /* validate & translate the filename */
350 if (!RtlDosPathNameToNtPathName_U ((LPWSTR
)lpNewFileName
,
355 DPRINT("Invalid destination path\n");
357 SetLastError(ERROR_PATH_NOT_FOUND
);
361 FileRename
= alloca(sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
);
362 if ((dwFlags
& MOVEFILE_REPLACE_EXISTING
) == MOVEFILE_REPLACE_EXISTING
)
363 FileRename
->ReplaceIfExists
= TRUE
;
365 FileRename
->ReplaceIfExists
= FALSE
;
367 memcpy(FileRename
->FileName
, DstPathU
.Buffer
, DstPathU
.Length
);
368 RtlFreeHeap (RtlGetProcessHeap (),
373 * Is the length the count of characters or the length of the buffer?
375 FileRename
->FileNameLength
= DstPathU
.Length
/ sizeof(WCHAR
);
376 errCode
= NtSetInformationFile (hFile
,
379 sizeof(FILE_RENAME_INFORMATION
) + DstPathU
.Length
,
380 FileRenameInformation
);
385 * Fail now move the folder
386 * Before we fail at CreateFileW
390 if (NT_SUCCESS(errCode
))
394 else if (STATUS_NOT_IMPLEMENTED
== errCode
)
398 Result
= CopyFileExW (lpExistingFileName
,
403 FileRename
->ReplaceIfExists
? 0 : COPY_FILE_FAIL_IF_EXISTS
);
406 /* Cleanup the source file */
407 AdjustFileAttributes(lpExistingFileName
, lpNewFileName
);
408 Result
= DeleteFileW (lpExistingFileName
);
413 /* move folder not complete code */
414 Result
= CreateDirectoryW (lpNewFileName
, NULL
);
415 if (Result
== FALSE
) return FALSE
;
417 /* add scan code for move the folder */
419 AdjustFileAttributes(lpExistingFileName
, lpNewFileName
);
420 Result
= RemoveDirectoryW(lpExistingFileName
);
425 /* FIXME file rename not yet implemented in all FSDs so it will always
426 * fail, even when the move is to the same device
428 else if (STATUS_NOT_IMPLEMENTED
== errCode
)
431 UNICODE_STRING SrcPathU
;
433 SrcPathU
.Buffer
= alloca(sizeof(WCHAR
) * MAX_PATH
);
434 SrcPathU
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
435 SrcPathU
.Length
= GetFullPathNameW(lpExistingFileName
, MAX_PATH
, SrcPathU
.Buffer
, NULL
);
436 if (SrcPathU
.Length
>= MAX_PATH
)
438 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
441 SrcPathU
.Length
*= sizeof(WCHAR
);
443 DstPathU
.Buffer
= alloca(sizeof(WCHAR
) * MAX_PATH
);
444 DstPathU
.MaximumLength
= MAX_PATH
* sizeof(WCHAR
);
445 DstPathU
.Length
= GetFullPathNameW(lpNewFileName
, MAX_PATH
, DstPathU
.Buffer
, NULL
);
446 if (DstPathU
.Length
>= MAX_PATH
)
448 SetLastError(ERROR_FILENAME_EXCED_RANGE
);
451 DstPathU
.Length
*= sizeof(WCHAR
);
453 if (0 == RtlCompareUnicodeString(&SrcPathU
, &DstPathU
, TRUE
))
455 /* Source and destination file are the same, nothing to do */
459 Result
= CopyFileExW (lpExistingFileName
,
464 FileRename
->ReplaceIfExists
? 0 : COPY_FILE_FAIL_IF_EXISTS
);
467 /* Cleanup the source file */
468 AdjustFileAttributes(lpExistingFileName
, lpNewFileName
);
469 Result
= DeleteFileW (lpExistingFileName
);
475 SetLastErrorByStatus (errCode
);
487 MoveFileWithProgressA (
488 LPCSTR lpExistingFileName
,
489 LPCSTR lpNewFileName
,
490 LPPROGRESS_ROUTINE lpProgressRoutine
,
495 PWCHAR ExistingFileNameW
;
499 if (!(ExistingFileNameW
= FilenameA2W(lpExistingFileName
, FALSE
)))
502 if (!(NewFileNameW
= FilenameA2W(lpNewFileName
, TRUE
)))
505 ret
= MoveFileWithProgressW (ExistingFileNameW
,
511 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW
);
523 LPCWSTR lpExistingFileName
,
524 LPCWSTR lpNewFileName
527 return MoveFileExW (lpExistingFileName
,
529 MOVEFILE_COPY_ALLOWED
);
539 LPCWSTR lpExistingFileName
,
540 LPCWSTR lpNewFileName
,
544 return MoveFileWithProgressW (lpExistingFileName
,
558 LPCSTR lpExistingFileName
,
562 return MoveFileExA (lpExistingFileName
,
564 MOVEFILE_COPY_ALLOWED
);
574 LPCSTR lpExistingFileName
,
575 LPCSTR lpNewFileName
,
579 return MoveFileWithProgressA (lpExistingFileName
,