branch update for HEAD-2003021201
[reactos.git] / 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 <kernel32/kernel32.h>
18 #include <kernel32/error.h>
19
20
21 #define FILE_RENAME_SIZE  MAX_PATH +sizeof(FILE_RENAME_INFORMATION)
22
23
24 /* FUNCTIONS ****************************************************************/
25
26 WINBOOL
27 STDCALL
28 MoveFileA (
29         LPCSTR  lpExistingFileName,
30         LPCSTR  lpNewFileName
31         )
32 {
33         return MoveFileExA (lpExistingFileName,
34                             lpNewFileName,
35                             MOVEFILE_COPY_ALLOWED);
36 }
37
38
39 WINBOOL
40 STDCALL
41 MoveFileExA (
42         LPCSTR  lpExistingFileName,
43         LPCSTR  lpNewFileName,
44         DWORD   dwFlags
45         )
46 {
47         return MoveFileWithProgressA (lpExistingFileName,
48                                       lpNewFileName,
49                                       NULL,
50                                       NULL,
51                                       dwFlags);
52 }
53
54
55 WINBOOL
56 STDCALL
57 MoveFileWithProgressA (
58         LPCSTR                  lpExistingFileName,
59         LPCSTR                  lpNewFileName,
60         LPPROGRESS_ROUTINE      lpProgressRoutine,
61         LPVOID                  lpData,
62         DWORD                   dwFlags
63         )
64 {
65         UNICODE_STRING ExistingFileNameU;
66         UNICODE_STRING NewFileNameU;
67         ANSI_STRING ExistingFileName;
68         ANSI_STRING NewFileName;
69         WINBOOL Result;
70
71         RtlInitAnsiString (&ExistingFileName,
72                            (LPSTR)lpExistingFileName);
73
74         RtlInitAnsiString (&NewFileName,
75                            (LPSTR)lpNewFileName);
76
77         /* convert ansi (or oem) string to unicode */
78         if (bIsFileApiAnsi)
79         {
80                 RtlAnsiStringToUnicodeString (&ExistingFileNameU,
81                                               &ExistingFileName,
82                                               TRUE);
83                 RtlAnsiStringToUnicodeString (&NewFileNameU,
84                                               &NewFileName,
85                                               TRUE);
86         }
87         else
88         {
89                 RtlOemStringToUnicodeString (&ExistingFileNameU,
90                                              &ExistingFileName,
91                                              TRUE);
92                 RtlOemStringToUnicodeString (&NewFileNameU,
93                                              &NewFileName,
94                                              TRUE);
95         }
96
97         Result = MoveFileWithProgressW (ExistingFileNameU.Buffer,
98                                         NewFileNameU.Buffer,
99                                         lpProgressRoutine,
100                                         lpData,
101                                         dwFlags);
102
103         RtlFreeHeap (RtlGetProcessHeap (),
104                      0,
105                      ExistingFileNameU.Buffer);
106         RtlFreeHeap (RtlGetProcessHeap (),
107                      0,
108                      NewFileNameU.Buffer);
109
110         return Result;
111 }
112
113
114 WINBOOL
115 STDCALL
116 MoveFileW (
117         LPCWSTR lpExistingFileName,
118         LPCWSTR lpNewFileName
119         )
120 {
121         return MoveFileExW (lpExistingFileName,
122                             lpNewFileName,
123                             MOVEFILE_COPY_ALLOWED);
124 }
125
126
127 WINBOOL
128 STDCALL
129 MoveFileExW (
130         LPCWSTR lpExistingFileName,
131         LPCWSTR lpNewFileName,
132         DWORD   dwFlags
133         )
134 {
135         return MoveFileWithProgressW (lpExistingFileName,
136                                       lpNewFileName,
137                                       NULL,
138                                       NULL,
139                                       dwFlags);
140 }
141
142
143 static WINBOOL
144 AdjustFileAttributes (
145         LPCWSTR ExistingFileName,
146         LPCWSTR NewFileName
147         )
148 {
149         IO_STATUS_BLOCK IoStatusBlock;
150         FILE_BASIC_INFORMATION ExistingInfo,
151                 NewInfo;
152         HANDLE hFile;
153         DWORD Attributes;
154         NTSTATUS errCode;
155         WINBOOL Result = FALSE;
156
157         hFile = CreateFileW (ExistingFileName,
158                              FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
159                              FILE_SHARE_READ,
160                              NULL,
161                              OPEN_EXISTING,
162                              FILE_ATTRIBUTE_NORMAL,
163                              NULL);
164         if (INVALID_HANDLE_VALUE != hFile)
165         {
166                 errCode = NtQueryInformationFile (hFile,
167                                                   &IoStatusBlock,
168                                                   &ExistingInfo,
169                                                   sizeof(FILE_BASIC_INFORMATION),
170                                                   FileBasicInformation);
171                 if (NT_SUCCESS (errCode))
172                 {
173                         if (0 != (ExistingInfo.FileAttributes & FILE_ATTRIBUTE_READONLY))
174                         {
175                                 Attributes = ExistingInfo.FileAttributes;
176                                 ExistingInfo.FileAttributes &= ~ FILE_ATTRIBUTE_READONLY;
177                                 if (0 == (ExistingInfo.FileAttributes &
178                                           (FILE_ATTRIBUTE_HIDDEN |
179                                            FILE_ATTRIBUTE_SYSTEM |
180                                            FILE_ATTRIBUTE_ARCHIVE)))
181                                 {
182                                         ExistingInfo.FileAttributes |= FILE_ATTRIBUTE_NORMAL;
183                                 }
184                                 errCode = NtSetInformationFile (hFile,
185                                                                 &IoStatusBlock,
186                                                                 &ExistingInfo,
187                                                                 sizeof(FILE_BASIC_INFORMATION),
188                                                                 FileBasicInformation);
189                                 if (!NT_SUCCESS(errCode))
190                                 {
191                                         DPRINT("Removing READONLY attribute from source failed with status 0x%08x\n", errCode);
192                                 }
193                                 ExistingInfo.FileAttributes = Attributes;
194                         }
195                         CloseHandle(hFile);
196
197                         if (NT_SUCCESS(errCode))
198                         {
199                                 hFile = CreateFileW (NewFileName,
200                                                      FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES,
201                                                      FILE_SHARE_READ,
202                                                      NULL,
203                                                      OPEN_EXISTING,
204                                                      FILE_ATTRIBUTE_NORMAL,
205                                                      NULL);
206                                 if (INVALID_HANDLE_VALUE != hFile)
207                                 {
208                                         errCode = NtQueryInformationFile(hFile,
209                                                                          &IoStatusBlock,
210                                                                          &NewInfo,
211                                                                          sizeof(FILE_BASIC_INFORMATION),
212                                                                          FileBasicInformation);
213                                         if (NT_SUCCESS(errCode))
214                                         {
215                                                 NewInfo.FileAttributes = (NewInfo.FileAttributes &
216                                                                           ~ (FILE_ATTRIBUTE_HIDDEN |
217                                                                              FILE_ATTRIBUTE_SYSTEM |
218                                                                              FILE_ATTRIBUTE_READONLY |
219                                                                              FILE_ATTRIBUTE_NORMAL)) |
220                                                                          (ExistingInfo.FileAttributes &
221                                                                           (FILE_ATTRIBUTE_HIDDEN |
222                                                                            FILE_ATTRIBUTE_SYSTEM |
223                                                                            FILE_ATTRIBUTE_READONLY |
224                                                                            FILE_ATTRIBUTE_NORMAL)) |
225                                                                          FILE_ATTRIBUTE_ARCHIVE;
226                                                 NewInfo.CreationTime = ExistingInfo.CreationTime;
227                                                 NewInfo.LastAccessTime = ExistingInfo.LastAccessTime;
228                                                 NewInfo.LastWriteTime = ExistingInfo.LastWriteTime;
229                                                 errCode = NtSetInformationFile (hFile,
230                                                                                 &IoStatusBlock,
231                                                                                 &NewInfo,
232                                                                                 sizeof(FILE_BASIC_INFORMATION),
233                                                                                 FileBasicInformation);
234                                                 if (NT_SUCCESS(errCode))
235                                                 {
236                                                         Result = TRUE;
237                                                 }
238                                                 else
239                                                 {
240                                                         DPRINT("Setting attributes on dest file failed with status 0x%08x\n", errCode);
241                                                 }
242                                         }
243                                         else
244                                         {
245                                                 DPRINT("Obtaining attributes from dest file failed with status 0x%08x\n", errCode);
246                                         }
247                                         CloseHandle(hFile);
248                                 }
249                                 else
250                                 {
251                                         DPRINT("Opening dest file to set attributes failed with code %d\n", GetLastError());
252                                 }
253                         }
254                 }
255                 else
256                 {
257                         DPRINT("Obtaining attributes from source file failed with status 0x%08x\n", errCode);
258                         CloseHandle(hFile);
259                 }
260         }
261         else
262         {
263                 DPRINT("Opening source file to obtain attributes failed with code %d\n", GetLastError());
264         }
265
266         return Result;
267 }
268
269
270 WINBOOL
271 STDCALL
272 MoveFileWithProgressW (
273         LPCWSTR                 lpExistingFileName,
274         LPCWSTR                 lpNewFileName,
275         LPPROGRESS_ROUTINE      lpProgressRoutine,
276         LPVOID                  lpData,
277         DWORD                   dwFlags
278         )
279 {
280         HANDLE hFile = NULL;
281         IO_STATUS_BLOCK IoStatusBlock;
282         FILE_RENAME_INFORMATION *FileRename;
283         USHORT Buffer[FILE_RENAME_SIZE];
284         NTSTATUS errCode;
285         DWORD err;
286         WINBOOL Result;
287
288         hFile = CreateFileW (lpExistingFileName,
289                              GENERIC_ALL,
290                              FILE_SHARE_WRITE|FILE_SHARE_READ,
291                              NULL,
292                              OPEN_EXISTING,
293                              FILE_ATTRIBUTE_NORMAL,
294                              NULL);
295
296         FileRename = (FILE_RENAME_INFORMATION *)Buffer;
297         if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == MOVEFILE_REPLACE_EXISTING)
298                 FileRename->Replace = TRUE;
299         else
300                 FileRename->Replace = FALSE;
301
302         FileRename->FileNameLength = wcslen (lpNewFileName);
303         memcpy (FileRename->FileName,
304                 lpNewFileName,
305                 min(FileRename->FileNameLength, MAX_PATH));
306
307         errCode = NtSetInformationFile (hFile,
308                                         &IoStatusBlock,
309                                         FileRename,
310                                         FILE_RENAME_SIZE,
311                                         FileRenameInformation);
312         CloseHandle(hFile);
313         if (NT_SUCCESS(errCode))
314         {
315                 Result = TRUE;
316         }
317         /* FIXME file rename not yet implemented in all FSDs so it will always
318          * fail, even when the move is to the same device
319          */
320 #if 0
321         else if (STATUS_NOT_SAME_DEVICE == errCode &&
322                  MOVEFILE_COPY_ALLOWED == (dwFlags & MOVEFILE_COPY_ALLOWED))
323 #else
324         else
325 #endif
326         {
327                 Result = CopyFileExW (lpExistingFileName,
328                                       lpNewFileName,
329                                       lpProgressRoutine,
330                                       lpData,
331                                       NULL,
332                                       FileRename->Replace ? 0 : COPY_FILE_FAIL_IF_EXISTS) &&
333                          AdjustFileAttributes(lpExistingFileName, lpNewFileName) &&
334                          DeleteFileW (lpExistingFileName);
335                 if (! Result)
336                 {
337                         /* Delete of the existing file failed so the
338                          * existing file is still there. Clean up the
339                          * new file (if possible)
340                          */
341                         err = GetLastError();
342                         if (! SetFileAttributesW (lpNewFileName, FILE_ATTRIBUTE_NORMAL))
343                         {
344                                 DPRINT("Removing possible READONLY attrib from new file failed with code %d\n", GetLastError());
345                         }
346                         if (! DeleteFileW (lpNewFileName))
347                         {
348                                 DPRINT("Deleting new file during cleanup failed with code %d\n", GetLastError());
349                         }
350                         SetLastError (err);
351                 }
352         }
353         /* See FIXME above */
354 #if 0
355         else
356         {
357                 SetLastErrorByStatus (errCode);
358                 Result = FALSE;
359         }
360 #endif
361
362         return Result;
363 }
364
365 /* EOF */