2 * File handling functions
4 * Copyright 1993 John Burton
5 * Copyright 1996 Alexandre Julliard
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Fix the CopyFileEx methods to implement the "extended" functionality.
23 * Right now, they simply call the CopyFile method.
27 #include "wine/port.h"
37 #ifdef HAVE_SYS_ERRNO_H
38 #include <sys/errno.h>
40 #include <sys/types.h>
42 #ifdef HAVE_SYS_MMAN_H
45 #ifdef HAVE_SYS_TIME_H
46 # include <sys/time.h>
48 #ifdef HAVE_SYS_POLL_H
49 # include <sys/poll.h>
59 #define NONAMELESSUNION
60 #define NONAMELESSSTRUCT
67 #include "wine/winbase16.h"
68 #include "wine/server.h"
73 #include "kernel_private.h"
76 #include "wine/unicode.h"
77 #include "wine/debug.h"
79 WINE_DEFAULT_DEBUG_CHANNEL(file);
81 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
82 #define MAP_ANON MAP_ANONYMOUS
85 #define IS_OPTION_TRUE(ch) ((ch) == 'y' || (ch) == 'Y' || (ch) == 't' || (ch) == 'T' || (ch) == '1')
87 HANDLE dos_handles[DOS_TABLE_SIZE];
90 /***********************************************************************
93 * Convert OF_* mode into flags for CreateFile.
95 void FILE_ConvertOFMode( INT mode, DWORD *access, DWORD *sharing )
99 case OF_READ: *access = GENERIC_READ; break;
100 case OF_WRITE: *access = GENERIC_WRITE; break;
101 case OF_READWRITE: *access = GENERIC_READ | GENERIC_WRITE; break;
102 default: *access = 0; break;
106 case OF_SHARE_EXCLUSIVE: *sharing = 0; break;
107 case OF_SHARE_DENY_WRITE: *sharing = FILE_SHARE_READ; break;
108 case OF_SHARE_DENY_READ: *sharing = FILE_SHARE_WRITE; break;
109 case OF_SHARE_DENY_NONE:
110 case OF_SHARE_COMPAT:
111 default: *sharing = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
116 /***********************************************************************
119 * locale-independent case conversion for file I/O
121 int FILE_strcasecmp( const char *str1, const char *str2 )
124 for ( ; ; str1++, str2++)
125 if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
130 /***********************************************************************
133 * locale-independent case conversion for file I/O
135 int FILE_strncasecmp( const char *str1, const char *str2, int len )
138 for ( ; len > 0; len--, str1++, str2++)
139 if ((ret = FILE_toupper(*str1) - FILE_toupper(*str2)) || !*str1) break;
144 /***********************************************************************
147 * Set the DOS error code from errno.
149 void FILE_SetDosError(void)
151 int save_errno = errno; /* errno gets overwritten by printf */
153 TRACE("errno = %d %s\n", errno, strerror(errno));
157 SetLastError( ERROR_SHARING_VIOLATION );
160 SetLastError( ERROR_INVALID_HANDLE );
163 SetLastError( ERROR_HANDLE_DISK_FULL );
168 SetLastError( ERROR_ACCESS_DENIED );
171 SetLastError( ERROR_LOCK_VIOLATION );
174 SetLastError( ERROR_FILE_NOT_FOUND );
177 SetLastError( ERROR_CANNOT_MAKE );
181 SetLastError( ERROR_NO_MORE_FILES );
184 SetLastError( ERROR_FILE_EXISTS );
188 SetLastError( ERROR_SEEK );
191 SetLastError( ERROR_DIR_NOT_EMPTY );
194 SetLastError( ERROR_BAD_FORMAT );
197 WARN("unknown file error: %s\n", strerror(save_errno) );
198 SetLastError( ERROR_GEN_FAILURE );
205 /******************************************************************
206 * OpenConsoleW (KERNEL32.@)
209 * Open a handle to the current process console.
210 * Returns INVALID_HANDLE_VALUE on failure.
212 HANDLE WINAPI OpenConsoleW(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa,
215 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
216 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
220 if (strcmpW(coninW, name) == 0)
222 else if (strcmpW(conoutW, name) == 0)
226 SetLastError(ERROR_INVALID_NAME);
227 return INVALID_HANDLE_VALUE;
229 if (creation != OPEN_EXISTING)
231 SetLastError(ERROR_INVALID_PARAMETER);
232 return INVALID_HANDLE_VALUE;
235 SERVER_START_REQ( open_console )
238 req->access = access;
239 req->share = FILE_SHARE_READ | FILE_SHARE_WRITE;
240 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
242 wine_server_call_err( req );
246 return ret ? console_handle_map(ret) : INVALID_HANDLE_VALUE;
249 /******************************************************************
250 * VerifyConsoleIoHandle (KERNEL32.@)
254 BOOL WINAPI VerifyConsoleIoHandle(HANDLE handle)
258 if (!is_console_handle(handle)) return FALSE;
259 SERVER_START_REQ(get_console_mode)
261 req->handle = console_handle_unmap(handle);
262 ret = !wine_server_call_err( req );
268 /******************************************************************
269 * DuplicateConsoleHandle (KERNEL32.@)
273 HANDLE WINAPI DuplicateConsoleHandle(HANDLE handle, DWORD access, BOOL inherit,
278 if (!is_console_handle(handle) ||
279 !DuplicateHandle(GetCurrentProcess(), console_handle_unmap(handle),
280 GetCurrentProcess(), &ret, access, inherit, options))
281 return INVALID_HANDLE_VALUE;
282 return console_handle_map(ret);
285 /******************************************************************
286 * CloseConsoleHandle (KERNEL32.@)
290 BOOL WINAPI CloseConsoleHandle(HANDLE handle)
292 if (!is_console_handle(handle))
294 SetLastError(ERROR_INVALID_PARAMETER);
297 return CloseHandle(console_handle_unmap(handle));
300 /******************************************************************
301 * GetConsoleInputWaitHandle (KERNEL32.@)
305 HANDLE WINAPI GetConsoleInputWaitHandle(void)
307 static HANDLE console_wait_event;
309 /* FIXME: this is not thread safe */
310 if (!console_wait_event)
312 SERVER_START_REQ(get_console_wait_event)
314 if (!wine_server_call_err( req )) console_wait_event = reply->handle;
318 return console_wait_event;
323 /* FIXME: those routines defined as pointers are needed, because this file is
324 * currently compiled into NTDLL whereas it belongs to kernel32.
325 * this shall go away once all the DLL separation process is done
327 typedef BOOL (WINAPI* pRW)(HANDLE, const void*, DWORD, DWORD*, void*);
329 static BOOL FILE_ReadConsole(HANDLE hCon, void* buf, DWORD nb, DWORD* nr, void* p)
331 static HANDLE hKernel /* = 0 */;
332 static pRW pReadConsole /* = 0 */;
334 if ((!hKernel && !(hKernel = LoadLibraryA("kernel32"))) ||
336 !(pReadConsole = GetProcAddress(hKernel, "ReadConsoleA"))))
341 return (pReadConsole)(hCon, buf, nb, nr, p);
344 static BOOL FILE_WriteConsole(HANDLE hCon, const void* buf, DWORD nb, DWORD* nr, void* p)
346 static HANDLE hKernel /* = 0 */;
347 static pRW pWriteConsole /* = 0 */;
349 if ((!hKernel && !(hKernel = LoadLibraryA("kernel32"))) ||
351 !(pWriteConsole = GetProcAddress(hKernel, "WriteConsoleA"))))
356 return (pWriteConsole)(hCon, buf, nb, nr, p);
360 /***********************************************************************
363 * Implementation of CreateFile. Takes a Unix path name.
364 * Returns 0 on failure.
366 HANDLE FILE_CreateFile( LPCSTR filename, DWORD access, DWORD sharing,
367 LPSECURITY_ATTRIBUTES sa, DWORD creation,
368 DWORD attributes, HANDLE template, BOOL fail_read_only,
376 SERVER_START_REQ( create_file )
378 req->access = access;
379 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
380 req->sharing = sharing;
381 req->create = creation;
382 req->attrs = attributes;
383 req->removable = (drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_CDROM);
384 wine_server_add_data( req, filename, strlen(filename) );
386 err = wine_server_call( req );
391 /* If write access failed, retry without GENERIC_WRITE */
393 if (!ret && !fail_read_only && (access & GENERIC_WRITE))
395 if ((err == STATUS_MEDIA_WRITE_PROTECTED) || (err == STATUS_ACCESS_DENIED))
397 TRACE("Write access failed for file '%s', trying without "
398 "write access\n", filename);
399 access &= ~GENERIC_WRITE;
406 /* In the case file creation was rejected due to CREATE_NEW flag
407 * was specified and file with that name already exists, correct
408 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
409 * Note: RtlNtStatusToDosError is not the subject to blame here.
411 if (err == STATUS_OBJECT_NAME_COLLISION)
412 SetLastError( ERROR_FILE_EXISTS );
414 SetLastError( RtlNtStatusToDosError(err) );
417 if (!ret) WARN("Unable to create file '%s' (GLE %ld)\n", filename, GetLastError());
423 /***********************************************************************
426 * Same as FILE_CreateFile but for a device
427 * Returns 0 on failure.
429 HANDLE FILE_CreateDevice( int client_id, DWORD access, LPSECURITY_ATTRIBUTES sa )
432 SERVER_START_REQ( create_device )
434 req->access = access;
435 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
438 wine_server_call_err( req );
445 static HANDLE FILE_OpenPipe(LPCWSTR name, DWORD access, LPSECURITY_ATTRIBUTES sa )
450 if (name && (len = strlenW(name)) > MAX_PATH)
452 SetLastError( ERROR_FILENAME_EXCED_RANGE );
455 SERVER_START_REQ( open_named_pipe )
457 req->access = access;
458 req->inherit = (sa && (sa->nLength>=sizeof(*sa)) && sa->bInheritHandle);
460 wine_server_add_data( req, name, len * sizeof(WCHAR) );
461 wine_server_call_err( req );
465 TRACE("Returned %p\n",ret);
469 /*************************************************************************
470 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
472 * Creates or opens an object, and returns a handle that can be used to
473 * access that object.
477 * filename [in] pointer to filename to be accessed
478 * access [in] access mode requested
479 * sharing [in] share mode
480 * sa [in] pointer to security attributes
481 * creation [in] how to create the file
482 * attributes [in] attributes for newly created file
483 * template [in] handle to file with extended attributes to copy
486 * Success: Open handle to specified file
487 * Failure: INVALID_HANDLE_VALUE
490 * Should call SetLastError() on failure.
494 * Doesn't support character devices, template files, or a
495 * lot of the 'attributes' flags yet.
497 HANDLE WINAPI CreateFileW( LPCWSTR filename, DWORD access, DWORD sharing,
498 LPSECURITY_ATTRIBUTES sa, DWORD creation,
499 DWORD attributes, HANDLE template )
501 DOS_FULL_NAME full_name;
503 static const WCHAR bkslashes_with_question_markW[] = {'\\','\\','?','\\',0};
504 static const WCHAR bkslashes_with_dotW[] = {'\\','\\','.','\\',0};
505 static const WCHAR bkslashesW[] = {'\\','\\',0};
506 static const WCHAR coninW[] = {'C','O','N','I','N','$',0};
507 static const WCHAR conoutW[] = {'C','O','N','O','U','T','$',0};
511 SetLastError( ERROR_INVALID_PARAMETER );
512 return INVALID_HANDLE_VALUE;
514 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename),
515 ((access & GENERIC_READ)==GENERIC_READ)?"GENERIC_READ ":"",
516 ((access & GENERIC_WRITE)==GENERIC_WRITE)?"GENERIC_WRITE ":"",
517 (!access)?"QUERY_ACCESS ":"",
518 ((sharing & FILE_SHARE_READ)==FILE_SHARE_READ)?"FILE_SHARE_READ ":"",
519 ((sharing & FILE_SHARE_WRITE)==FILE_SHARE_WRITE)?"FILE_SHARE_WRITE ":"",
520 ((sharing & FILE_SHARE_DELETE)==FILE_SHARE_DELETE)?"FILE_SHARE_DELETE ":"",
521 (creation ==CREATE_NEW)?"CREATE_NEW":
522 (creation ==CREATE_ALWAYS)?"CREATE_ALWAYS ":
523 (creation ==OPEN_EXISTING)?"OPEN_EXISTING ":
524 (creation ==OPEN_ALWAYS)?"OPEN_ALWAYS ":
525 (creation ==TRUNCATE_EXISTING)?"TRUNCATE_EXISTING ":"", attributes);
527 /* If the name starts with '\\?\', ignore the first 4 chars. */
528 if (!strncmpW(filename, bkslashes_with_question_markW, 4))
530 static const WCHAR uncW[] = {'U','N','C','\\',0};
532 if (!strncmpiW(filename, uncW, 4))
534 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename) );
535 SetLastError( ERROR_PATH_NOT_FOUND );
536 return INVALID_HANDLE_VALUE;
540 if (!strncmpW(filename, bkslashes_with_dotW, 4))
542 static const WCHAR pipeW[] = {'P','I','P','E','\\',0};
543 if(!strncmpiW(filename + 4, pipeW, 5))
545 TRACE("Opening a pipe: %s\n", debugstr_w(filename));
546 ret = FILE_OpenPipe( filename, access, sa );
549 else if (isalphaW(filename[4]) && filename[5] == ':' && filename[6] == '\0')
551 ret = FILE_CreateDevice( (toupperW(filename[4]) - 'A') | 0x20000, access, sa );
554 else if (!DOSFS_GetDevice( filename ))
556 ret = DEVICE_Open( filename+4, access, sa );
560 filename+=4; /* fall into DOSFS_Device case below */
563 /* If the name still starts with '\\', it's a UNC name. */
564 if (!strncmpW(filename, bkslashesW, 2))
566 ret = SMB_CreateFileW(filename, access, sharing, sa, creation, attributes, template );
570 /* If the name contains a DOS wild card (* or ?), do no create a file */
571 if(strchrW(filename, '*') || strchrW(filename, '?'))
573 SetLastError(ERROR_BAD_PATHNAME);
574 return INVALID_HANDLE_VALUE;
577 /* Open a console for CONIN$ or CONOUT$ */
578 if (!strcmpiW(filename, coninW) || !strcmpiW(filename, conoutW))
580 ret = OpenConsoleW(filename, access, sa, creation);
584 if (DOSFS_GetDevice( filename ))
586 TRACE("opening device %s\n", debugstr_w(filename) );
588 if (!(ret = DOSFS_OpenDevice( filename, access, attributes, sa )))
590 /* Do not silence this please. It is a critical error. -MM */
591 ERR("Couldn't open device %s!\n", debugstr_w(filename));
592 SetLastError( ERROR_FILE_NOT_FOUND );
597 /* check for filename, don't check for last entry if creating */
598 if (!DOSFS_GetFullName( filename,
599 (creation == OPEN_EXISTING) ||
600 (creation == TRUNCATE_EXISTING),
602 WARN("Unable to get full filename from %s (GLE %ld)\n",
603 debugstr_w(filename), GetLastError());
604 return INVALID_HANDLE_VALUE;
607 ret = FILE_CreateFile( full_name.long_name, access, sharing,
608 sa, creation, attributes, template,
609 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
610 GetDriveTypeW( full_name.short_name ) );
612 if (!ret) ret = INVALID_HANDLE_VALUE;
613 TRACE("returning %p\n", ret);
619 /*************************************************************************
620 * CreateFileA (KERNEL32.@)
622 HANDLE WINAPI CreateFileA( LPCSTR filename, DWORD access, DWORD sharing,
623 LPSECURITY_ATTRIBUTES sa, DWORD creation,
624 DWORD attributes, HANDLE template)
626 UNICODE_STRING filenameW;
627 HANDLE ret = INVALID_HANDLE_VALUE;
631 SetLastError( ERROR_INVALID_PARAMETER );
632 return INVALID_HANDLE_VALUE;
635 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
637 ret = CreateFileW(filenameW.Buffer, access, sharing, sa, creation,
638 attributes, template);
639 RtlFreeUnicodeString(&filenameW);
642 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
647 /***********************************************************************
650 * Fill a file information from a struct stat.
652 static void FILE_FillInfo( struct stat *st, BY_HANDLE_FILE_INFORMATION *info )
654 if (S_ISDIR(st->st_mode))
655 info->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
657 info->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
658 if (!(st->st_mode & S_IWUSR))
659 info->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
661 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftCreationTime );
662 RtlSecondsSince1970ToTime( st->st_mtime, (LARGE_INTEGER *)&info->ftLastWriteTime );
663 RtlSecondsSince1970ToTime( st->st_atime, (LARGE_INTEGER *)&info->ftLastAccessTime );
665 info->dwVolumeSerialNumber = 0; /* FIXME */
666 info->nFileSizeHigh = 0;
667 info->nFileSizeLow = 0;
668 if (!S_ISDIR(st->st_mode)) {
669 info->nFileSizeHigh = st->st_size >> 32;
670 info->nFileSizeLow = st->st_size & 0xffffffff;
672 info->nNumberOfLinks = st->st_nlink;
673 info->nFileIndexHigh = 0;
674 info->nFileIndexLow = st->st_ino;
678 /***********************************************************************
679 * get_show_dot_files_option
681 static BOOL get_show_dot_files_option(void)
683 static const WCHAR WineW[] = {'M','a','c','h','i','n','e','\\',
684 'S','o','f','t','w','a','r','e','\\',
685 'W','i','n','e','\\','W','i','n','e','\\',
686 'C','o','n','f','i','g','\\','W','i','n','e',0};
687 static const WCHAR ShowDotFilesW[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
692 OBJECT_ATTRIBUTES attr;
693 UNICODE_STRING nameW;
696 attr.Length = sizeof(attr);
697 attr.RootDirectory = 0;
698 attr.ObjectName = &nameW;
700 attr.SecurityDescriptor = NULL;
701 attr.SecurityQualityOfService = NULL;
702 RtlInitUnicodeString( &nameW, WineW );
704 if (!NtOpenKey( &hkey, KEY_ALL_ACCESS, &attr ))
706 RtlInitUnicodeString( &nameW, ShowDotFilesW );
707 if (!NtQueryValueKey( hkey, &nameW, KeyValuePartialInformation, tmp, sizeof(tmp), &dummy ))
709 WCHAR *str = (WCHAR *)((KEY_VALUE_PARTIAL_INFORMATION *)tmp)->Data;
710 ret = IS_OPTION_TRUE( str[0] );
718 /***********************************************************************
721 * Stat a Unix path name. Return TRUE if OK.
723 BOOL FILE_Stat( LPCSTR unixName, BY_HANDLE_FILE_INFORMATION *info, BOOL *is_symlink_ptr )
729 if (lstat( unixName, &st ) == -1)
734 is_symlink = S_ISLNK(st.st_mode);
737 /* do a "real" stat to find out
738 about the type of the symlink destination */
739 if (stat( unixName, &st ) == -1)
746 /* fill in the information we gathered so far */
747 FILE_FillInfo( &st, info );
749 /* and now see if this is a hidden file, based on the name */
750 p = strrchr( unixName, '/');
751 p = p ? p + 1 : unixName;
752 if (*p == '.' && *(p+1) && (*(p+1) != '.' || *(p+2)))
754 static int show_dot_files = -1;
755 if (show_dot_files == -1)
756 show_dot_files = get_show_dot_files_option();
758 info->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
760 if (is_symlink_ptr) *is_symlink_ptr = is_symlink;
765 /***********************************************************************
766 * GetFileInformationByHandle (KERNEL32.@)
768 DWORD WINAPI GetFileInformationByHandle( HANDLE hFile,
769 BY_HANDLE_FILE_INFORMATION *info )
774 TRACE("%p\n", hFile);
776 SERVER_START_REQ( get_file_info )
779 if ((ret = !wine_server_call_err( req )))
781 /* FIXME: which file types are supported ?
782 * Serial ports (FILE_TYPE_CHAR) are not,
783 * and MSDN also says that pipes are not supported.
784 * FILE_TYPE_REMOTE seems to be supported according to
785 * MSDN q234741.txt */
786 if ((reply->type == FILE_TYPE_DISK) || (reply->type == FILE_TYPE_REMOTE))
788 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftCreationTime );
789 RtlSecondsSince1970ToTime( reply->write_time, (LARGE_INTEGER *)&info->ftLastWriteTime );
790 RtlSecondsSince1970ToTime( reply->access_time, (LARGE_INTEGER *)&info->ftLastAccessTime );
791 info->dwFileAttributes = reply->attr;
792 info->dwVolumeSerialNumber = reply->serial;
793 info->nFileSizeHigh = reply->size_high;
794 info->nFileSizeLow = reply->size_low;
795 info->nNumberOfLinks = reply->links;
796 info->nFileIndexHigh = reply->index_high;
797 info->nFileIndexLow = reply->index_low;
801 SetLastError(ERROR_NOT_SUPPORTED);
811 /**************************************************************************
812 * GetFileAttributesW (KERNEL32.@)
814 DWORD WINAPI GetFileAttributesW( LPCWSTR name )
816 DOS_FULL_NAME full_name;
817 BY_HANDLE_FILE_INFORMATION info;
821 SetLastError( ERROR_INVALID_PARAMETER );
822 return INVALID_FILE_ATTRIBUTES;
824 if (!DOSFS_GetFullName( name, TRUE, &full_name) )
825 return INVALID_FILE_ATTRIBUTES;
826 if (!FILE_Stat( full_name.long_name, &info, NULL ))
827 return INVALID_FILE_ATTRIBUTES;
828 return info.dwFileAttributes;
832 /**************************************************************************
833 * GetFileAttributesA (KERNEL32.@)
835 DWORD WINAPI GetFileAttributesA( LPCSTR name )
837 UNICODE_STRING nameW;
838 DWORD ret = INVALID_FILE_ATTRIBUTES;
842 SetLastError( ERROR_INVALID_PARAMETER );
843 return INVALID_FILE_ATTRIBUTES;
846 if (RtlCreateUnicodeStringFromAsciiz(&nameW, name))
848 ret = GetFileAttributesW(nameW.Buffer);
849 RtlFreeUnicodeString(&nameW);
852 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
857 /**************************************************************************
858 * SetFileAttributesW (KERNEL32.@)
860 BOOL WINAPI SetFileAttributesW(LPCWSTR lpFileName, DWORD attributes)
863 DOS_FULL_NAME full_name;
867 SetLastError( ERROR_INVALID_PARAMETER );
871 TRACE("(%s,%lx)\n", debugstr_w(lpFileName), attributes);
873 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name ))
876 if(stat(full_name.long_name,&buf)==-1)
881 if (attributes & FILE_ATTRIBUTE_READONLY)
883 if(S_ISDIR(buf.st_mode))
885 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
887 buf.st_mode &= ~0222; /* octal!, clear write permission bits */
888 attributes &= ~FILE_ATTRIBUTE_READONLY;
892 /* add write permission */
893 buf.st_mode |= (0600 | ((buf.st_mode & 044) >> 1)) & (~FILE_umask);
895 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
897 if (!S_ISDIR(buf.st_mode))
898 FIXME("SetFileAttributes expected the file %s to be a directory\n",
899 debugstr_w(lpFileName));
900 attributes &= ~FILE_ATTRIBUTE_DIRECTORY;
902 attributes &= ~(FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
904 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName), attributes);
905 if (-1==chmod(full_name.long_name,buf.st_mode))
907 if (GetDriveTypeW(lpFileName) == DRIVE_CDROM)
909 SetLastError( ERROR_ACCESS_DENIED );
914 * FIXME: We don't return FALSE here because of differences between
915 * Linux and Windows privileges. Under Linux only the owner of
916 * the file is allowed to change file attributes. Under Windows,
917 * applications expect that if you can write to a file, you can also
918 * change its attributes (see GENERIC_WRITE). We could try to be
919 * clever here but that would break multi-user installations where
920 * users share read-only DLLs. This is because some installers like
921 * to change attributes of already installed DLLs.
923 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
924 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name.long_name);
930 /**************************************************************************
931 * SetFileAttributesA (KERNEL32.@)
933 BOOL WINAPI SetFileAttributesA(LPCSTR lpFileName, DWORD attributes)
935 UNICODE_STRING filenameW;
940 SetLastError( ERROR_INVALID_PARAMETER );
944 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
946 ret = SetFileAttributesW(filenameW.Buffer, attributes);
947 RtlFreeUnicodeString(&filenameW);
950 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
955 /******************************************************************************
956 * GetCompressedFileSizeA [KERNEL32.@]
958 DWORD WINAPI GetCompressedFileSizeA(
960 LPDWORD lpFileSizeHigh)
962 UNICODE_STRING filenameW;
965 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, lpFileName))
967 ret = GetCompressedFileSizeW(filenameW.Buffer, lpFileSizeHigh);
968 RtlFreeUnicodeString(&filenameW);
972 ret = INVALID_FILE_SIZE;
973 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
979 /******************************************************************************
980 * GetCompressedFileSizeW [KERNEL32.@]
983 * Success: Low-order doubleword of number of bytes
984 * Failure: INVALID_FILE_SIZE
986 DWORD WINAPI GetCompressedFileSizeW(
987 LPCWSTR lpFileName, /* [in] Pointer to name of file */
988 LPDWORD lpFileSizeHigh) /* [out] Receives high-order doubleword of size */
990 DOS_FULL_NAME full_name;
994 TRACE("(%s,%p)\n",debugstr_w(lpFileName),lpFileSizeHigh);
996 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return INVALID_FILE_SIZE;
997 if (stat(full_name.long_name, &st) != 0)
1000 return INVALID_FILE_SIZE;
1002 #if HAVE_STRUCT_STAT_ST_BLOCKS
1003 /* blocks are 512 bytes long */
1004 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_blocks >> 23);
1005 low = (DWORD)(st.st_blocks << 9);
1007 if (lpFileSizeHigh) *lpFileSizeHigh = (st.st_size >> 32);
1008 low = (DWORD)st.st_size;
1014 /***********************************************************************
1015 * GetFileTime (KERNEL32.@)
1017 BOOL WINAPI GetFileTime( HANDLE hFile, FILETIME *lpCreationTime,
1018 FILETIME *lpLastAccessTime,
1019 FILETIME *lpLastWriteTime )
1021 BY_HANDLE_FILE_INFORMATION info;
1022 if (!GetFileInformationByHandle( hFile, &info )) return FALSE;
1023 if (lpCreationTime) *lpCreationTime = info.ftCreationTime;
1024 if (lpLastAccessTime) *lpLastAccessTime = info.ftLastAccessTime;
1025 if (lpLastWriteTime) *lpLastWriteTime = info.ftLastWriteTime;
1030 /***********************************************************************
1031 * GetTempFileNameA (KERNEL32.@)
1033 UINT WINAPI GetTempFileNameA( LPCSTR path, LPCSTR prefix, UINT unique,
1036 UNICODE_STRING pathW, prefixW;
1037 WCHAR bufferW[MAX_PATH];
1040 if ( !path || !prefix || !buffer )
1042 SetLastError( ERROR_INVALID_PARAMETER );
1046 RtlCreateUnicodeStringFromAsciiz(&pathW, path);
1047 RtlCreateUnicodeStringFromAsciiz(&prefixW, prefix);
1049 ret = GetTempFileNameW(pathW.Buffer, prefixW.Buffer, unique, bufferW);
1051 WideCharToMultiByte(CP_ACP, 0, bufferW, -1, buffer, MAX_PATH, NULL, NULL);
1053 RtlFreeUnicodeString(&pathW);
1054 RtlFreeUnicodeString(&prefixW);
1058 /***********************************************************************
1059 * GetTempFileNameW (KERNEL32.@)
1061 UINT WINAPI GetTempFileNameW( LPCWSTR path, LPCWSTR prefix, UINT unique,
1064 static const WCHAR formatW[] = {'%','0','4','x','.','t','m','p',0};
1066 DOS_FULL_NAME full_name;
1070 if ( !path || !prefix || !buffer )
1072 SetLastError( ERROR_INVALID_PARAMETER );
1076 strcpyW( buffer, path );
1077 p = buffer + strlenW(buffer);
1079 /* add a \, if there isn't one and path is more than just the drive letter ... */
1080 if ( !((strlenW(buffer) == 2) && (buffer[1] == ':'))
1081 && ((p == buffer) || (p[-1] != '\\'))) *p++ = '\\';
1083 for (i = 3; (i > 0) && (*prefix); i--) *p++ = *prefix++;
1087 if (unique) sprintfW( p, formatW, unique );
1090 /* get a "random" unique number and try to create the file */
1092 UINT num = GetTickCount() & 0xffff;
1098 sprintfW( p, formatW, unique );
1099 handle = CreateFileW( buffer, GENERIC_WRITE, 0, NULL,
1100 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
1101 if (handle != INVALID_HANDLE_VALUE)
1102 { /* We created it */
1103 TRACE("created %s\n", debugstr_w(buffer) );
1104 CloseHandle( handle );
1107 if (GetLastError() != ERROR_FILE_EXISTS &&
1108 GetLastError() != ERROR_SHARING_VIOLATION)
1109 break; /* No need to go on */
1110 if (!(++unique & 0xffff)) unique = 1;
1111 } while (unique != num);
1114 /* Get the full path name */
1116 if (DOSFS_GetFullName( buffer, FALSE, &full_name ))
1119 /* Check if we have write access in the directory */
1120 if ((slash = strrchr( full_name.long_name, '/' ))) *slash = '\0';
1121 if (access( full_name.long_name, W_OK ) == -1)
1122 WARN("returns %s, which doesn't seem to be writeable.\n",
1123 debugstr_w(buffer) );
1125 TRACE("returning %s\n", debugstr_w(buffer) );
1130 /***********************************************************************
1133 * Implementation of OpenFile16() and OpenFile32().
1135 static HFILE FILE_DoOpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode, BOOL win32 )
1140 WORD filedatetime[2];
1141 DOS_FULL_NAME full_name;
1142 DWORD access, sharing;
1144 WCHAR buffer[MAX_PATH];
1147 if (!ofs) return HFILE_ERROR;
1149 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name,
1150 ((mode & 0x3 )==OF_READ)?"OF_READ":
1151 ((mode & 0x3 )==OF_WRITE)?"OF_WRITE":
1152 ((mode & 0x3 )==OF_READWRITE)?"OF_READWRITE":"unknown",
1153 ((mode & 0x70 )==OF_SHARE_COMPAT)?"OF_SHARE_COMPAT":
1154 ((mode & 0x70 )==OF_SHARE_DENY_NONE)?"OF_SHARE_DENY_NONE":
1155 ((mode & 0x70 )==OF_SHARE_DENY_READ)?"OF_SHARE_DENY_READ":
1156 ((mode & 0x70 )==OF_SHARE_DENY_WRITE)?"OF_SHARE_DENY_WRITE":
1157 ((mode & 0x70 )==OF_SHARE_EXCLUSIVE)?"OF_SHARE_EXCLUSIVE":"unknown",
1158 ((mode & OF_PARSE )==OF_PARSE)?"OF_PARSE ":"",
1159 ((mode & OF_DELETE )==OF_DELETE)?"OF_DELETE ":"",
1160 ((mode & OF_VERIFY )==OF_VERIFY)?"OF_VERIFY ":"",
1161 ((mode & OF_SEARCH )==OF_SEARCH)?"OF_SEARCH ":"",
1162 ((mode & OF_CANCEL )==OF_CANCEL)?"OF_CANCEL ":"",
1163 ((mode & OF_CREATE )==OF_CREATE)?"OF_CREATE ":"",
1164 ((mode & OF_PROMPT )==OF_PROMPT)?"OF_PROMPT ":"",
1165 ((mode & OF_EXIST )==OF_EXIST)?"OF_EXIST ":"",
1166 ((mode & OF_REOPEN )==OF_REOPEN)?"OF_REOPEN ":""
1170 ofs->cBytes = sizeof(OFSTRUCT);
1172 if (mode & OF_REOPEN) name = ofs->szPathName;
1175 ERR("called with `name' set to NULL ! Please debug.\n");
1179 TRACE("%s %04x\n", name, mode );
1181 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1182 Are there any cases where getting the path here is wrong?
1183 Uwe Bonnes 1997 Apr 2 */
1184 if (!GetFullPathNameA( name, sizeof(ofs->szPathName),
1185 ofs->szPathName, NULL )) goto error;
1186 FILE_ConvertOFMode( mode, &access, &sharing );
1188 /* OF_PARSE simply fills the structure */
1190 if (mode & OF_PARSE)
1192 ofs->fFixedDisk = (GetDriveType16( ofs->szPathName[0]-'A' )
1193 != DRIVE_REMOVABLE);
1194 TRACE("(%s): OF_PARSE, res = '%s'\n",
1195 name, ofs->szPathName );
1199 /* OF_CREATE is completely different from all other options, so
1202 if (mode & OF_CREATE)
1204 if ((handle = CreateFileA( name, GENERIC_READ | GENERIC_WRITE,
1205 sharing, NULL, CREATE_ALWAYS,
1206 FILE_ATTRIBUTE_NORMAL, 0 ))== INVALID_HANDLE_VALUE)
1211 MultiByteToWideChar(CP_ACP, 0, name, -1, buffer, MAX_PATH);
1214 /* If OF_SEARCH is set, ignore the given path */
1216 if ((mode & OF_SEARCH) && !(mode & OF_REOPEN))
1218 /* First try the file name as is */
1219 if (DOSFS_GetFullName( nameW, TRUE, &full_name )) goto found;
1220 /* Now remove the path */
1221 if (nameW[0] && (nameW[1] == ':')) nameW += 2;
1222 if ((p = strrchrW( nameW, '\\' ))) nameW = p + 1;
1223 if ((p = strrchrW( nameW, '/' ))) nameW = p + 1;
1224 if (!nameW[0]) goto not_found;
1227 /* Now look for the file */
1229 if (!DIR_SearchPath( NULL, nameW, NULL, &full_name, win32 )) goto not_found;
1232 TRACE("found %s = %s\n",
1233 full_name.long_name, debugstr_w(full_name.short_name) );
1234 WideCharToMultiByte(CP_ACP, 0, full_name.short_name, -1,
1235 ofs->szPathName, sizeof(ofs->szPathName), NULL, NULL);
1237 if (mode & OF_DELETE)
1239 handle = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1240 NULL, OPEN_EXISTING, 0, 0, TRUE,
1241 GetDriveTypeW( full_name.short_name ) );
1242 if (!handle) goto error;
1243 CloseHandle( handle );
1244 if (unlink( full_name.long_name ) == -1) goto not_found;
1245 TRACE("(%s): OF_DELETE return = OK\n", name);
1249 handle = FILE_CreateFile( full_name.long_name, access, sharing,
1250 NULL, OPEN_EXISTING, 0, 0,
1251 DRIVE_GetFlags(full_name.drive) & DRIVE_FAIL_READ_ONLY,
1252 GetDriveTypeW( full_name.short_name ) );
1253 if (!handle) goto not_found;
1255 GetFileTime( handle, NULL, NULL, &filetime );
1256 FileTimeToDosDateTime( &filetime, &filedatetime[0], &filedatetime[1] );
1257 if ((mode & OF_VERIFY) && (mode & OF_REOPEN))
1259 if (ofs->Reserved1 != filedatetime[0] || ofs->Reserved2 != filedatetime[1] )
1261 CloseHandle( handle );
1262 WARN("(%s): OF_VERIFY failed\n", name );
1263 /* FIXME: what error here? */
1264 SetLastError( ERROR_FILE_NOT_FOUND );
1268 ofs->Reserved1 = filedatetime[0];
1269 ofs->Reserved2 = filedatetime[1];
1271 success: /* We get here if the open was successful */
1272 TRACE("(%s): OK, return = %p\n", name, handle );
1275 hFileRet = (HFILE)handle;
1276 if (mode & OF_EXIST) /* Return the handle, but close it first */
1277 CloseHandle( handle );
1281 hFileRet = Win32HandleToDosFileHandle( handle );
1282 if (hFileRet == HFILE_ERROR16) goto error;
1283 if (mode & OF_EXIST) /* Return the handle, but close it first */
1284 _lclose16( hFileRet );
1288 not_found: /* We get here if the file does not exist */
1289 WARN("'%s' not found or sharing violation\n", name );
1290 SetLastError( ERROR_FILE_NOT_FOUND );
1293 error: /* We get here if there was an error opening the file */
1294 ofs->nErrCode = GetLastError();
1295 WARN("(%s): return = HFILE_ERROR error= %d\n",
1296 name,ofs->nErrCode );
1301 /***********************************************************************
1302 * OpenFile (KERNEL.74)
1303 * OpenFileEx (KERNEL.360)
1305 HFILE16 WINAPI OpenFile16( LPCSTR name, OFSTRUCT *ofs, UINT16 mode )
1307 return FILE_DoOpenFile( name, ofs, mode, FALSE );
1311 /***********************************************************************
1312 * OpenFile (KERNEL32.@)
1314 HFILE WINAPI OpenFile( LPCSTR name, OFSTRUCT *ofs, UINT mode )
1316 return FILE_DoOpenFile( name, ofs, mode, TRUE );
1320 /***********************************************************************
1321 * FILE_InitProcessDosHandles
1323 * Allocates the default DOS handles for a process. Called either by
1324 * Win32HandleToDosFileHandle below or by the DOSVM stuff.
1326 static void FILE_InitProcessDosHandles( void )
1328 HANDLE cp = GetCurrentProcess();
1329 DuplicateHandle(cp, GetStdHandle(STD_INPUT_HANDLE), cp, &dos_handles[0],
1330 0, TRUE, DUPLICATE_SAME_ACCESS);
1331 DuplicateHandle(cp, GetStdHandle(STD_OUTPUT_HANDLE), cp, &dos_handles[1],
1332 0, TRUE, DUPLICATE_SAME_ACCESS);
1333 DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[2],
1334 0, TRUE, DUPLICATE_SAME_ACCESS);
1335 DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[3],
1336 0, TRUE, DUPLICATE_SAME_ACCESS);
1337 DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[4],
1338 0, TRUE, DUPLICATE_SAME_ACCESS);
1341 /***********************************************************************
1342 * Win32HandleToDosFileHandle (KERNEL32.21)
1344 * Allocate a DOS handle for a Win32 handle. The Win32 handle is no
1345 * longer valid after this function (even on failure).
1347 * Note: this is not exactly right, since on Win95 the Win32 handles
1348 * are on top of DOS handles and we do it the other way
1349 * around. Should be good enough though.
1351 HFILE WINAPI Win32HandleToDosFileHandle( HANDLE handle )
1355 if (!handle || (handle == INVALID_HANDLE_VALUE))
1358 for (i = 5; i < DOS_TABLE_SIZE; i++)
1359 if (!dos_handles[i])
1361 dos_handles[i] = handle;
1362 TRACE("Got %d for h32 %p\n", i, handle );
1365 CloseHandle( handle );
1366 SetLastError( ERROR_TOO_MANY_OPEN_FILES );
1371 /***********************************************************************
1372 * DosFileHandleToWin32Handle (KERNEL32.20)
1374 * Return the Win32 handle for a DOS handle.
1376 * Note: this is not exactly right, since on Win95 the Win32 handles
1377 * are on top of DOS handles and we do it the other way
1378 * around. Should be good enough though.
1380 HANDLE WINAPI DosFileHandleToWin32Handle( HFILE handle )
1382 HFILE16 hfile = (HFILE16)handle;
1383 if (hfile < 5 && !dos_handles[hfile]) FILE_InitProcessDosHandles();
1384 if ((hfile >= DOS_TABLE_SIZE) || !dos_handles[hfile])
1386 SetLastError( ERROR_INVALID_HANDLE );
1387 return INVALID_HANDLE_VALUE;
1389 return dos_handles[hfile];
1393 /***********************************************************************
1394 * DisposeLZ32Handle (KERNEL32.22)
1396 * Note: this is not entirely correct, we should only close the
1397 * 32-bit handle and not the 16-bit one, but we cannot do
1398 * this because of the way our DOS handles are implemented.
1399 * It shouldn't break anything though.
1401 void WINAPI DisposeLZ32Handle( HANDLE handle )
1405 if (!handle || (handle == INVALID_HANDLE_VALUE)) return;
1407 for (i = 5; i < DOS_TABLE_SIZE; i++)
1408 if (dos_handles[i] == handle)
1411 CloseHandle( handle );
1417 /***********************************************************************
1420 * dup2() function for DOS handles.
1422 HFILE16 FILE_Dup2( HFILE16 hFile1, HFILE16 hFile2 )
1426 if (hFile1 < 5 && !dos_handles[hFile1]) FILE_InitProcessDosHandles();
1428 if ((hFile1 >= DOS_TABLE_SIZE) || (hFile2 >= DOS_TABLE_SIZE) || !dos_handles[hFile1])
1430 SetLastError( ERROR_INVALID_HANDLE );
1431 return HFILE_ERROR16;
1433 if (!DuplicateHandle( GetCurrentProcess(), dos_handles[hFile1],
1434 GetCurrentProcess(), &new_handle,
1435 0, FALSE, DUPLICATE_SAME_ACCESS ))
1436 return HFILE_ERROR16;
1437 if (dos_handles[hFile2]) CloseHandle( dos_handles[hFile2] );
1438 dos_handles[hFile2] = new_handle;
1443 /***********************************************************************
1444 * _lclose (KERNEL.81)
1446 HFILE16 WINAPI _lclose16( HFILE16 hFile )
1448 if ((hFile >= DOS_TABLE_SIZE) || !dos_handles[hFile])
1450 SetLastError( ERROR_INVALID_HANDLE );
1451 return HFILE_ERROR16;
1453 TRACE("%d (handle32=%p)\n", hFile, dos_handles[hFile] );
1454 CloseHandle( dos_handles[hFile] );
1455 dos_handles[hFile] = 0;
1460 /******************************************************************
1461 * FILE_ReadWriteApc (internal)
1465 static void WINAPI FILE_ReadWriteApc(void* apc_user, PIO_STATUS_BLOCK io_status, ULONG len)
1467 LPOVERLAPPED_COMPLETION_ROUTINE cr = (LPOVERLAPPED_COMPLETION_ROUTINE)apc_user;
1469 cr(RtlNtStatusToDosError(io_status->u.Status), len, (LPOVERLAPPED)io_status);
1472 /***********************************************************************
1473 * ReadFileEx (KERNEL32.@)
1475 BOOL WINAPI ReadFileEx(HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1476 LPOVERLAPPED overlapped,
1477 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1479 LARGE_INTEGER offset;
1481 PIO_STATUS_BLOCK io_status;
1485 SetLastError(ERROR_INVALID_PARAMETER);
1489 offset.s.LowPart = overlapped->Offset;
1490 offset.s.HighPart = overlapped->OffsetHigh;
1491 io_status = (PIO_STATUS_BLOCK)overlapped;
1492 io_status->u.Status = STATUS_PENDING;
1494 status = NtReadFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1495 io_status, buffer, bytesToRead, &offset, NULL);
1499 SetLastError( RtlNtStatusToDosError(status) );
1505 /***********************************************************************
1506 * ReadFile (KERNEL32.@)
1508 BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead,
1509 LPDWORD bytesRead, LPOVERLAPPED overlapped )
1511 LARGE_INTEGER offset;
1512 PLARGE_INTEGER poffset = NULL;
1513 IO_STATUS_BLOCK iosb;
1514 PIO_STATUS_BLOCK io_status = &iosb;
1518 TRACE("%p %p %ld %p %p\n", hFile, buffer, bytesToRead,
1519 bytesRead, overlapped );
1521 if (bytesRead) *bytesRead = 0; /* Do this before anything else */
1522 if (!bytesToRead) return TRUE;
1524 if (IsBadReadPtr(buffer, bytesToRead))
1526 SetLastError(ERROR_WRITE_FAULT); /* FIXME */
1529 if (is_console_handle(hFile))
1530 return FILE_ReadConsole(hFile, buffer, bytesToRead, bytesRead, NULL);
1532 if (overlapped != NULL)
1534 offset.s.LowPart = overlapped->Offset;
1535 offset.s.HighPart = overlapped->OffsetHigh;
1537 hEvent = overlapped->hEvent;
1538 io_status = (PIO_STATUS_BLOCK)overlapped;
1540 io_status->u.Status = STATUS_PENDING;
1541 io_status->Information = 0;
1543 status = NtReadFile(hFile, hEvent, NULL, NULL, io_status, buffer, bytesToRead, poffset, NULL);
1545 if (status != STATUS_PENDING && bytesRead)
1546 *bytesRead = io_status->Information;
1548 if (status && status != STATUS_END_OF_FILE)
1550 SetLastError( RtlNtStatusToDosError(status) );
1557 /***********************************************************************
1558 * WriteFileEx (KERNEL32.@)
1560 BOOL WINAPI WriteFileEx(HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1561 LPOVERLAPPED overlapped,
1562 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
1564 LARGE_INTEGER offset;
1566 PIO_STATUS_BLOCK io_status;
1568 TRACE("%p %p %ld %p %p\n",
1569 hFile, buffer, bytesToWrite, overlapped, lpCompletionRoutine);
1571 if (overlapped == NULL)
1573 SetLastError(ERROR_INVALID_PARAMETER);
1576 offset.s.LowPart = overlapped->Offset;
1577 offset.s.HighPart = overlapped->OffsetHigh;
1579 io_status = (PIO_STATUS_BLOCK)overlapped;
1580 io_status->u.Status = STATUS_PENDING;
1582 status = NtWriteFile(hFile, NULL, FILE_ReadWriteApc, lpCompletionRoutine,
1583 io_status, buffer, bytesToWrite, &offset, NULL);
1585 if (status) SetLastError( RtlNtStatusToDosError(status) );
1589 /***********************************************************************
1590 * WriteFile (KERNEL32.@)
1592 BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite,
1593 LPDWORD bytesWritten, LPOVERLAPPED overlapped )
1595 HANDLE hEvent = NULL;
1596 LARGE_INTEGER offset;
1597 PLARGE_INTEGER poffset = NULL;
1599 IO_STATUS_BLOCK iosb;
1600 PIO_STATUS_BLOCK piosb = &iosb;
1602 TRACE("%p %p %ld %p %p\n",
1603 hFile, buffer, bytesToWrite, bytesWritten, overlapped );
1605 if (is_console_handle(hFile))
1606 return FILE_WriteConsole(hFile, buffer, bytesToWrite, bytesWritten, NULL);
1608 if (IsBadReadPtr(buffer, bytesToWrite))
1610 SetLastError(ERROR_READ_FAULT); /* FIXME */
1616 offset.s.LowPart = overlapped->Offset;
1617 offset.s.HighPart = overlapped->OffsetHigh;
1619 hEvent = overlapped->hEvent;
1620 piosb = (PIO_STATUS_BLOCK)overlapped;
1622 piosb->u.Status = STATUS_PENDING;
1623 piosb->Information = 0;
1625 status = NtWriteFile(hFile, hEvent, NULL, NULL, piosb,
1626 buffer, bytesToWrite, poffset, NULL);
1629 SetLastError( RtlNtStatusToDosError(status) );
1632 if (bytesWritten) *bytesWritten = piosb->Information;
1638 /***********************************************************************
1639 * SetFilePointer (KERNEL32.@)
1641 DWORD WINAPI SetFilePointer( HANDLE hFile, LONG distance, LONG *highword,
1644 DWORD ret = INVALID_SET_FILE_POINTER;
1646 TRACE("handle %p offset %ld high %ld origin %ld\n",
1647 hFile, distance, highword?*highword:0, method );
1649 SERVER_START_REQ( set_file_pointer )
1651 req->handle = hFile;
1652 req->low = distance;
1653 req->high = highword ? *highword : (distance >= 0) ? 0 : -1;
1654 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1655 req->whence = method;
1657 if (!wine_server_call_err( req ))
1659 ret = reply->new_low;
1660 if (highword) *highword = reply->new_high;
1668 /*************************************************************************
1669 * SetHandleCount (KERNEL32.@)
1671 UINT WINAPI SetHandleCount( UINT count )
1673 return min( 256, count );
1677 /**************************************************************************
1678 * SetEndOfFile (KERNEL32.@)
1680 BOOL WINAPI SetEndOfFile( HANDLE hFile )
1683 SERVER_START_REQ( truncate_file )
1685 req->handle = hFile;
1686 ret = !wine_server_call_err( req );
1693 /***********************************************************************
1694 * DeleteFileW (KERNEL32.@)
1696 BOOL WINAPI DeleteFileW( LPCWSTR path )
1698 DOS_FULL_NAME full_name;
1701 TRACE("%s\n", debugstr_w(path) );
1702 if (!path || !*path)
1704 SetLastError(ERROR_PATH_NOT_FOUND);
1707 if (DOSFS_GetDevice( path ))
1709 WARN("cannot remove DOS device %s!\n", debugstr_w(path));
1710 SetLastError( ERROR_FILE_NOT_FOUND );
1714 if (!DOSFS_GetFullName( path, TRUE, &full_name )) return FALSE;
1716 /* check if we are allowed to delete the source */
1717 hFile = FILE_CreateFile( full_name.long_name, GENERIC_READ|GENERIC_WRITE, 0,
1718 NULL, OPEN_EXISTING, 0, 0, TRUE,
1719 GetDriveTypeW( full_name.short_name ) );
1720 if (!hFile) return FALSE;
1722 if (unlink( full_name.long_name ) == -1)
1733 /***********************************************************************
1734 * DeleteFileA (KERNEL32.@)
1736 BOOL WINAPI DeleteFileA( LPCSTR path )
1738 UNICODE_STRING pathW;
1743 SetLastError(ERROR_INVALID_PARAMETER);
1747 if (RtlCreateUnicodeStringFromAsciiz(&pathW, path))
1749 ret = DeleteFileW(pathW.Buffer);
1750 RtlFreeUnicodeString(&pathW);
1753 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1758 /***********************************************************************
1759 * GetFileType (KERNEL32.@)
1761 DWORD WINAPI GetFileType( HANDLE hFile )
1763 DWORD ret = FILE_TYPE_UNKNOWN;
1765 if (is_console_handle( hFile ))
1766 return FILE_TYPE_CHAR;
1768 SERVER_START_REQ( get_file_info )
1770 req->handle = hFile;
1771 if (!wine_server_call_err( req )) ret = reply->type;
1778 /* check if a file name is for an executable file (.exe or .com) */
1779 inline static BOOL is_executable( const char *name )
1781 int len = strlen(name);
1783 if (len < 4) return FALSE;
1784 return (!strcasecmp( name + len - 4, ".exe" ) ||
1785 !strcasecmp( name + len - 4, ".com" ));
1789 /***********************************************************************
1790 * FILE_AddBootRenameEntry
1792 * Adds an entry to the registry that is loaded when windows boots and
1793 * checks if there are some files to be removed or renamed/moved.
1794 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1795 * non-NULL then the file is moved, otherwise it is deleted. The
1796 * entry of the registrykey is always appended with two zero
1797 * terminated strings. If <fn2> is NULL then the second entry is
1798 * simply a single 0-byte. Otherwise the second filename goes
1799 * there. The entries are prepended with \??\ before the path and the
1800 * second filename gets also a '!' as the first character if
1801 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1802 * 0-byte follows to indicate the end of the strings.
1804 * \??\D:\test\file1[0]
1805 * !\??\D:\test\file1_renamed[0]
1806 * \??\D:\Test|delete[0]
1807 * [0] <- file is to be deleted, second string empty
1808 * \??\D:\test\file2[0]
1809 * !\??\D:\test\file2_renamed[0]
1810 * [0] <- indicates end of strings
1813 * \??\D:\test\file1[0]
1814 * !\??\D:\test\file1_renamed[0]
1815 * \??\D:\Test|delete[0]
1816 * [0] <- file is to be deleted, second string empty
1817 * [0] <- indicates end of strings
1820 static BOOL FILE_AddBootRenameEntry( LPCWSTR fn1, LPCWSTR fn2, DWORD flags )
1822 static const WCHAR PreString[] = {'\\','?','?','\\',0};
1823 static const WCHAR ValueName[] = {'P','e','n','d','i','n','g',
1824 'F','i','l','e','R','e','n','a','m','e',
1825 'O','p','e','r','a','t','i','o','n','s',0};
1826 static const WCHAR SessionW[] = {'M','a','c','h','i','n','e','\\',
1827 'S','y','s','t','e','m','\\',
1828 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1829 'C','o','n','t','r','o','l','\\',
1830 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1831 static const int info_size = FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION, Data );
1833 OBJECT_ATTRIBUTES attr;
1834 UNICODE_STRING nameW;
1835 KEY_VALUE_PARTIAL_INFORMATION *info;
1838 DWORD len0, len1, len2;
1840 BYTE *Buffer = NULL;
1843 attr.Length = sizeof(attr);
1844 attr.RootDirectory = 0;
1845 attr.ObjectName = &nameW;
1846 attr.Attributes = 0;
1847 attr.SecurityDescriptor = NULL;
1848 attr.SecurityQualityOfService = NULL;
1849 RtlInitUnicodeString( &nameW, SessionW );
1851 if (NtCreateKey( &Reboot, KEY_ALL_ACCESS, &attr, 0, NULL, 0, NULL ) != STATUS_SUCCESS)
1853 WARN("Error creating key for reboot managment [%s]\n",
1854 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1858 len0 = strlenW(PreString);
1859 len1 = strlenW(fn1) + len0 + 1;
1862 len2 = strlenW(fn2) + len0 + 1;
1863 if (flags & MOVEFILE_REPLACE_EXISTING) len2++; /* Plus 1 because of the leading '!' */
1865 else len2 = 1; /* minimum is the 0 characters for the empty second string */
1867 /* convert characters to bytes */
1868 len0 *= sizeof(WCHAR);
1869 len1 *= sizeof(WCHAR);
1870 len2 *= sizeof(WCHAR);
1872 RtlInitUnicodeString( &nameW, ValueName );
1874 /* First we check if the key exists and if so how many bytes it already contains. */
1875 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1876 NULL, 0, &DataSize ) == STATUS_BUFFER_OVERFLOW)
1878 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1880 if (NtQueryValueKey( Reboot, &nameW, KeyValuePartialInformation,
1881 Buffer, DataSize, &DataSize )) goto Quit;
1882 info = (KEY_VALUE_PARTIAL_INFORMATION *)Buffer;
1883 if (info->Type != REG_MULTI_SZ) goto Quit;
1884 if (DataSize > sizeof(info)) DataSize -= sizeof(WCHAR); /* remove terminating null (will be added back later) */
1888 DataSize = info_size;
1889 if (!(Buffer = HeapAlloc( GetProcessHeap(), 0, DataSize + len1 + len2 + sizeof(WCHAR) )))
1893 p = (WCHAR *)(Buffer + DataSize);
1894 strcpyW( p, PreString );
1899 p = (WCHAR *)(Buffer + DataSize);
1900 if (flags & MOVEFILE_REPLACE_EXISTING)
1902 strcpyW( p, PreString );
1908 p = (WCHAR *)(Buffer + DataSize);
1910 DataSize += sizeof(WCHAR);
1913 /* add final null */
1914 p = (WCHAR *)(Buffer + DataSize);
1916 DataSize += sizeof(WCHAR);
1918 rc = !NtSetValueKey(Reboot, &nameW, 0, REG_MULTI_SZ, Buffer + info_size, DataSize - info_size);
1921 if (Reboot) NtClose(Reboot);
1922 if (Buffer) HeapFree( GetProcessHeap(), 0, Buffer );
1927 /**************************************************************************
1928 * MoveFileExW (KERNEL32.@)
1930 BOOL WINAPI MoveFileExW( LPCWSTR fn1, LPCWSTR fn2, DWORD flag )
1932 DOS_FULL_NAME full_name1, full_name2;
1934 DWORD attr = INVALID_FILE_ATTRIBUTES;
1936 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1), debugstr_w(fn2), flag);
1938 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1939 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1940 to be really compatible. Most programs wont have any problems though. In case
1941 you encounter one, this is what you should return here. I don't know what's up
1942 with NT 3.5. Is this function available there or not?
1943 Does anybody really care about 3.5? :)
1946 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1947 if the source file has to be deleted.
1950 SetLastError(ERROR_INVALID_PARAMETER);
1954 /* This function has to be run through in order to process the name properly.
1955 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1956 that is the behaviour on NT 4.0. The operation accepts the filenames as
1957 they are given but it can't reply with a reasonable returncode. Success
1958 means in that case success for entering the values into the registry.
1960 if(!DOSFS_GetFullName( fn1, TRUE, &full_name1 ))
1962 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1966 if (fn2) /* !fn2 means delete fn1 */
1968 if (DOSFS_GetFullName( fn2, TRUE, &full_name2 ))
1970 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1972 /* target exists, check if we may overwrite */
1973 if (!(flag & MOVEFILE_REPLACE_EXISTING))
1975 SetLastError( ERROR_FILE_EXISTS );
1982 if (!DOSFS_GetFullName( fn2, FALSE, &full_name2 ))
1984 if(!(flag & MOVEFILE_DELAY_UNTIL_REBOOT))
1989 /* Source name and target path are valid */
1991 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
1993 return FILE_AddBootRenameEntry( fn1, fn2, flag );
1996 attr = GetFileAttributesW( fn1 );
1997 if ( attr == INVALID_FILE_ATTRIBUTES ) return FALSE;
1999 /* check if we are allowed to rename the source */
2000 hFile = FILE_CreateFile( full_name1.long_name, 0, 0,
2001 NULL, OPEN_EXISTING, 0, 0, TRUE,
2002 GetDriveTypeW( full_name1.short_name ) );
2005 if (GetLastError() != ERROR_ACCESS_DENIED) return FALSE;
2006 if ( !(attr & FILE_ATTRIBUTE_DIRECTORY) ) return FALSE;
2007 /* if it's a directory we can continue */
2009 else CloseHandle(hFile);
2011 /* check, if we are allowed to delete the destination,
2012 ** (but the file not being there is fine) */
2013 hFile = FILE_CreateFile( full_name2.long_name, GENERIC_READ|GENERIC_WRITE, 0,
2014 NULL, OPEN_EXISTING, 0, 0, TRUE,
2015 GetDriveTypeW( full_name2.short_name ) );
2016 if(!hFile && GetLastError() != ERROR_FILE_NOT_FOUND) return FALSE;
2019 if (full_name1.drive != full_name2.drive)
2021 if (!(flag & MOVEFILE_COPY_ALLOWED))
2023 SetLastError( ERROR_NOT_SAME_DEVICE );
2026 else if ( attr & FILE_ATTRIBUTE_DIRECTORY )
2028 /* Strange, but that's what Windows returns */
2029 SetLastError ( ERROR_ACCESS_DENIED );
2033 if (rename( full_name1.long_name, full_name2.long_name ) == -1)
2034 /* Try copy/delete unless it's a directory. */
2035 /* FIXME: This does not handle the (unlikely) case that the two locations
2036 are on the same Wine drive, but on different Unix file systems. */
2038 if ( attr & FILE_ATTRIBUTE_DIRECTORY )
2045 if ( ! CopyFileW( fn1, fn2, !(flag & MOVEFILE_REPLACE_EXISTING) ))
2047 if ( ! DeleteFileW ( fn1 ) )
2051 if (is_executable( full_name1.long_name ) != is_executable( full_name2.long_name ))
2054 if (stat( full_name2.long_name, &fstat ) != -1)
2056 if (is_executable( full_name2.long_name ))
2057 /* set executable bit where read bit is set */
2058 fstat.st_mode |= (fstat.st_mode & 0444) >> 2;
2060 fstat.st_mode &= ~0111;
2061 chmod( full_name2.long_name, fstat.st_mode );
2066 else /* fn2 == NULL means delete source */
2068 if (flag & MOVEFILE_DELAY_UNTIL_REBOOT)
2070 if (flag & MOVEFILE_COPY_ALLOWED) {
2071 WARN("Illegal flag\n");
2072 SetLastError( ERROR_GEN_FAILURE );
2076 return FILE_AddBootRenameEntry( fn1, NULL, flag );
2079 if (unlink( full_name1.long_name ) == -1)
2084 return TRUE; /* successfully deleted */
2088 /**************************************************************************
2089 * MoveFileExA (KERNEL32.@)
2091 BOOL WINAPI MoveFileExA( LPCSTR fn1, LPCSTR fn2, DWORD flag )
2093 UNICODE_STRING fn1W, fn2W;
2098 SetLastError(ERROR_INVALID_PARAMETER);
2102 RtlCreateUnicodeStringFromAsciiz(&fn1W, fn1);
2103 if (fn2) RtlCreateUnicodeStringFromAsciiz(&fn2W, fn2);
2104 else fn2W.Buffer = NULL;
2106 ret = MoveFileExW( fn1W.Buffer, fn2W.Buffer, flag );
2108 RtlFreeUnicodeString(&fn1W);
2109 RtlFreeUnicodeString(&fn2W);
2114 /**************************************************************************
2115 * MoveFileW (KERNEL32.@)
2117 * Move file or directory
2119 BOOL WINAPI MoveFileW( LPCWSTR fn1, LPCWSTR fn2 )
2121 return MoveFileExW( fn1, fn2, MOVEFILE_COPY_ALLOWED );
2125 /**************************************************************************
2126 * MoveFileA (KERNEL32.@)
2128 BOOL WINAPI MoveFileA( LPCSTR fn1, LPCSTR fn2 )
2130 return MoveFileExA( fn1, fn2, MOVEFILE_COPY_ALLOWED );
2134 /**************************************************************************
2135 * CopyFileW (KERNEL32.@)
2137 BOOL WINAPI CopyFileW( LPCWSTR source, LPCWSTR dest, BOOL fail_if_exists )
2140 BY_HANDLE_FILE_INFORMATION info;
2145 if (!source || !dest)
2147 SetLastError(ERROR_INVALID_PARAMETER);
2151 TRACE("%s -> %s\n", debugstr_w(source), debugstr_w(dest));
2153 if ((h1 = CreateFileW(source, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
2154 NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE)
2156 WARN("Unable to open source %s\n", debugstr_w(source));
2160 if (!GetFileInformationByHandle( h1, &info ))
2162 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source));
2167 if ((h2 = CreateFileW( dest, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
2168 fail_if_exists ? CREATE_NEW : CREATE_ALWAYS,
2169 info.dwFileAttributes, h1 )) == INVALID_HANDLE_VALUE)
2171 WARN("Unable to open dest %s\n", debugstr_w(dest));
2176 while (ReadFile( h1, buffer, sizeof(buffer), &count, NULL ) && count)
2182 if (!WriteFile( h2, p, count, &res, NULL ) || !res) goto done;
2195 /**************************************************************************
2196 * CopyFileA (KERNEL32.@)
2198 BOOL WINAPI CopyFileA( LPCSTR source, LPCSTR dest, BOOL fail_if_exists)
2200 UNICODE_STRING sourceW, destW;
2203 if (!source || !dest)
2205 SetLastError(ERROR_INVALID_PARAMETER);
2209 RtlCreateUnicodeStringFromAsciiz(&sourceW, source);
2210 RtlCreateUnicodeStringFromAsciiz(&destW, dest);
2212 ret = CopyFileW(sourceW.Buffer, destW.Buffer, fail_if_exists);
2214 RtlFreeUnicodeString(&sourceW);
2215 RtlFreeUnicodeString(&destW);
2220 /**************************************************************************
2221 * CopyFileExW (KERNEL32.@)
2223 * This implementation ignores most of the extra parameters passed-in into
2224 * the "ex" version of the method and calls the CopyFile method.
2225 * It will have to be fixed eventually.
2227 BOOL WINAPI CopyFileExW(LPCWSTR sourceFilename, LPCWSTR destFilename,
2228 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
2229 LPBOOL cancelFlagPointer, DWORD copyFlags)
2232 * Interpret the only flag that CopyFile can interpret.
2234 return CopyFileW(sourceFilename, destFilename, (copyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0);
2238 /**************************************************************************
2239 * CopyFileExA (KERNEL32.@)
2241 BOOL WINAPI CopyFileExA(LPCSTR sourceFilename, LPCSTR destFilename,
2242 LPPROGRESS_ROUTINE progressRoutine, LPVOID appData,
2243 LPBOOL cancelFlagPointer, DWORD copyFlags)
2245 UNICODE_STRING sourceW, destW;
2248 if (!sourceFilename || !destFilename)
2250 SetLastError(ERROR_INVALID_PARAMETER);
2254 RtlCreateUnicodeStringFromAsciiz(&sourceW, sourceFilename);
2255 RtlCreateUnicodeStringFromAsciiz(&destW, destFilename);
2257 ret = CopyFileExW(sourceW.Buffer, destW.Buffer, progressRoutine, appData,
2258 cancelFlagPointer, copyFlags);
2260 RtlFreeUnicodeString(&sourceW);
2261 RtlFreeUnicodeString(&destW);
2266 /***********************************************************************
2267 * SetFileTime (KERNEL32.@)
2269 BOOL WINAPI SetFileTime( HANDLE hFile,
2270 const FILETIME *lpCreationTime,
2271 const FILETIME *lpLastAccessTime,
2272 const FILETIME *lpLastWriteTime )
2275 SERVER_START_REQ( set_file_time )
2277 req->handle = hFile;
2278 if (lpLastAccessTime)
2279 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastAccessTime, (DWORD *)&req->access_time );
2281 req->access_time = 0; /* FIXME */
2282 if (lpLastWriteTime)
2283 RtlTimeToSecondsSince1970( (PLARGE_INTEGER) lpLastWriteTime, (DWORD *)&req->write_time );
2285 req->write_time = 0; /* FIXME */
2286 ret = !wine_server_call_err( req );
2293 /**************************************************************************
2294 * GetFileAttributesExW (KERNEL32.@)
2296 BOOL WINAPI GetFileAttributesExW(
2297 LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2298 LPVOID lpFileInformation)
2300 DOS_FULL_NAME full_name;
2301 BY_HANDLE_FILE_INFORMATION info;
2303 if (!lpFileName || !lpFileInformation)
2305 SetLastError(ERROR_INVALID_PARAMETER);
2309 if (fInfoLevelId == GetFileExInfoStandard) {
2310 LPWIN32_FILE_ATTRIBUTE_DATA lpFad =
2311 (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation;
2312 if (!DOSFS_GetFullName( lpFileName, TRUE, &full_name )) return FALSE;
2313 if (!FILE_Stat( full_name.long_name, &info, NULL )) return FALSE;
2315 lpFad->dwFileAttributes = info.dwFileAttributes;
2316 lpFad->ftCreationTime = info.ftCreationTime;
2317 lpFad->ftLastAccessTime = info.ftLastAccessTime;
2318 lpFad->ftLastWriteTime = info.ftLastWriteTime;
2319 lpFad->nFileSizeHigh = info.nFileSizeHigh;
2320 lpFad->nFileSizeLow = info.nFileSizeLow;
2323 FIXME("invalid info level %d!\n", fInfoLevelId);
2331 /**************************************************************************
2332 * GetFileAttributesExA (KERNEL32.@)
2334 BOOL WINAPI GetFileAttributesExA(
2335 LPCSTR filename, GET_FILEEX_INFO_LEVELS fInfoLevelId,
2336 LPVOID lpFileInformation)
2338 UNICODE_STRING filenameW;
2341 if (!filename || !lpFileInformation)
2343 SetLastError(ERROR_INVALID_PARAMETER);
2347 if (RtlCreateUnicodeStringFromAsciiz(&filenameW, filename))
2349 ret = GetFileAttributesExW(filenameW.Buffer, fInfoLevelId, lpFileInformation);
2350 RtlFreeUnicodeString(&filenameW);
2353 SetLastError(ERROR_NOT_ENOUGH_MEMORY);