migrate substitution keywords to SVN
[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 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <k32.h>
15
16 #define NDEBUG
17 #include "../include/debug.h"
18
19 /* GLOBALS *****************************************************************/
20
21 /* FUNCTIONS ****************************************************************/
22
23 static BOOL
24 AdjustFileAttributes (
25 LPCWSTR ExistingFileName,
26 LPCWSTR NewFileName
27 )
28 {
29 IO_STATUS_BLOCK IoStatusBlock;
30 FILE_BASIC_INFORMATION ExistingInfo,
31 NewInfo;
32 HANDLE hFile;
33 DWORD Attributes;
34 NTSTATUS errCode;
35 BOOL Result = FALSE;
36
37 hFile = CreateFileW (ExistingFileName,
38 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
39 FILE_SHARE_READ,
40 NULL,
41 OPEN_EXISTING,
42 FILE_ATTRIBUTE_NORMAL,
43 NULL);
44 if (INVALID_HANDLE_VALUE != hFile)
45 {
46 errCode = NtQueryInformationFile (hFile,
47 &IoStatusBlock,
48 &ExistingInfo,
49 sizeof(FILE_BASIC_INFORMATION),
50 FileBasicInformation);
51 if (NT_SUCCESS (errCode))
52 {
53 if (0 != (ExistingInfo.FileAttributes & FILE_ATTRIBUTE_READONLY))
54 {
55 Attributes = ExistingInfo.FileAttributes;
56 ExistingInfo.FileAttributes &= ~ FILE_ATTRIBUTE_READONLY;
57 if (0 == (ExistingInfo.FileAttributes &
58 (FILE_ATTRIBUTE_HIDDEN |
59 FILE_ATTRIBUTE_SYSTEM |
60 FILE_ATTRIBUTE_ARCHIVE)))
61 {
62 ExistingInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
63 }
64 errCode = NtSetInformationFile (hFile,
65 &IoStatusBlock,
66 &ExistingInfo,
67 sizeof(FILE_BASIC_INFORMATION),
68 FileBasicInformation);
69 if (!NT_SUCCESS(errCode))
70 {
71 DPRINT("Removing READONLY attribute from source failed with status 0x%08x\n", errCode);
72 }
73 ExistingInfo.FileAttributes = Attributes;
74 }
75 CloseHandle(hFile);
76
77 if (NT_SUCCESS(errCode))
78 {
79 hFile = CreateFileW (NewFileName,
80 FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
81 FILE_SHARE_READ,
82 NULL,
83 OPEN_EXISTING,
84 FILE_ATTRIBUTE_NORMAL,
85 NULL);
86 if (INVALID_HANDLE_VALUE != hFile)
87 {
88 errCode = NtQueryInformationFile(hFile,
89 &IoStatusBlock,
90 &NewInfo,
91 sizeof(FILE_BASIC_INFORMATION),
92 FileBasicInformation);
93 if (NT_SUCCESS(errCode))
94 {
95 NewInfo.FileAttributes = (NewInfo.FileAttributes &
96 ~ (FILE_ATTRIBUTE_HIDDEN |
97 FILE_ATTRIBUTE_SYSTEM |
98 FILE_ATTRIBUTE_READONLY |
99 FILE_ATTRIBUTE_NORMAL)) |
100 (ExistingInfo.FileAttributes &
101 (FILE_ATTRIBUTE_HIDDEN |
102 FILE_ATTRIBUTE_SYSTEM |
103 FILE_ATTRIBUTE_READONLY |
104 FILE_ATTRIBUTE_NORMAL)) |
105 FILE_ATTRIBUTE_ARCHIVE;
106 NewInfo.CreationTime = ExistingInfo.CreationTime;
107 NewInfo.LastAccessTime = ExistingInfo.LastAccessTime;
108 NewInfo.LastWriteTime = ExistingInfo.LastWriteTime;
109 errCode = NtSetInformationFile (hFile,
110 &IoStatusBlock,
111 &NewInfo,
112 sizeof(FILE_BASIC_INFORMATION),
113 FileBasicInformation);
114 if (NT_SUCCESS(errCode))
115 {
116 Result = TRUE;
117 }
118 else
119 {
120 DPRINT("Setting attributes on dest file failed with status 0x%08x\n", errCode);
121 }
122 }
123 else
124 {
125 DPRINT("Obtaining attributes from dest file failed with status 0x%08x\n", errCode);
126 }
127 CloseHandle(hFile);
128 }
129 else
130 {
131 DPRINT("Opening dest file to set attributes failed with code %d\n", GetLastError());
132 }
133 }
134 }
135 else
136 {
137 DPRINT("Obtaining attributes from source file failed with status 0x%08x\n", errCode);
138 CloseHandle(hFile);
139 }
140 }
141 else
142 {
143 DPRINT("Opening source file to obtain attributes failed with code %d\n", GetLastError());
144 }
145
146 return Result;
147 }
148
149
150 /*
151 * @implemented
152 */
153 BOOL
154 STDCALL
155 MoveFileWithProgressW (
156 LPCWSTR lpExistingFileName,
157 LPCWSTR lpNewFileName,
158 LPPROGRESS_ROUTINE lpProgressRoutine,
159 LPVOID lpData,
160 DWORD dwFlags
161 )
162 {
163 HANDLE hFile = NULL;
164 IO_STATUS_BLOCK IoStatusBlock;
165 PFILE_RENAME_INFORMATION FileRename;
166 NTSTATUS errCode;
167 BOOL Result;
168 UNICODE_STRING DstPathU;
169
170 DPRINT("MoveFileWithProgressW()\n");
171
172 hFile = CreateFileW (lpExistingFileName,
173 GENERIC_ALL,
174 FILE_SHARE_WRITE|FILE_SHARE_READ,
175 NULL,
176 OPEN_EXISTING,
177 FILE_ATTRIBUTE_NORMAL,
178 NULL);
179
180 if (hFile == INVALID_HANDLE_VALUE)
181 {
182 return FALSE;
183 }
184
185 /* validate & translate the filename */
186 if (!RtlDosPathNameToNtPathName_U ((LPWSTR)lpNewFileName,
187 &DstPathU,
188 NULL,
189 NULL))
190 {
191 DPRINT("Invalid destination path\n");
192 CloseHandle(hFile);
193 SetLastError(ERROR_PATH_NOT_FOUND);
194 return FALSE;
195 }
196
197 FileRename = alloca(sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length);
198 if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == MOVEFILE_REPLACE_EXISTING)
199 FileRename->ReplaceIfExists = TRUE;
200 else
201 FileRename->ReplaceIfExists = FALSE;
202
203 memcpy(FileRename->FileName, DstPathU.Buffer, DstPathU.Length);
204 RtlFreeHeap (RtlGetProcessHeap (),
205 0,
206 DstPathU.Buffer);
207 /*
208 * FIXME:
209 * Is the length the count of characters or the length of the buffer?
210 */
211 FileRename->FileNameLength = DstPathU.Length / sizeof(WCHAR);
212 errCode = NtSetInformationFile (hFile,
213 &IoStatusBlock,
214 FileRename,
215 sizeof(FILE_RENAME_INFORMATION) + DstPathU.Length,
216 FileRenameInformation);
217 CloseHandle(hFile);
218 if (NT_SUCCESS(errCode))
219 {
220 Result = TRUE;
221 }
222 else if (STATUS_NOT_SAME_DEVICE == errCode &&
223 MOVEFILE_COPY_ALLOWED == (dwFlags & MOVEFILE_COPY_ALLOWED))
224 {
225 Result = CopyFileExW (lpExistingFileName,
226 lpNewFileName,
227 lpProgressRoutine,
228 lpData,
229 NULL,
230 FileRename->ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS);
231 if (Result)
232 {
233 /* Cleanup the source file */
234 AdjustFileAttributes(lpExistingFileName, lpNewFileName);
235 Result = DeleteFileW (lpExistingFileName);
236 }
237 }
238 #if 1
239 /* FIXME file rename not yet implemented in all FSDs so it will always
240 * fail, even when the move is to the same device
241 */
242 else if (STATUS_NOT_IMPLEMENTED == errCode)
243 {
244
245 UNICODE_STRING SrcPathU;
246
247 SrcPathU.Buffer = alloca(sizeof(WCHAR) * MAX_PATH);
248 SrcPathU.MaximumLength = MAX_PATH * sizeof(WCHAR);
249 SrcPathU.Length = GetFullPathNameW(lpExistingFileName, MAX_PATH, SrcPathU.Buffer, NULL);
250 if (SrcPathU.Length >= MAX_PATH)
251 {
252 SetLastError(ERROR_FILENAME_EXCED_RANGE);
253 return FALSE;
254 }
255 SrcPathU.Length *= sizeof(WCHAR);
256
257 DstPathU.Buffer = alloca(sizeof(WCHAR) * MAX_PATH);
258 DstPathU.MaximumLength = MAX_PATH * sizeof(WCHAR);
259 DstPathU.Length = GetFullPathNameW(lpNewFileName, MAX_PATH, DstPathU.Buffer, NULL);
260 if (DstPathU.Length >= MAX_PATH)
261 {
262 SetLastError(ERROR_FILENAME_EXCED_RANGE);
263 return FALSE;
264 }
265 DstPathU.Length *= sizeof(WCHAR);
266
267 if (0 == RtlCompareUnicodeString(&SrcPathU, &DstPathU, TRUE))
268 {
269 /* Source and destination file are the same, nothing to do */
270 return TRUE;
271 }
272
273 Result = CopyFileExW (lpExistingFileName,
274 lpNewFileName,
275 lpProgressRoutine,
276 lpData,
277 NULL,
278 FileRename->ReplaceIfExists ? 0 : COPY_FILE_FAIL_IF_EXISTS);
279 if (Result)
280 {
281 /* Cleanup the source file */
282 AdjustFileAttributes(lpExistingFileName, lpNewFileName);
283 Result = DeleteFileW (lpExistingFileName);
284 }
285 }
286 #endif
287 else
288 {
289 SetLastErrorByStatus (errCode);
290 Result = FALSE;
291 }
292 return Result;
293 }
294
295
296 /*
297 * @implemented
298 */
299 BOOL
300 STDCALL
301 MoveFileWithProgressA (
302 LPCSTR lpExistingFileName,
303 LPCSTR lpNewFileName,
304 LPPROGRESS_ROUTINE lpProgressRoutine,
305 LPVOID lpData,
306 DWORD dwFlags
307 )
308 {
309 UNICODE_STRING ExistingFileNameU;
310 UNICODE_STRING NewFileNameU;
311 ANSI_STRING ExistingFileName;
312 ANSI_STRING NewFileName;
313 BOOL Result;
314
315 RtlInitAnsiString (&ExistingFileName,
316 (LPSTR)lpExistingFileName);
317
318 RtlInitAnsiString (&NewFileName,
319 (LPSTR)lpNewFileName);
320
321 /* convert ansi (or oem) string to unicode */
322 if (bIsFileApiAnsi)
323 {
324 RtlAnsiStringToUnicodeString (&ExistingFileNameU,
325 &ExistingFileName,
326 TRUE);
327 RtlAnsiStringToUnicodeString (&NewFileNameU,
328 &NewFileName,
329 TRUE);
330 }
331 else
332 {
333 RtlOemStringToUnicodeString (&ExistingFileNameU,
334 &ExistingFileName,
335 TRUE);
336 RtlOemStringToUnicodeString (&NewFileNameU,
337 &NewFileName,
338 TRUE);
339 }
340
341 Result = MoveFileWithProgressW (ExistingFileNameU.Buffer,
342 NewFileNameU.Buffer,
343 lpProgressRoutine,
344 lpData,
345 dwFlags);
346
347 RtlFreeHeap (RtlGetProcessHeap (),
348 0,
349 ExistingFileNameU.Buffer);
350 RtlFreeHeap (RtlGetProcessHeap (),
351 0,
352 NewFileNameU.Buffer);
353
354 return Result;
355 }
356
357
358 /*
359 * @implemented
360 */
361 BOOL
362 STDCALL
363 MoveFileW (
364 LPCWSTR lpExistingFileName,
365 LPCWSTR lpNewFileName
366 )
367 {
368 return MoveFileExW (lpExistingFileName,
369 lpNewFileName,
370 MOVEFILE_COPY_ALLOWED);
371 }
372
373
374 /*
375 * @implemented
376 */
377 BOOL
378 STDCALL
379 MoveFileExW (
380 LPCWSTR lpExistingFileName,
381 LPCWSTR lpNewFileName,
382 DWORD dwFlags
383 )
384 {
385 return MoveFileWithProgressW (lpExistingFileName,
386 lpNewFileName,
387 NULL,
388 NULL,
389 dwFlags);
390 }
391
392
393 /*
394 * @implemented
395 */
396 BOOL
397 STDCALL
398 MoveFileA (
399 LPCSTR lpExistingFileName,
400 LPCSTR lpNewFileName
401 )
402 {
403 return MoveFileExA (lpExistingFileName,
404 lpNewFileName,
405 MOVEFILE_COPY_ALLOWED);
406 }
407
408
409 /*
410 * @implemented
411 */
412 BOOL
413 STDCALL
414 MoveFileExA (
415 LPCSTR lpExistingFileName,
416 LPCSTR lpNewFileName,
417 DWORD dwFlags
418 )
419 {
420 return MoveFileWithProgressA (lpExistingFileName,
421 lpNewFileName,
422 NULL,
423 NULL,
424 dwFlags);
425 }
426
427 /* EOF */