kernel32: Implement SetDefaultDllDirectories.
[wine.git] / dlls / kernel32 / path.c
blobec7c53b34cc2e8015a3ee1865826e2eb63077c42
1 /*
2 * File handling functions
4 * Copyright 1993 Erik Bos
5 * Copyright 1996, 2004 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
25 #include "wine/port.h"
27 #include <errno.h>
28 #include <stdio.h>
29 #include <stdarg.h>
31 #include "winerror.h"
32 #include "ntstatus.h"
33 #define WIN32_NO_STATUS
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winternl.h"
38 #include "kernel_private.h"
39 #include "wine/unicode.h"
40 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(file);
44 #define MAX_PATHNAME_LEN 1024
47 /* check if a file name is for an executable file (.exe or .com) */
48 static inline BOOL is_executable( const WCHAR *name )
50 static const WCHAR exeW[] = {'.','e','x','e',0};
51 static const WCHAR comW[] = {'.','c','o','m',0};
52 int len = strlenW(name);
54 if (len < 4) return FALSE;
55 return (!strcmpiW( name + len - 4, exeW ) || !strcmpiW( name + len - 4, comW ));
58 /***********************************************************************
59 * copy_filename_WtoA
61 * copy a file name back to OEM/Ansi, but only if the buffer is large enough
63 static DWORD copy_filename_WtoA( LPCWSTR nameW, LPSTR buffer, DWORD len )
65 UNICODE_STRING strW;
66 DWORD ret;
67 BOOL is_ansi = AreFileApisANSI();
69 RtlInitUnicodeString( &strW, nameW );
71 ret = is_ansi ? RtlUnicodeStringToAnsiSize(&strW) : RtlUnicodeStringToOemSize(&strW);
72 if (buffer && ret <= len)
74 ANSI_STRING str;
76 str.Buffer = buffer;
77 str.MaximumLength = min( len, UNICODE_STRING_MAX_CHARS );
78 if (is_ansi)
79 RtlUnicodeStringToAnsiString( &str, &strW, FALSE );
80 else
81 RtlUnicodeStringToOemString( &str, &strW, FALSE );
82 ret = str.Length; /* length without terminating 0 */
84 return ret;
87 /***********************************************************************
88 * add_boot_rename_entry
90 * Adds an entry to the registry that is loaded when windows boots and
91 * checks if there are some files to be removed or renamed/moved.
92 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
93 * non-NULL then the file is moved, otherwise it is deleted. The
94 * entry of the registry key is always appended with two zero
95 * terminated strings. If <fn2> is NULL then the second entry is
96 * simply a single 0-byte. Otherwise the second filename goes
97 * there. The entries are prepended with \??\ before the path and the
98 * second filename gets also a '!' as the first character if
99 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
100 * 0-byte follows to indicate the end of the strings.
101 * i.e.:
102 * \??\D:\test\file1[0]
103 * !\??\D:\test\file1_renamed[0]
104 * \??\D:\Test|delete[0]
105 * [0] <- file is to be deleted, second string empty
106 * \??\D:\test\file2[0]
107 * !\??\D:\test\file2_renamed[0]
108 * [0] <- indicates end of strings
110 * or:
111 * \??\D:\test\file1[0]
112 * !\??\D:\test\file1_renamed[0]
113 * \??\D:\Test|delete[0]
114 * [0] <- file is to be deleted, second string empty
115 * [0] <- indicates end of strings
118 static BOOL add_boot_rename_entry( LPCWSTR source, LPCWSTR dest, DWORD flags )
120 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
121 'F','i','l','e','R','e','n','a','m','e',
122 'O','p','e','r','a','t','i','o','n','s',0};
123 static const WCHAR SessionW[] = {'\\','R','e','g','i','s','t','r','y','\\',
124 'M','a','c','h','i','n','e','\\',
125 'S','y','s','t','e','m','\\',
126 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
127 'C','o','n','t','r','o','l','\\',
128 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
129 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
131 OBJECT_ATTRIBUTES attr;
132 UNICODE_STRING nameW, source_name, dest_name;
133 KEY_VALUE_PARTIAL_INFORMATION *info;
134 BOOL rc = FALSE;
135 HANDLE Reboot = 0;
136 DWORD len1, len2;
137 DWORD DataSize = 0;
138 BYTE *Buffer = NULL;
139 WCHAR *p;
141 if (!RtlDosPathNameToNtPathName_U( source, &source_name, NULL, NULL ))
143 SetLastError( ERROR_PATH_NOT_FOUND );
144 return FALSE;
146 dest_name.Buffer = NULL;
147 if (dest && !RtlDosPathNameToNtPathName_U( dest, &dest_name, NULL, NULL ))
149 RtlFreeUnicodeString( &source_name );
150 SetLastError( ERROR_PATH_NOT_FOUND );
151 return FALSE;
154 attr.Length = sizeof(attr);
155 attr.RootDirectory = 0;
156 attr.ObjectName = &nameW;
157 attr.Attributes = 0;
158 attr.SecurityDescriptor = NULL;
159 attr.SecurityQualityOfService = NULL;
160 RtlInitUnicodeString( &nameW, SessionW );
162 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
164 WARN("Error creating key for reboot management [%s]\n",
165 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
166 RtlFreeUnicodeString( &source_name );
167 RtlFreeUnicodeString( &dest_name );
168 return FALSE;
171 len1 = source_name.Length + sizeof(WCHAR);
172 if (dest)
174 len2 = dest_name.Length + sizeof(WCHAR);
175 if (flags & MOVEFILE_REPLACE_EXISTING)
176 len2 += sizeof(WCHAR); /* Plus 1 because of the leading '!' */
178 else len2 = sizeof(WCHAR); /* minimum is the 0 characters for the empty second string */
180 RtlInitUnicodeString( &nameW, ValueName );
182 /* First we check if the key exists and if so how many bytes it already contains. */
183 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
184 NULL, 0, &DataSize ) == STATUS_BUFFER_TOO_SMALL)
186 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
187 goto Quit;
188 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
189 Buffer, DataSize, &DataSize )) goto Quit;
190 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
191 if (info->Type != REG_MULTI_SZ) goto Quit;
192 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
194 else
196 DataSize = info_size;
197 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
198 goto Quit;
201 memcpy( Buffer + DataSize, source_name.Buffer, len1 );
202 DataSize += len1;
203 p = (WCHAR *)(Buffer + DataSize);
204 if (dest)
206 if (flags & MOVEFILE_REPLACE_EXISTING)
207 *p++ = '!';
208 memcpy( p, dest_name.Buffer, len2 );
209 DataSize += len2;
211 else
213 *p = 0;
214 DataSize += sizeof(WCHAR);
217 /* add final null */
218 p = (WCHAR *)(Buffer + DataSize);
219 *p = 0;
220 DataSize += sizeof(WCHAR);
222 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
224 Quit:
225 RtlFreeUnicodeString( &source_name );
226 RtlFreeUnicodeString( &dest_name );
227 if (Reboot) NtClose(Reboot);
228 HeapFree( GetProcessHeap(), 0, Buffer );
229 return(rc);
233 /***********************************************************************
234 * GetFullPathNameW (KERNEL32.@)
235 * NOTES
236 * if the path closed with '\', *lastpart is 0
238 DWORD WINAPI GetFullPathNameW( LPCWSTR name, DWORD len, LPWSTR buffer,
239 LPWSTR *lastpart )
241 return RtlGetFullPathName_U(name, len * sizeof(WCHAR), buffer, lastpart) / sizeof(WCHAR);
244 /***********************************************************************
245 * GetFullPathNameA (KERNEL32.@)
246 * NOTES
247 * if the path closed with '\', *lastpart is 0
249 DWORD WINAPI GetFullPathNameA( LPCSTR name, DWORD len, LPSTR buffer,
250 LPSTR *lastpart )
252 WCHAR *nameW;
253 WCHAR bufferW[MAX_PATH], *lastpartW = NULL;
254 DWORD ret;
256 if (!(nameW = FILE_name_AtoW( name, FALSE ))) return 0;
258 ret = GetFullPathNameW( nameW, MAX_PATH, bufferW, &lastpartW);
260 if (!ret) return 0;
261 if (ret > MAX_PATH)
263 SetLastError(ERROR_FILENAME_EXCED_RANGE);
264 return 0;
266 ret = copy_filename_WtoA( bufferW, buffer, len );
267 if (ret < len && lastpart)
269 if (lastpartW)
270 *lastpart = buffer + FILE_name_WtoA( bufferW, lastpartW - bufferW, NULL, 0 );
271 else
272 *lastpart = NULL;
274 return ret;
278 /***********************************************************************
279 * GetLongPathNameW (KERNEL32.@)
281 * NOTES
282 * observed (Win2000):
283 * shortpath=NULL: LastError=ERROR_INVALID_PARAMETER, ret=0
284 * shortpath="": LastError=ERROR_PATH_NOT_FOUND, ret=0
286 DWORD WINAPI GetLongPathNameW( LPCWSTR shortpath, LPWSTR longpath, DWORD longlen )
288 WCHAR tmplongpath[MAX_PATHNAME_LEN];
289 LPCWSTR p;
290 DWORD sp = 0, lp = 0;
291 DWORD tmplen;
292 BOOL unixabsolute;
293 WIN32_FIND_DATAW wfd;
294 HANDLE goit;
295 BOOL is_legal_8dot3;
297 if (!shortpath)
299 SetLastError(ERROR_INVALID_PARAMETER);
300 return 0;
302 if (!shortpath[0])
304 SetLastError(ERROR_PATH_NOT_FOUND);
305 return 0;
308 TRACE("%s,%p,%d\n", debugstr_w(shortpath), longpath, longlen);
310 if (shortpath[0] == '\\' && shortpath[1] == '\\')
312 FIXME("UNC pathname %s\n", debugstr_w(shortpath));
314 tmplen = strlenW(shortpath);
315 if (tmplen < longlen)
317 if (longpath != shortpath) strcpyW( longpath, shortpath );
318 return tmplen;
320 return tmplen + 1;
323 unixabsolute = (shortpath[0] == '/');
325 /* check for drive letter */
326 if (!unixabsolute && shortpath[1] == ':' )
328 tmplongpath[0] = shortpath[0];
329 tmplongpath[1] = ':';
330 lp = sp = 2;
333 while (shortpath[sp])
335 /* check for path delimiters and reproduce them */
336 if (shortpath[sp] == '\\' || shortpath[sp] == '/')
338 tmplongpath[lp++] = shortpath[sp++];
339 tmplongpath[lp] = 0; /* terminate string */
340 continue;
343 p = shortpath + sp;
344 for (; *p && *p != '/' && *p != '\\'; p++);
345 tmplen = p - (shortpath + sp);
346 lstrcpynW(tmplongpath + lp, shortpath + sp, tmplen + 1);
348 if (tmplongpath[lp] == '.')
350 if (tmplen == 1 || (tmplen == 2 && tmplongpath[lp + 1] == '.'))
352 lp += tmplen;
353 sp += tmplen;
354 continue;
358 /* Check if the file exists */
359 goit = FindFirstFileW(tmplongpath, &wfd);
360 if (goit == INVALID_HANDLE_VALUE)
362 TRACE("not found %s!\n", debugstr_w(tmplongpath));
363 SetLastError ( ERROR_FILE_NOT_FOUND );
364 return 0;
366 FindClose(goit);
368 is_legal_8dot3 = FALSE;
369 CheckNameLegalDOS8Dot3W(tmplongpath + lp, NULL, 0, NULL, &is_legal_8dot3);
370 /* Use the existing file name if it's a short name */
371 if (is_legal_8dot3)
372 strcpyW(tmplongpath + lp, wfd.cFileName);
373 lp += strlenW(tmplongpath + lp);
374 sp += tmplen;
376 tmplen = strlenW(shortpath) - 1;
377 if ((shortpath[tmplen] == '/' || shortpath[tmplen] == '\\') &&
378 (tmplongpath[lp - 1] != '/' && tmplongpath[lp - 1] != '\\'))
379 tmplongpath[lp++] = shortpath[tmplen];
380 tmplongpath[lp] = 0;
382 tmplen = strlenW(tmplongpath) + 1;
383 if (tmplen <= longlen)
385 strcpyW(longpath, tmplongpath);
386 TRACE("returning %s\n", debugstr_w(longpath));
387 tmplen--; /* length without 0 */
390 return tmplen;
393 /***********************************************************************
394 * GetLongPathNameA (KERNEL32.@)
396 DWORD WINAPI GetLongPathNameA( LPCSTR shortpath, LPSTR longpath, DWORD longlen )
398 WCHAR *shortpathW;
399 WCHAR longpathW[MAX_PATH];
400 DWORD ret;
402 TRACE("%s\n", debugstr_a(shortpath));
404 if (!(shortpathW = FILE_name_AtoW( shortpath, FALSE ))) return 0;
406 ret = GetLongPathNameW(shortpathW, longpathW, MAX_PATH);
408 if (!ret) return 0;
409 if (ret > MAX_PATH)
411 SetLastError(ERROR_FILENAME_EXCED_RANGE);
412 return 0;
414 return copy_filename_WtoA( longpathW, longpath, longlen );
418 /***********************************************************************
419 * GetShortPathNameW (KERNEL32.@)
421 * NOTES
422 * observed:
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
428 * longpath > 8.3 ->
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
434 * in the long name
435 * - longpath and shortpath may have the same address
436 * Peter Ganten, 1999
438 DWORD WINAPI GetShortPathNameW( LPCWSTR longpath, LPWSTR shortpath, DWORD shortlen )
440 WCHAR *tmpshortpath;
441 LPCWSTR p;
442 DWORD sp = 0, lp = 0;
443 DWORD tmplen, buf_len;
444 WIN32_FIND_DATAW wfd;
445 HANDLE goit;
447 TRACE("%s\n", debugstr_w(longpath));
449 if (!longpath)
451 SetLastError(ERROR_INVALID_PARAMETER);
452 return 0;
454 if (!longpath[0])
456 SetLastError(ERROR_BAD_PATHNAME);
457 return 0;
460 /* code below only removes characters from string, never adds, so this is
461 * the largest buffer that tmpshortpath will need to have */
462 buf_len = strlenW(longpath) + 1;
463 tmpshortpath = HeapAlloc(GetProcessHeap(), 0, buf_len * sizeof(WCHAR));
464 if (!tmpshortpath)
466 SetLastError(ERROR_OUTOFMEMORY);
467 return 0;
470 if (longpath[0] == '\\' && longpath[1] == '\\' && longpath[2] == '?' && longpath[3] == '\\')
472 memcpy(tmpshortpath, longpath, 4 * sizeof(WCHAR));
473 sp = lp = 4;
476 /* check for drive letter */
477 if (longpath[lp] != '/' && longpath[lp + 1] == ':' )
479 tmpshortpath[sp] = longpath[lp];
480 tmpshortpath[sp + 1] = ':';
481 sp += 2;
482 lp += 2;
485 while (longpath[lp])
487 /* check for path delimiters and reproduce them */
488 if (longpath[lp] == '\\' || longpath[lp] == '/')
490 tmpshortpath[sp++] = longpath[lp++];
491 tmpshortpath[sp] = 0; /* terminate string */
492 continue;
495 p = longpath + lp;
496 for (; *p && *p != '/' && *p != '\\'; p++);
497 tmplen = p - (longpath + lp);
498 lstrcpynW(tmpshortpath + sp, longpath + lp, tmplen + 1);
500 if (tmpshortpath[sp] == '.')
502 if (tmplen == 1 || (tmplen == 2 && tmpshortpath[sp + 1] == '.'))
504 sp += tmplen;
505 lp += tmplen;
506 continue;
510 /* Check if the file exists and use the existing short file name */
511 goit = FindFirstFileW(tmpshortpath, &wfd);
512 if (goit == INVALID_HANDLE_VALUE) goto notfound;
513 FindClose(goit);
515 /* In rare cases (like "a.abcd") short path may be longer than original path.
516 * Make sure we have enough space in temp buffer. */
517 if (wfd.cAlternateFileName[0] && tmplen < strlenW(wfd.cAlternateFileName))
519 WCHAR *new_buf;
520 buf_len += strlenW(wfd.cAlternateFileName) - tmplen;
521 new_buf = HeapReAlloc(GetProcessHeap(), 0, tmpshortpath, buf_len * sizeof(WCHAR));
522 if(!new_buf)
524 HeapFree(GetProcessHeap(), 0, tmpshortpath);
525 SetLastError(ERROR_OUTOFMEMORY);
526 return 0;
528 tmpshortpath = new_buf;
531 strcpyW(tmpshortpath + sp, wfd.cAlternateFileName[0] ? wfd.cAlternateFileName : wfd.cFileName);
532 sp += strlenW(tmpshortpath + sp);
533 lp += tmplen;
535 tmpshortpath[sp] = 0;
537 tmplen = strlenW(tmpshortpath) + 1;
538 if (tmplen <= shortlen)
540 strcpyW(shortpath, tmpshortpath);
541 TRACE("returning %s\n", debugstr_w(shortpath));
542 tmplen--; /* length without 0 */
545 HeapFree(GetProcessHeap(), 0, tmpshortpath);
546 return tmplen;
548 notfound:
549 HeapFree(GetProcessHeap(), 0, tmpshortpath);
550 TRACE("not found!\n" );
551 SetLastError ( ERROR_FILE_NOT_FOUND );
552 return 0;
555 /***********************************************************************
556 * GetShortPathNameA (KERNEL32.@)
558 DWORD WINAPI GetShortPathNameA( LPCSTR longpath, LPSTR shortpath, DWORD shortlen )
560 WCHAR *longpathW;
561 WCHAR shortpathW[MAX_PATH];
562 DWORD ret;
564 TRACE("%s\n", debugstr_a(longpath));
566 if (!(longpathW = FILE_name_AtoW( longpath, FALSE ))) return 0;
568 ret = GetShortPathNameW(longpathW, shortpathW, MAX_PATH);
570 if (!ret) return 0;
571 if (ret > MAX_PATH)
573 SetLastError(ERROR_FILENAME_EXCED_RANGE);
574 return 0;
576 return copy_filename_WtoA( shortpathW, shortpath, shortlen );
580 /***********************************************************************
581 * GetTempPathA (KERNEL32.@)
583 DWORD WINAPI GetTempPathA( DWORD count, LPSTR path )
585 WCHAR pathW[MAX_PATH];
586 UINT ret;
588 ret = GetTempPathW(MAX_PATH, pathW);
590 if (!ret)
591 return 0;
593 if (ret > MAX_PATH)
595 SetLastError(ERROR_FILENAME_EXCED_RANGE);
596 return 0;
598 return copy_filename_WtoA( pathW, path, count );
602 /***********************************************************************
603 * GetTempPathW (KERNEL32.@)
605 DWORD WINAPI GetTempPathW( DWORD count, LPWSTR path )
607 static const WCHAR tmp[] = { 'T', 'M', 'P', 0 };
608 static const WCHAR temp[] = { 'T', 'E', 'M', 'P', 0 };
609 static const WCHAR userprofile[] = { 'U','S','E','R','P','R','O','F','I','L','E',0 };
610 WCHAR tmp_path[MAX_PATH];
611 UINT ret;
613 TRACE("%u,%p\n", count, path);
615 if (!(ret = GetEnvironmentVariableW( tmp, tmp_path, MAX_PATH )) &&
616 !(ret = GetEnvironmentVariableW( temp, tmp_path, MAX_PATH )) &&
617 !(ret = GetEnvironmentVariableW( userprofile, tmp_path, MAX_PATH )) &&
618 !(ret = GetWindowsDirectoryW( tmp_path, MAX_PATH )))
619 return 0;
621 if (ret > MAX_PATH)
623 SetLastError(ERROR_FILENAME_EXCED_RANGE);
624 return 0;
627 ret = GetFullPathNameW(tmp_path, MAX_PATH, tmp_path, NULL);
628 if (!ret) return 0;
630 if (ret > MAX_PATH - 2)
632 SetLastError(ERROR_FILENAME_EXCED_RANGE);
633 return 0;
636 if (tmp_path[ret-1] != '\\')
638 tmp_path[ret++] = '\\';
639 tmp_path[ret] = '\0';
642 ret++; /* add space for terminating 0 */
644 if (count >= ret)
646 lstrcpynW(path, tmp_path, count);
647 /* the remaining buffer must be zeroed up to 32766 bytes in XP or 32767
648 * bytes after it, we will assume the > XP behavior for now */
649 memset(path + ret, 0, (min(count, 32767) - ret) * sizeof(WCHAR));
650 ret--; /* return length without 0 */
652 else if (count)
654 /* the buffer must be cleared if contents will not fit */
655 memset(path, 0, count * sizeof(WCHAR));
658 TRACE("returning %u, %s\n", ret, debugstr_w(path));
659 return ret;
663 /***********************************************************************
664 * GetTempFileNameA (KERNEL32.@)
666 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique, LPSTR buffer)
668 WCHAR *pathW, *prefixW = NULL;
669 WCHAR bufferW[MAX_PATH];
670 UINT ret;
672 if (!(pathW = FILE_name_AtoW( path, FALSE ))) return 0;
673 if (prefix && !(prefixW = FILE_name_AtoW( prefix, TRUE ))) return 0;
675 ret = GetTempFileNameW(pathW, prefixW, unique, bufferW);
676 if (ret) FILE_name_WtoA( bufferW, -1, buffer, MAX_PATH );
678 HeapFree( GetProcessHeap(), 0, prefixW );
679 return ret;
682 /***********************************************************************
683 * GetTempFileNameW (KERNEL32.@)
685 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique, LPWSTR buffer )
687 static const WCHAR formatW[] = {'%','x','.','t','m','p',0};
689 int i;
690 LPWSTR p;
691 DWORD attr;
693 if ( !path || !buffer )
695 SetLastError( ERROR_INVALID_PARAMETER );
696 return 0;
699 /* ensure that the provided directory exists */
700 attr = GetFileAttributesW(path);
701 if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
703 TRACE("path not found %s\n", debugstr_w(path));
704 SetLastError( ERROR_DIRECTORY );
705 return 0;
708 strcpyW( buffer, path );
709 p = buffer + strlenW(buffer);
711 /* add a \, if there isn't one */
712 if ((p == buffer) || (p[-1] != '\\')) *p++ = '\\';
714 if (prefix)
715 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
717 unique &= 0xffff;
719 if (unique) sprintfW( p, formatW, unique );
720 else
722 /* get a "random" unique number and try to create the file */
723 HANDLE handle;
724 UINT num = GetTickCount() & 0xffff;
725 static UINT last;
727 /* avoid using the same name twice in a short interval */
728 if (last - num < 10) num = last + 1;
729 if (!num) num = 1;
730 unique = num;
733 sprintfW( p, formatW, unique );
734 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
735 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
736 if (handle != INVALID_HANDLE_VALUE)
737 { /* We created it */
738 TRACE("created %s\n", debugstr_w(buffer) );
739 CloseHandle( handle );
740 last = unique;
741 break;
743 if (GetLastError() != ERROR_FILE_EXISTS &&
744 GetLastError() != ERROR_SHARING_VIOLATION)
745 break; /* No need to go on */
746 if (!(++unique & 0xffff)) unique = 1;
747 } while (unique != num);
750 TRACE("returning %s\n", debugstr_w(buffer) );
751 return unique;
755 /***********************************************************************
756 * contains_pathW
758 * Check if the file name contains a path; helper for SearchPathW.
759 * A relative path is not considered a path unless it starts with ./ or ../
761 static inline BOOL contains_pathW (LPCWSTR name)
763 if (RtlDetermineDosPathNameType_U( name ) != RELATIVE_PATH) return TRUE;
764 if (name[0] != '.') return FALSE;
765 if (name[1] == '/' || name[1] == '\\') return TRUE;
766 return (name[1] == '.' && (name[2] == '/' || name[2] == '\\'));
769 /***********************************************************************
770 * find_actctx_dllpath
772 * Find the path (if any) of the dll from the activation context.
773 * Returned path doesn't include a name.
775 static NTSTATUS find_actctx_dllpath(const WCHAR *libname, WCHAR **path)
777 static const WCHAR winsxsW[] = {'\\','w','i','n','s','x','s','\\'};
778 static const WCHAR dotManifestW[] = {'.','m','a','n','i','f','e','s','t',0};
780 ACTIVATION_CONTEXT_ASSEMBLY_DETAILED_INFORMATION *info;
781 ACTCTX_SECTION_KEYED_DATA data;
782 UNICODE_STRING nameW;
783 NTSTATUS status;
784 SIZE_T needed, size = 1024;
785 WCHAR *p;
787 RtlInitUnicodeString( &nameW, libname );
788 data.cbSize = sizeof(data);
789 status = RtlFindActivationContextSectionString( FIND_ACTCTX_SECTION_KEY_RETURN_HACTCTX, NULL,
790 ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
791 &nameW, &data );
792 if (status != STATUS_SUCCESS) return status;
794 for (;;)
796 if (!(info = HeapAlloc( GetProcessHeap(), 0, size )))
798 status = STATUS_NO_MEMORY;
799 goto done;
801 status = RtlQueryInformationActivationContext( 0, data.hActCtx, &data.ulAssemblyRosterIndex,
802 AssemblyDetailedInformationInActivationContext,
803 info, size, &needed );
804 if (status == STATUS_SUCCESS) break;
805 if (status != STATUS_BUFFER_TOO_SMALL) goto done;
806 HeapFree( GetProcessHeap(), 0, info );
807 size = needed;
808 /* restart with larger buffer */
811 if (!info->lpAssemblyManifestPath || !info->lpAssemblyDirectoryName)
813 status = STATUS_SXS_KEY_NOT_FOUND;
814 goto done;
817 if ((p = strrchrW( info->lpAssemblyManifestPath, '\\' )))
819 DWORD dirlen = info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
821 p++;
822 if (strncmpiW( p, info->lpAssemblyDirectoryName, dirlen ) || strcmpiW( p + dirlen, dotManifestW ))
824 /* manifest name does not match directory name, so it's not a global
825 * windows/winsxs manifest; use the manifest directory name instead */
826 dirlen = p - info->lpAssemblyManifestPath;
827 needed = (dirlen + 1) * sizeof(WCHAR);
828 if (!(*path = p = HeapAlloc( GetProcessHeap(), 0, needed )))
830 status = STATUS_NO_MEMORY;
831 goto done;
833 memcpy( p, info->lpAssemblyManifestPath, dirlen * sizeof(WCHAR) );
834 *(p + dirlen) = 0;
835 goto done;
839 needed = (strlenW( DIR_Windows ) * sizeof(WCHAR) +
840 sizeof(winsxsW) + info->ulAssemblyDirectoryNameLength + 2*sizeof(WCHAR));
842 if (!(*path = p = HeapAlloc( GetProcessHeap(), 0, needed )))
844 status = STATUS_NO_MEMORY;
845 goto done;
847 strcpyW( p, DIR_Windows );
848 p += strlenW(p);
849 memcpy( p, winsxsW, sizeof(winsxsW) );
850 p += sizeof(winsxsW) / sizeof(WCHAR);
851 memcpy( p, info->lpAssemblyDirectoryName, info->ulAssemblyDirectoryNameLength );
852 p += info->ulAssemblyDirectoryNameLength / sizeof(WCHAR);
853 *p++ = '\\';
854 *p = 0;
855 done:
856 HeapFree( GetProcessHeap(), 0, info );
857 RtlReleaseActivationContext( data.hActCtx );
858 return status;
861 /***********************************************************************
862 * SearchPathW [KERNEL32.@]
864 * Searches for a specified file in the search path.
866 * PARAMS
867 * path [I] Path to search (NULL means default)
868 * name [I] Filename to search for.
869 * ext [I] File extension to append to file name. The first
870 * character must be a period. This parameter is
871 * specified only if the filename given does not
872 * contain an extension.
873 * buflen [I] size of buffer, in characters
874 * buffer [O] buffer for found filename
875 * lastpart [O] address of pointer to last used character in
876 * buffer (the final '\')
878 * RETURNS
879 * Success: length of string copied into buffer, not including
880 * terminating null character. If the filename found is
881 * longer than the length of the buffer, the length of the
882 * filename is returned.
883 * Failure: Zero
885 * NOTES
886 * If the file is not found, calls SetLastError(ERROR_FILE_NOT_FOUND)
887 * (tested on NT 4.0)
889 DWORD WINAPI SearchPathW( LPCWSTR path, LPCWSTR name, LPCWSTR ext, DWORD buflen,
890 LPWSTR buffer, LPWSTR *lastpart )
892 DWORD ret = 0;
894 if (!name || !name[0])
896 SetLastError(ERROR_INVALID_PARAMETER);
897 return 0;
900 /* If the name contains an explicit path, ignore the path */
902 if (contains_pathW(name))
904 /* try first without extension */
905 if (RtlDoesFileExists_U( name ))
906 return GetFullPathNameW( name, buflen, buffer, lastpart );
908 if (ext)
910 LPCWSTR p = strrchrW( name, '.' );
911 if (p && !strchrW( p, '/' ) && !strchrW( p, '\\' ))
912 ext = NULL; /* Ignore the specified extension */
915 /* Allocate a buffer for the file name and extension */
916 if (ext)
918 LPWSTR tmp;
919 DWORD len = strlenW(name) + strlenW(ext);
921 if (!(tmp = HeapAlloc( GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) )))
923 SetLastError( ERROR_OUTOFMEMORY );
924 return 0;
926 strcpyW( tmp, name );
927 strcatW( tmp, ext );
928 if (RtlDoesFileExists_U( tmp ))
929 ret = GetFullPathNameW( tmp, buflen, buffer, lastpart );
930 HeapFree( GetProcessHeap(), 0, tmp );
933 else if (path && path[0]) /* search in the specified path */
935 ret = RtlDosSearchPath_U( path, name, ext, buflen * sizeof(WCHAR),
936 buffer, lastpart ) / sizeof(WCHAR);
938 else /* search in active context and default path */
940 WCHAR *dll_path = NULL, *search = NULL;
941 DWORD req_len, name_len;
943 req_len = name_len = strlenW(name);
945 if (strchrW( name, '.' )) ext = NULL;
946 if (ext)
948 DWORD ext_len = strlenW(ext);
950 req_len += ext_len;
951 name_len += ext_len;
953 search = HeapAlloc( GetProcessHeap(), 0, (name_len + ext_len + 1) * sizeof(WCHAR) );
954 if (!search)
956 SetLastError( ERROR_OUTOFMEMORY );
957 return 0;
959 strcpyW( search, name );
960 strcatW( search, ext );
961 name = search;
963 /* now that we have combined name we don't need extension any more */
966 /* When file is found with activation context no attempt is made
967 to check if it's really exist, path is returned only basing on context info. */
968 if (find_actctx_dllpath( name, &dll_path ) == STATUS_SUCCESS)
970 DWORD path_len;
972 path_len = strlenW(dll_path);
973 req_len += path_len;
975 if (lastpart) *lastpart = NULL;
977 /* count null termination char too */
978 if (req_len + 1 <= buflen)
980 memcpy( buffer, dll_path, path_len * sizeof(WCHAR) );
981 memcpy( &buffer[path_len], name, name_len * sizeof(WCHAR) );
982 buffer[req_len] = 0;
983 if (lastpart) *lastpart = buffer + path_len;
984 ret = req_len;
986 else
987 ret = req_len + 1;
989 HeapFree( GetProcessHeap(), 0, dll_path );
990 HeapFree( GetProcessHeap(), 0, search );
992 else
994 if ((dll_path = MODULE_get_dll_load_path( NULL )))
996 ret = RtlDosSearchPath_U( dll_path, name, NULL, buflen * sizeof(WCHAR),
997 buffer, lastpart ) / sizeof(WCHAR);
998 HeapFree( GetProcessHeap(), 0, dll_path );
999 HeapFree( GetProcessHeap(), 0, search );
1001 else
1003 SetLastError( ERROR_OUTOFMEMORY );
1004 return 0;
1009 if (!ret) SetLastError( ERROR_FILE_NOT_FOUND );
1010 else TRACE( "found %s\n", debugstr_w(buffer) );
1011 return ret;
1015 /***********************************************************************
1016 * SearchPathA (KERNEL32.@)
1018 * See SearchPathW.
1020 DWORD WINAPI SearchPathA( LPCSTR path, LPCSTR name, LPCSTR ext,
1021 DWORD buflen, LPSTR buffer, LPSTR *lastpart )
1023 WCHAR *pathW = NULL, *nameW, *extW = NULL;
1024 WCHAR bufferW[MAX_PATH];
1025 DWORD ret;
1027 if (!name)
1029 SetLastError(ERROR_INVALID_PARAMETER);
1030 return 0;
1033 if (!(nameW = FILE_name_AtoW( name, FALSE ))) return 0;
1034 if (path && !(pathW = FILE_name_AtoW( path, TRUE ))) return 0;
1036 if (ext && !(extW = FILE_name_AtoW( ext, TRUE )))
1038 HeapFree( GetProcessHeap(), 0, pathW );
1039 return 0;
1042 ret = SearchPathW(pathW, nameW, extW, MAX_PATH, bufferW, NULL);
1044 HeapFree( GetProcessHeap(), 0, pathW );
1045 HeapFree( GetProcessHeap(), 0, extW );
1047 if (!ret) return 0;
1048 if (ret > MAX_PATH)
1050 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1051 return 0;
1053 ret = copy_filename_WtoA( bufferW, buffer, buflen );
1054 if (buflen > ret && lastpart)
1055 *lastpart = strrchr(buffer, '\\') + 1;
1056 return ret;
1059 static BOOL is_same_file(HANDLE h1, HANDLE h2)
1061 int fd1;
1062 BOOL ret = FALSE;
1063 if (wine_server_handle_to_fd(h1, 0, &fd1, NULL) == STATUS_SUCCESS)
1065 int fd2;
1066 if (wine_server_handle_to_fd(h2, 0, &fd2, NULL) == STATUS_SUCCESS)
1068 struct stat stat1, stat2;
1069 if (fstat(fd1, &stat1) == 0 && fstat(fd2, &stat2) == 0)
1070 ret = (stat1.st_dev == stat2.st_dev && stat1.st_ino == stat2.st_ino);
1071 wine_server_release_fd(h2, fd2);
1073 wine_server_release_fd(h1, fd1);
1075 return ret;
1078 /**************************************************************************
1079 * CopyFileW (KERNEL32.@)
1081 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
1083 return CopyFileExW( source, dest, NULL, NULL, NULL,
1084 fail_if_exists ? COPY_FILE_FAIL_IF_EXISTS : 0 );
1088 /**************************************************************************
1089 * CopyFileA (KERNEL32.@)
1091 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
1093 WCHAR *sourceW, *destW;
1094 BOOL ret;
1096 if (!(sourceW = FILE_name_AtoW( source, FALSE ))) return FALSE;
1097 if (!(destW = FILE_name_AtoW( dest, TRUE ))) return FALSE;
1099 ret = CopyFileW( sourceW, destW, fail_if_exists );
1101 HeapFree( GetProcessHeap(), 0, destW );
1102 return ret;
1106 /**************************************************************************
1107 * CopyFileExW (KERNEL32.@)
1109 BOOL WINAPI CopyFileExW(LPCWSTR source, LPCWSTR dest,
1110 LPPROGRESS_ROUTINE progress, LPVOID param,
1111 LPBOOL cancel_ptr, DWORD flags)
1113 static const int buffer_size = 65536;
1114 HANDLE h1, h2;
1115 BY_HANDLE_FILE_INFORMATION info;
1116 DWORD count;
1117 BOOL ret = FALSE;
1118 char *buffer;
1120 if (!source || !dest)
1122 SetLastError(ERROR_INVALID_PARAMETER);
1123 return FALSE;
1125 if (!(buffer = HeapAlloc( GetProcessHeap(), 0, buffer_size )))
1127 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1128 return FALSE;
1131 TRACE("%s -> %s, %x\n", debugstr_w(source), debugstr_w(dest), flags);
1133 if ((h1 = CreateFileW(source, GENERIC_READ,
1134 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1135 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
1137 WARN("Unable to open source %s\n", debugstr_w(source));
1138 HeapFree( GetProcessHeap(), 0, buffer );
1139 return FALSE;
1142 if (!GetFileInformationByHandle( h1, &info ))
1144 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
1145 HeapFree( GetProcessHeap(), 0, buffer );
1146 CloseHandle( h1 );
1147 return FALSE;
1150 if (!(flags & COPY_FILE_FAIL_IF_EXISTS))
1152 BOOL same_file = FALSE;
1153 h2 = CreateFileW( dest, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1154 OPEN_EXISTING, 0, 0);
1155 if (h2 != INVALID_HANDLE_VALUE)
1157 same_file = is_same_file( h1, h2 );
1158 CloseHandle( h2 );
1160 if (same_file)
1162 HeapFree( GetProcessHeap(), 0, buffer );
1163 CloseHandle( h1 );
1164 SetLastError( ERROR_SHARING_VIOLATION );
1165 return FALSE;
1169 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1170 (flags & COPY_FILE_FAIL_IF_EXISTS) ? CREATE_NEW : CREATE_ALWAYS,
1171 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
1173 WARN("Unable to open dest %s\n", debugstr_w(dest));
1174 HeapFree( GetProcessHeap(), 0, buffer );
1175 CloseHandle( h1 );
1176 return FALSE;
1179 while (ReadFile( h1, buffer, buffer_size, &count, NULL ) && count)
1181 char *p = buffer;
1182 while (count != 0)
1184 DWORD res;
1185 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
1186 p += res;
1187 count -= res;
1190 ret = TRUE;
1191 done:
1192 /* Maintain the timestamp of source file to destination file */
1193 SetFileTime(h2, NULL, NULL, &info.ftLastWriteTime);
1194 HeapFree( GetProcessHeap(), 0, buffer );
1195 CloseHandle( h1 );
1196 CloseHandle( h2 );
1197 return ret;
1201 /**************************************************************************
1202 * CopyFileExA (KERNEL32.@)
1204 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
1205 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
1206 LPBOOL cancelFlagPointer, DWORD copyFlags)
1208 WCHAR *sourceW, *destW;
1209 BOOL ret;
1211 /* can't use the TEB buffer since we may have a callback routine */
1212 if (!(sourceW = FILE_name_AtoW( sourceFilename, TRUE ))) return FALSE;
1213 if (!(destW = FILE_name_AtoW( destFilename, TRUE )))
1215 HeapFree( GetProcessHeap(), 0, sourceW );
1216 return FALSE;
1218 ret = CopyFileExW(sourceW, destW, progressRoutine, appData,
1219 cancelFlagPointer, copyFlags);
1220 HeapFree( GetProcessHeap(), 0, sourceW );
1221 HeapFree( GetProcessHeap(), 0, destW );
1222 return ret;
1226 /**************************************************************************
1227 * MoveFileWithProgressW (KERNEL32.@)
1229 BOOL WINAPI MoveFileWithProgressW( LPCWSTR source, LPCWSTR dest,
1230 LPPROGRESS_ROUTINE fnProgress,
1231 LPVOID param, DWORD flag )
1233 FILE_BASIC_INFORMATION info;
1234 UNICODE_STRING nt_name;
1235 OBJECT_ATTRIBUTES attr;
1236 IO_STATUS_BLOCK io;
1237 NTSTATUS status;
1238 HANDLE source_handle = 0, dest_handle;
1239 ANSI_STRING source_unix, dest_unix;
1241 TRACE("(%s,%s,%p,%p,%04x)\n",
1242 debugstr_w(source), debugstr_w(dest), fnProgress, param, flag );
1244 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1245 return add_boot_rename_entry( source, dest, flag );
1247 if (!dest)
1248 return DeleteFileW( source );
1250 if (flag & MOVEFILE_WRITE_THROUGH)
1251 FIXME("MOVEFILE_WRITE_THROUGH unimplemented\n");
1253 /* check if we are allowed to rename the source */
1255 if (!RtlDosPathNameToNtPathName_U( source, &nt_name, NULL, NULL ))
1257 SetLastError( ERROR_PATH_NOT_FOUND );
1258 return FALSE;
1260 source_unix.Buffer = NULL;
1261 dest_unix.Buffer = NULL;
1262 attr.Length = sizeof(attr);
1263 attr.RootDirectory = 0;
1264 attr.Attributes = OBJ_CASE_INSENSITIVE;
1265 attr.ObjectName = &nt_name;
1266 attr.SecurityDescriptor = NULL;
1267 attr.SecurityQualityOfService = NULL;
1269 status = NtOpenFile( &source_handle, SYNCHRONIZE, &attr, &io, 0, FILE_SYNCHRONOUS_IO_NONALERT );
1270 if (status == STATUS_SUCCESS)
1271 status = wine_nt_to_unix_file_name( &nt_name, &source_unix, FILE_OPEN, FALSE );
1272 RtlFreeUnicodeString( &nt_name );
1273 if (status != STATUS_SUCCESS)
1275 SetLastError( RtlNtStatusToDosError(status) );
1276 goto error;
1278 status = NtQueryInformationFile( source_handle, &io, &info, sizeof(info), FileBasicInformation );
1279 if (status != STATUS_SUCCESS)
1281 SetLastError( RtlNtStatusToDosError(status) );
1282 goto error;
1285 /* we must have write access to the destination, and it must */
1286 /* not exist except if MOVEFILE_REPLACE_EXISTING is set */
1288 if (!RtlDosPathNameToNtPathName_U( dest, &nt_name, NULL, NULL ))
1290 SetLastError( ERROR_PATH_NOT_FOUND );
1291 goto error;
1293 status = NtOpenFile( &dest_handle, GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &attr, &io, 0,
1294 FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1295 if (status == STATUS_SUCCESS) /* destination exists */
1297 NtClose( dest_handle );
1298 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1300 SetLastError( ERROR_ALREADY_EXISTS );
1301 RtlFreeUnicodeString( &nt_name );
1302 goto error;
1304 else if (info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) /* cannot replace directory */
1306 SetLastError( ERROR_ACCESS_DENIED );
1307 goto error;
1310 else if (status != STATUS_OBJECT_NAME_NOT_FOUND)
1312 SetLastError( RtlNtStatusToDosError(status) );
1313 RtlFreeUnicodeString( &nt_name );
1314 goto error;
1317 status = wine_nt_to_unix_file_name( &nt_name, &dest_unix, FILE_OPEN_IF, FALSE );
1318 RtlFreeUnicodeString( &nt_name );
1319 if (status != STATUS_SUCCESS && status != STATUS_NO_SUCH_FILE)
1321 SetLastError( RtlNtStatusToDosError(status) );
1322 goto error;
1325 /* now perform the rename */
1327 if (rename( source_unix.Buffer, dest_unix.Buffer ) == -1)
1329 if (errno == EXDEV && (flag & MOVEFILE_COPY_ALLOWED))
1331 NtClose( source_handle );
1332 RtlFreeAnsiString( &source_unix );
1333 RtlFreeAnsiString( &dest_unix );
1334 if (!CopyFileExW( source, dest, fnProgress,
1335 param, NULL, COPY_FILE_FAIL_IF_EXISTS ))
1336 return FALSE;
1337 return DeleteFileW( source );
1339 FILE_SetDosError();
1340 /* if we created the destination, remove it */
1341 if (io.Information == FILE_CREATED) unlink( dest_unix.Buffer );
1342 goto error;
1345 /* fixup executable permissions */
1347 if (is_executable( source ) != is_executable( dest ))
1349 struct stat fstat;
1350 if (stat( dest_unix.Buffer, &fstat ) != -1)
1352 if (is_executable( dest ))
1353 /* set executable bit where read bit is set */
1354 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
1355 else
1356 fstat.st_mode &= ~0111;
1357 chmod( dest_unix.Buffer, fstat.st_mode );
1361 NtClose( source_handle );
1362 RtlFreeAnsiString( &source_unix );
1363 RtlFreeAnsiString( &dest_unix );
1364 return TRUE;
1366 error:
1367 if (source_handle) NtClose( source_handle );
1368 RtlFreeAnsiString( &source_unix );
1369 RtlFreeAnsiString( &dest_unix );
1370 return FALSE;
1373 /**************************************************************************
1374 * MoveFileWithProgressA (KERNEL32.@)
1376 BOOL WINAPI MoveFileWithProgressA( LPCSTR source, LPCSTR dest,
1377 LPPROGRESS_ROUTINE fnProgress,
1378 LPVOID param, DWORD flag )
1380 WCHAR *sourceW, *destW;
1381 BOOL ret;
1383 if (!(sourceW = FILE_name_AtoW( source, FALSE ))) return FALSE;
1384 if (dest)
1386 if (!(destW = FILE_name_AtoW( dest, TRUE ))) return FALSE;
1388 else
1389 destW = NULL;
1391 ret = MoveFileWithProgressW( sourceW, destW, fnProgress, param, flag );
1392 HeapFree( GetProcessHeap(), 0, destW );
1393 return ret;
1396 /**************************************************************************
1397 * MoveFileExW (KERNEL32.@)
1399 BOOL WINAPI MoveFileExW( LPCWSTR source, LPCWSTR dest, DWORD flag )
1401 return MoveFileWithProgressW( source, dest, NULL, NULL, flag );
1404 /**************************************************************************
1405 * MoveFileExA (KERNEL32.@)
1407 BOOL WINAPI MoveFileExA( LPCSTR source, LPCSTR dest, DWORD flag )
1409 return MoveFileWithProgressA( source, dest, NULL, NULL, flag );
1413 /**************************************************************************
1414 * MoveFileW (KERNEL32.@)
1416 * Move file or directory
1418 BOOL WINAPI MoveFileW( LPCWSTR source, LPCWSTR dest )
1420 return MoveFileExW( source, dest, MOVEFILE_COPY_ALLOWED );
1424 /**************************************************************************
1425 * MoveFileA (KERNEL32.@)
1427 BOOL WINAPI MoveFileA( LPCSTR source, LPCSTR dest )
1429 return MoveFileExA( source, dest, MOVEFILE_COPY_ALLOWED );
1433 /*************************************************************************
1434 * CreateHardLinkW (KERNEL32.@)
1436 BOOL WINAPI CreateHardLinkW(LPCWSTR lpFileName, LPCWSTR lpExistingFileName,
1437 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1439 NTSTATUS status;
1440 UNICODE_STRING ntDest, ntSource;
1441 ANSI_STRING unixDest, unixSource;
1442 BOOL ret = FALSE;
1444 TRACE("(%s, %s, %p)\n", debugstr_w(lpFileName),
1445 debugstr_w(lpExistingFileName), lpSecurityAttributes);
1447 ntDest.Buffer = ntSource.Buffer = NULL;
1448 if (!RtlDosPathNameToNtPathName_U( lpFileName, &ntDest, NULL, NULL ) ||
1449 !RtlDosPathNameToNtPathName_U( lpExistingFileName, &ntSource, NULL, NULL ))
1451 SetLastError( ERROR_PATH_NOT_FOUND );
1452 goto err;
1455 unixSource.Buffer = unixDest.Buffer = NULL;
1456 status = wine_nt_to_unix_file_name( &ntSource, &unixSource, FILE_OPEN, FALSE );
1457 if (!status)
1459 status = wine_nt_to_unix_file_name( &ntDest, &unixDest, FILE_CREATE, FALSE );
1460 if (!status) /* destination must not exist */
1462 status = STATUS_OBJECT_NAME_EXISTS;
1463 } else if (status == STATUS_NO_SUCH_FILE)
1465 status = STATUS_SUCCESS;
1469 if (status)
1470 SetLastError( RtlNtStatusToDosError(status) );
1471 else if (!link( unixSource.Buffer, unixDest.Buffer ))
1473 TRACE("Hardlinked '%s' to '%s'\n", debugstr_a( unixDest.Buffer ),
1474 debugstr_a( unixSource.Buffer ));
1475 ret = TRUE;
1477 else
1478 FILE_SetDosError();
1480 RtlFreeAnsiString( &unixSource );
1481 RtlFreeAnsiString( &unixDest );
1483 err:
1484 RtlFreeUnicodeString( &ntSource );
1485 RtlFreeUnicodeString( &ntDest );
1486 return ret;
1490 /*************************************************************************
1491 * CreateHardLinkA (KERNEL32.@)
1493 BOOL WINAPI CreateHardLinkA(LPCSTR lpFileName, LPCSTR lpExistingFileName,
1494 LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1496 WCHAR *sourceW, *destW;
1497 BOOL res;
1499 if (!(sourceW = FILE_name_AtoW( lpExistingFileName, TRUE )))
1501 return FALSE;
1503 if (!(destW = FILE_name_AtoW( lpFileName, TRUE )))
1505 HeapFree( GetProcessHeap(), 0, sourceW );
1506 return FALSE;
1509 res = CreateHardLinkW( destW, sourceW, lpSecurityAttributes );
1511 HeapFree( GetProcessHeap(), 0, sourceW );
1512 HeapFree( GetProcessHeap(), 0, destW );
1514 return res;
1518 /***********************************************************************
1519 * CreateDirectoryW (KERNEL32.@)
1520 * RETURNS:
1521 * TRUE : success
1522 * FALSE : failure
1523 * ERROR_DISK_FULL: on full disk
1524 * ERROR_ALREADY_EXISTS: if directory name exists (even as file)
1525 * ERROR_ACCESS_DENIED: on permission problems
1526 * ERROR_FILENAME_EXCED_RANGE: too long filename(s)
1528 BOOL WINAPI CreateDirectoryW( LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1530 OBJECT_ATTRIBUTES attr;
1531 UNICODE_STRING nt_name;
1532 IO_STATUS_BLOCK io;
1533 NTSTATUS status;
1534 HANDLE handle;
1535 BOOL ret = FALSE;
1537 TRACE( "%s\n", debugstr_w(path) );
1539 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1541 SetLastError( ERROR_PATH_NOT_FOUND );
1542 return FALSE;
1544 attr.Length = sizeof(attr);
1545 attr.RootDirectory = 0;
1546 attr.Attributes = OBJ_CASE_INSENSITIVE;
1547 attr.ObjectName = &nt_name;
1548 attr.SecurityDescriptor = sa ? sa->lpSecurityDescriptor : NULL;
1549 attr.SecurityQualityOfService = NULL;
1551 status = NtCreateFile( &handle, GENERIC_READ | SYNCHRONIZE, &attr, &io, NULL,
1552 FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_CREATE,
1553 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 );
1555 if (status == STATUS_SUCCESS)
1557 NtClose( handle );
1558 ret = TRUE;
1560 else SetLastError( RtlNtStatusToDosError(status) );
1562 RtlFreeUnicodeString( &nt_name );
1563 return ret;
1567 /***********************************************************************
1568 * CreateDirectoryA (KERNEL32.@)
1570 BOOL WINAPI CreateDirectoryA( LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1572 WCHAR *pathW;
1574 if (!(pathW = FILE_name_AtoW( path, FALSE ))) return FALSE;
1575 return CreateDirectoryW( pathW, sa );
1579 /***********************************************************************
1580 * CreateDirectoryExA (KERNEL32.@)
1582 BOOL WINAPI CreateDirectoryExA( LPCSTR template, LPCSTR path, LPSECURITY_ATTRIBUTES sa )
1584 WCHAR *pathW, *templateW = NULL;
1585 BOOL ret;
1587 if (!(pathW = FILE_name_AtoW( path, FALSE ))) return FALSE;
1588 if (template && !(templateW = FILE_name_AtoW( template, TRUE ))) return FALSE;
1590 ret = CreateDirectoryExW( templateW, pathW, sa );
1591 HeapFree( GetProcessHeap(), 0, templateW );
1592 return ret;
1596 /***********************************************************************
1597 * CreateDirectoryExW (KERNEL32.@)
1599 BOOL WINAPI CreateDirectoryExW( LPCWSTR template, LPCWSTR path, LPSECURITY_ATTRIBUTES sa )
1601 return CreateDirectoryW( path, sa );
1605 /***********************************************************************
1606 * RemoveDirectoryW (KERNEL32.@)
1608 BOOL WINAPI RemoveDirectoryW( LPCWSTR path )
1610 OBJECT_ATTRIBUTES attr;
1611 UNICODE_STRING nt_name;
1612 ANSI_STRING unix_name;
1613 IO_STATUS_BLOCK io;
1614 NTSTATUS status;
1615 HANDLE handle;
1616 BOOL ret = FALSE;
1618 TRACE( "%s\n", debugstr_w(path) );
1620 if (!RtlDosPathNameToNtPathName_U( path, &nt_name, NULL, NULL ))
1622 SetLastError( ERROR_PATH_NOT_FOUND );
1623 return FALSE;
1625 attr.Length = sizeof(attr);
1626 attr.RootDirectory = 0;
1627 attr.Attributes = OBJ_CASE_INSENSITIVE;
1628 attr.ObjectName = &nt_name;
1629 attr.SecurityDescriptor = NULL;
1630 attr.SecurityQualityOfService = NULL;
1632 status = NtOpenFile( &handle, DELETE | SYNCHRONIZE, &attr, &io,
1633 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
1634 FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT );
1635 if (status != STATUS_SUCCESS)
1637 SetLastError( RtlNtStatusToDosError(status) );
1638 RtlFreeUnicodeString( &nt_name );
1639 return FALSE;
1642 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN, FALSE );
1643 RtlFreeUnicodeString( &nt_name );
1644 if (status != STATUS_SUCCESS)
1646 SetLastError( RtlNtStatusToDosError(status) );
1647 NtClose( handle );
1648 return FALSE;
1651 if (!(ret = (rmdir( unix_name.Buffer ) != -1))) FILE_SetDosError();
1652 RtlFreeAnsiString( &unix_name );
1653 NtClose( handle );
1654 return ret;
1658 /***********************************************************************
1659 * RemoveDirectoryA (KERNEL32.@)
1661 BOOL WINAPI RemoveDirectoryA( LPCSTR path )
1663 WCHAR *pathW;
1665 if (!(pathW = FILE_name_AtoW( path, FALSE ))) return FALSE;
1666 return RemoveDirectoryW( pathW );
1670 /***********************************************************************
1671 * GetCurrentDirectoryW (KERNEL32.@)
1673 UINT WINAPI GetCurrentDirectoryW( UINT buflen, LPWSTR buf )
1675 return RtlGetCurrentDirectory_U( buflen * sizeof(WCHAR), buf ) / sizeof(WCHAR);
1679 /***********************************************************************
1680 * GetCurrentDirectoryA (KERNEL32.@)
1682 UINT WINAPI GetCurrentDirectoryA( UINT buflen, LPSTR buf )
1684 WCHAR bufferW[MAX_PATH];
1685 DWORD ret;
1687 if (buflen && buf && ((ULONG_PTR)buf >> 16) == 0)
1689 /* Win9x catches access violations here, returning zero.
1690 * This behaviour resulted in some people not noticing
1691 * that they got the argument order wrong. So let's be
1692 * nice and fail gracefully if buf is invalid and looks
1693 * more like a buflen. */
1694 SetLastError(ERROR_INVALID_PARAMETER);
1695 return 0;
1698 ret = RtlGetCurrentDirectory_U( sizeof(bufferW), bufferW );
1699 if (!ret) return 0;
1700 if (ret > sizeof(bufferW))
1702 SetLastError(ERROR_FILENAME_EXCED_RANGE);
1703 return 0;
1705 return copy_filename_WtoA( bufferW, buf, buflen );
1709 /***********************************************************************
1710 * SetCurrentDirectoryW (KERNEL32.@)
1712 BOOL WINAPI SetCurrentDirectoryW( LPCWSTR dir )
1714 UNICODE_STRING dirW;
1715 NTSTATUS status;
1717 RtlInitUnicodeString( &dirW, dir );
1718 status = RtlSetCurrentDirectory_U( &dirW );
1719 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) );
1720 return !status;
1724 /***********************************************************************
1725 * SetCurrentDirectoryA (KERNEL32.@)
1727 BOOL WINAPI SetCurrentDirectoryA( LPCSTR dir )
1729 WCHAR *dirW;
1730 UNICODE_STRING strW;
1731 NTSTATUS status;
1733 if (!(dirW = FILE_name_AtoW( dir, FALSE ))) return FALSE;
1734 RtlInitUnicodeString( &strW, dirW );
1735 status = RtlSetCurrentDirectory_U( &strW );
1736 if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) );
1737 return !status;
1741 /***********************************************************************
1742 * GetWindowsDirectoryW (KERNEL32.@)
1744 * See comment for GetWindowsDirectoryA.
1746 UINT WINAPI GetWindowsDirectoryW( LPWSTR path, UINT count )
1748 UINT len = strlenW( DIR_Windows ) + 1;
1749 if (path && count >= len)
1751 strcpyW( path, DIR_Windows );
1752 len--;
1754 return len;
1758 /***********************************************************************
1759 * GetWindowsDirectoryA (KERNEL32.@)
1761 * Return value:
1762 * If buffer is large enough to hold full path and terminating '\0' character
1763 * function copies path to buffer and returns length of the path without '\0'.
1764 * Otherwise function returns required size including '\0' character and
1765 * does not touch the buffer.
1767 UINT WINAPI GetWindowsDirectoryA( LPSTR path, UINT count )
1769 return copy_filename_WtoA( DIR_Windows, path, count );
1773 /***********************************************************************
1774 * GetSystemWindowsDirectoryA (KERNEL32.@) W2K, TS4.0SP4
1776 UINT WINAPI GetSystemWindowsDirectoryA( LPSTR path, UINT count )
1778 return GetWindowsDirectoryA( path, count );
1782 /***********************************************************************
1783 * GetSystemWindowsDirectoryW (KERNEL32.@) W2K, TS4.0SP4
1785 UINT WINAPI GetSystemWindowsDirectoryW( LPWSTR path, UINT count )
1787 return GetWindowsDirectoryW( path, count );
1791 /***********************************************************************
1792 * GetSystemDirectoryW (KERNEL32.@)
1794 * See comment for GetWindowsDirectoryA.
1796 UINT WINAPI GetSystemDirectoryW( LPWSTR path, UINT count )
1798 UINT len = strlenW( DIR_System ) + 1;
1799 if (path && count >= len)
1801 strcpyW( path, DIR_System );
1802 len--;
1804 return len;
1808 /***********************************************************************
1809 * GetSystemDirectoryA (KERNEL32.@)
1811 * See comment for GetWindowsDirectoryA.
1813 UINT WINAPI GetSystemDirectoryA( LPSTR path, UINT count )
1815 return copy_filename_WtoA( DIR_System, path, count );
1819 /***********************************************************************
1820 * GetSystemWow64DirectoryW (KERNEL32.@)
1822 * As seen on MSDN
1823 * - On Win32 we should return ERROR_CALL_NOT_IMPLEMENTED
1824 * - On Win64 we should return the SysWow64 (system64) directory
1826 UINT WINAPI GetSystemWow64DirectoryW( LPWSTR path, UINT count )
1828 UINT len;
1830 if (!DIR_SysWow64)
1832 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1833 return 0;
1835 len = strlenW( DIR_SysWow64 ) + 1;
1836 if (path && count >= len)
1838 strcpyW( path, DIR_SysWow64 );
1839 len--;
1841 return len;
1845 /***********************************************************************
1846 * GetSystemWow64DirectoryA (KERNEL32.@)
1848 * See comment for GetWindowsWow64DirectoryW.
1850 UINT WINAPI GetSystemWow64DirectoryA( LPSTR path, UINT count )
1852 if (!DIR_SysWow64)
1854 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
1855 return 0;
1857 return copy_filename_WtoA( DIR_SysWow64, path, count );
1861 /***********************************************************************
1862 * Wow64EnableWow64FsRedirection (KERNEL32.@)
1864 BOOLEAN WINAPI Wow64EnableWow64FsRedirection( BOOLEAN enable )
1866 NTSTATUS status = RtlWow64EnableFsRedirection( enable );
1867 if (status) SetLastError( RtlNtStatusToDosError(status) );
1868 return !status;
1872 /***********************************************************************
1873 * Wow64DisableWow64FsRedirection (KERNEL32.@)
1875 BOOL WINAPI Wow64DisableWow64FsRedirection( PVOID *old_value )
1877 NTSTATUS status = RtlWow64EnableFsRedirectionEx( TRUE, (ULONG *)old_value );
1878 if (status) SetLastError( RtlNtStatusToDosError(status) );
1879 return !status;
1883 /***********************************************************************
1884 * Wow64RevertWow64FsRedirection (KERNEL32.@)
1886 BOOL WINAPI Wow64RevertWow64FsRedirection( PVOID old_value )
1888 NTSTATUS status = RtlWow64EnableFsRedirection( !old_value );
1889 if (status) SetLastError( RtlNtStatusToDosError(status) );
1890 return !status;
1894 /***********************************************************************
1895 * NeedCurrentDirectoryForExePathW (KERNEL32.@)
1897 BOOL WINAPI NeedCurrentDirectoryForExePathW( LPCWSTR name )
1899 static const WCHAR env_name[] = {'N','o','D','e','f','a','u','l','t',
1900 'C','u','r','r','e','n','t',
1901 'D','i','r','e','c','t','o','r','y',
1902 'I','n','E','x','e','P','a','t','h',0};
1903 WCHAR env_val;
1905 /* MSDN mentions some 'registry location'. We do not use registry. */
1906 FIXME("(%s): partial stub\n", debugstr_w(name));
1908 if (strchrW(name, '\\'))
1909 return TRUE;
1911 /* Check the existence of the variable, not value */
1912 if (!GetEnvironmentVariableW( env_name, &env_val, 1 ))
1913 return TRUE;
1915 return FALSE;
1919 /***********************************************************************
1920 * NeedCurrentDirectoryForExePathA (KERNEL32.@)
1922 BOOL WINAPI NeedCurrentDirectoryForExePathA( LPCSTR name )
1924 WCHAR *nameW;
1926 if (!(nameW = FILE_name_AtoW( name, FALSE ))) return TRUE;
1927 return NeedCurrentDirectoryForExePathW( nameW );
1931 /***********************************************************************
1932 * wine_get_unix_file_name (KERNEL32.@) Not a Windows API
1934 * Return the full Unix file name for a given path.
1935 * Returned buffer must be freed by caller.
1937 char * CDECL wine_get_unix_file_name( LPCWSTR dosW )
1939 UNICODE_STRING nt_name;
1940 ANSI_STRING unix_name;
1941 NTSTATUS status;
1943 if (!RtlDosPathNameToNtPathName_U( dosW, &nt_name, NULL, NULL )) return NULL;
1944 status = wine_nt_to_unix_file_name( &nt_name, &unix_name, FILE_OPEN_IF, FALSE );
1945 RtlFreeUnicodeString( &nt_name );
1946 if (status && status != STATUS_NO_SUCH_FILE)
1948 SetLastError( RtlNtStatusToDosError( status ) );
1949 return NULL;
1951 return unix_name.Buffer;
1955 /***********************************************************************
1956 * wine_get_dos_file_name (KERNEL32.@) Not a Windows API
1958 * Return the full DOS file name for a given Unix path.
1959 * Returned buffer must be freed by caller.
1961 WCHAR * CDECL wine_get_dos_file_name( LPCSTR str )
1963 UNICODE_STRING nt_name;
1964 ANSI_STRING unix_name;
1965 NTSTATUS status;
1966 DWORD len;
1968 RtlInitAnsiString( &unix_name, str );
1969 status = wine_unix_to_nt_file_name( &unix_name, &nt_name );
1970 if (status)
1972 SetLastError( RtlNtStatusToDosError( status ) );
1973 return NULL;
1975 if (nt_name.Buffer[5] == ':')
1977 /* get rid of the \??\ prefix */
1978 /* FIXME: should implement RtlNtPathNameToDosPathName and use that instead */
1979 len = nt_name.Length - 4 * sizeof(WCHAR);
1980 memmove( nt_name.Buffer, nt_name.Buffer + 4, len );
1981 nt_name.Buffer[len / sizeof(WCHAR)] = 0;
1983 else
1984 nt_name.Buffer[1] = '\\';
1985 return nt_name.Buffer;
1988 /*************************************************************************
1989 * CreateSymbolicLinkW (KERNEL32.@)
1991 BOOLEAN WINAPI CreateSymbolicLinkW(LPCWSTR link, LPCWSTR target, DWORD flags)
1993 FIXME("(%s %s %d): stub\n", debugstr_w(link), debugstr_w(target), flags);
1994 return TRUE;
1997 /*************************************************************************
1998 * CreateSymbolicLinkA (KERNEL32.@)
2000 BOOLEAN WINAPI CreateSymbolicLinkA(LPCSTR link, LPCSTR target, DWORD flags)
2002 FIXME("(%s %s %d): stub\n", debugstr_a(link), debugstr_a(target), flags);
2003 return TRUE;
2006 /*************************************************************************
2007 * CreateHardLinkTransactedA (KERNEL32.@)
2009 BOOL WINAPI CreateHardLinkTransactedA(LPCSTR link, LPCSTR target, LPSECURITY_ATTRIBUTES sa, HANDLE transaction)
2011 FIXME("(%s %s %p %p): stub\n", debugstr_a(link), debugstr_a(target), sa, transaction);
2012 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2013 return FALSE;
2016 /*************************************************************************
2017 * CreateHardLinkTransactedW (KERNEL32.@)
2019 BOOL WINAPI CreateHardLinkTransactedW(LPCWSTR link, LPCWSTR target, LPSECURITY_ATTRIBUTES sa, HANDLE transaction)
2021 FIXME("(%s %s %p %p): stub\n", debugstr_w(link), debugstr_w(target), sa, transaction);
2022 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
2023 return FALSE;
2026 /*************************************************************************
2027 * CheckNameLegalDOS8Dot3A (KERNEL32.@)
2029 BOOL WINAPI CheckNameLegalDOS8Dot3A(const char *name, char *oemname, DWORD oemname_len,
2030 BOOL *contains_spaces, BOOL *is_legal)
2032 WCHAR *nameW;
2034 TRACE("(%s %p %u %p %p)\n", name, oemname,
2035 oemname_len, contains_spaces, is_legal);
2037 if (!name || !is_legal)
2038 return FALSE;
2040 if (!(nameW = FILE_name_AtoW( name, FALSE ))) return FALSE;
2042 return CheckNameLegalDOS8Dot3W( nameW, oemname, oemname_len, contains_spaces, is_legal );
2045 /*************************************************************************
2046 * CheckNameLegalDOS8Dot3W (KERNEL32.@)
2048 BOOL WINAPI CheckNameLegalDOS8Dot3W(const WCHAR *name, char *oemname, DWORD oemname_len,
2049 BOOL *contains_spaces_ret, BOOL *is_legal)
2051 OEM_STRING oem_str;
2052 UNICODE_STRING nameW;
2053 BOOLEAN contains_spaces;
2055 TRACE("(%s %p %u %p %p)\n", wine_dbgstr_w(name), oemname,
2056 oemname_len, contains_spaces_ret, is_legal);
2058 if (!name || !is_legal)
2059 return FALSE;
2061 RtlInitUnicodeString( &nameW, name );
2063 if (oemname) {
2064 oem_str.Length = oemname_len;
2065 oem_str.MaximumLength = oemname_len;
2066 oem_str.Buffer = oemname;
2069 *is_legal = RtlIsNameLegalDOS8Dot3( &nameW, oemname ? &oem_str : NULL, &contains_spaces );
2070 if (contains_spaces_ret) *contains_spaces_ret = contains_spaces;
2072 return TRUE;
2075 /*************************************************************************
2076 * SetSearchPathMode (KERNEL32.@)
2078 BOOL WINAPI SetSearchPathMode(DWORD flags)
2080 FIXME("(%x): stub\n", flags);
2081 SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
2082 return FALSE;