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 /***********************************************************************
208 * Implementation of CreateFile. Takes a Unix path name.
209 * Returns 0 on failure.
211 HANDLE
FILE_CreateFile( LPCSTR filename
, DWORD access
, DWORD sharing
,
212 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
213 DWORD attributes
, HANDLE
template, BOOL fail_read_only
,
221 SERVER_START_REQ( create_file
)
223 req
->access
= access
;
224 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
225 req
->sharing
= sharing
;
226 req
->create
= creation
;
227 req
->attrs
= attributes
;
228 req
->removable
= (drive_type
== DRIVE_REMOVABLE
|| drive_type
== DRIVE_CDROM
);
229 wine_server_add_data( req
, filename
, strlen(filename
) );
231 err
= wine_server_call( req
);
236 /* If write access failed, retry without GENERIC_WRITE */
238 if (!ret
&& !fail_read_only
&& (access
& GENERIC_WRITE
))
240 if ((err
== STATUS_MEDIA_WRITE_PROTECTED
) || (err
== STATUS_ACCESS_DENIED
))
242 TRACE("Write access failed for file '%s', trying without "
243 "write access\n", filename
);
244 access
&= ~GENERIC_WRITE
;
251 /* In the case file creation was rejected due to CREATE_NEW flag
252 * was specified and file with that name already exists, correct
253 * last error is ERROR_FILE_EXISTS and not ERROR_ALREADY_EXISTS.
254 * Note: RtlNtStatusToDosError is not the subject to blame here.
256 if (err
== STATUS_OBJECT_NAME_COLLISION
)
257 SetLastError( ERROR_FILE_EXISTS
);
259 SetLastError( RtlNtStatusToDosError(err
) );
262 if (!ret
) WARN("Unable to create file '%s' (GLE %ld)\n", filename
, GetLastError());
268 /***********************************************************************
271 * Same as FILE_CreateFile but for a device
272 * Returns 0 on failure.
274 HANDLE
FILE_CreateDevice( int client_id
, DWORD access
, LPSECURITY_ATTRIBUTES sa
)
277 SERVER_START_REQ( create_device
)
279 req
->access
= access
;
280 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
283 wine_server_call_err( req
);
290 static HANDLE
FILE_OpenPipe(LPCWSTR name
, DWORD access
, LPSECURITY_ATTRIBUTES sa
)
295 if (name
&& (len
= strlenW(name
)) > MAX_PATH
)
297 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
300 SERVER_START_REQ( open_named_pipe
)
302 req
->access
= access
;
303 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
305 wine_server_add_data( req
, name
, len
* sizeof(WCHAR
) );
306 wine_server_call_err( req
);
310 TRACE("Returned %p\n",ret
);
314 /*************************************************************************
315 * CreateFileW [KERNEL32.@] Creates or opens a file or other object
317 * Creates or opens an object, and returns a handle that can be used to
318 * access that object.
322 * filename [in] pointer to filename to be accessed
323 * access [in] access mode requested
324 * sharing [in] share mode
325 * sa [in] pointer to security attributes
326 * creation [in] how to create the file
327 * attributes [in] attributes for newly created file
328 * template [in] handle to file with extended attributes to copy
331 * Success: Open handle to specified file
332 * Failure: INVALID_HANDLE_VALUE
335 * Should call SetLastError() on failure.
339 * Doesn't support character devices, template files, or a
340 * lot of the 'attributes' flags yet.
342 HANDLE WINAPI
CreateFileW( LPCWSTR filename
, DWORD access
, DWORD sharing
,
343 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
344 DWORD attributes
, HANDLE
template )
346 DOS_FULL_NAME full_name
;
348 static const WCHAR bkslashes_with_question_markW
[] = {'\\','\\','?','\\',0};
349 static const WCHAR bkslashes_with_dotW
[] = {'\\','\\','.','\\',0};
350 static const WCHAR bkslashesW
[] = {'\\','\\',0};
351 static const WCHAR coninW
[] = {'C','O','N','I','N','$',0};
352 static const WCHAR conoutW
[] = {'C','O','N','O','U','T','$',0};
356 SetLastError( ERROR_INVALID_PARAMETER
);
357 return INVALID_HANDLE_VALUE
;
359 TRACE("%s %s%s%s%s%s%s%s attributes 0x%lx\n", debugstr_w(filename
),
360 ((access
& GENERIC_READ
)==GENERIC_READ
)?"GENERIC_READ ":"",
361 ((access
& GENERIC_WRITE
)==GENERIC_WRITE
)?"GENERIC_WRITE ":"",
362 (!access
)?"QUERY_ACCESS ":"",
363 ((sharing
& FILE_SHARE_READ
)==FILE_SHARE_READ
)?"FILE_SHARE_READ ":"",
364 ((sharing
& FILE_SHARE_WRITE
)==FILE_SHARE_WRITE
)?"FILE_SHARE_WRITE ":"",
365 ((sharing
& FILE_SHARE_DELETE
)==FILE_SHARE_DELETE
)?"FILE_SHARE_DELETE ":"",
366 (creation
==CREATE_NEW
)?"CREATE_NEW":
367 (creation
==CREATE_ALWAYS
)?"CREATE_ALWAYS ":
368 (creation
==OPEN_EXISTING
)?"OPEN_EXISTING ":
369 (creation
==OPEN_ALWAYS
)?"OPEN_ALWAYS ":
370 (creation
==TRUNCATE_EXISTING
)?"TRUNCATE_EXISTING ":"", attributes
);
372 /* If the name starts with '\\?\', ignore the first 4 chars. */
373 if (!strncmpW(filename
, bkslashes_with_question_markW
, 4))
375 static const WCHAR uncW
[] = {'U','N','C','\\',0};
377 if (!strncmpiW(filename
, uncW
, 4))
379 FIXME("UNC name (%s) not supported.\n", debugstr_w(filename
) );
380 SetLastError( ERROR_PATH_NOT_FOUND
);
381 return INVALID_HANDLE_VALUE
;
385 if (!strncmpW(filename
, bkslashes_with_dotW
, 4))
387 static const WCHAR pipeW
[] = {'P','I','P','E','\\',0};
388 if(!strncmpiW(filename
+ 4, pipeW
, 5))
390 TRACE("Opening a pipe: %s\n", debugstr_w(filename
));
391 ret
= FILE_OpenPipe( filename
, access
, sa
);
394 else if (isalphaW(filename
[4]) && filename
[5] == ':' && filename
[6] == '\0')
396 ret
= FILE_CreateDevice( (toupperW(filename
[4]) - 'A') | 0x20000, access
, sa
);
399 else if (!DOSFS_GetDevice( filename
))
401 ret
= DEVICE_Open( filename
+4, access
, sa
);
405 filename
+=4; /* fall into DOSFS_Device case below */
408 /* If the name still starts with '\\', it's a UNC name. */
409 if (!strncmpW(filename
, bkslashesW
, 2))
411 ret
= SMB_CreateFileW(filename
, access
, sharing
, sa
, creation
, attributes
, template );
415 /* If the name contains a DOS wild card (* or ?), do no create a file */
416 if(strchrW(filename
, '*') || strchrW(filename
, '?'))
418 SetLastError(ERROR_BAD_PATHNAME
);
419 return INVALID_HANDLE_VALUE
;
422 /* Open a console for CONIN$ or CONOUT$ */
423 if (!strcmpiW(filename
, coninW
) || !strcmpiW(filename
, conoutW
))
425 ret
= OpenConsoleW(filename
, access
, sa
, creation
);
429 if (DOSFS_GetDevice( filename
))
431 TRACE("opening device %s\n", debugstr_w(filename
) );
433 if (!(ret
= DOSFS_OpenDevice( filename
, access
, attributes
, sa
)))
435 /* Do not silence this please. It is a critical error. -MM */
436 ERR("Couldn't open device %s!\n", debugstr_w(filename
));
437 SetLastError( ERROR_FILE_NOT_FOUND
);
442 /* check for filename, don't check for last entry if creating */
443 if (!DOSFS_GetFullName( filename
,
444 (creation
== OPEN_EXISTING
) ||
445 (creation
== TRUNCATE_EXISTING
),
447 WARN("Unable to get full filename from %s (GLE %ld)\n",
448 debugstr_w(filename
), GetLastError());
449 return INVALID_HANDLE_VALUE
;
452 ret
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
453 sa
, creation
, attributes
, template,
454 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
455 GetDriveTypeW( full_name
.short_name
) );
457 if (!ret
) ret
= INVALID_HANDLE_VALUE
;
458 TRACE("returning %p\n", ret
);
464 /*************************************************************************
465 * CreateFileA (KERNEL32.@)
467 HANDLE WINAPI
CreateFileA( LPCSTR filename
, DWORD access
, DWORD sharing
,
468 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
469 DWORD attributes
, HANDLE
template)
471 UNICODE_STRING filenameW
;
472 HANDLE ret
= INVALID_HANDLE_VALUE
;
476 SetLastError( ERROR_INVALID_PARAMETER
);
477 return INVALID_HANDLE_VALUE
;
480 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
))
482 ret
= CreateFileW(filenameW
.Buffer
, access
, sharing
, sa
, creation
,
483 attributes
, template);
484 RtlFreeUnicodeString(&filenameW
);
487 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
492 /***********************************************************************
495 * Fill a file information from a struct stat.
497 static void FILE_FillInfo( struct stat
*st
, BY_HANDLE_FILE_INFORMATION
*info
)
499 if (S_ISDIR(st
->st_mode
))
500 info
->dwFileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
502 info
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
503 if (!(st
->st_mode
& S_IWUSR
))
504 info
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
506 RtlSecondsSince1970ToTime( st
->st_mtime
, (LARGE_INTEGER
*)&info
->ftCreationTime
);
507 RtlSecondsSince1970ToTime( st
->st_mtime
, (LARGE_INTEGER
*)&info
->ftLastWriteTime
);
508 RtlSecondsSince1970ToTime( st
->st_atime
, (LARGE_INTEGER
*)&info
->ftLastAccessTime
);
510 info
->dwVolumeSerialNumber
= 0; /* FIXME */
511 info
->nFileSizeHigh
= 0;
512 info
->nFileSizeLow
= 0;
513 if (!S_ISDIR(st
->st_mode
)) {
514 info
->nFileSizeHigh
= st
->st_size
>> 32;
515 info
->nFileSizeLow
= st
->st_size
& 0xffffffff;
517 info
->nNumberOfLinks
= st
->st_nlink
;
518 info
->nFileIndexHigh
= 0;
519 info
->nFileIndexLow
= st
->st_ino
;
523 /***********************************************************************
524 * get_show_dot_files_option
526 static BOOL
get_show_dot_files_option(void)
528 static const WCHAR WineW
[] = {'M','a','c','h','i','n','e','\\',
529 'S','o','f','t','w','a','r','e','\\',
530 'W','i','n','e','\\','W','i','n','e','\\',
531 'C','o','n','f','i','g','\\','W','i','n','e',0};
532 static const WCHAR ShowDotFilesW
[] = {'S','h','o','w','D','o','t','F','i','l','e','s',0};
537 OBJECT_ATTRIBUTES attr
;
538 UNICODE_STRING nameW
;
541 attr
.Length
= sizeof(attr
);
542 attr
.RootDirectory
= 0;
543 attr
.ObjectName
= &nameW
;
545 attr
.SecurityDescriptor
= NULL
;
546 attr
.SecurityQualityOfService
= NULL
;
547 RtlInitUnicodeString( &nameW
, WineW
);
549 if (!NtOpenKey( &hkey
, KEY_ALL_ACCESS
, &attr
))
551 RtlInitUnicodeString( &nameW
, ShowDotFilesW
);
552 if (!NtQueryValueKey( hkey
, &nameW
, KeyValuePartialInformation
, tmp
, sizeof(tmp
), &dummy
))
554 WCHAR
*str
= (WCHAR
*)((KEY_VALUE_PARTIAL_INFORMATION
*)tmp
)->Data
;
555 ret
= IS_OPTION_TRUE( str
[0] );
563 /***********************************************************************
566 * Stat a Unix path name. Return TRUE if OK.
568 BOOL
FILE_Stat( LPCSTR unixName
, BY_HANDLE_FILE_INFORMATION
*info
, BOOL
*is_symlink_ptr
)
574 if (lstat( unixName
, &st
) == -1)
579 is_symlink
= S_ISLNK(st
.st_mode
);
582 /* do a "real" stat to find out
583 about the type of the symlink destination */
584 if (stat( unixName
, &st
) == -1)
591 /* fill in the information we gathered so far */
592 FILE_FillInfo( &st
, info
);
594 /* and now see if this is a hidden file, based on the name */
595 p
= strrchr( unixName
, '/');
596 p
= p
? p
+ 1 : unixName
;
597 if (*p
== '.' && *(p
+1) && (*(p
+1) != '.' || *(p
+2)))
599 static int show_dot_files
= -1;
600 if (show_dot_files
== -1)
601 show_dot_files
= get_show_dot_files_option();
603 info
->dwFileAttributes
|= FILE_ATTRIBUTE_HIDDEN
;
605 if (is_symlink_ptr
) *is_symlink_ptr
= is_symlink
;
610 /***********************************************************************
611 * GetFileInformationByHandle (KERNEL32.@)
613 DWORD WINAPI
GetFileInformationByHandle( HANDLE hFile
,
614 BY_HANDLE_FILE_INFORMATION
*info
)
619 TRACE("%p\n", hFile
);
621 SERVER_START_REQ( get_file_info
)
624 if ((ret
= !wine_server_call_err( req
)))
626 /* FIXME: which file types are supported ?
627 * Serial ports (FILE_TYPE_CHAR) are not,
628 * and MSDN also says that pipes are not supported.
629 * FILE_TYPE_REMOTE seems to be supported according to
630 * MSDN q234741.txt */
631 if ((reply
->type
== FILE_TYPE_DISK
) || (reply
->type
== FILE_TYPE_REMOTE
))
633 RtlSecondsSince1970ToTime( reply
->write_time
, (LARGE_INTEGER
*)&info
->ftCreationTime
);
634 RtlSecondsSince1970ToTime( reply
->write_time
, (LARGE_INTEGER
*)&info
->ftLastWriteTime
);
635 RtlSecondsSince1970ToTime( reply
->access_time
, (LARGE_INTEGER
*)&info
->ftLastAccessTime
);
636 info
->dwFileAttributes
= reply
->attr
;
637 info
->dwVolumeSerialNumber
= reply
->serial
;
638 info
->nFileSizeHigh
= reply
->size_high
;
639 info
->nFileSizeLow
= reply
->size_low
;
640 info
->nNumberOfLinks
= reply
->links
;
641 info
->nFileIndexHigh
= reply
->index_high
;
642 info
->nFileIndexLow
= reply
->index_low
;
646 SetLastError(ERROR_NOT_SUPPORTED
);
656 /**************************************************************************
657 * GetFileAttributesW (KERNEL32.@)
659 DWORD WINAPI
GetFileAttributesW( LPCWSTR name
)
661 DOS_FULL_NAME full_name
;
662 BY_HANDLE_FILE_INFORMATION info
;
666 SetLastError( ERROR_INVALID_PARAMETER
);
667 return INVALID_FILE_ATTRIBUTES
;
669 if (!DOSFS_GetFullName( name
, TRUE
, &full_name
) )
670 return INVALID_FILE_ATTRIBUTES
;
671 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
))
672 return INVALID_FILE_ATTRIBUTES
;
673 return info
.dwFileAttributes
;
677 /**************************************************************************
678 * GetFileAttributesA (KERNEL32.@)
680 DWORD WINAPI
GetFileAttributesA( LPCSTR name
)
682 UNICODE_STRING nameW
;
683 DWORD ret
= INVALID_FILE_ATTRIBUTES
;
687 SetLastError( ERROR_INVALID_PARAMETER
);
688 return INVALID_FILE_ATTRIBUTES
;
691 if (RtlCreateUnicodeStringFromAsciiz(&nameW
, name
))
693 ret
= GetFileAttributesW(nameW
.Buffer
);
694 RtlFreeUnicodeString(&nameW
);
697 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
702 /**************************************************************************
703 * SetFileAttributesW (KERNEL32.@)
705 BOOL WINAPI
SetFileAttributesW(LPCWSTR lpFileName
, DWORD attributes
)
708 DOS_FULL_NAME full_name
;
712 SetLastError( ERROR_INVALID_PARAMETER
);
716 TRACE("(%s,%lx)\n", debugstr_w(lpFileName
), attributes
);
718 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
))
721 if(stat(full_name
.long_name
,&buf
)==-1)
726 if (attributes
& FILE_ATTRIBUTE_READONLY
)
728 if(S_ISDIR(buf
.st_mode
))
730 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
732 buf
.st_mode
&= ~0222; /* octal!, clear write permission bits */
733 attributes
&= ~FILE_ATTRIBUTE_READONLY
;
737 /* add write permission */
738 buf
.st_mode
|= (0600 | ((buf
.st_mode
& 044) >> 1)) & (~FILE_umask
);
740 if (attributes
& FILE_ATTRIBUTE_DIRECTORY
)
742 if (!S_ISDIR(buf
.st_mode
))
743 FIXME("SetFileAttributes expected the file %s to be a directory\n",
744 debugstr_w(lpFileName
));
745 attributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
747 attributes
&= ~(FILE_ATTRIBUTE_NORMAL
|FILE_ATTRIBUTE_ARCHIVE
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_SYSTEM
);
749 FIXME("(%s):%lx attribute(s) not implemented.\n", debugstr_w(lpFileName
), attributes
);
750 if (-1==chmod(full_name
.long_name
,buf
.st_mode
))
752 if (GetDriveTypeW(lpFileName
) == DRIVE_CDROM
)
754 SetLastError( ERROR_ACCESS_DENIED
);
759 * FIXME: We don't return FALSE here because of differences between
760 * Linux and Windows privileges. Under Linux only the owner of
761 * the file is allowed to change file attributes. Under Windows,
762 * applications expect that if you can write to a file, you can also
763 * change its attributes (see GENERIC_WRITE). We could try to be
764 * clever here but that would break multi-user installations where
765 * users share read-only DLLs. This is because some installers like
766 * to change attributes of already installed DLLs.
768 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
769 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name
.long_name
);
775 /**************************************************************************
776 * SetFileAttributesA (KERNEL32.@)
778 BOOL WINAPI
SetFileAttributesA(LPCSTR lpFileName
, DWORD attributes
)
780 UNICODE_STRING filenameW
;
785 SetLastError( ERROR_INVALID_PARAMETER
);
789 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, lpFileName
))
791 ret
= SetFileAttributesW(filenameW
.Buffer
, attributes
);
792 RtlFreeUnicodeString(&filenameW
);
795 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
800 /******************************************************************************
801 * GetCompressedFileSizeA [KERNEL32.@]
803 DWORD WINAPI
GetCompressedFileSizeA(
805 LPDWORD lpFileSizeHigh
)
807 UNICODE_STRING filenameW
;
810 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, lpFileName
))
812 ret
= GetCompressedFileSizeW(filenameW
.Buffer
, lpFileSizeHigh
);
813 RtlFreeUnicodeString(&filenameW
);
817 ret
= INVALID_FILE_SIZE
;
818 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
824 /******************************************************************************
825 * GetCompressedFileSizeW [KERNEL32.@]
828 * Success: Low-order doubleword of number of bytes
829 * Failure: INVALID_FILE_SIZE
831 DWORD WINAPI
GetCompressedFileSizeW(
832 LPCWSTR lpFileName
, /* [in] Pointer to name of file */
833 LPDWORD lpFileSizeHigh
) /* [out] Receives high-order doubleword of size */
835 DOS_FULL_NAME full_name
;
839 TRACE("(%s,%p)\n",debugstr_w(lpFileName
),lpFileSizeHigh
);
841 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
)) return INVALID_FILE_SIZE
;
842 if (stat(full_name
.long_name
, &st
) != 0)
845 return INVALID_FILE_SIZE
;
847 #if HAVE_STRUCT_STAT_ST_BLOCKS
848 /* blocks are 512 bytes long */
849 if (lpFileSizeHigh
) *lpFileSizeHigh
= (st
.st_blocks
>> 23);
850 low
= (DWORD
)(st
.st_blocks
<< 9);
852 if (lpFileSizeHigh
) *lpFileSizeHigh
= (st
.st_size
>> 32);
853 low
= (DWORD
)st
.st_size
;
859 /***********************************************************************
860 * GetFileTime (KERNEL32.@)
862 BOOL WINAPI
GetFileTime( HANDLE hFile
, FILETIME
*lpCreationTime
,
863 FILETIME
*lpLastAccessTime
,
864 FILETIME
*lpLastWriteTime
)
866 BY_HANDLE_FILE_INFORMATION info
;
867 if (!GetFileInformationByHandle( hFile
, &info
)) return FALSE
;
868 if (lpCreationTime
) *lpCreationTime
= info
.ftCreationTime
;
869 if (lpLastAccessTime
) *lpLastAccessTime
= info
.ftLastAccessTime
;
870 if (lpLastWriteTime
) *lpLastWriteTime
= info
.ftLastWriteTime
;
875 /***********************************************************************
876 * GetTempFileNameA (KERNEL32.@)
878 UINT WINAPI
GetTempFileNameA( LPCSTR path
, LPCSTR prefix
, UINT unique
,
881 UNICODE_STRING pathW
, prefixW
;
882 WCHAR bufferW
[MAX_PATH
];
885 if ( !path
|| !prefix
|| !buffer
)
887 SetLastError( ERROR_INVALID_PARAMETER
);
891 RtlCreateUnicodeStringFromAsciiz(&pathW
, path
);
892 RtlCreateUnicodeStringFromAsciiz(&prefixW
, prefix
);
894 ret
= GetTempFileNameW(pathW
.Buffer
, prefixW
.Buffer
, unique
, bufferW
);
896 WideCharToMultiByte(CP_ACP
, 0, bufferW
, -1, buffer
, MAX_PATH
, NULL
, NULL
);
898 RtlFreeUnicodeString(&pathW
);
899 RtlFreeUnicodeString(&prefixW
);
903 /***********************************************************************
904 * GetTempFileNameW (KERNEL32.@)
906 UINT WINAPI
GetTempFileNameW( LPCWSTR path
, LPCWSTR prefix
, UINT unique
,
909 static const WCHAR formatW
[] = {'%','0','4','x','.','t','m','p',0};
911 DOS_FULL_NAME full_name
;
915 if ( !path
|| !prefix
|| !buffer
)
917 SetLastError( ERROR_INVALID_PARAMETER
);
921 strcpyW( buffer
, path
);
922 p
= buffer
+ strlenW(buffer
);
924 /* add a \, if there isn't one and path is more than just the drive letter ... */
925 if ( !((strlenW(buffer
) == 2) && (buffer
[1] == ':'))
926 && ((p
== buffer
) || (p
[-1] != '\\'))) *p
++ = '\\';
928 for (i
= 3; (i
> 0) && (*prefix
); i
--) *p
++ = *prefix
++;
932 if (unique
) sprintfW( p
, formatW
, unique
);
935 /* get a "random" unique number and try to create the file */
937 UINT num
= GetTickCount() & 0xffff;
943 sprintfW( p
, formatW
, unique
);
944 handle
= CreateFileW( buffer
, GENERIC_WRITE
, 0, NULL
,
945 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
946 if (handle
!= INVALID_HANDLE_VALUE
)
947 { /* We created it */
948 TRACE("created %s\n", debugstr_w(buffer
) );
949 CloseHandle( handle
);
952 if (GetLastError() != ERROR_FILE_EXISTS
&&
953 GetLastError() != ERROR_SHARING_VIOLATION
)
954 break; /* No need to go on */
955 if (!(++unique
& 0xffff)) unique
= 1;
956 } while (unique
!= num
);
959 /* Get the full path name */
961 if (DOSFS_GetFullName( buffer
, FALSE
, &full_name
))
964 /* Check if we have write access in the directory */
965 if ((slash
= strrchr( full_name
.long_name
, '/' ))) *slash
= '\0';
966 if (access( full_name
.long_name
, W_OK
) == -1)
967 WARN("returns %s, which doesn't seem to be writeable.\n",
968 debugstr_w(buffer
) );
970 TRACE("returning %s\n", debugstr_w(buffer
) );
975 /***********************************************************************
978 * Implementation of OpenFile16() and OpenFile32().
980 static HFILE
FILE_DoOpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
, BOOL win32
)
985 WORD filedatetime
[2];
986 DOS_FULL_NAME full_name
;
987 DWORD access
, sharing
;
989 WCHAR buffer
[MAX_PATH
];
992 if (!ofs
) return HFILE_ERROR
;
994 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name
,
995 ((mode
& 0x3 )==OF_READ
)?"OF_READ":
996 ((mode
& 0x3 )==OF_WRITE
)?"OF_WRITE":
997 ((mode
& 0x3 )==OF_READWRITE
)?"OF_READWRITE":"unknown",
998 ((mode
& 0x70 )==OF_SHARE_COMPAT
)?"OF_SHARE_COMPAT":
999 ((mode
& 0x70 )==OF_SHARE_DENY_NONE
)?"OF_SHARE_DENY_NONE":
1000 ((mode
& 0x70 )==OF_SHARE_DENY_READ
)?"OF_SHARE_DENY_READ":
1001 ((mode
& 0x70 )==OF_SHARE_DENY_WRITE
)?"OF_SHARE_DENY_WRITE":
1002 ((mode
& 0x70 )==OF_SHARE_EXCLUSIVE
)?"OF_SHARE_EXCLUSIVE":"unknown",
1003 ((mode
& OF_PARSE
)==OF_PARSE
)?"OF_PARSE ":"",
1004 ((mode
& OF_DELETE
)==OF_DELETE
)?"OF_DELETE ":"",
1005 ((mode
& OF_VERIFY
)==OF_VERIFY
)?"OF_VERIFY ":"",
1006 ((mode
& OF_SEARCH
)==OF_SEARCH
)?"OF_SEARCH ":"",
1007 ((mode
& OF_CANCEL
)==OF_CANCEL
)?"OF_CANCEL ":"",
1008 ((mode
& OF_CREATE
)==OF_CREATE
)?"OF_CREATE ":"",
1009 ((mode
& OF_PROMPT
)==OF_PROMPT
)?"OF_PROMPT ":"",
1010 ((mode
& OF_EXIST
)==OF_EXIST
)?"OF_EXIST ":"",
1011 ((mode
& OF_REOPEN
)==OF_REOPEN
)?"OF_REOPEN ":""
1015 ofs
->cBytes
= sizeof(OFSTRUCT
);
1017 if (mode
& OF_REOPEN
) name
= ofs
->szPathName
;
1020 ERR("called with `name' set to NULL ! Please debug.\n");
1024 TRACE("%s %04x\n", name
, mode
);
1026 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1027 Are there any cases where getting the path here is wrong?
1028 Uwe Bonnes 1997 Apr 2 */
1029 if (!GetFullPathNameA( name
, sizeof(ofs
->szPathName
),
1030 ofs
->szPathName
, NULL
)) goto error
;
1031 FILE_ConvertOFMode( mode
, &access
, &sharing
);
1033 /* OF_PARSE simply fills the structure */
1035 if (mode
& OF_PARSE
)
1037 ofs
->fFixedDisk
= (GetDriveType16( ofs
->szPathName
[0]-'A' )
1038 != DRIVE_REMOVABLE
);
1039 TRACE("(%s): OF_PARSE, res = '%s'\n",
1040 name
, ofs
->szPathName
);
1044 /* OF_CREATE is completely different from all other options, so
1047 if (mode
& OF_CREATE
)
1049 if ((handle
= CreateFileA( name
, GENERIC_READ
| GENERIC_WRITE
,
1050 sharing
, NULL
, CREATE_ALWAYS
,
1051 FILE_ATTRIBUTE_NORMAL
, 0 ))== INVALID_HANDLE_VALUE
)
1056 MultiByteToWideChar(CP_ACP
, 0, name
, -1, buffer
, MAX_PATH
);
1059 /* If OF_SEARCH is set, ignore the given path */
1061 if ((mode
& OF_SEARCH
) && !(mode
& OF_REOPEN
))
1063 /* First try the file name as is */
1064 if (DOSFS_GetFullName( nameW
, TRUE
, &full_name
)) goto found
;
1065 /* Now remove the path */
1066 if (nameW
[0] && (nameW
[1] == ':')) nameW
+= 2;
1067 if ((p
= strrchrW( nameW
, '\\' ))) nameW
= p
+ 1;
1068 if ((p
= strrchrW( nameW
, '/' ))) nameW
= p
+ 1;
1069 if (!nameW
[0]) goto not_found
;
1072 /* Now look for the file */
1074 if (!DIR_SearchPath( NULL
, nameW
, NULL
, &full_name
, win32
)) goto not_found
;
1077 TRACE("found %s = %s\n",
1078 full_name
.long_name
, debugstr_w(full_name
.short_name
) );
1079 WideCharToMultiByte(CP_ACP
, 0, full_name
.short_name
, -1,
1080 ofs
->szPathName
, sizeof(ofs
->szPathName
), NULL
, NULL
);
1082 if (mode
& OF_DELETE
)
1084 handle
= FILE_CreateFile( full_name
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1085 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1086 GetDriveTypeW( full_name
.short_name
) );
1087 if (!handle
) goto error
;
1088 CloseHandle( handle
);
1089 if (unlink( full_name
.long_name
) == -1) goto not_found
;
1090 TRACE("(%s): OF_DELETE return = OK\n", name
);
1094 handle
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
1095 NULL
, OPEN_EXISTING
, 0, 0,
1096 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
1097 GetDriveTypeW( full_name
.short_name
) );
1098 if (!handle
) goto not_found
;
1100 GetFileTime( handle
, NULL
, NULL
, &filetime
);
1101 FileTimeToDosDateTime( &filetime
, &filedatetime
[0], &filedatetime
[1] );
1102 if ((mode
& OF_VERIFY
) && (mode
& OF_REOPEN
))
1104 if (ofs
->Reserved1
!= filedatetime
[0] || ofs
->Reserved2
!= filedatetime
[1] )
1106 CloseHandle( handle
);
1107 WARN("(%s): OF_VERIFY failed\n", name
);
1108 /* FIXME: what error here? */
1109 SetLastError( ERROR_FILE_NOT_FOUND
);
1113 ofs
->Reserved1
= filedatetime
[0];
1114 ofs
->Reserved2
= filedatetime
[1];
1116 success
: /* We get here if the open was successful */
1117 TRACE("(%s): OK, return = %p\n", name
, handle
);
1120 hFileRet
= (HFILE
)handle
;
1121 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1122 CloseHandle( handle
);
1126 hFileRet
= Win32HandleToDosFileHandle( handle
);
1127 if (hFileRet
== HFILE_ERROR16
) goto error
;
1128 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1129 _lclose16( hFileRet
);
1133 not_found
: /* We get here if the file does not exist */
1134 WARN("'%s' not found or sharing violation\n", name
);
1135 SetLastError( ERROR_FILE_NOT_FOUND
);
1138 error
: /* We get here if there was an error opening the file */
1139 ofs
->nErrCode
= GetLastError();
1140 WARN("(%s): return = HFILE_ERROR error= %d\n",
1141 name
,ofs
->nErrCode
);
1146 /***********************************************************************
1147 * OpenFile (KERNEL.74)
1148 * OpenFileEx (KERNEL.360)
1150 HFILE16 WINAPI
OpenFile16( LPCSTR name
, OFSTRUCT
*ofs
, UINT16 mode
)
1152 return FILE_DoOpenFile( name
, ofs
, mode
, FALSE
);
1156 /***********************************************************************
1157 * OpenFile (KERNEL32.@)
1159 HFILE WINAPI
OpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
)
1161 return FILE_DoOpenFile( name
, ofs
, mode
, TRUE
);
1165 /***********************************************************************
1166 * FILE_InitProcessDosHandles
1168 * Allocates the default DOS handles for a process. Called either by
1169 * Win32HandleToDosFileHandle below or by the DOSVM stuff.
1171 static void FILE_InitProcessDosHandles( void )
1173 HANDLE cp
= GetCurrentProcess();
1174 DuplicateHandle(cp
, GetStdHandle(STD_INPUT_HANDLE
), cp
, &dos_handles
[0],
1175 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1176 DuplicateHandle(cp
, GetStdHandle(STD_OUTPUT_HANDLE
), cp
, &dos_handles
[1],
1177 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1178 DuplicateHandle(cp
, GetStdHandle(STD_ERROR_HANDLE
), cp
, &dos_handles
[2],
1179 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1180 DuplicateHandle(cp
, GetStdHandle(STD_ERROR_HANDLE
), cp
, &dos_handles
[3],
1181 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1182 DuplicateHandle(cp
, GetStdHandle(STD_ERROR_HANDLE
), cp
, &dos_handles
[4],
1183 0, TRUE
, DUPLICATE_SAME_ACCESS
);
1186 /***********************************************************************
1187 * Win32HandleToDosFileHandle (KERNEL32.21)
1189 * Allocate a DOS handle for a Win32 handle. The Win32 handle is no
1190 * longer valid after this function (even on failure).
1192 * Note: this is not exactly right, since on Win95 the Win32 handles
1193 * are on top of DOS handles and we do it the other way
1194 * around. Should be good enough though.
1196 HFILE WINAPI
Win32HandleToDosFileHandle( HANDLE handle
)
1200 if (!handle
|| (handle
== INVALID_HANDLE_VALUE
))
1203 for (i
= 5; i
< DOS_TABLE_SIZE
; i
++)
1204 if (!dos_handles
[i
])
1206 dos_handles
[i
] = handle
;
1207 TRACE("Got %d for h32 %p\n", i
, handle
);
1210 CloseHandle( handle
);
1211 SetLastError( ERROR_TOO_MANY_OPEN_FILES
);
1216 /***********************************************************************
1217 * DosFileHandleToWin32Handle (KERNEL32.20)
1219 * Return the Win32 handle for a DOS handle.
1221 * Note: this is not exactly right, since on Win95 the Win32 handles
1222 * are on top of DOS handles and we do it the other way
1223 * around. Should be good enough though.
1225 HANDLE WINAPI
DosFileHandleToWin32Handle( HFILE handle
)
1227 HFILE16 hfile
= (HFILE16
)handle
;
1228 if (hfile
< 5 && !dos_handles
[hfile
]) FILE_InitProcessDosHandles();
1229 if ((hfile
>= DOS_TABLE_SIZE
) || !dos_handles
[hfile
])
1231 SetLastError( ERROR_INVALID_HANDLE
);
1232 return INVALID_HANDLE_VALUE
;
1234 return dos_handles
[hfile
];
1238 /***********************************************************************
1239 * DisposeLZ32Handle (KERNEL32.22)
1241 * Note: this is not entirely correct, we should only close the
1242 * 32-bit handle and not the 16-bit one, but we cannot do
1243 * this because of the way our DOS handles are implemented.
1244 * It shouldn't break anything though.
1246 void WINAPI
DisposeLZ32Handle( HANDLE handle
)
1250 if (!handle
|| (handle
== INVALID_HANDLE_VALUE
)) return;
1252 for (i
= 5; i
< DOS_TABLE_SIZE
; i
++)
1253 if (dos_handles
[i
] == handle
)
1256 CloseHandle( handle
);
1262 /***********************************************************************
1265 * dup2() function for DOS handles.
1267 HFILE16
FILE_Dup2( HFILE16 hFile1
, HFILE16 hFile2
)
1271 if (hFile1
< 5 && !dos_handles
[hFile1
]) FILE_InitProcessDosHandles();
1273 if ((hFile1
>= DOS_TABLE_SIZE
) || (hFile2
>= DOS_TABLE_SIZE
) || !dos_handles
[hFile1
])
1275 SetLastError( ERROR_INVALID_HANDLE
);
1276 return HFILE_ERROR16
;
1278 if (!DuplicateHandle( GetCurrentProcess(), dos_handles
[hFile1
],
1279 GetCurrentProcess(), &new_handle
,
1280 0, FALSE
, DUPLICATE_SAME_ACCESS
))
1281 return HFILE_ERROR16
;
1282 if (dos_handles
[hFile2
]) CloseHandle( dos_handles
[hFile2
] );
1283 dos_handles
[hFile2
] = new_handle
;
1288 /***********************************************************************
1289 * _lclose (KERNEL.81)
1291 HFILE16 WINAPI
_lclose16( HFILE16 hFile
)
1293 if ((hFile
>= DOS_TABLE_SIZE
) || !dos_handles
[hFile
])
1295 SetLastError( ERROR_INVALID_HANDLE
);
1296 return HFILE_ERROR16
;
1298 TRACE("%d (handle32=%p)\n", hFile
, dos_handles
[hFile
] );
1299 CloseHandle( dos_handles
[hFile
] );
1300 dos_handles
[hFile
] = 0;
1305 /******************************************************************
1306 * FILE_ReadWriteApc (internal)
1310 static void WINAPI
FILE_ReadWriteApc(void* apc_user
, PIO_STATUS_BLOCK io_status
, ULONG len
)
1312 LPOVERLAPPED_COMPLETION_ROUTINE cr
= (LPOVERLAPPED_COMPLETION_ROUTINE
)apc_user
;
1314 cr(RtlNtStatusToDosError(io_status
->u
.Status
), len
, (LPOVERLAPPED
)io_status
);
1317 /***********************************************************************
1318 * ReadFileEx (KERNEL32.@)
1320 BOOL WINAPI
ReadFileEx(HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1321 LPOVERLAPPED overlapped
,
1322 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1324 LARGE_INTEGER offset
;
1326 PIO_STATUS_BLOCK io_status
;
1330 SetLastError(ERROR_INVALID_PARAMETER
);
1334 offset
.s
.LowPart
= overlapped
->Offset
;
1335 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1336 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1337 io_status
->u
.Status
= STATUS_PENDING
;
1339 status
= NtReadFile(hFile
, NULL
, FILE_ReadWriteApc
, lpCompletionRoutine
,
1340 io_status
, buffer
, bytesToRead
, &offset
, NULL
);
1344 SetLastError( RtlNtStatusToDosError(status
) );
1350 /***********************************************************************
1351 * ReadFile (KERNEL32.@)
1353 BOOL WINAPI
ReadFile( HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1354 LPDWORD bytesRead
, LPOVERLAPPED overlapped
)
1356 LARGE_INTEGER offset
;
1357 PLARGE_INTEGER poffset
= NULL
;
1358 IO_STATUS_BLOCK iosb
;
1359 PIO_STATUS_BLOCK io_status
= &iosb
;
1363 TRACE("%p %p %ld %p %p\n", hFile
, buffer
, bytesToRead
,
1364 bytesRead
, overlapped
);
1366 if (bytesRead
) *bytesRead
= 0; /* Do this before anything else */
1367 if (!bytesToRead
) return TRUE
;
1369 if (IsBadReadPtr(buffer
, bytesToRead
))
1371 SetLastError(ERROR_WRITE_FAULT
); /* FIXME */
1374 if (is_console_handle(hFile
))
1375 return ReadConsoleA(hFile
, buffer
, bytesToRead
, bytesRead
, NULL
);
1377 if (overlapped
!= NULL
)
1379 offset
.s
.LowPart
= overlapped
->Offset
;
1380 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1382 hEvent
= overlapped
->hEvent
;
1383 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1385 io_status
->u
.Status
= STATUS_PENDING
;
1386 io_status
->Information
= 0;
1388 status
= NtReadFile(hFile
, hEvent
, NULL
, NULL
, io_status
, buffer
, bytesToRead
, poffset
, NULL
);
1390 if (status
!= STATUS_PENDING
&& bytesRead
)
1391 *bytesRead
= io_status
->Information
;
1393 if (status
&& status
!= STATUS_END_OF_FILE
)
1395 SetLastError( RtlNtStatusToDosError(status
) );
1402 /***********************************************************************
1403 * WriteFileEx (KERNEL32.@)
1405 BOOL WINAPI
WriteFileEx(HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1406 LPOVERLAPPED overlapped
,
1407 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1409 LARGE_INTEGER offset
;
1411 PIO_STATUS_BLOCK io_status
;
1413 TRACE("%p %p %ld %p %p\n",
1414 hFile
, buffer
, bytesToWrite
, overlapped
, lpCompletionRoutine
);
1416 if (overlapped
== NULL
)
1418 SetLastError(ERROR_INVALID_PARAMETER
);
1421 offset
.s
.LowPart
= overlapped
->Offset
;
1422 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1424 io_status
= (PIO_STATUS_BLOCK
)overlapped
;
1425 io_status
->u
.Status
= STATUS_PENDING
;
1427 status
= NtWriteFile(hFile
, NULL
, FILE_ReadWriteApc
, lpCompletionRoutine
,
1428 io_status
, buffer
, bytesToWrite
, &offset
, NULL
);
1430 if (status
) SetLastError( RtlNtStatusToDosError(status
) );
1434 /***********************************************************************
1435 * WriteFile (KERNEL32.@)
1437 BOOL WINAPI
WriteFile( HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1438 LPDWORD bytesWritten
, LPOVERLAPPED overlapped
)
1440 HANDLE hEvent
= NULL
;
1441 LARGE_INTEGER offset
;
1442 PLARGE_INTEGER poffset
= NULL
;
1444 IO_STATUS_BLOCK iosb
;
1445 PIO_STATUS_BLOCK piosb
= &iosb
;
1447 TRACE("%p %p %ld %p %p\n",
1448 hFile
, buffer
, bytesToWrite
, bytesWritten
, overlapped
);
1450 if (is_console_handle(hFile
))
1451 return WriteConsoleA(hFile
, buffer
, bytesToWrite
, bytesWritten
, NULL
);
1453 if (IsBadReadPtr(buffer
, bytesToWrite
))
1455 SetLastError(ERROR_READ_FAULT
); /* FIXME */
1461 offset
.s
.LowPart
= overlapped
->Offset
;
1462 offset
.s
.HighPart
= overlapped
->OffsetHigh
;
1464 hEvent
= overlapped
->hEvent
;
1465 piosb
= (PIO_STATUS_BLOCK
)overlapped
;
1467 piosb
->u
.Status
= STATUS_PENDING
;
1468 piosb
->Information
= 0;
1470 status
= NtWriteFile(hFile
, hEvent
, NULL
, NULL
, piosb
,
1471 buffer
, bytesToWrite
, poffset
, NULL
);
1474 SetLastError( RtlNtStatusToDosError(status
) );
1477 if (bytesWritten
) *bytesWritten
= piosb
->Information
;
1483 /***********************************************************************
1484 * SetFilePointer (KERNEL32.@)
1486 DWORD WINAPI
SetFilePointer( HANDLE hFile
, LONG distance
, LONG
*highword
,
1489 DWORD ret
= INVALID_SET_FILE_POINTER
;
1491 TRACE("handle %p offset %ld high %ld origin %ld\n",
1492 hFile
, distance
, highword
?*highword
:0, method
);
1494 SERVER_START_REQ( set_file_pointer
)
1496 req
->handle
= hFile
;
1497 req
->low
= distance
;
1498 req
->high
= highword
? *highword
: (distance
>= 0) ? 0 : -1;
1499 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1500 req
->whence
= method
;
1502 if (!wine_server_call_err( req
))
1504 ret
= reply
->new_low
;
1505 if (highword
) *highword
= reply
->new_high
;
1513 /*************************************************************************
1514 * SetHandleCount (KERNEL32.@)
1516 UINT WINAPI
SetHandleCount( UINT count
)
1518 return min( 256, count
);
1522 /**************************************************************************
1523 * SetEndOfFile (KERNEL32.@)
1525 BOOL WINAPI
SetEndOfFile( HANDLE hFile
)
1528 SERVER_START_REQ( truncate_file
)
1530 req
->handle
= hFile
;
1531 ret
= !wine_server_call_err( req
);
1538 /***********************************************************************
1539 * DeleteFileW (KERNEL32.@)
1541 BOOL WINAPI
DeleteFileW( LPCWSTR path
)
1543 DOS_FULL_NAME full_name
;
1546 TRACE("%s\n", debugstr_w(path
) );
1547 if (!path
|| !*path
)
1549 SetLastError(ERROR_PATH_NOT_FOUND
);
1552 if (DOSFS_GetDevice( path
))
1554 WARN("cannot remove DOS device %s!\n", debugstr_w(path
));
1555 SetLastError( ERROR_FILE_NOT_FOUND
);
1559 if (!DOSFS_GetFullName( path
, TRUE
, &full_name
)) return FALSE
;
1561 /* check if we are allowed to delete the source */
1562 hFile
= FILE_CreateFile( full_name
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1563 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1564 GetDriveTypeW( full_name
.short_name
) );
1565 if (!hFile
) return FALSE
;
1567 if (unlink( full_name
.long_name
) == -1)
1578 /***********************************************************************
1579 * DeleteFileA (KERNEL32.@)
1581 BOOL WINAPI
DeleteFileA( LPCSTR path
)
1583 UNICODE_STRING pathW
;
1588 SetLastError(ERROR_INVALID_PARAMETER
);
1592 if (RtlCreateUnicodeStringFromAsciiz(&pathW
, path
))
1594 ret
= DeleteFileW(pathW
.Buffer
);
1595 RtlFreeUnicodeString(&pathW
);
1598 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1603 /***********************************************************************
1604 * GetFileType (KERNEL32.@)
1606 DWORD WINAPI
GetFileType( HANDLE hFile
)
1608 DWORD ret
= FILE_TYPE_UNKNOWN
;
1610 if (is_console_handle( hFile
))
1611 return FILE_TYPE_CHAR
;
1613 SERVER_START_REQ( get_file_info
)
1615 req
->handle
= hFile
;
1616 if (!wine_server_call_err( req
)) ret
= reply
->type
;
1623 /* check if a file name is for an executable file (.exe or .com) */
1624 inline static BOOL
is_executable( const char *name
)
1626 int len
= strlen(name
);
1628 if (len
< 4) return FALSE
;
1629 return (!strcasecmp( name
+ len
- 4, ".exe" ) ||
1630 !strcasecmp( name
+ len
- 4, ".com" ));
1634 /***********************************************************************
1635 * FILE_AddBootRenameEntry
1637 * Adds an entry to the registry that is loaded when windows boots and
1638 * checks if there are some files to be removed or renamed/moved.
1639 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
1640 * non-NULL then the file is moved, otherwise it is deleted. The
1641 * entry of the registrykey is always appended with two zero
1642 * terminated strings. If <fn2> is NULL then the second entry is
1643 * simply a single 0-byte. Otherwise the second filename goes
1644 * there. The entries are prepended with \??\ before the path and the
1645 * second filename gets also a '!' as the first character if
1646 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
1647 * 0-byte follows to indicate the end of the strings.
1649 * \??\D:\test\file1[0]
1650 * !\??\D:\test\file1_renamed[0]
1651 * \??\D:\Test|delete[0]
1652 * [0] <- file is to be deleted, second string empty
1653 * \??\D:\test\file2[0]
1654 * !\??\D:\test\file2_renamed[0]
1655 * [0] <- indicates end of strings
1658 * \??\D:\test\file1[0]
1659 * !\??\D:\test\file1_renamed[0]
1660 * \??\D:\Test|delete[0]
1661 * [0] <- file is to be deleted, second string empty
1662 * [0] <- indicates end of strings
1665 static BOOL
FILE_AddBootRenameEntry( LPCWSTR fn1
, LPCWSTR fn2
, DWORD flags
)
1667 static const WCHAR PreString
[] = {'\\','?','?','\\',0};
1668 static const WCHAR ValueName
[] = {'P','e','n','d','i','n','g',
1669 'F','i','l','e','R','e','n','a','m','e',
1670 'O','p','e','r','a','t','i','o','n','s',0};
1671 static const WCHAR SessionW
[] = {'M','a','c','h','i','n','e','\\',
1672 'S','y','s','t','e','m','\\',
1673 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
1674 'C','o','n','t','r','o','l','\\',
1675 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r',0};
1676 static const int info_size
= FIELD_OFFSET( KEY_VALUE_PARTIAL_INFORMATION
, Data
);
1678 OBJECT_ATTRIBUTES attr
;
1679 UNICODE_STRING nameW
;
1680 KEY_VALUE_PARTIAL_INFORMATION
*info
;
1683 DWORD len0
, len1
, len2
;
1685 BYTE
*Buffer
= NULL
;
1688 attr
.Length
= sizeof(attr
);
1689 attr
.RootDirectory
= 0;
1690 attr
.ObjectName
= &nameW
;
1691 attr
.Attributes
= 0;
1692 attr
.SecurityDescriptor
= NULL
;
1693 attr
.SecurityQualityOfService
= NULL
;
1694 RtlInitUnicodeString( &nameW
, SessionW
);
1696 if (NtCreateKey( &Reboot
, KEY_ALL_ACCESS
, &attr
, 0, NULL
, 0, NULL
) != STATUS_SUCCESS
)
1698 WARN("Error creating key for reboot managment [%s]\n",
1699 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
1703 len0
= strlenW(PreString
);
1704 len1
= strlenW(fn1
) + len0
+ 1;
1707 len2
= strlenW(fn2
) + len0
+ 1;
1708 if (flags
& MOVEFILE_REPLACE_EXISTING
) len2
++; /* Plus 1 because of the leading '!' */
1710 else len2
= 1; /* minimum is the 0 characters for the empty second string */
1712 /* convert characters to bytes */
1713 len0
*= sizeof(WCHAR
);
1714 len1
*= sizeof(WCHAR
);
1715 len2
*= sizeof(WCHAR
);
1717 RtlInitUnicodeString( &nameW
, ValueName
);
1719 /* First we check if the key exists and if so how many bytes it already contains. */
1720 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
1721 NULL
, 0, &DataSize
) == STATUS_BUFFER_OVERFLOW
)
1723 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
1725 if (NtQueryValueKey( Reboot
, &nameW
, KeyValuePartialInformation
,
1726 Buffer
, DataSize
, &DataSize
)) goto Quit
;
1727 info
= (KEY_VALUE_PARTIAL_INFORMATION
*)Buffer
;
1728 if (info
->Type
!= REG_MULTI_SZ
) goto Quit
;
1729 if (DataSize
> sizeof(info
)) DataSize
-= sizeof(WCHAR
); /* remove terminating null (will be added back later) */
1733 DataSize
= info_size
;
1734 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ sizeof(WCHAR
) )))
1738 p
= (WCHAR
*)(Buffer
+ DataSize
);
1739 strcpyW( p
, PreString
);
1744 p
= (WCHAR
*)(Buffer
+ DataSize
);
1745 if (flags
& MOVEFILE_REPLACE_EXISTING
)
1747 strcpyW( p
, PreString
);
1753 p
= (WCHAR
*)(Buffer
+ DataSize
);
1755 DataSize
+= sizeof(WCHAR
);
1758 /* add final null */
1759 p
= (WCHAR
*)(Buffer
+ DataSize
);
1761 DataSize
+= sizeof(WCHAR
);
1763 rc
= !NtSetValueKey(Reboot
, &nameW
, 0, REG_MULTI_SZ
, Buffer
+ info_size
, DataSize
- info_size
);
1766 if (Reboot
) NtClose(Reboot
);
1767 if (Buffer
) HeapFree( GetProcessHeap(), 0, Buffer
);
1772 /**************************************************************************
1773 * MoveFileExW (KERNEL32.@)
1775 BOOL WINAPI
MoveFileExW( LPCWSTR fn1
, LPCWSTR fn2
, DWORD flag
)
1777 DOS_FULL_NAME full_name1
, full_name2
;
1779 DWORD attr
= INVALID_FILE_ATTRIBUTES
;
1781 TRACE("(%s,%s,%04lx)\n", debugstr_w(fn1
), debugstr_w(fn2
), flag
);
1783 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
1784 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
1785 to be really compatible. Most programs wont have any problems though. In case
1786 you encounter one, this is what you should return here. I don't know what's up
1787 with NT 3.5. Is this function available there or not?
1788 Does anybody really care about 3.5? :)
1791 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
1792 if the source file has to be deleted.
1795 SetLastError(ERROR_INVALID_PARAMETER
);
1799 /* This function has to be run through in order to process the name properly.
1800 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
1801 that is the behaviour on NT 4.0. The operation accepts the filenames as
1802 they are given but it can't reply with a reasonable returncode. Success
1803 means in that case success for entering the values into the registry.
1805 if(!DOSFS_GetFullName( fn1
, TRUE
, &full_name1
))
1807 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1811 if (fn2
) /* !fn2 means delete fn1 */
1813 if (DOSFS_GetFullName( fn2
, TRUE
, &full_name2
))
1815 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1817 /* target exists, check if we may overwrite */
1818 if (!(flag
& MOVEFILE_REPLACE_EXISTING
))
1820 SetLastError( ERROR_ALREADY_EXISTS
);
1827 if (!DOSFS_GetFullName( fn2
, FALSE
, &full_name2
))
1829 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
1834 /* Source name and target path are valid */
1836 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
1838 return FILE_AddBootRenameEntry( fn1
, fn2
, flag
);
1841 attr
= GetFileAttributesW( fn1
);
1842 if ( attr
== INVALID_FILE_ATTRIBUTES
) return FALSE
;
1844 /* check if we are allowed to rename the source */
1845 hFile
= FILE_CreateFile( full_name1
.long_name
, 0, 0,
1846 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1847 GetDriveTypeW( full_name1
.short_name
) );
1850 if (GetLastError() != ERROR_ACCESS_DENIED
) return FALSE
;
1851 if ( !(attr
& FILE_ATTRIBUTE_DIRECTORY
) ) return FALSE
;
1852 /* if it's a directory we can continue */
1854 else CloseHandle(hFile
);
1856 /* check, if we are allowed to delete the destination,
1857 ** (but the file not being there is fine) */
1858 hFile
= FILE_CreateFile( full_name2
.long_name
, GENERIC_READ
|GENERIC_WRITE
, 0,
1859 NULL
, OPEN_EXISTING
, 0, 0, TRUE
,
1860 GetDriveTypeW( full_name2
.short_name
) );
1861 if(!hFile
&& GetLastError() != ERROR_FILE_NOT_FOUND
) return FALSE
;
1864 if (full_name1
.drive
!= full_name2
.drive
)
1866 if (!(flag
& MOVEFILE_COPY_ALLOWED
))
1868 SetLastError( ERROR_NOT_SAME_DEVICE
);
1871 else if ( attr
& FILE_ATTRIBUTE_DIRECTORY
)
1873 /* Strange, but that's what Windows returns */
1874 SetLastError ( ERROR_ACCESS_DENIED
);
1878 if (rename( full_name1
.long_name
, full_name2
.long_name
) == -1)
1879 /* Try copy/delete unless it's a directory. */
1880 /* FIXME: This does not handle the (unlikely) case that the two locations
1881 are on the same Wine drive, but on different Unix file systems. */
1883 if ( attr
& FILE_ATTRIBUTE_DIRECTORY
)
1890 if ( ! CopyFileW( fn1
, fn2
, !(flag
& MOVEFILE_REPLACE_EXISTING
) ))
1892 if ( ! DeleteFileW ( fn1
) )
1896 if (is_executable( full_name1
.long_name
) != is_executable( full_name2
.long_name
))
1899 if (stat( full_name2
.long_name
, &fstat
) != -1)
1901 if (is_executable( full_name2
.long_name
))
1902 /* set executable bit where read bit is set */
1903 fstat
.st_mode
|= (fstat
.st_mode
& 0444) >> 2;
1905 fstat
.st_mode
&= ~0111;
1906 chmod( full_name2
.long_name
, fstat
.st_mode
);
1911 else /* fn2 == NULL means delete source */
1913 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
1915 if (flag
& MOVEFILE_COPY_ALLOWED
) {
1916 WARN("Illegal flag\n");
1917 SetLastError( ERROR_GEN_FAILURE
);
1921 return FILE_AddBootRenameEntry( fn1
, NULL
, flag
);
1924 if (unlink( full_name1
.long_name
) == -1)
1929 return TRUE
; /* successfully deleted */
1933 /**************************************************************************
1934 * MoveFileExA (KERNEL32.@)
1936 BOOL WINAPI
MoveFileExA( LPCSTR fn1
, LPCSTR fn2
, DWORD flag
)
1938 UNICODE_STRING fn1W
, fn2W
;
1943 SetLastError(ERROR_INVALID_PARAMETER
);
1947 RtlCreateUnicodeStringFromAsciiz(&fn1W
, fn1
);
1948 if (fn2
) RtlCreateUnicodeStringFromAsciiz(&fn2W
, fn2
);
1949 else fn2W
.Buffer
= NULL
;
1951 ret
= MoveFileExW( fn1W
.Buffer
, fn2W
.Buffer
, flag
);
1953 RtlFreeUnicodeString(&fn1W
);
1954 RtlFreeUnicodeString(&fn2W
);
1959 /**************************************************************************
1960 * MoveFileW (KERNEL32.@)
1962 * Move file or directory
1964 BOOL WINAPI
MoveFileW( LPCWSTR fn1
, LPCWSTR fn2
)
1966 return MoveFileExW( fn1
, fn2
, MOVEFILE_COPY_ALLOWED
);
1970 /**************************************************************************
1971 * MoveFileA (KERNEL32.@)
1973 BOOL WINAPI
MoveFileA( LPCSTR fn1
, LPCSTR fn2
)
1975 return MoveFileExA( fn1
, fn2
, MOVEFILE_COPY_ALLOWED
);
1979 /**************************************************************************
1980 * CopyFileW (KERNEL32.@)
1982 BOOL WINAPI
CopyFileW( LPCWSTR source
, LPCWSTR dest
, BOOL fail_if_exists
)
1985 BY_HANDLE_FILE_INFORMATION info
;
1990 if (!source
|| !dest
)
1992 SetLastError(ERROR_INVALID_PARAMETER
);
1996 TRACE("%s -> %s\n", debugstr_w(source
), debugstr_w(dest
));
1998 if ((h1
= CreateFileW(source
, GENERIC_READ
, FILE_SHARE_READ
| FILE_SHARE_WRITE
,
1999 NULL
, OPEN_EXISTING
, 0, 0)) == INVALID_HANDLE_VALUE
)
2001 WARN("Unable to open source %s\n", debugstr_w(source
));
2005 if (!GetFileInformationByHandle( h1
, &info
))
2007 WARN("GetFileInformationByHandle returned error for %s\n", debugstr_w(source
));
2012 if ((h2
= CreateFileW( dest
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
2013 fail_if_exists
? CREATE_NEW
: CREATE_ALWAYS
,
2014 info
.dwFileAttributes
, h1
)) == INVALID_HANDLE_VALUE
)
2016 WARN("Unable to open dest %s\n", debugstr_w(dest
));
2021 while (ReadFile( h1
, buffer
, sizeof(buffer
), &count
, NULL
) && count
)
2027 if (!WriteFile( h2
, p
, count
, &res
, NULL
) || !res
) goto done
;
2040 /**************************************************************************
2041 * CopyFileA (KERNEL32.@)
2043 BOOL WINAPI
CopyFileA( LPCSTR source
, LPCSTR dest
, BOOL fail_if_exists
)
2045 UNICODE_STRING sourceW
, destW
;
2048 if (!source
|| !dest
)
2050 SetLastError(ERROR_INVALID_PARAMETER
);
2054 RtlCreateUnicodeStringFromAsciiz(&sourceW
, source
);
2055 RtlCreateUnicodeStringFromAsciiz(&destW
, dest
);
2057 ret
= CopyFileW(sourceW
.Buffer
, destW
.Buffer
, fail_if_exists
);
2059 RtlFreeUnicodeString(&sourceW
);
2060 RtlFreeUnicodeString(&destW
);
2065 /**************************************************************************
2066 * CopyFileExW (KERNEL32.@)
2068 * This implementation ignores most of the extra parameters passed-in into
2069 * the "ex" version of the method and calls the CopyFile method.
2070 * It will have to be fixed eventually.
2072 BOOL WINAPI
CopyFileExW(LPCWSTR sourceFilename
, LPCWSTR destFilename
,
2073 LPPROGRESS_ROUTINE progressRoutine
, LPVOID appData
,
2074 LPBOOL cancelFlagPointer
, DWORD copyFlags
)
2077 * Interpret the only flag that CopyFile can interpret.
2079 return CopyFileW(sourceFilename
, destFilename
, (copyFlags
& COPY_FILE_FAIL_IF_EXISTS
) != 0);
2083 /**************************************************************************
2084 * CopyFileExA (KERNEL32.@)
2086 BOOL WINAPI
CopyFileExA(LPCSTR sourceFilename
, LPCSTR destFilename
,
2087 LPPROGRESS_ROUTINE progressRoutine
, LPVOID appData
,
2088 LPBOOL cancelFlagPointer
, DWORD copyFlags
)
2090 UNICODE_STRING sourceW
, destW
;
2093 if (!sourceFilename
|| !destFilename
)
2095 SetLastError(ERROR_INVALID_PARAMETER
);
2099 RtlCreateUnicodeStringFromAsciiz(&sourceW
, sourceFilename
);
2100 RtlCreateUnicodeStringFromAsciiz(&destW
, destFilename
);
2102 ret
= CopyFileExW(sourceW
.Buffer
, destW
.Buffer
, progressRoutine
, appData
,
2103 cancelFlagPointer
, copyFlags
);
2105 RtlFreeUnicodeString(&sourceW
);
2106 RtlFreeUnicodeString(&destW
);
2111 /***********************************************************************
2112 * SetFileTime (KERNEL32.@)
2114 BOOL WINAPI
SetFileTime( HANDLE hFile
,
2115 const FILETIME
*lpCreationTime
,
2116 const FILETIME
*lpLastAccessTime
,
2117 const FILETIME
*lpLastWriteTime
)
2120 SERVER_START_REQ( set_file_time
)
2122 req
->handle
= hFile
;
2123 if (lpLastAccessTime
)
2124 RtlTimeToSecondsSince1970( (PLARGE_INTEGER
) lpLastAccessTime
, (DWORD
*)&req
->access_time
);
2126 req
->access_time
= 0; /* FIXME */
2127 if (lpLastWriteTime
)
2128 RtlTimeToSecondsSince1970( (PLARGE_INTEGER
) lpLastWriteTime
, (DWORD
*)&req
->write_time
);
2130 req
->write_time
= 0; /* FIXME */
2131 ret
= !wine_server_call_err( req
);
2138 /**************************************************************************
2139 * GetFileAttributesExW (KERNEL32.@)
2141 BOOL WINAPI
GetFileAttributesExW(
2142 LPCWSTR lpFileName
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
2143 LPVOID lpFileInformation
)
2145 DOS_FULL_NAME full_name
;
2146 BY_HANDLE_FILE_INFORMATION info
;
2148 if (!lpFileName
|| !lpFileInformation
)
2150 SetLastError(ERROR_INVALID_PARAMETER
);
2154 if (fInfoLevelId
== GetFileExInfoStandard
) {
2155 LPWIN32_FILE_ATTRIBUTE_DATA lpFad
=
2156 (LPWIN32_FILE_ATTRIBUTE_DATA
) lpFileInformation
;
2157 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
)) return FALSE
;
2158 if (!FILE_Stat( full_name
.long_name
, &info
, NULL
)) return FALSE
;
2160 lpFad
->dwFileAttributes
= info
.dwFileAttributes
;
2161 lpFad
->ftCreationTime
= info
.ftCreationTime
;
2162 lpFad
->ftLastAccessTime
= info
.ftLastAccessTime
;
2163 lpFad
->ftLastWriteTime
= info
.ftLastWriteTime
;
2164 lpFad
->nFileSizeHigh
= info
.nFileSizeHigh
;
2165 lpFad
->nFileSizeLow
= info
.nFileSizeLow
;
2168 FIXME("invalid info level %d!\n", fInfoLevelId
);
2176 /**************************************************************************
2177 * GetFileAttributesExA (KERNEL32.@)
2179 BOOL WINAPI
GetFileAttributesExA(
2180 LPCSTR filename
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
2181 LPVOID lpFileInformation
)
2183 UNICODE_STRING filenameW
;
2186 if (!filename
|| !lpFileInformation
)
2188 SetLastError(ERROR_INVALID_PARAMETER
);
2192 if (RtlCreateUnicodeStringFromAsciiz(&filenameW
, filename
))
2194 ret
= GetFileAttributesExW(filenameW
.Buffer
, fInfoLevelId
, lpFileInformation
);
2195 RtlFreeUnicodeString(&filenameW
);
2198 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);