2 * File handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996 Alexandre Julliard
6 * Copyright 2003 Eric Pouech
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include "wine/port.h"
31 #define NONAMELESSUNION
32 #define NONAMELESSSTRUCT
40 #include "kernel_private.h"
42 #include "wine/unicode.h"
43 #include "wine/debug.h"
45 WINE_DEFAULT_DEBUG_CHANNEL(file);
47 #define MAX_PATHNAME_LEN 1024
50 /* check if a file name is for an executable file (.exe or .com) */
51 inline static BOOL is_executable( const WCHAR *name )
53 static const WCHAR exeW[] = {'.','e','x','e',0};
54 static const WCHAR comW[] = {'.','c','o','m',0};
55 int len = strlenW(name);
57 if (len < 4) return FALSE;
58 return (!strcmpiW( name + len - 4, exeW ) || !strcmpiW( name + len - 4, comW ));
62 /***********************************************************************
63 * add_boot_rename_entry
65 * Adds an entry to the registry that is loaded when windows boots and
66 * checks if there are some files to be removed or renamed/moved.
67 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
68 * non-NULL then the file is moved, otherwise it is deleted. The
69 * entry of the registrykey is always appended with two zero
70 * terminated strings. If <fn2> is NULL then the second entry is
71 * simply a single 0-byte. Otherwise the second filename goes
72 * there. The entries are prepended with \??\ before the path and the
73 * second filename gets also a '!' as the first character if
74 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
75 * 0-byte follows to indicate the end of the strings.
77 * \??\D:\test\file1[0]
78 * !\??\D:\test\file1_renamed[0]
79 * \??\D:\Test|delete[0]
80 * [0] <- file is to be deleted, second string empty
81 * \??\D:\test\file2[0]
82 * !\??\D:\test\file2_renamed[0]
83 * [0] <- indicates end of strings
86 * \??\D:\test\file1[0]
87 * !\??\D:\test\file1_renamed[0]
88 * \??\D:\Test|delete[0]
89 * [0] <- file is to be deleted, second string empty
90 * [0] <- indicates end of strings
93 static BOOL add_boot_rename_entry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
95 static const WCHAR PreString[] = {'\\','?','?','\\',0};
96 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
97 'F','i','l','e','R','e','n','a','m','e',
98 'O','p','e','r','a','t','i','o','n','s',0};
99 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
100 'S','y','s','t','e','m','\\',
101 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
102 'C','o','n','t','r','o','l','\\',
103 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
104 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
106 OBJECT_ATTRIBUTES attr;
107 UNICODE_STRING nameW;
108 KEY_VALUE_PARTIAL_INFORMATION *info;
111 DWORD len0, len1, len2;
116 attr.Length = sizeof(attr);
117 attr.RootDirectory = 0;
118 attr.ObjectName = &nameW;
120 attr.SecurityDescriptor = NULL;
121 attr.SecurityQualityOfService = NULL;
122 RtlInitUnicodeString( &nameW, SessionW );
124 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
126 WARN("Error creating key for reboot managment [%s]\n",
127 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
131 len0 = strlenW(PreString);
132 len1 = strlenW(fn1) + len0 + 1;
135 len2 = strlenW(fn2) + len0 + 1;
136 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
138 else len2 = 1; /* minimum is the 0 characters for the empty second string */
140 /* convert characters to bytes */
141 len0 *= sizeof(WCHAR);
142 len1 *= sizeof(WCHAR);
143 len2 *= sizeof(WCHAR);
145 RtlInitUnicodeString( &nameW, ValueName );
147 /* First we check if the key exists and if so how many bytes it already contains. */
148 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
149 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
151 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
153 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
154 Buffer, DataSize, &DataSize )) goto Quit;
155 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
156 if (info->Type != REG_MULTI_SZ) goto Quit;
157 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
161 DataSize = info_size;
162 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
166 p = (WCHAR *)(Buffer + DataSize);
167 strcpyW( p, PreString );
172 p = (WCHAR *)(Buffer + DataSize);
173 if (flags & MOVEFILE_REPLACE_EXISTING)
175 strcpyW( p, PreString );
181 p = (WCHAR *)(Buffer + DataSize);
183 DataSize += sizeof(WCHAR);
187 p = (WCHAR *)(Buffer + DataSize);
189 DataSize += sizeof(WCHAR);
191 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
194 if (Reboot) NtClose(Reboot);
195 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
200 /***********************************************************************
201 * GetFullPathNameW (KERNEL32.@)
203 * if the path closed with '\', *lastpart is 0
205 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
208 return RtlGetFullPathName_U(name, len * sizeof(WCHAR), buffer, lastpart) / sizeof(WCHAR);
211 /***********************************************************************
212 * GetFullPathNameA (KERNEL32.@)
214 * if the path closed with '\', *lastpart is 0
216 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
219 UNICODE_STRING nameW;
220 WCHAR bufferW[MAX_PATH];
225 SetLastError(ERROR_INVALID_PARAMETER);
229 if (!RtlCreateUnicodeStringFromAsciiz(&nameW, name))
231 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
235 retW = GetFullPathNameW( nameW.Buffer, MAX_PATH, bufferW, NULL);
239 else if (retW > MAX_PATH)
241 SetLastError(ERROR_FILENAME_EXCED_RANGE);
246 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
247 if (ret && ret <= len)
249 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, len, NULL, NULL);
250 ret--; /* length without 0 */
254 LPSTR p = buffer + strlen(buffer) - 1;
258 while ((p > buffer + 2) && (*p != '\\')) p--;
261 else *lastpart = NULL;
266 RtlFreeUnicodeString(&nameW);
271 /***********************************************************************
272 * GetLongPathNameW (KERNEL32.@)
275 * observed (Win2000):
276 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
277 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
279 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
281 WCHAR tmplongpath[MAX_PATHNAME_LEN];
283 DWORD sp = 0, lp = 0;
285 BOOL unixabsolute = (shortpath[0] == '/');
286 WIN32_FIND_DATAW wfd;
291 SetLastError(ERROR_INVALID_PARAMETER);
296 SetLastError(ERROR_PATH_NOT_FOUND);
300 TRACE("%s,%p,%ld\n", debugstr_w(shortpath), longpath, longlen);
302 if (shortpath[0] == '\\' && shortpath[1] == '\\')
304 ERR("UNC pathname %s\n", debugstr_w(shortpath));
305 lstrcpynW( longpath, shortpath, longlen );
306 return strlenW(longpath);
309 /* check for drive letter */
310 if (!unixabsolute && shortpath[1] == ':' )
312 tmplongpath[0] = shortpath[0];
313 tmplongpath[1] = ':';
317 while (shortpath[sp])
319 /* check for path delimiters and reproduce them */
320 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
322 if (!lp || tmplongpath[lp-1] != '\\')
324 /* strip double "\\" */
325 tmplongpath[lp++] = '\\';
327 tmplongpath[lp] = 0; /* terminate string */
333 if (sp == 0 && p[0] == '.' && (p[1] == '/' || p[1] == '\\'))
335 tmplongpath[lp++] = *p++;
336 tmplongpath[lp++] = *p++;
338 for (; *p && *p != '/' && *p != '\\'; p++);
339 tmplen = p - (shortpath + sp);
340 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
341 /* Check if the file exists and use the existing file name */
342 goit = FindFirstFileW(tmplongpath, &wfd);
343 if (goit == INVALID_HANDLE_VALUE)
345 TRACE("not found %s!\n", debugstr_w(tmplongpath));
346 SetLastError ( ERROR_FILE_NOT_FOUND );
350 strcpyW(tmplongpath + lp, wfd.cFileName);
351 lp += strlenW(tmplongpath + lp);
354 tmplen = strlenW(shortpath) - 1;
355 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
356 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
357 tmplongpath[lp++] = shortpath[tmplen];
360 tmplen = strlenW(tmplongpath) + 1;
361 if (tmplen <= longlen)
363 strcpyW(longpath, tmplongpath);
364 TRACE("returning %s\n", debugstr_w(longpath));
365 tmplen--; /* length without 0 */
371 /***********************************************************************
372 * GetLongPathNameA (KERNEL32.@)
374 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
376 UNICODE_STRING shortpathW;
377 WCHAR longpathW[MAX_PATH];
382 SetLastError(ERROR_INVALID_PARAMETER);
386 TRACE("%s\n", debugstr_a(shortpath));
388 if (!RtlCreateUnicodeStringFromAsciiz(&shortpathW, shortpath))
390 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
394 retW = GetLongPathNameW(shortpathW.Buffer, longpathW, MAX_PATH);
398 else if (retW > MAX_PATH)
400 SetLastError(ERROR_FILENAME_EXCED_RANGE);
405 ret = WideCharToMultiByte(CP_ACP, 0, longpathW, -1, NULL, 0, NULL, NULL);
408 WideCharToMultiByte(CP_ACP, 0, longpathW, -1, longpath, longlen, NULL, NULL);
409 ret--; /* length without 0 */
413 RtlFreeUnicodeString(&shortpathW);
418 /***********************************************************************
419 * GetShortPathNameW (KERNEL32.@)
423 * longpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
424 * longpath="" or invalid: LastError=ERROR_BAD_PATHNAME, ret=0
426 * more observations ( with NT 3.51 (WinDD) ):
427 * longpath <= 8.3 -> just copy longpath to shortpath
429 * a) file does not exist -> return 0, LastError = ERROR_FILE_NOT_FOUND
430 * b) file does exist -> set the short filename.
431 * - trailing slashes are reproduced in the short name, even if the
432 * file is not a directory
433 * - the absolute/relative path of the short name is reproduced like found
435 * - longpath and shortpath may have the same address
438 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
440 WCHAR tmpshortpath[MAX_PATHNAME_LEN];
442 DWORD sp = 0, lp = 0;
444 BOOL unixabsolute = (longpath[0] == '/');
445 WIN32_FIND_DATAW wfd;
448 WCHAR ustr_buf[8+1+3+1];
450 TRACE("%s\n", debugstr_w(longpath));
454 SetLastError(ERROR_INVALID_PARAMETER);
459 SetLastError(ERROR_BAD_PATHNAME);
463 /* check for drive letter */
464 if (!unixabsolute && longpath[1] == ':' )
466 tmpshortpath[0] = longpath[0];
467 tmpshortpath[1] = ':';
471 ustr.Buffer = ustr_buf;
473 ustr.MaximumLength = sizeof(ustr_buf);
477 /* check for path delimiters and reproduce them */
478 if (longpath[lp] == '\\' || longpath[lp] == '/')
480 if (!sp || tmpshortpath[sp-1] != '\\')
482 /* strip double "\\" */
483 tmpshortpath[sp] = '\\';
486 tmpshortpath[sp] = 0; /* terminate string */
491 for (p = longpath + lp; *p && *p != '/' && *p != '\\'; p++);
492 tmplen = p - (longpath + lp);
493 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
494 /* Check, if the current element is a valid dos name */
495 if (tmplen <= 8+1+3+1)
498 memcpy(ustr_buf, longpath + lp, tmplen * sizeof(WCHAR));
499 ustr_buf[tmplen] = '\0';
500 ustr.Length = tmplen * sizeof(WCHAR);
501 if (RtlIsNameLegalDOS8Dot3(&ustr, NULL, &spaces) && !spaces)
509 /* Check if the file exists and use the existing short file name */
510 goit = FindFirstFileW(tmpshortpath, &wfd);
511 if (goit == INVALID_HANDLE_VALUE) goto notfound;
513 strcpyW(tmpshortpath + sp, wfd.cAlternateFileName);
514 sp += strlenW(tmpshortpath + sp);
517 tmpshortpath[sp] = 0;
519 tmplen = strlenW(tmpshortpath) + 1;
520 if (tmplen <= shortlen)
522 strcpyW(shortpath, tmpshortpath);
523 TRACE("returning %s\n", debugstr_w(shortpath));
524 tmplen--; /* length without 0 */
530 TRACE("not found!\n" );
531 SetLastError ( ERROR_FILE_NOT_FOUND );
535 /***********************************************************************
536 * GetShortPathNameA (KERNEL32.@)
538 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
540 UNICODE_STRING longpathW;
541 WCHAR shortpathW[MAX_PATH];
546 SetLastError(ERROR_INVALID_PARAMETER);
550 TRACE("%s\n", debugstr_a(longpath));
552 if (!RtlCreateUnicodeStringFromAsciiz(&longpathW, longpath))
554 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
558 retW = GetShortPathNameW(longpathW.Buffer, shortpathW, MAX_PATH);
562 else if (retW > MAX_PATH)
564 SetLastError(ERROR_FILENAME_EXCED_RANGE);
569 ret = WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, NULL, 0, NULL, NULL);
572 WideCharToMultiByte(CP_ACP, 0, shortpathW, -1, shortpath, shortlen, NULL, NULL);
573 ret--; /* length without 0 */
577 RtlFreeUnicodeString(&longpathW);
582 /***********************************************************************
583 * GetTempPathA (KERNEL32.@)
585 UINT WINAPI GetTempPathA( UINT count, LPSTR path )
587 WCHAR pathW[MAX_PATH];
590 ret = GetTempPathW(MAX_PATH, pathW);
597 SetLastError(ERROR_FILENAME_EXCED_RANGE);
601 ret = WideCharToMultiByte(CP_ACP, 0, pathW, -1, NULL, 0, NULL, NULL);
604 WideCharToMultiByte(CP_ACP, 0, pathW, -1, path, count, NULL, NULL);
605 ret--; /* length without 0 */
611 /***********************************************************************
612 * GetTempPathW (KERNEL32.@)
614 UINT WINAPI GetTempPathW( UINT count, LPWSTR path )
616 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
617 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
618 WCHAR tmp_path[MAX_PATH];
621 TRACE("%u,%p\n", count, path);
623 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )))
624 if (!(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )))
625 if (!(ret = GetCurrentDirectoryW( MAX_PATH, tmp_path )))
630 SetLastError(ERROR_FILENAME_EXCED_RANGE);
634 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
637 if (ret > MAX_PATH - 2)
639 SetLastError(ERROR_FILENAME_EXCED_RANGE);
643 if (tmp_path[ret-1] != '\\')
645 tmp_path[ret++] = '\\';
646 tmp_path[ret] = '\0';
649 ret++; /* add space for terminating 0 */
653 lstrcpynW(path, tmp_path, count);
655 ret--; /* return length without 0 */
657 path[0] = 0; /* avoid returning ambiguous "X:" */
660 TRACE("returning %u, %s\n", ret, debugstr_w(path));
665 /***********************************************************************
668 * Check if the file name contains a path; helper for SearchPathW.
669 * A relative path is not considered a path unless it starts with ./ or ../
671 inline static BOOL contains_pathW (LPCWSTR name)
673 if (RtlDetermineDosPathNameType_U( name ) != RELATIVE_PATH) return TRUE;
674 if (name[0] != '.') return FALSE;
675 if (name[1] == '/' || name[1] == '\\') return TRUE;
676 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
680 /***********************************************************************
681 * SearchPathW [KERNEL32.@]
683 * Searches for a specified file in the search path.
686 * path [I] Path to search
687 * name [I] Filename to search for.
688 * ext [I] File extension to append to file name. The first
689 * character must be a period. This parameter is
690 * specified only if the filename given does not
691 * contain an extension.
692 * buflen [I] size of buffer, in characters
693 * buffer [O] buffer for found filename
694 * lastpart [O] address of pointer to last used character in
695 * buffer (the final '\')
698 * Success: length of string copied into buffer, not including
699 * terminating null character. If the filename found is
700 * longer than the length of the buffer, the length of the
701 * filename is returned.
705 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
708 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
709 LPWSTR buffer, LPWSTR *lastpart )
713 /* If the name contains an explicit path, ignore the path */
715 if (contains_pathW(name))
717 /* try first without extension */
718 if (RtlDoesFileExists_U( name ))
719 return GetFullPathNameW( name, buflen, buffer, lastpart );
723 LPCWSTR p = strrchrW( name, '.' );
724 if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
725 ext = NULL; /* Ignore the specified extension */
728 /* Allocate a buffer for the file name and extension */
732 DWORD len = strlenW(name) + strlenW(ext);
734 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
736 SetLastError( ERROR_OUTOFMEMORY );
739 strcpyW( tmp, name );
741 if (RtlDoesFileExists_U( tmp ))
742 ret = GetFullPathNameW( tmp, buflen, buffer, lastpart );
743 HeapFree( GetProcessHeap(), 0, tmp );
746 else if (path && path[0]) /* search in the specified path */
748 ret = RtlDosSearchPath_U( path, name, ext, buflen * sizeof(WCHAR),
749 buffer, lastpart ) / sizeof(WCHAR);
751 else /* search in the default path */
753 WCHAR *dll_path = MODULE_get_dll_load_path( NULL );
757 ret = RtlDosSearchPath_U( dll_path, name, ext, buflen * sizeof(WCHAR),
758 buffer, lastpart ) / sizeof(WCHAR);
759 HeapFree( GetProcessHeap(), 0, dll_path );
763 SetLastError( ERROR_OUTOFMEMORY );
768 if (!ret) SetLastError( ERROR_FILE_NOT_FOUND );
769 else TRACE( "found %s\n", debugstr_w(buffer) );
774 /***********************************************************************
775 * SearchPathA (KERNEL32.@)
777 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext,
778 DWORD buflen, LPSTR buffer, LPSTR *lastpart )
780 UNICODE_STRING pathW, nameW, extW;
781 WCHAR bufferW[MAX_PATH];
784 if (path) RtlCreateUnicodeStringFromAsciiz(&pathW, path);
785 else pathW.Buffer = NULL;
786 if (name) RtlCreateUnicodeStringFromAsciiz(&nameW, name);
787 else nameW.Buffer = NULL;
788 if (ext) RtlCreateUnicodeStringFromAsciiz(&extW, ext);
789 else extW.Buffer = NULL;
791 retW = SearchPathW(pathW.Buffer, nameW.Buffer, extW.Buffer, MAX_PATH, bufferW, NULL);
795 else if (retW > MAX_PATH)
797 SetLastError(ERROR_FILENAME_EXCED_RANGE);
802 ret = WideCharToMultiByte(CP_ACP, 0, bufferW, -1, NULL, 0, NULL, NULL);
805 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, buflen, NULL, NULL);
806 ret--; /* length without 0 */
807 if (lastpart) *lastpart = strrchr(buffer, '\\') + 1;
811 RtlFreeUnicodeString(&pathW);
812 RtlFreeUnicodeString(&nameW);
813 RtlFreeUnicodeString(&extW);
818 /**************************************************************************
819 * MoveFileExW (KERNEL32.@)
821 BOOL WINAPI MoveFileExW( LPCWSTR source, LPCWSTR dest, DWORD flag )
823 FILE_BASIC_INFORMATION info;
824 UNICODE_STRING nt_name;
825 OBJECT_ATTRIBUTES attr;
828 HANDLE source_handle = 0, dest_handle;
829 ANSI_STRING source_unix, dest_unix;
831 TRACE("(%s,%s,%04lx)\n", debugstr_w(source), debugstr_w(dest), flag);
833 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
834 return add_boot_rename_entry( source, dest, flag );
837 return DeleteFileW( source );
839 /* check if we are allowed to rename the source */
841 if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL ))
843 SetLastError( ERROR_PATH_NOT_FOUND );
846 source_unix.Buffer = NULL;
847 dest_unix.Buffer = NULL;
848 attr.Length = sizeof(attr);
849 attr.RootDirectory = 0;
850 attr.Attributes = OBJ_CASE_INSENSITIVE;
851 attr.ObjectName = &nt_name;
852 attr.SecurityDescriptor = NULL;
853 attr.SecurityQualityOfService = NULL;
855 status = NtOpenFile( &source_handle, 0, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT );
856 if (status == STATUS_SUCCESS)
857 status = wine_nt_to_unix_file_name( &nt_name, &source_unix, FILE_OPEN, FALSE );
858 RtlFreeUnicodeString( &nt_name );
859 if (status != STATUS_SUCCESS)
861 SetLastError( RtlNtStatusToDosError(status) );
864 status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation );
865 if (status != STATUS_SUCCESS)
867 SetLastError( RtlNtStatusToDosError(status) );
871 if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
873 if (flag & MOVEFILE_REPLACE_EXISTING) /* cannot replace directory */
875 SetLastError( ERROR_INVALID_PARAMETER );
880 /* we must have write access to the destination, and it must */
881 /* not exist except if MOVEFILE_REPLACE_EXISTING is set */
883 if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL ))
885 SetLastError( ERROR_PATH_NOT_FOUND );
888 status = NtOpenFile( &dest_handle, GENERIC_READ | GENERIC_WRITE, &attr, &io, 0,
889 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
890 if (status == STATUS_SUCCESS)
892 NtClose( dest_handle );
893 if (!(flag & MOVEFILE_REPLACE_EXISTING))
895 SetLastError( ERROR_ALREADY_EXISTS );
896 RtlFreeUnicodeString( &nt_name );
900 else if (status != STATUS_OBJECT_NAME_NOT_FOUND)
902 SetLastError( RtlNtStatusToDosError(status) );
903 RtlFreeUnicodeString( &nt_name );
907 status = wine_nt_to_unix_file_name( &nt_name, &dest_unix, FILE_OPEN_IF, FALSE );
908 RtlFreeUnicodeString( &nt_name );
909 if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
911 SetLastError( RtlNtStatusToDosError(status) );
915 /* now perform the rename */
917 if (rename( source_unix.Buffer, dest_unix.Buffer ) == -1)
919 if (errno == EXDEV && (flag & MOVEFILE_COPY_ALLOWED))
921 NtClose( source_handle );
922 RtlFreeAnsiString( &source_unix );
923 RtlFreeAnsiString( &dest_unix );
924 return (CopyFileW( source, dest, TRUE ) && DeleteFileW( source ));
927 /* if we created the destination, remove it */
928 if (io.Information == FILE_CREATED) unlink( dest_unix.Buffer );
932 /* fixup executable permissions */
934 if (is_executable( source ) != is_executable( dest ))
937 if (stat( dest_unix.Buffer, &fstat ) != -1)
939 if (is_executable( dest ))
940 /* set executable bit where read bit is set */
941 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
943 fstat.st_mode &= ~0111;
944 chmod( dest_unix.Buffer, fstat.st_mode );
948 NtClose( source_handle );
949 RtlFreeAnsiString( &source_unix );
950 RtlFreeAnsiString( &dest_unix );
954 if (source_handle) NtClose( source_handle );
955 RtlFreeAnsiString( &source_unix );
956 RtlFreeAnsiString( &dest_unix );
960 /**************************************************************************
961 * MoveFileExA (KERNEL32.@)
963 BOOL WINAPI MoveFileExA( LPCSTR source, LPCSTR dest, DWORD flag )
965 UNICODE_STRING sourceW, destW;
970 SetLastError(ERROR_INVALID_PARAMETER);
974 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
975 if (dest) RtlCreateUnicodeStringFromAsciiz(&destW, dest);
976 else destW.Buffer = NULL;
978 ret = MoveFileExW( sourceW.Buffer, destW.Buffer, flag );
980 RtlFreeUnicodeString(&sourceW);
981 RtlFreeUnicodeString(&destW);
986 /**************************************************************************
987 * MoveFileW (KERNEL32.@)
989 * Move file or directory
991 BOOL WINAPI MoveFileW( LPCWSTR source, LPCWSTR dest )
993 return MoveFileExW( source, dest, MOVEFILE_COPY_ALLOWED );
997 /**************************************************************************
998 * MoveFileA (KERNEL32.@)
1000 BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest )
1002 return MoveFileExA( source, dest, MOVEFILE_COPY_ALLOWED );
1006 /***********************************************************************
1007 * CreateDirectoryW (KERNEL32.@)
1011 * ERROR_DISK_FULL: on full disk
1012 * ERROR_ALREADY_EXISTS: if directory name exists (even as file)
1013 * ERROR_ACCESS_DENIED: on permission problems
1014 * ERROR_FILENAME_EXCED_RANGE: too long filename(s)
1016 BOOL WINAPI CreateDirectoryW( LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1018 OBJECT_ATTRIBUTES attr;
1019 UNICODE_STRING nt_name;
1025 TRACE( "%s\n", debugstr_w(path) );
1027 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1029 SetLastError( ERROR_PATH_NOT_FOUND );
1032 attr.Length = sizeof(attr);
1033 attr.RootDirectory = 0;
1034 attr.Attributes = OBJ_CASE_INSENSITIVE;
1035 attr.ObjectName = &nt_name;
1036 attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL;
1037 attr.SecurityQualityOfService = NULL;
1039 status = NtCreateFile( &handle, GENERIC_READ, &attr, &io, NULL,
1040 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_CREATE,
1041 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
1043 if (status == STATUS_SUCCESS)
1048 else SetLastError( RtlNtStatusToDosError(status) );
1050 RtlFreeUnicodeString( &nt_name );
1055 /***********************************************************************
1056 * CreateDirectoryA (KERNEL32.@)
1058 BOOL WINAPI CreateDirectoryA( LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1060 UNICODE_STRING pathW;
1065 if (!RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1067 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1071 else pathW.Buffer = NULL;
1072 ret = CreateDirectoryW( pathW.Buffer, sa );
1073 RtlFreeUnicodeString( &pathW );
1078 /***********************************************************************
1079 * CreateDirectoryExA (KERNEL32.@)
1081 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1083 UNICODE_STRING pathW, templateW;
1088 if (!RtlCreateUnicodeStringFromAsciiz( &pathW, path ))
1090 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1094 else pathW.Buffer = NULL;
1098 if (!RtlCreateUnicodeStringFromAsciiz( &templateW, template ))
1100 RtlFreeUnicodeString( &pathW );
1101 SetLastError( ERROR_NOT_ENOUGH_MEMORY );
1105 else templateW.Buffer = NULL;
1107 ret = CreateDirectoryExW( templateW.Buffer, pathW.Buffer, sa );
1108 RtlFreeUnicodeString( &pathW );
1109 RtlFreeUnicodeString( &templateW );
1114 /***********************************************************************
1115 * CreateDirectoryExW (KERNEL32.@)
1117 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1119 return CreateDirectoryW( path, sa );
1123 /***********************************************************************
1124 * RemoveDirectoryW (KERNEL32.@)
1126 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
1128 OBJECT_ATTRIBUTES attr;
1129 UNICODE_STRING nt_name;
1130 ANSI_STRING unix_name;
1136 TRACE( "%s\n", debugstr_w(path) );
1138 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1140 SetLastError( ERROR_PATH_NOT_FOUND );
1143 attr.Length = sizeof(attr);
1144 attr.RootDirectory = 0;
1145 attr.Attributes = OBJ_CASE_INSENSITIVE;
1146 attr.ObjectName = &nt_name;
1147 attr.SecurityDescriptor = NULL;
1148 attr.SecurityQualityOfService = NULL;
1150 status = NtOpenFile( &handle, GENERIC_READ, &attr, &io,
1151 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1152 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1153 if (status == STATUS_SUCCESS)
1154 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
1155 RtlFreeUnicodeString( &nt_name );
1157 if (status != STATUS_SUCCESS)
1159 SetLastError( RtlNtStatusToDosError(status) );
1163 if (!(ret = (rmdir( unix_name.Buffer ) != -1))) FILE_SetDosError();
1164 RtlFreeAnsiString( &unix_name );
1170 /***********************************************************************
1171 * RemoveDirectoryA (KERNEL32.@)
1173 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
1175 UNICODE_STRING pathW;
1180 SetLastError(ERROR_INVALID_PARAMETER);
1184 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1186 ret = RemoveDirectoryW(pathW.Buffer);
1187 RtlFreeUnicodeString(&pathW);
1190 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1195 /***********************************************************************
1196 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1198 * Return the full Unix file name for a given path.
1199 * Returned buffer must be freed by caller.
1201 char *wine_get_unix_file_name( LPCWSTR dosW )
1203 UNICODE_STRING nt_name;
1204 ANSI_STRING unix_name;
1207 if (!RtlDosPathNameToNtPathName_U( dosW, &nt_name, NULL, NULL )) return NULL;
1208 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
1209 RtlFreeUnicodeString( &nt_name );
1210 if (status && status != STATUS_NO_SUCH_FILE) return NULL;
1211 return unix_name.Buffer;