Copy w32api from trunk
[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 PWCHAR ExistingFileNameW;
310 PWCHAR NewFileNameW;
311 BOOL ret;
312
313 if (!(ExistingFileNameW = FilenameA2W(lpExistingFileName, FALSE)))
314 return FALSE;
315
316 if (!(NewFileNameW= FilenameA2W(lpNewFileName, TRUE)))
317 return FALSE;
318
319 ret = MoveFileWithProgressW (ExistingFileNameW ,
320 NewFileNameW,
321 lpProgressRoutine,
322 lpData,
323 dwFlags);
324
325 RtlFreeHeap (RtlGetProcessHeap (), 0, NewFileNameW);
326
327 return ret;
328 }
329
330
331 /*
332 * @implemented
333 */
334 BOOL
335 STDCALL
336 MoveFileW (
337 LPCWSTR lpExistingFileName,
338 LPCWSTR lpNewFileName
339 )
340 {
341 return MoveFileExW (lpExistingFileName,
342 lpNewFileName,
343 MOVEFILE_COPY_ALLOWED);
344 }
345
346
347 /*
348 * @implemented
349 */
350 BOOL
351 STDCALL
352 MoveFileExW (
353 LPCWSTR lpExistingFileName,
354 LPCWSTR lpNewFileName,
355 DWORD dwFlags
356 )
357 {
358 return MoveFileWithProgressW (lpExistingFileName,
359 lpNewFileName,
360 NULL,
361 NULL,
362 dwFlags);
363 }
364
365
366 /*
367 * @implemented
368 */
369 BOOL
370 STDCALL
371 MoveFileA (
372 LPCSTR lpExistingFileName,
373 LPCSTR lpNewFileName
374 )
375 {
376 return MoveFileExA (lpExistingFileName,
377 lpNewFileName,
378 MOVEFILE_COPY_ALLOWED);
379 }
380
381
382 /*
383 * @implemented
384 */
385 BOOL
386 STDCALL
387 MoveFileExA (
388 LPCSTR lpExistingFileName,
389 LPCSTR lpNewFileName,
390 DWORD dwFlags
391 )
392 {
393 return MoveFileWithProgressA (lpExistingFileName,
394 lpNewFileName,
395 NULL,
396 NULL,
397 dwFlags);
398 }
399
400 /* EOF */