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