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"
36 #ifdef HAVE_SYS_ERRNO_H
37 #include <sys/errno.h>
39 #include <sys/types.h>
41 #ifdef HAVE_SYS_MMAN_H
53 #include "wine/winbase16.h"
54 #include "wine/server.h"
64 #include "wine/debug.h"
66 WINE_DEFAULT_DEBUG_CHANNEL(file
);
68 #if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
69 #define MAP_ANON MAP_ANONYMOUS
72 /* Size of per-process table of DOS handles */
73 #define DOS_TABLE_SIZE 256
75 /* Macro to derive file offset from OVERLAPPED struct */
76 #define OVERLAPPED_OFFSET(overlapped) ((off_t) (overlapped)->Offset + ((off_t) (overlapped)->OffsetHigh << 32))
78 static HANDLE dos_handles
[DOS_TABLE_SIZE
];
82 extern HANDLE WINAPI
FILE_SmbOpen(LPCSTR name
);
84 /***********************************************************************
85 * Asynchronous file I/O *
87 static DWORD
fileio_get_async_status (const async_private
*ovp
);
88 static DWORD
fileio_get_async_count (const async_private
*ovp
);
89 static void fileio_set_async_status (async_private
*ovp
, const DWORD status
);
90 static void CALLBACK
fileio_call_completion_func (ULONG_PTR data
);
91 static void fileio_async_cleanup (async_private
*ovp
);
93 static async_ops fileio_async_ops
=
95 fileio_get_async_status
, /* get_status */
96 fileio_set_async_status
, /* set_status */
97 fileio_get_async_count
, /* get_count */
98 fileio_call_completion_func
, /* call_completion */
99 fileio_async_cleanup
/* cleanup */
102 static async_ops fileio_nocomp_async_ops
=
104 fileio_get_async_status
, /* get_status */
105 fileio_set_async_status
, /* set_status */
106 fileio_get_async_count
, /* get_count */
107 NULL
, /* call_completion */
108 fileio_async_cleanup
/* cleanup */
111 typedef struct async_fileio
113 struct async_private async
;
114 LPOVERLAPPED lpOverlapped
;
115 LPOVERLAPPED_COMPLETION_ROUTINE completion_func
;
118 enum fd_type fd_type
;
121 static DWORD
fileio_get_async_status (const struct async_private
*ovp
)
123 return ((async_fileio
*) ovp
)->lpOverlapped
->Internal
;
126 static void fileio_set_async_status (async_private
*ovp
, const DWORD status
)
128 ((async_fileio
*) ovp
)->lpOverlapped
->Internal
= status
;
131 static DWORD
fileio_get_async_count (const struct async_private
*ovp
)
133 async_fileio
*fileio
= (async_fileio
*) ovp
;
134 DWORD ret
= fileio
->count
- fileio
->lpOverlapped
->InternalHigh
;
135 return (ret
< 0 ? 0 : ret
);
138 static void CALLBACK
fileio_call_completion_func (ULONG_PTR data
)
140 async_fileio
*ovp
= (async_fileio
*) data
;
141 TRACE ("data: %p\n", ovp
);
143 ovp
->completion_func( ovp
->lpOverlapped
->Internal
,
144 ovp
->lpOverlapped
->InternalHigh
,
147 fileio_async_cleanup ( &ovp
->async
);
150 static void fileio_async_cleanup ( struct async_private
*ovp
)
152 HeapFree ( GetProcessHeap(), 0, ovp
);
155 /***********************************************************************
158 * Convert OF_* mode into flags for CreateFile.
160 static void FILE_ConvertOFMode( INT mode
, DWORD
*access
, DWORD
*sharing
)
164 case OF_READ
: *access
= GENERIC_READ
; break;
165 case OF_WRITE
: *access
= GENERIC_WRITE
; break;
166 case OF_READWRITE
: *access
= GENERIC_READ
| GENERIC_WRITE
; break;
167 default: *access
= 0; break;
171 case OF_SHARE_EXCLUSIVE
: *sharing
= 0; break;
172 case OF_SHARE_DENY_WRITE
: *sharing
= FILE_SHARE_READ
; break;
173 case OF_SHARE_DENY_READ
: *sharing
= FILE_SHARE_WRITE
; break;
174 case OF_SHARE_DENY_NONE
:
175 case OF_SHARE_COMPAT
:
176 default: *sharing
= FILE_SHARE_READ
| FILE_SHARE_WRITE
; break;
181 /***********************************************************************
184 * locale-independent case conversion for file I/O
186 int FILE_strcasecmp( const char *str1
, const char *str2
)
190 int ret
= FILE_toupper(*str1
) - FILE_toupper(*str2
);
191 if (ret
|| !*str1
) return ret
;
198 /***********************************************************************
201 * locale-independent case conversion for file I/O
203 int FILE_strncasecmp( const char *str1
, const char *str2
, int len
)
206 for ( ; len
> 0; len
--, str1
++, str2
++)
207 if ((ret
= FILE_toupper(*str1
) - FILE_toupper(*str2
)) || !*str1
) break;
212 /***********************************************************************
213 * FILE_GetNtStatus(void)
215 * Retrieve the Nt Status code from errno.
216 * Try to be consistent with FILE_SetDosError().
218 DWORD
FILE_GetNtStatus(void)
222 TRACE ( "errno = %d\n", errno
);
225 case EAGAIN
: nt
= STATUS_SHARING_VIOLATION
; break;
226 case EBADF
: nt
= STATUS_INVALID_HANDLE
; break;
227 case ENOSPC
: nt
= STATUS_DISK_FULL
; break;
230 case EACCES
: nt
= STATUS_ACCESS_DENIED
; break;
231 case ENOENT
: nt
= STATUS_SHARING_VIOLATION
; break;
232 case EISDIR
: nt
= STATUS_FILE_IS_A_DIRECTORY
; break;
234 case ENFILE
: nt
= STATUS_NO_MORE_FILES
; break;
236 case ENOTEMPTY
: nt
= STATUS_DIRECTORY_NOT_EMPTY
; break;
237 case EPIPE
: nt
= STATUS_PIPE_BROKEN
; break;
238 case ENOEXEC
: /* ?? */
239 case ESPIPE
: /* ?? */
240 case EEXIST
: /* ?? */
242 FIXME ( "Converting errno %d to STATUS_UNSUCCESSFUL\n", err
);
243 nt
= STATUS_UNSUCCESSFUL
;
248 /***********************************************************************
251 * Set the DOS error code from errno.
253 void FILE_SetDosError(void)
255 int save_errno
= errno
; /* errno gets overwritten by printf */
257 TRACE("errno = %d %s\n", errno
, strerror(errno
));
261 SetLastError( ERROR_SHARING_VIOLATION
);
264 SetLastError( ERROR_INVALID_HANDLE
);
267 SetLastError( ERROR_HANDLE_DISK_FULL
);
272 SetLastError( ERROR_ACCESS_DENIED
);
275 SetLastError( ERROR_LOCK_VIOLATION
);
278 SetLastError( ERROR_FILE_NOT_FOUND
);
281 SetLastError( ERROR_CANNOT_MAKE
);
285 SetLastError( ERROR_NO_MORE_FILES
);
288 SetLastError( ERROR_FILE_EXISTS
);
292 SetLastError( ERROR_SEEK
);
295 SetLastError( ERROR_DIR_NOT_EMPTY
);
298 SetLastError( ERROR_BAD_FORMAT
);
301 WARN("unknown file error: %s\n", strerror(save_errno
) );
302 SetLastError( ERROR_GEN_FAILURE
);
309 /***********************************************************************
312 * Duplicate a Unix handle into a task handle.
313 * Returns 0 on failure.
315 HANDLE
FILE_DupUnixHandle( int fd
, DWORD access
, BOOL inherit
)
319 wine_server_send_fd( fd
);
321 SERVER_START_REQ( alloc_file_handle
)
323 req
->access
= access
;
324 req
->inherit
= inherit
;
326 wine_server_call( req
);
334 /***********************************************************************
335 * FILE_GetUnixHandleType
337 * Retrieve the Unix handle corresponding to a file handle.
338 * Returns -1 on failure.
340 static int FILE_GetUnixHandleType( HANDLE handle
, DWORD access
, enum fd_type
*type
, int *flags_ptr
)
342 int ret
, flags
, fd
= -1;
344 ret
= wine_server_handle_to_fd( handle
, access
, &fd
, type
, &flags
);
345 if (flags_ptr
) *flags_ptr
= flags
;
346 if (ret
) SetLastError( RtlNtStatusToDosError(ret
) );
347 else if (((access
& GENERIC_READ
) && (flags
& FD_FLAG_RECV_SHUTDOWN
)) ||
348 ((access
& GENERIC_WRITE
) && (flags
& FD_FLAG_SEND_SHUTDOWN
)))
351 SetLastError ( ERROR_PIPE_NOT_CONNECTED
);
357 /***********************************************************************
360 * Retrieve the Unix handle corresponding to a file handle.
361 * Returns -1 on failure.
363 int FILE_GetUnixHandle( HANDLE handle
, DWORD access
)
365 return FILE_GetUnixHandleType( handle
, access
, NULL
, NULL
);
368 /*************************************************************************
371 * Open a handle to the current process console.
372 * Returns 0 on failure.
374 static HANDLE
FILE_OpenConsole( BOOL output
, DWORD access
, DWORD sharing
, LPSECURITY_ATTRIBUTES sa
)
378 SERVER_START_REQ( open_console
)
381 req
->access
= access
;
382 req
->share
= sharing
;
383 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
385 wine_server_call_err( req
);
393 /***********************************************************************
396 * Implementation of CreateFile. Takes a Unix path name.
397 * Returns 0 on failure.
399 HANDLE
FILE_CreateFile( LPCSTR filename
, DWORD access
, DWORD sharing
,
400 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
401 DWORD attributes
, HANDLE
template, BOOL fail_read_only
,
409 SERVER_START_REQ( create_file
)
411 req
->access
= access
;
412 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
413 req
->sharing
= sharing
;
414 req
->create
= creation
;
415 req
->attrs
= attributes
;
416 req
->drive_type
= drive_type
;
417 wine_server_add_data( req
, filename
, strlen(filename
) );
419 err
= wine_server_call( req
);
424 /* If write access failed, retry without GENERIC_WRITE */
426 if (!ret
&& !fail_read_only
&& (access
& GENERIC_WRITE
))
428 if ((err
== STATUS_MEDIA_WRITE_PROTECTED
) || (err
== STATUS_ACCESS_DENIED
))
430 TRACE("Write access failed for file '%s', trying without "
431 "write access\n", filename
);
432 access
&= ~GENERIC_WRITE
;
437 if (err
) SetLastError( RtlNtStatusToDosError(err
) );
439 if (!ret
) WARN("Unable to create file '%s' (GLE %ld)\n", filename
, GetLastError());
445 /***********************************************************************
448 * Same as FILE_CreateFile but for a device
449 * Returns 0 on failure.
451 HANDLE
FILE_CreateDevice( int client_id
, DWORD access
, LPSECURITY_ATTRIBUTES sa
)
454 SERVER_START_REQ( create_device
)
456 req
->access
= access
;
457 req
->inherit
= (sa
&& (sa
->nLength
>=sizeof(*sa
)) && sa
->bInheritHandle
);
460 wine_server_call_err( req
);
467 static HANDLE
FILE_OpenPipe(LPCSTR name
, DWORD access
)
469 WCHAR buffer
[MAX_PATH
];
473 if (name
&& !(len
= MultiByteToWideChar( CP_ACP
, 0, name
, strlen(name
), buffer
, MAX_PATH
)))
475 SetLastError( ERROR_FILENAME_EXCED_RANGE
);
478 SERVER_START_REQ( open_named_pipe
)
480 req
->access
= access
;
482 wine_server_add_data( req
, buffer
, len
* sizeof(WCHAR
) );
483 wine_server_call_err( req
);
487 TRACE("Returned %d\n",ret
);
491 /*************************************************************************
492 * CreateFileA [KERNEL32.@] Creates or opens a file or other object
494 * Creates or opens an object, and returns a handle that can be used to
495 * access that object.
499 * filename [in] pointer to filename to be accessed
500 * access [in] access mode requested
501 * sharing [in] share mode
502 * sa [in] pointer to security attributes
503 * creation [in] how to create the file
504 * attributes [in] attributes for newly created file
505 * template [in] handle to file with extended attributes to copy
508 * Success: Open handle to specified file
509 * Failure: INVALID_HANDLE_VALUE
512 * Should call SetLastError() on failure.
516 * Doesn't support character devices, template files, or a
517 * lot of the 'attributes' flags yet.
519 HANDLE WINAPI
CreateFileA( LPCSTR filename
, DWORD access
, DWORD sharing
,
520 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
521 DWORD attributes
, HANDLE
template )
523 DOS_FULL_NAME full_name
;
528 SetLastError( ERROR_INVALID_PARAMETER
);
529 return INVALID_HANDLE_VALUE
;
531 TRACE("%s %s%s%s%s%s%s%s\n",filename
,
532 ((access
& GENERIC_READ
)==GENERIC_READ
)?"GENERIC_READ ":"",
533 ((access
& GENERIC_WRITE
)==GENERIC_WRITE
)?"GENERIC_WRITE ":"",
534 (!access
)?"QUERY_ACCESS ":"",
535 ((sharing
& FILE_SHARE_READ
)==FILE_SHARE_READ
)?"FILE_SHARE_READ ":"",
536 ((sharing
& FILE_SHARE_WRITE
)==FILE_SHARE_WRITE
)?"FILE_SHARE_WRITE ":"",
537 ((sharing
& FILE_SHARE_DELETE
)==FILE_SHARE_DELETE
)?"FILE_SHARE_DELETE ":"",
538 (creation
==CREATE_NEW
)?"CREATE_NEW":
539 (creation
==CREATE_ALWAYS
)?"CREATE_ALWAYS ":
540 (creation
==OPEN_EXISTING
)?"OPEN_EXISTING ":
541 (creation
==OPEN_ALWAYS
)?"OPEN_ALWAYS ":
542 (creation
==TRUNCATE_EXISTING
)?"TRUNCATE_EXISTING ":"");
544 /* If the name starts with '\\?\', ignore the first 4 chars. */
545 if (!strncmp(filename
, "\\\\?\\", 4))
548 if (!strncmp(filename
, "UNC\\", 4))
550 FIXME("UNC name (%s) not supported.\n", filename
);
551 SetLastError( ERROR_PATH_NOT_FOUND
);
552 return INVALID_HANDLE_VALUE
;
556 if (!strncmp(filename
, "\\\\.\\", 4)) {
557 if(!strncasecmp(&filename
[4],"pipe\\",5))
559 TRACE("Opening a pipe: %s\n",filename
);
560 ret
= FILE_OpenPipe(filename
,access
);
563 else if (isalpha(filename
[4]) && filename
[5] == ':' && filename
[6] == '\0')
565 ret
= FILE_CreateDevice( (toupper(filename
[4]) - 'A') | 0x20000, access
, sa
);
568 else if (!DOSFS_GetDevice( filename
))
570 ret
= DEVICE_Open( filename
+4, access
, sa
);
574 filename
+=4; /* fall into DOSFS_Device case below */
577 /* If the name still starts with '\\', it's a UNC name. */
578 if (!strncmp(filename
, "\\\\", 2))
580 ret
= SMB_CreateFileA(filename
, access
, sharing
, sa
, creation
, attributes
, template );
584 /* If the name contains a DOS wild card (* or ?), do no create a file */
585 if(strchr(filename
,'*') || strchr(filename
,'?'))
586 return INVALID_HANDLE_VALUE
;
588 /* Open a console for CONIN$ or CONOUT$ */
589 if (!strcasecmp(filename
, "CONIN$"))
591 ret
= FILE_OpenConsole( FALSE
, access
, sharing
, sa
);
594 if (!strcasecmp(filename
, "CONOUT$"))
596 ret
= FILE_OpenConsole( TRUE
, access
, sharing
, sa
);
600 if (DOSFS_GetDevice( filename
))
602 TRACE("opening device '%s'\n", filename
);
604 if (!(ret
= DOSFS_OpenDevice( filename
, access
, attributes
, sa
)))
606 /* Do not silence this please. It is a critical error. -MM */
607 ERR("Couldn't open device '%s'!\n",filename
);
608 SetLastError( ERROR_FILE_NOT_FOUND
);
613 /* check for filename, don't check for last entry if creating */
614 if (!DOSFS_GetFullName( filename
,
615 (creation
== OPEN_EXISTING
) ||
616 (creation
== TRUNCATE_EXISTING
),
618 WARN("Unable to get full filename from '%s' (GLE %ld)\n",
619 filename
, GetLastError());
620 return INVALID_HANDLE_VALUE
;
623 ret
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
624 sa
, creation
, attributes
, template,
625 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
626 GetDriveTypeA( full_name
.short_name
) );
628 if (!ret
) ret
= INVALID_HANDLE_VALUE
;
634 /*************************************************************************
635 * CreateFileW (KERNEL32.@)
637 HANDLE WINAPI
CreateFileW( LPCWSTR filename
, DWORD access
, DWORD sharing
,
638 LPSECURITY_ATTRIBUTES sa
, DWORD creation
,
639 DWORD attributes
, HANDLE
template)
641 LPSTR afn
= HEAP_strdupWtoA( GetProcessHeap(), 0, filename
);
642 HANDLE res
= CreateFileA( afn
, access
, sharing
, sa
, creation
, attributes
, template );
643 HeapFree( GetProcessHeap(), 0, afn
);
648 /***********************************************************************
651 * Fill a file information from a struct stat.
653 static void FILE_FillInfo( struct stat
*st
, BY_HANDLE_FILE_INFORMATION
*info
)
655 if (S_ISDIR(st
->st_mode
))
656 info
->dwFileAttributes
= FILE_ATTRIBUTE_DIRECTORY
;
658 info
->dwFileAttributes
= FILE_ATTRIBUTE_ARCHIVE
;
659 if (!(st
->st_mode
& S_IWUSR
))
660 info
->dwFileAttributes
|= FILE_ATTRIBUTE_READONLY
;
662 RtlSecondsSince1970ToTime( st
->st_mtime
, &info
->ftCreationTime
);
663 RtlSecondsSince1970ToTime( st
->st_mtime
, &info
->ftLastWriteTime
);
664 RtlSecondsSince1970ToTime( st
->st_atime
, &info
->ftLastAccessTime
);
666 info
->dwVolumeSerialNumber
= 0; /* FIXME */
667 info
->nFileSizeHigh
= 0;
668 info
->nFileSizeLow
= 0;
669 if (!S_ISDIR(st
->st_mode
)) {
670 info
->nFileSizeHigh
= st
->st_size
>> 32;
671 info
->nFileSizeLow
= st
->st_size
& 0xffffffff;
673 info
->nNumberOfLinks
= st
->st_nlink
;
674 info
->nFileIndexHigh
= 0;
675 info
->nFileIndexLow
= st
->st_ino
;
679 /***********************************************************************
682 * Stat a Unix path name. Return TRUE if OK.
684 BOOL
FILE_Stat( LPCSTR unixName
, BY_HANDLE_FILE_INFORMATION
*info
)
688 if (lstat( unixName
, &st
) == -1)
693 if (!S_ISLNK(st
.st_mode
)) FILE_FillInfo( &st
, info
);
696 /* do a "real" stat to find out
697 about the type of the symlink destination */
698 if (stat( unixName
, &st
) == -1)
703 FILE_FillInfo( &st
, info
);
704 info
->dwFileAttributes
|= FILE_ATTRIBUTE_SYMLINK
;
710 /***********************************************************************
711 * GetFileInformationByHandle (KERNEL32.@)
713 DWORD WINAPI
GetFileInformationByHandle( HANDLE hFile
,
714 BY_HANDLE_FILE_INFORMATION
*info
)
719 SERVER_START_REQ( get_file_info
)
722 if ((ret
= !wine_server_call_err( req
)))
724 /* FIXME: which file types are supported ?
725 * Serial ports (FILE_TYPE_CHAR) are not,
726 * and MSDN also says that pipes are not supported.
727 * FILE_TYPE_REMOTE seems to be supported according to
728 * MSDN q234741.txt */
729 if ((reply
->type
== FILE_TYPE_DISK
) || (reply
->type
== FILE_TYPE_REMOTE
))
731 RtlSecondsSince1970ToTime( reply
->write_time
, &info
->ftCreationTime
);
732 RtlSecondsSince1970ToTime( reply
->write_time
, &info
->ftLastWriteTime
);
733 RtlSecondsSince1970ToTime( reply
->access_time
, &info
->ftLastAccessTime
);
734 info
->dwFileAttributes
= reply
->attr
;
735 info
->dwVolumeSerialNumber
= reply
->serial
;
736 info
->nFileSizeHigh
= reply
->size_high
;
737 info
->nFileSizeLow
= reply
->size_low
;
738 info
->nNumberOfLinks
= reply
->links
;
739 info
->nFileIndexHigh
= reply
->index_high
;
740 info
->nFileIndexLow
= reply
->index_low
;
744 SetLastError(ERROR_NOT_SUPPORTED
);
754 /**************************************************************************
755 * GetFileAttributes (KERNEL.420)
757 DWORD WINAPI
GetFileAttributes16( LPCSTR name
)
759 return GetFileAttributesA( name
);
763 /**************************************************************************
764 * GetFileAttributesA (KERNEL32.@)
766 DWORD WINAPI
GetFileAttributesA( LPCSTR name
)
768 DOS_FULL_NAME full_name
;
769 BY_HANDLE_FILE_INFORMATION info
;
773 SetLastError( ERROR_INVALID_PARAMETER
);
776 if (!DOSFS_GetFullName( name
, TRUE
, &full_name
) )
778 if (!FILE_Stat( full_name
.long_name
, &info
)) return -1;
779 return info
.dwFileAttributes
;
783 /**************************************************************************
784 * GetFileAttributesW (KERNEL32.@)
786 DWORD WINAPI
GetFileAttributesW( LPCWSTR name
)
788 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, name
);
789 DWORD res
= GetFileAttributesA( nameA
);
790 HeapFree( GetProcessHeap(), 0, nameA
);
795 /**************************************************************************
796 * SetFileAttributes (KERNEL.421)
798 BOOL16 WINAPI
SetFileAttributes16( LPCSTR lpFileName
, DWORD attributes
)
800 return SetFileAttributesA( lpFileName
, attributes
);
804 /**************************************************************************
805 * SetFileAttributesA (KERNEL32.@)
807 BOOL WINAPI
SetFileAttributesA(LPCSTR lpFileName
, DWORD attributes
)
810 DOS_FULL_NAME full_name
;
812 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
))
815 TRACE("(%s,%lx)\n",lpFileName
,attributes
);
816 if (attributes
& FILE_ATTRIBUTE_NORMAL
) {
817 attributes
&= ~FILE_ATTRIBUTE_NORMAL
;
819 FIXME("(%s):%lx illegal combination with FILE_ATTRIBUTE_NORMAL.\n", lpFileName
,attributes
);
821 if(stat(full_name
.long_name
,&buf
)==-1)
826 if (attributes
& FILE_ATTRIBUTE_READONLY
)
828 if(S_ISDIR(buf
.st_mode
))
830 WARN("FILE_ATTRIBUTE_READONLY ignored for directory.\n");
832 buf
.st_mode
&= ~0222; /* octal!, clear write permission bits */
833 attributes
&= ~FILE_ATTRIBUTE_READONLY
;
837 /* add write permission */
838 buf
.st_mode
|= (0600 | ((buf
.st_mode
& 044) >> 1)) & (~FILE_umask
);
840 if (attributes
& FILE_ATTRIBUTE_DIRECTORY
)
842 if (!S_ISDIR(buf
.st_mode
))
843 FIXME("SetFileAttributes expected the file '%s' to be a directory",
845 attributes
&= ~FILE_ATTRIBUTE_DIRECTORY
;
847 attributes
&= ~(FILE_ATTRIBUTE_ARCHIVE
|FILE_ATTRIBUTE_HIDDEN
|FILE_ATTRIBUTE_SYSTEM
);
849 FIXME("(%s):%lx attribute(s) not implemented.\n", lpFileName
,attributes
);
850 if (-1==chmod(full_name
.long_name
,buf
.st_mode
))
852 if(GetDriveTypeA(lpFileName
) == DRIVE_CDROM
) {
853 SetLastError( ERROR_ACCESS_DENIED
);
858 * FIXME: We don't return FALSE here because of differences between
859 * Linux and Windows privileges. Under Linux only the owner of
860 * the file is allowed to change file attributes. Under Windows,
861 * applications expect that if you can write to a file, you can also
862 * change its attributes (see GENERIC_WRITE). We could try to be
863 * clever here but that would break multi-user installations where
864 * users share read-only DLLs. This is because some installers like
865 * to change attributes of already installed DLLs.
867 FIXME("Couldn't set file attributes for existing file \"%s\".\n"
868 "Check permissions or set VFAT \"quiet\" mount flag\n", full_name
.long_name
);
874 /**************************************************************************
875 * SetFileAttributesW (KERNEL32.@)
877 BOOL WINAPI
SetFileAttributesW(LPCWSTR lpFileName
, DWORD attributes
)
880 DWORD len
= WideCharToMultiByte( CP_ACP
, 0, lpFileName
, -1, NULL
, 0, NULL
, NULL
);
881 LPSTR afn
= HeapAlloc( GetProcessHeap(), 0, len
);
883 WideCharToMultiByte( CP_ACP
, 0, lpFileName
, -1, afn
, len
, NULL
, NULL
);
884 res
= SetFileAttributesA( afn
, attributes
);
885 HeapFree( GetProcessHeap(), 0, afn
);
890 /***********************************************************************
891 * GetFileSize (KERNEL32.@)
893 DWORD WINAPI
GetFileSize( HANDLE hFile
, LPDWORD filesizehigh
)
895 BY_HANDLE_FILE_INFORMATION info
;
896 if (!GetFileInformationByHandle( hFile
, &info
)) return -1;
897 if (filesizehigh
) *filesizehigh
= info
.nFileSizeHigh
;
898 return info
.nFileSizeLow
;
902 /***********************************************************************
903 * GetFileTime (KERNEL32.@)
905 BOOL WINAPI
GetFileTime( HANDLE hFile
, FILETIME
*lpCreationTime
,
906 FILETIME
*lpLastAccessTime
,
907 FILETIME
*lpLastWriteTime
)
909 BY_HANDLE_FILE_INFORMATION info
;
910 if (!GetFileInformationByHandle( hFile
, &info
)) return FALSE
;
911 if (lpCreationTime
) *lpCreationTime
= info
.ftCreationTime
;
912 if (lpLastAccessTime
) *lpLastAccessTime
= info
.ftLastAccessTime
;
913 if (lpLastWriteTime
) *lpLastWriteTime
= info
.ftLastWriteTime
;
917 /***********************************************************************
918 * CompareFileTime (KERNEL32.@)
920 INT WINAPI
CompareFileTime( LPFILETIME x
, LPFILETIME y
)
922 if (!x
|| !y
) return -1;
924 if (x
->dwHighDateTime
> y
->dwHighDateTime
)
926 if (x
->dwHighDateTime
< y
->dwHighDateTime
)
928 if (x
->dwLowDateTime
> y
->dwLowDateTime
)
930 if (x
->dwLowDateTime
< y
->dwLowDateTime
)
935 /***********************************************************************
936 * FILE_GetTempFileName : utility for GetTempFileName
938 static UINT
FILE_GetTempFileName( LPCSTR path
, LPCSTR prefix
, UINT unique
,
939 LPSTR buffer
, BOOL isWin16
)
941 static UINT unique_temp
;
942 DOS_FULL_NAME full_name
;
947 if ( !path
|| !prefix
|| !buffer
) return 0;
949 if (!unique_temp
) unique_temp
= time(NULL
) & 0xffff;
950 num
= unique
? (unique
& 0xffff) : (unique_temp
++ & 0xffff);
952 strcpy( buffer
, path
);
953 p
= buffer
+ strlen(buffer
);
955 /* add a \, if there isn't one and path is more than just the drive letter ... */
956 if ( !((strlen(buffer
) == 2) && (buffer
[1] == ':'))
957 && ((p
== buffer
) || (p
[-1] != '\\'))) *p
++ = '\\';
959 if (isWin16
) *p
++ = '~';
960 for (i
= 3; (i
> 0) && (*prefix
); i
--) *p
++ = *prefix
++;
961 sprintf( p
, "%04x.tmp", num
);
963 /* Now try to create it */
969 HFILE handle
= CreateFileA( buffer
, GENERIC_WRITE
, 0, NULL
,
970 CREATE_NEW
, FILE_ATTRIBUTE_NORMAL
, 0 );
971 if (handle
!= INVALID_HANDLE_VALUE
)
972 { /* We created it */
973 TRACE("created %s\n",
975 CloseHandle( handle
);
978 if (GetLastError() != ERROR_FILE_EXISTS
)
979 break; /* No need to go on */
981 sprintf( p
, "%04x.tmp", num
);
982 } while (num
!= (unique
& 0xffff));
985 /* Get the full path name */
987 if (DOSFS_GetFullName( buffer
, FALSE
, &full_name
))
989 /* Check if we have write access in the directory */
990 if ((p
= strrchr( full_name
.long_name
, '/' ))) *p
= '\0';
991 if (access( full_name
.long_name
, W_OK
) == -1)
992 WARN("returns '%s', which doesn't seem to be writeable.\n",
995 TRACE("returning %s\n", buffer
);
996 return unique
? unique
: num
;
1000 /***********************************************************************
1001 * GetTempFileNameA (KERNEL32.@)
1003 UINT WINAPI
GetTempFileNameA( LPCSTR path
, LPCSTR prefix
, UINT unique
,
1006 return FILE_GetTempFileName(path
, prefix
, unique
, buffer
, FALSE
);
1009 /***********************************************************************
1010 * GetTempFileNameW (KERNEL32.@)
1012 UINT WINAPI
GetTempFileNameW( LPCWSTR path
, LPCWSTR prefix
, UINT unique
,
1015 LPSTR patha
,prefixa
;
1019 if (!path
) return 0;
1020 patha
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
1021 prefixa
= HEAP_strdupWtoA( GetProcessHeap(), 0, prefix
);
1022 ret
= FILE_GetTempFileName( patha
, prefixa
, unique
, buffera
, FALSE
);
1023 MultiByteToWideChar( CP_ACP
, 0, buffera
, -1, buffer
, MAX_PATH
);
1024 HeapFree( GetProcessHeap(), 0, patha
);
1025 HeapFree( GetProcessHeap(), 0, prefixa
);
1030 /***********************************************************************
1031 * GetTempFileName (KERNEL.97)
1033 UINT16 WINAPI
GetTempFileName16( BYTE drive
, LPCSTR prefix
, UINT16 unique
,
1038 if (!(drive
& ~TF_FORCEDRIVE
)) /* drive 0 means current default drive */
1039 drive
|= DRIVE_GetCurrentDrive() + 'A';
1041 if ((drive
& TF_FORCEDRIVE
) &&
1042 !DRIVE_IsValid( toupper(drive
& ~TF_FORCEDRIVE
) - 'A' ))
1044 drive
&= ~TF_FORCEDRIVE
;
1045 WARN("invalid drive %d specified\n", drive
);
1048 if (drive
& TF_FORCEDRIVE
)
1049 sprintf(temppath
,"%c:", drive
& ~TF_FORCEDRIVE
);
1051 GetTempPathA( 132, temppath
);
1052 return (UINT16
)FILE_GetTempFileName( temppath
, prefix
, unique
, buffer
, TRUE
);
1055 /***********************************************************************
1058 * Implementation of OpenFile16() and OpenFile32().
1060 static HFILE
FILE_DoOpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
,
1065 WORD filedatetime
[2];
1066 DOS_FULL_NAME full_name
;
1067 DWORD access
, sharing
;
1070 if (!ofs
) return HFILE_ERROR
;
1072 TRACE("%s %s %s %s%s%s%s%s%s%s%s%s\n",name
,
1073 ((mode
& 0x3 )==OF_READ
)?"OF_READ":
1074 ((mode
& 0x3 )==OF_WRITE
)?"OF_WRITE":
1075 ((mode
& 0x3 )==OF_READWRITE
)?"OF_READWRITE":"unknown",
1076 ((mode
& 0x70 )==OF_SHARE_COMPAT
)?"OF_SHARE_COMPAT":
1077 ((mode
& 0x70 )==OF_SHARE_DENY_NONE
)?"OF_SHARE_DENY_NONE":
1078 ((mode
& 0x70 )==OF_SHARE_DENY_READ
)?"OF_SHARE_DENY_READ":
1079 ((mode
& 0x70 )==OF_SHARE_DENY_WRITE
)?"OF_SHARE_DENY_WRITE":
1080 ((mode
& 0x70 )==OF_SHARE_EXCLUSIVE
)?"OF_SHARE_EXCLUSIVE":"unknown",
1081 ((mode
& OF_PARSE
)==OF_PARSE
)?"OF_PARSE ":"",
1082 ((mode
& OF_DELETE
)==OF_DELETE
)?"OF_DELETE ":"",
1083 ((mode
& OF_VERIFY
)==OF_VERIFY
)?"OF_VERIFY ":"",
1084 ((mode
& OF_SEARCH
)==OF_SEARCH
)?"OF_SEARCH ":"",
1085 ((mode
& OF_CANCEL
)==OF_CANCEL
)?"OF_CANCEL ":"",
1086 ((mode
& OF_CREATE
)==OF_CREATE
)?"OF_CREATE ":"",
1087 ((mode
& OF_PROMPT
)==OF_PROMPT
)?"OF_PROMPT ":"",
1088 ((mode
& OF_EXIST
)==OF_EXIST
)?"OF_EXIST ":"",
1089 ((mode
& OF_REOPEN
)==OF_REOPEN
)?"OF_REOPEN ":""
1093 ofs
->cBytes
= sizeof(OFSTRUCT
);
1095 if (mode
& OF_REOPEN
) name
= ofs
->szPathName
;
1098 ERR("called with `name' set to NULL ! Please debug.\n");
1102 TRACE("%s %04x\n", name
, mode
);
1104 /* the watcom 10.6 IDE relies on a valid path returned in ofs->szPathName
1105 Are there any cases where getting the path here is wrong?
1106 Uwe Bonnes 1997 Apr 2 */
1107 if (!GetFullPathNameA( name
, sizeof(ofs
->szPathName
),
1108 ofs
->szPathName
, NULL
)) goto error
;
1109 FILE_ConvertOFMode( mode
, &access
, &sharing
);
1111 /* OF_PARSE simply fills the structure */
1113 if (mode
& OF_PARSE
)
1115 ofs
->fFixedDisk
= (GetDriveType16( ofs
->szPathName
[0]-'A' )
1116 != DRIVE_REMOVABLE
);
1117 TRACE("(%s): OF_PARSE, res = '%s'\n",
1118 name
, ofs
->szPathName
);
1122 /* OF_CREATE is completely different from all other options, so
1125 if (mode
& OF_CREATE
)
1127 if ((hFileRet
= CreateFileA( name
, GENERIC_READ
| GENERIC_WRITE
,
1128 sharing
, NULL
, CREATE_ALWAYS
,
1129 FILE_ATTRIBUTE_NORMAL
, 0 ))== INVALID_HANDLE_VALUE
)
1134 /* If OF_SEARCH is set, ignore the given path */
1136 if ((mode
& OF_SEARCH
) && !(mode
& OF_REOPEN
))
1138 /* First try the file name as is */
1139 if (DOSFS_GetFullName( name
, TRUE
, &full_name
)) goto found
;
1140 /* Now remove the path */
1141 if (name
[0] && (name
[1] == ':')) name
+= 2;
1142 if ((p
= strrchr( name
, '\\' ))) name
= p
+ 1;
1143 if ((p
= strrchr( name
, '/' ))) name
= p
+ 1;
1144 if (!name
[0]) goto not_found
;
1147 /* Now look for the file */
1149 if (!DIR_SearchPath( NULL
, name
, NULL
, &full_name
, win32
)) goto not_found
;
1152 TRACE("found %s = %s\n",
1153 full_name
.long_name
, full_name
.short_name
);
1154 lstrcpynA( ofs
->szPathName
, full_name
.short_name
,
1155 sizeof(ofs
->szPathName
) );
1157 if (mode
& OF_SHARE_EXCLUSIVE
)
1158 /* Some InstallShield version uses OF_SHARE_EXCLUSIVE
1159 on the file <tempdir>/_ins0432._mp to determine how
1160 far installation has proceeded.
1161 _ins0432._mp is an executable and while running the
1162 application expects the open with OF_SHARE_ to fail*/
1164 As our loader closes the files after loading the executable,
1165 we can't find the running executable with FILE_InUse.
1166 The loader should keep the file open, as Windows does that, too.
1169 char *last
= strrchr(full_name
.long_name
,'/');
1171 last
= full_name
.long_name
- 1;
1172 if (GetModuleHandle16(last
+1))
1174 TRACE("Denying shared open for %s\n",full_name
.long_name
);
1179 if (mode
& OF_DELETE
)
1181 if (unlink( full_name
.long_name
) == -1) goto not_found
;
1182 TRACE("(%s): OF_DELETE return = OK\n", name
);
1186 hFileRet
= FILE_CreateFile( full_name
.long_name
, access
, sharing
,
1187 NULL
, OPEN_EXISTING
, 0, 0,
1188 DRIVE_GetFlags(full_name
.drive
) & DRIVE_FAIL_READ_ONLY
,
1189 GetDriveTypeA( full_name
.short_name
) );
1190 if (!hFileRet
) goto not_found
;
1192 GetFileTime( hFileRet
, NULL
, NULL
, &filetime
);
1193 FileTimeToDosDateTime( &filetime
, &filedatetime
[0], &filedatetime
[1] );
1194 if ((mode
& OF_VERIFY
) && (mode
& OF_REOPEN
))
1196 if (memcmp( ofs
->reserved
, filedatetime
, sizeof(ofs
->reserved
) ))
1198 CloseHandle( hFileRet
);
1199 WARN("(%s): OF_VERIFY failed\n", name
);
1200 /* FIXME: what error here? */
1201 SetLastError( ERROR_FILE_NOT_FOUND
);
1205 memcpy( ofs
->reserved
, filedatetime
, sizeof(ofs
->reserved
) );
1207 success
: /* We get here if the open was successful */
1208 TRACE("(%s): OK, return = %d\n", name
, hFileRet
);
1211 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1212 CloseHandle( hFileRet
);
1216 hFileRet
= Win32HandleToDosFileHandle( hFileRet
);
1217 if (hFileRet
== HFILE_ERROR16
) goto error
;
1218 if (mode
& OF_EXIST
) /* Return the handle, but close it first */
1219 _lclose16( hFileRet
);
1223 not_found
: /* We get here if the file does not exist */
1224 WARN("'%s' not found or sharing violation\n", name
);
1225 SetLastError( ERROR_FILE_NOT_FOUND
);
1228 error
: /* We get here if there was an error opening the file */
1229 ofs
->nErrCode
= GetLastError();
1230 WARN("(%s): return = HFILE_ERROR error= %d\n",
1231 name
,ofs
->nErrCode
);
1236 /***********************************************************************
1237 * OpenFile (KERNEL.74)
1238 * OpenFileEx (KERNEL.360)
1240 HFILE16 WINAPI
OpenFile16( LPCSTR name
, OFSTRUCT
*ofs
, UINT16 mode
)
1242 return FILE_DoOpenFile( name
, ofs
, mode
, FALSE
);
1246 /***********************************************************************
1247 * OpenFile (KERNEL32.@)
1249 HFILE WINAPI
OpenFile( LPCSTR name
, OFSTRUCT
*ofs
, UINT mode
)
1251 return FILE_DoOpenFile( name
, ofs
, mode
, TRUE
);
1255 /***********************************************************************
1256 * FILE_InitProcessDosHandles
1258 * Allocates the default DOS handles for a process. Called either by
1259 * Win32HandleToDosFileHandle below or by the DOSVM stuff.
1261 static void FILE_InitProcessDosHandles( void )
1263 dos_handles
[0] = GetStdHandle(STD_INPUT_HANDLE
);
1264 dos_handles
[1] = GetStdHandle(STD_OUTPUT_HANDLE
);
1265 dos_handles
[2] = GetStdHandle(STD_ERROR_HANDLE
);
1266 dos_handles
[3] = GetStdHandle(STD_ERROR_HANDLE
);
1267 dos_handles
[4] = GetStdHandle(STD_ERROR_HANDLE
);
1270 /***********************************************************************
1271 * Win32HandleToDosFileHandle (KERNEL32.21)
1273 * Allocate a DOS handle for a Win32 handle. The Win32 handle is no
1274 * longer valid after this function (even on failure).
1276 * Note: this is not exactly right, since on Win95 the Win32 handles
1277 * are on top of DOS handles and we do it the other way
1278 * around. Should be good enough though.
1280 HFILE WINAPI
Win32HandleToDosFileHandle( HANDLE handle
)
1284 if (!handle
|| (handle
== INVALID_HANDLE_VALUE
))
1287 for (i
= 5; i
< DOS_TABLE_SIZE
; i
++)
1288 if (!dos_handles
[i
])
1290 dos_handles
[i
] = handle
;
1291 TRACE("Got %d for h32 %d\n", i
, handle
);
1294 CloseHandle( handle
);
1295 SetLastError( ERROR_TOO_MANY_OPEN_FILES
);
1300 /***********************************************************************
1301 * DosFileHandleToWin32Handle (KERNEL32.20)
1303 * Return the Win32 handle for a DOS handle.
1305 * Note: this is not exactly right, since on Win95 the Win32 handles
1306 * are on top of DOS handles and we do it the other way
1307 * around. Should be good enough though.
1309 HANDLE WINAPI
DosFileHandleToWin32Handle( HFILE handle
)
1311 HFILE16 hfile
= (HFILE16
)handle
;
1312 if (hfile
< 5 && !dos_handles
[hfile
]) FILE_InitProcessDosHandles();
1313 if ((hfile
>= DOS_TABLE_SIZE
) || !dos_handles
[hfile
])
1315 SetLastError( ERROR_INVALID_HANDLE
);
1316 return INVALID_HANDLE_VALUE
;
1318 return dos_handles
[hfile
];
1322 /***********************************************************************
1323 * DisposeLZ32Handle (KERNEL32.22)
1325 * Note: this is not entirely correct, we should only close the
1326 * 32-bit handle and not the 16-bit one, but we cannot do
1327 * this because of the way our DOS handles are implemented.
1328 * It shouldn't break anything though.
1330 void WINAPI
DisposeLZ32Handle( HANDLE handle
)
1334 if (!handle
|| (handle
== INVALID_HANDLE_VALUE
)) return;
1336 for (i
= 5; i
< DOS_TABLE_SIZE
; i
++)
1337 if (dos_handles
[i
] == handle
)
1340 CloseHandle( handle
);
1346 /***********************************************************************
1349 * dup2() function for DOS handles.
1351 HFILE16
FILE_Dup2( HFILE16 hFile1
, HFILE16 hFile2
)
1355 if (hFile1
< 5 && !dos_handles
[hFile1
]) FILE_InitProcessDosHandles();
1357 if ((hFile1
>= DOS_TABLE_SIZE
) || (hFile2
>= DOS_TABLE_SIZE
) || !dos_handles
[hFile1
])
1359 SetLastError( ERROR_INVALID_HANDLE
);
1360 return HFILE_ERROR16
;
1364 FIXME("stdio handle closed, need proper conversion\n" );
1365 SetLastError( ERROR_INVALID_HANDLE
);
1366 return HFILE_ERROR16
;
1368 if (!DuplicateHandle( GetCurrentProcess(), dos_handles
[hFile1
],
1369 GetCurrentProcess(), &new_handle
,
1370 0, FALSE
, DUPLICATE_SAME_ACCESS
))
1371 return HFILE_ERROR16
;
1372 if (dos_handles
[hFile2
]) CloseHandle( dos_handles
[hFile2
] );
1373 dos_handles
[hFile2
] = new_handle
;
1378 /***********************************************************************
1379 * _lclose (KERNEL.81)
1381 HFILE16 WINAPI
_lclose16( HFILE16 hFile
)
1385 FIXME("stdio handle closed, need proper conversion\n" );
1386 SetLastError( ERROR_INVALID_HANDLE
);
1387 return HFILE_ERROR16
;
1389 if ((hFile
>= DOS_TABLE_SIZE
) || !dos_handles
[hFile
])
1391 SetLastError( ERROR_INVALID_HANDLE
);
1392 return HFILE_ERROR16
;
1394 TRACE("%d (handle32=%d)\n", hFile
, dos_handles
[hFile
] );
1395 CloseHandle( dos_handles
[hFile
] );
1396 dos_handles
[hFile
] = 0;
1401 /***********************************************************************
1402 * _lclose (KERNEL32.@)
1404 HFILE WINAPI
_lclose( HFILE hFile
)
1406 TRACE("handle %d\n", hFile
);
1407 return CloseHandle( hFile
) ? 0 : HFILE_ERROR
;
1410 /***********************************************************************
1411 * GetOverlappedResult (KERNEL32.@)
1413 * Check the result of an Asynchronous data transfer from a file.
1419 * If successful (and relevant) lpTransferred will hold the number of
1420 * bytes transferred during the async operation.
1424 * Currently only works for WaitCommEvent, ReadFile, WriteFile
1425 * with communications ports.
1428 BOOL WINAPI
GetOverlappedResult(
1429 HANDLE hFile
, /* [in] handle of file to check on */
1430 LPOVERLAPPED lpOverlapped
, /* [in/out] pointer to overlapped */
1431 LPDWORD lpTransferred
, /* [in/out] number of bytes transferred */
1432 BOOL bWait
/* [in] wait for the transfer to complete ? */
1436 TRACE("(%d %p %p %x)\n", hFile
, lpOverlapped
, lpTransferred
, bWait
);
1438 if(lpOverlapped
==NULL
)
1440 ERR("lpOverlapped was null\n");
1443 if(!lpOverlapped
->hEvent
)
1445 ERR("lpOverlapped->hEvent was null\n");
1450 TRACE("waiting on %p\n",lpOverlapped
);
1451 r
= WaitForSingleObjectEx(lpOverlapped
->hEvent
, bWait
?INFINITE
:0, TRUE
);
1452 TRACE("wait on %p returned %ld\n",lpOverlapped
,r
);
1453 } while (r
==STATUS_USER_APC
);
1456 *lpTransferred
= lpOverlapped
->InternalHigh
;
1458 SetLastError ( lpOverlapped
->Internal
== STATUS_PENDING
?
1459 ERROR_IO_INCOMPLETE
: RtlNtStatusToDosError ( lpOverlapped
->Internal
) );
1461 return (r
==WAIT_OBJECT_0
);
1464 /***********************************************************************
1465 * CancelIo (KERNEL32.@)
1467 BOOL WINAPI
CancelIo(HANDLE handle
)
1469 async_private
*ovp
,*t
;
1471 TRACE("handle = %x\n",handle
);
1473 for (ovp
= NtCurrentTeb()->pending_list
; ovp
; ovp
= t
)
1476 if ( ovp
->handle
== handle
)
1477 cancel_async ( ovp
);
1479 WaitForMultipleObjectsEx(0,NULL
,FALSE
,1,TRUE
);
1483 /***********************************************************************
1484 * FILE_AsyncReadService (INTERNAL)
1486 * This function is called while the client is waiting on the
1487 * server, so we can't make any server calls here.
1489 static void FILE_AsyncReadService(async_private
*ovp
)
1491 async_fileio
*fileio
= (async_fileio
*) ovp
;
1492 LPOVERLAPPED lpOverlapped
= fileio
->lpOverlapped
;
1494 int already
= lpOverlapped
->InternalHigh
;
1496 TRACE("%p %p\n", lpOverlapped
, fileio
->buffer
);
1498 /* check to see if the data is ready (non-blocking) */
1500 if ( fileio
->fd_type
== FD_TYPE_SOCKET
)
1501 result
= read (ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
1504 result
= pread (ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
,
1505 OVERLAPPED_OFFSET (lpOverlapped
) + already
);
1506 if ((result
< 0) && (errno
== ESPIPE
))
1507 result
= read (ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
1510 if ( (result
<0) && ((errno
== EAGAIN
) || (errno
== EINTR
)))
1512 TRACE("Deferred read %d\n",errno
);
1517 /* check to see if the transfer is complete */
1520 r
= FILE_GetNtStatus ();
1524 lpOverlapped
->InternalHigh
+= result
;
1525 TRACE("read %d more bytes %ld/%d so far\n",result
,lpOverlapped
->InternalHigh
,fileio
->count
);
1527 if(lpOverlapped
->InternalHigh
>= fileio
->count
|| fileio
->fd_type
== FD_TYPE_SOCKET
)
1533 lpOverlapped
->Internal
= r
;
1536 /***********************************************************************
1537 * FILE_ReadFileEx (INTERNAL)
1539 static BOOL
FILE_ReadFileEx(HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1540 LPOVERLAPPED overlapped
,
1541 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
,
1549 TRACE("file %d to buf %p num %ld %p func %p\n",
1550 hFile
, buffer
, bytesToRead
, overlapped
, lpCompletionRoutine
);
1552 /* check that there is an overlapped struct */
1553 if (overlapped
==NULL
)
1555 SetLastError(ERROR_INVALID_PARAMETER
);
1559 fd
= FILE_GetUnixHandleType ( hFile
, GENERIC_READ
, &type
, &flags
);
1562 WARN ( "Couldn't get FD\n" );
1566 ovp
= (async_fileio
*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio
));
1569 TRACE("HeapAlloc Failed\n");
1570 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1574 ovp
->async
.ops
= ( lpCompletionRoutine
? &fileio_async_ops
: &fileio_nocomp_async_ops
);
1575 ovp
->async
.handle
= hFile
;
1577 ovp
->async
.type
= ASYNC_TYPE_READ
;
1578 ovp
->async
.func
= FILE_AsyncReadService
;
1579 ovp
->async
.event
= hEvent
;
1580 ovp
->lpOverlapped
= overlapped
;
1581 ovp
->count
= bytesToRead
;
1582 ovp
->completion_func
= lpCompletionRoutine
;
1583 ovp
->buffer
= buffer
;
1584 ovp
->fd_type
= type
;
1586 return !register_new_async (&ovp
->async
);
1594 /***********************************************************************
1595 * ReadFileEx (KERNEL32.@)
1597 BOOL WINAPI
ReadFileEx(HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1598 LPOVERLAPPED overlapped
,
1599 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1601 overlapped
->InternalHigh
= 0;
1602 return FILE_ReadFileEx(hFile
,buffer
,bytesToRead
,overlapped
,lpCompletionRoutine
, INVALID_HANDLE_VALUE
);
1605 static BOOL
FILE_TimeoutRead(HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
, LPDWORD bytesRead
)
1610 TRACE("%d %p %ld %p\n", hFile
, buffer
, bytesToRead
, bytesRead
);
1612 ZeroMemory(&ov
, sizeof (OVERLAPPED
));
1613 if(STATUS_SUCCESS
==NtCreateEvent(&ov
.hEvent
, SYNCHRONIZE
, NULL
, 0, 0))
1615 if(FILE_ReadFileEx(hFile
, buffer
, bytesToRead
, &ov
, NULL
, ov
.hEvent
))
1617 r
= GetOverlappedResult(hFile
, &ov
, bytesRead
, TRUE
);
1620 CloseHandle(ov
.hEvent
);
1624 /***********************************************************************
1625 * ReadFile (KERNEL32.@)
1627 BOOL WINAPI
ReadFile( HANDLE hFile
, LPVOID buffer
, DWORD bytesToRead
,
1628 LPDWORD bytesRead
, LPOVERLAPPED overlapped
)
1630 int unix_handle
, result
, flags
;
1633 TRACE("%d %p %ld %p %p\n", hFile
, buffer
, bytesToRead
,
1634 bytesRead
, overlapped
);
1636 if (bytesRead
) *bytesRead
= 0; /* Do this before anything else */
1637 if (!bytesToRead
) return TRUE
;
1639 unix_handle
= FILE_GetUnixHandleType( hFile
, GENERIC_READ
, &type
, &flags
);
1641 if (flags
& FD_FLAG_OVERLAPPED
)
1643 if (unix_handle
== -1) return FALSE
;
1644 if ( (overlapped
==NULL
) || NtResetEvent( overlapped
->hEvent
, NULL
) )
1646 TRACE("Overlapped not specified or invalid event flag\n");
1648 SetLastError(ERROR_INVALID_PARAMETER
);
1653 overlapped
->InternalHigh
= 0;
1655 if(!FILE_ReadFileEx(hFile
, buffer
, bytesToRead
, overlapped
, NULL
, overlapped
->hEvent
))
1658 if ( !GetOverlappedResult (hFile
, overlapped
, bytesRead
, FALSE
) )
1660 if ( GetLastError() == ERROR_IO_INCOMPLETE
)
1661 SetLastError ( ERROR_IO_PENDING
);
1667 if (flags
& FD_FLAG_TIMEOUT
)
1670 return FILE_TimeoutRead(hFile
, buffer
, bytesToRead
, bytesRead
);
1675 return SMB_ReadFile(hFile
, buffer
, bytesToRead
, bytesRead
, NULL
);
1676 case FD_TYPE_CONSOLE
:
1677 return ReadConsoleA(hFile
, buffer
, bytesToRead
, bytesRead
, NULL
);
1680 /* normal unix files */
1681 if (unix_handle
== -1)
1686 SetLastError(ERROR_INVALID_PARAMETER
);
1692 /* code for synchronous reads */
1693 while ((result
= read( unix_handle
, buffer
, bytesToRead
)) == -1)
1695 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
1696 if ((errno
== EFAULT
) && !IsBadWritePtr( buffer
, bytesToRead
)) continue;
1700 close( unix_handle
);
1701 if (result
== -1) return FALSE
;
1702 if (bytesRead
) *bytesRead
= result
;
1707 /***********************************************************************
1708 * FILE_AsyncWriteService (INTERNAL)
1710 * This function is called while the client is waiting on the
1711 * server, so we can't make any server calls here.
1713 static void FILE_AsyncWriteService(struct async_private
*ovp
)
1715 async_fileio
*fileio
= (async_fileio
*) ovp
;
1716 LPOVERLAPPED lpOverlapped
= fileio
->lpOverlapped
;
1718 int already
= lpOverlapped
->InternalHigh
;
1720 TRACE("(%p %p)\n",lpOverlapped
,fileio
->buffer
);
1722 /* write some data (non-blocking) */
1724 if ( fileio
->fd_type
== FD_TYPE_SOCKET
)
1725 result
= write(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
1728 result
= pwrite(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
,
1729 OVERLAPPED_OFFSET (lpOverlapped
) + already
);
1730 if ((result
< 0) && (errno
== ESPIPE
))
1731 result
= write(ovp
->fd
, &fileio
->buffer
[already
], fileio
->count
- already
);
1734 if ( (result
<0) && ((errno
== EAGAIN
) || (errno
== EINTR
)))
1740 /* check to see if the transfer is complete */
1743 r
= FILE_GetNtStatus ();
1747 lpOverlapped
->InternalHigh
+= result
;
1749 TRACE("wrote %d more bytes %ld/%d so far\n",result
,lpOverlapped
->InternalHigh
,fileio
->count
);
1751 if(lpOverlapped
->InternalHigh
< fileio
->count
)
1757 lpOverlapped
->Internal
= r
;
1760 /***********************************************************************
1763 static BOOL
FILE_WriteFileEx(HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1764 LPOVERLAPPED overlapped
,
1765 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
,
1773 TRACE("file %d to buf %p num %ld %p func %p handle %d\n",
1774 hFile
, buffer
, bytesToWrite
, overlapped
, lpCompletionRoutine
, hEvent
);
1776 if (overlapped
== NULL
)
1778 SetLastError(ERROR_INVALID_PARAMETER
);
1782 fd
= FILE_GetUnixHandleType ( hFile
, GENERIC_WRITE
, &type
, &flags
);
1785 TRACE( "Couldn't get FD\n" );
1789 ovp
= (async_fileio
*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_fileio
));
1792 TRACE("HeapAlloc Failed\n");
1793 SetLastError(ERROR_NOT_ENOUGH_MEMORY
);
1797 ovp
->async
.ops
= ( lpCompletionRoutine
? &fileio_async_ops
: &fileio_nocomp_async_ops
);
1798 ovp
->async
.handle
= hFile
;
1800 ovp
->async
.type
= ASYNC_TYPE_WRITE
;
1801 ovp
->async
.func
= FILE_AsyncWriteService
;
1802 ovp
->lpOverlapped
= overlapped
;
1803 ovp
->async
.event
= hEvent
;
1804 ovp
->buffer
= (LPVOID
) buffer
;
1805 ovp
->count
= bytesToWrite
;
1806 ovp
->completion_func
= lpCompletionRoutine
;
1807 ovp
->fd_type
= type
;
1809 return !register_new_async (&ovp
->async
);
1816 /***********************************************************************
1817 * WriteFileEx (KERNEL32.@)
1819 BOOL WINAPI
WriteFileEx(HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1820 LPOVERLAPPED overlapped
,
1821 LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine
)
1823 overlapped
->InternalHigh
= 0;
1825 return FILE_WriteFileEx(hFile
, buffer
, bytesToWrite
, overlapped
, lpCompletionRoutine
, INVALID_HANDLE_VALUE
);
1828 /***********************************************************************
1829 * WriteFile (KERNEL32.@)
1831 BOOL WINAPI
WriteFile( HANDLE hFile
, LPCVOID buffer
, DWORD bytesToWrite
,
1832 LPDWORD bytesWritten
, LPOVERLAPPED overlapped
)
1834 int unix_handle
, result
, flags
;
1837 TRACE("%d %p %ld %p %p\n", hFile
, buffer
, bytesToWrite
,
1838 bytesWritten
, overlapped
);
1840 if (bytesWritten
) *bytesWritten
= 0; /* Do this before anything else */
1841 if (!bytesToWrite
) return TRUE
;
1843 unix_handle
= FILE_GetUnixHandleType( hFile
, GENERIC_WRITE
, &type
, &flags
);
1845 if (flags
& FD_FLAG_OVERLAPPED
)
1847 if (unix_handle
== -1) return FALSE
;
1848 if ( (overlapped
==NULL
) || NtResetEvent( overlapped
->hEvent
, NULL
) )
1850 TRACE("Overlapped not specified or invalid event flag\n");
1852 SetLastError(ERROR_INVALID_PARAMETER
);
1857 overlapped
->InternalHigh
= 0;
1859 if(!FILE_WriteFileEx(hFile
, buffer
, bytesToWrite
, overlapped
, NULL
, overlapped
->hEvent
))
1862 if ( !GetOverlappedResult (hFile
, overlapped
, bytesWritten
, FALSE
) )
1864 if ( GetLastError() == ERROR_IO_INCOMPLETE
)
1865 SetLastError ( ERROR_IO_PENDING
);
1874 case FD_TYPE_CONSOLE
:
1875 TRACE("%d %s %ld %p %p\n", hFile
, debugstr_an(buffer
, bytesToWrite
), bytesToWrite
,
1876 bytesWritten
, overlapped
);
1877 return WriteConsoleA(hFile
, buffer
, bytesToWrite
, bytesWritten
, NULL
);
1879 if (unix_handle
== -1)
1883 /* synchronous file write */
1884 while ((result
= write( unix_handle
, buffer
, bytesToWrite
)) == -1)
1886 if ((errno
== EAGAIN
) || (errno
== EINTR
)) continue;
1887 if ((errno
== EFAULT
) && !IsBadReadPtr( buffer
, bytesToWrite
)) continue;
1888 if (errno
== ENOSPC
)
1889 SetLastError( ERROR_DISK_FULL
);
1894 close( unix_handle
);
1895 if (result
== -1) return FALSE
;
1896 if (bytesWritten
) *bytesWritten
= result
;
1901 /***********************************************************************
1902 * _hread (KERNEL.349)
1904 LONG WINAPI
WIN16_hread( HFILE16 hFile
, SEGPTR buffer
, LONG count
)
1908 TRACE("%d %08lx %ld\n",
1909 hFile
, (DWORD
)buffer
, count
);
1911 /* Some programs pass a count larger than the allocated buffer */
1912 maxlen
= GetSelectorLimit16( SELECTOROF(buffer
) ) - OFFSETOF(buffer
) + 1;
1913 if (count
> maxlen
) count
= maxlen
;
1914 return _lread(DosFileHandleToWin32Handle(hFile
), MapSL(buffer
), count
);
1918 /***********************************************************************
1919 * _lread (KERNEL.82)
1921 UINT16 WINAPI
WIN16_lread( HFILE16 hFile
, SEGPTR buffer
, UINT16 count
)
1923 return (UINT16
)WIN16_hread( hFile
, buffer
, (LONG
)count
);
1927 /***********************************************************************
1928 * _lread (KERNEL32.@)
1930 UINT WINAPI
_lread( HFILE handle
, LPVOID buffer
, UINT count
)
1933 if (!ReadFile( handle
, buffer
, count
, &result
, NULL
)) return -1;
1938 /***********************************************************************
1939 * _lread16 (KERNEL.82)
1941 UINT16 WINAPI
_lread16( HFILE16 hFile
, LPVOID buffer
, UINT16 count
)
1943 return (UINT16
)_lread(DosFileHandleToWin32Handle(hFile
), buffer
, (LONG
)count
);
1947 /***********************************************************************
1948 * _lcreat (KERNEL.83)
1950 HFILE16 WINAPI
_lcreat16( LPCSTR path
, INT16 attr
)
1952 return Win32HandleToDosFileHandle( _lcreat( path
, attr
) );
1956 /***********************************************************************
1957 * _lcreat (KERNEL32.@)
1959 HFILE WINAPI
_lcreat( LPCSTR path
, INT attr
)
1961 /* Mask off all flags not explicitly allowed by the doc */
1962 attr
&= FILE_ATTRIBUTE_READONLY
| FILE_ATTRIBUTE_HIDDEN
| FILE_ATTRIBUTE_SYSTEM
;
1963 TRACE("%s %02x\n", path
, attr
);
1964 return CreateFileA( path
, GENERIC_READ
| GENERIC_WRITE
,
1965 FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
1966 CREATE_ALWAYS
, attr
, 0 );
1970 /***********************************************************************
1971 * SetFilePointer (KERNEL32.@)
1973 DWORD WINAPI
SetFilePointer( HANDLE hFile
, LONG distance
, LONG
*highword
,
1976 DWORD ret
= 0xffffffff;
1978 TRACE("handle %d offset %ld high %ld origin %ld\n",
1979 hFile
, distance
, highword
?*highword
:0, method
);
1981 SERVER_START_REQ( set_file_pointer
)
1983 req
->handle
= hFile
;
1984 req
->low
= distance
;
1985 req
->high
= highword
? *highword
: (distance
>= 0) ? 0 : -1;
1986 /* FIXME: assumes 1:1 mapping between Windows and Unix seek constants */
1987 req
->whence
= method
;
1989 if (!wine_server_call_err( req
))
1991 ret
= reply
->new_low
;
1992 if (highword
) *highword
= reply
->new_high
;
2000 /***********************************************************************
2001 * _llseek (KERNEL.84)
2004 * Seeking before the start of the file should be allowed for _llseek16,
2005 * but cause subsequent I/O operations to fail (cf. interrupt list)
2008 LONG WINAPI
_llseek16( HFILE16 hFile
, LONG lOffset
, INT16 nOrigin
)
2010 return SetFilePointer( DosFileHandleToWin32Handle(hFile
), lOffset
, NULL
, nOrigin
);
2014 /***********************************************************************
2015 * _llseek (KERNEL32.@)
2017 LONG WINAPI
_llseek( HFILE hFile
, LONG lOffset
, INT nOrigin
)
2019 return SetFilePointer( hFile
, lOffset
, NULL
, nOrigin
);
2023 /***********************************************************************
2024 * _lopen (KERNEL.85)
2026 HFILE16 WINAPI
_lopen16( LPCSTR path
, INT16 mode
)
2028 return Win32HandleToDosFileHandle( _lopen( path
, mode
) );
2032 /***********************************************************************
2033 * _lopen (KERNEL32.@)
2035 HFILE WINAPI
_lopen( LPCSTR path
, INT mode
)
2037 DWORD access
, sharing
;
2039 TRACE("('%s',%04x)\n", path
, mode
);
2040 FILE_ConvertOFMode( mode
, &access
, &sharing
);
2041 return CreateFileA( path
, access
, sharing
, NULL
, OPEN_EXISTING
, 0, 0 );
2045 /***********************************************************************
2046 * _lwrite (KERNEL.86)
2048 UINT16 WINAPI
_lwrite16( HFILE16 hFile
, LPCSTR buffer
, UINT16 count
)
2050 return (UINT16
)_hwrite( DosFileHandleToWin32Handle(hFile
), buffer
, (LONG
)count
);
2053 /***********************************************************************
2054 * _lwrite (KERNEL32.@)
2056 UINT WINAPI
_lwrite( HFILE hFile
, LPCSTR buffer
, UINT count
)
2058 return (UINT
)_hwrite( hFile
, buffer
, (LONG
)count
);
2062 /***********************************************************************
2063 * _hread16 (KERNEL.349)
2065 LONG WINAPI
_hread16( HFILE16 hFile
, LPVOID buffer
, LONG count
)
2067 return _lread( DosFileHandleToWin32Handle(hFile
), buffer
, count
);
2071 /***********************************************************************
2072 * _hread (KERNEL32.@)
2074 LONG WINAPI
_hread( HFILE hFile
, LPVOID buffer
, LONG count
)
2076 return _lread( hFile
, buffer
, count
);
2080 /***********************************************************************
2081 * _hwrite (KERNEL.350)
2083 LONG WINAPI
_hwrite16( HFILE16 hFile
, LPCSTR buffer
, LONG count
)
2085 return _hwrite( DosFileHandleToWin32Handle(hFile
), buffer
, count
);
2089 /***********************************************************************
2090 * _hwrite (KERNEL32.@)
2092 * experimentation yields that _lwrite:
2093 * o truncates the file at the current position with
2095 * o returns 0 on a 0 length write
2096 * o works with console handles
2099 LONG WINAPI
_hwrite( HFILE handle
, LPCSTR buffer
, LONG count
)
2103 TRACE("%d %p %ld\n", handle
, buffer
, count
);
2107 /* Expand or truncate at current position */
2108 if (!SetEndOfFile( handle
)) return HFILE_ERROR
;
2111 if (!WriteFile( handle
, buffer
, count
, &result
, NULL
))
2117 /***********************************************************************
2118 * SetHandleCount (KERNEL.199)
2120 UINT16 WINAPI
SetHandleCount16( UINT16 count
)
2122 return SetHandleCount( count
);
2126 /*************************************************************************
2127 * SetHandleCount (KERNEL32.@)
2129 UINT WINAPI
SetHandleCount( UINT count
)
2131 return min( 256, count
);
2135 /***********************************************************************
2136 * FlushFileBuffers (KERNEL32.@)
2138 BOOL WINAPI
FlushFileBuffers( HANDLE hFile
)
2141 SERVER_START_REQ( flush_file
)
2143 req
->handle
= hFile
;
2144 ret
= !wine_server_call_err( req
);
2151 /**************************************************************************
2152 * SetEndOfFile (KERNEL32.@)
2154 BOOL WINAPI
SetEndOfFile( HANDLE hFile
)
2157 SERVER_START_REQ( truncate_file
)
2159 req
->handle
= hFile
;
2160 ret
= !wine_server_call_err( req
);
2167 /***********************************************************************
2168 * DeleteFile (KERNEL.146)
2170 BOOL16 WINAPI
DeleteFile16( LPCSTR path
)
2172 return DeleteFileA( path
);
2176 /***********************************************************************
2177 * DeleteFileA (KERNEL32.@)
2179 BOOL WINAPI
DeleteFileA( LPCSTR path
)
2181 DOS_FULL_NAME full_name
;
2185 SetLastError(ERROR_INVALID_PARAMETER
);
2188 TRACE("'%s'\n", path
);
2192 ERR("Empty path passed\n");
2195 if (DOSFS_GetDevice( path
))
2197 WARN("cannot remove DOS device '%s'!\n", path
);
2198 SetLastError( ERROR_FILE_NOT_FOUND
);
2202 if (!DOSFS_GetFullName( path
, TRUE
, &full_name
)) return FALSE
;
2203 if (unlink( full_name
.long_name
) == -1)
2212 /***********************************************************************
2213 * DeleteFileW (KERNEL32.@)
2215 BOOL WINAPI
DeleteFileW( LPCWSTR path
)
2217 LPSTR xpath
= HEAP_strdupWtoA( GetProcessHeap(), 0, path
);
2218 BOOL ret
= DeleteFileA( xpath
);
2219 HeapFree( GetProcessHeap(), 0, xpath
);
2224 /***********************************************************************
2225 * GetFileType (KERNEL32.@)
2227 DWORD WINAPI
GetFileType( HANDLE hFile
)
2229 DWORD ret
= FILE_TYPE_UNKNOWN
;
2230 SERVER_START_REQ( get_file_info
)
2232 req
->handle
= hFile
;
2233 if (!wine_server_call_err( req
)) ret
= reply
->type
;
2240 /* check if a file name is for an executable file (.exe or .com) */
2241 inline static BOOL
is_executable( const char *name
)
2243 int len
= strlen(name
);
2245 if (len
< 4) return FALSE
;
2246 return (!strcasecmp( name
+ len
- 4, ".exe" ) ||
2247 !strcasecmp( name
+ len
- 4, ".com" ));
2251 /***********************************************************************
2252 * FILE_AddBootRenameEntry
2254 * Adds an entry to the registry that is loaded when windows boots and
2255 * checks if there are some files to be removed or renamed/moved.
2256 * <fn1> has to be valid and <fn2> may be NULL. If both pointers are
2257 * non-NULL then the file is moved, otherwise it is deleted. The
2258 * entry of the registrykey is always appended with two zero
2259 * terminated strings. If <fn2> is NULL then the second entry is
2260 * simply a single 0-byte. Otherwise the second filename goes
2261 * there. The entries are prepended with \??\ before the path and the
2262 * second filename gets also a '!' as the first character if
2263 * MOVEFILE_REPLACE_EXISTING is set. After the final string another
2264 * 0-byte follows to indicate the end of the strings.
2266 * \??\D:\test\file1[0]
2267 * !\??\D:\test\file1_renamed[0]
2268 * \??\D:\Test|delete[0]
2269 * [0] <- file is to be deleted, second string empty
2270 * \??\D:\test\file2[0]
2271 * !\??\D:\test\file2_renamed[0]
2272 * [0] <- indicates end of strings
2275 * \??\D:\test\file1[0]
2276 * !\??\D:\test\file1_renamed[0]
2277 * \??\D:\Test|delete[0]
2278 * [0] <- file is to be deleted, second string empty
2279 * [0] <- indicates end of strings
2282 static BOOL
FILE_AddBootRenameEntry( const char *fn1
, const char *fn2
, DWORD flags
)
2284 static const char PreString
[] = "\\??\\";
2285 static const char ValueName
[] = "PendingFileRenameOperations";
2289 DWORD Type
, len1
, len2
, l
;
2291 BYTE
*Buffer
= NULL
;
2293 if(RegCreateKeyA(HKEY_LOCAL_MACHINE
, "SYSTEM\\CurrentControlSet\\Control\\Session Manager",
2294 &Reboot
) != ERROR_SUCCESS
)
2296 WARN("Error creating key for reboot managment [%s]\n",
2297 "SYSTEM\\CurrentControlSet\\Control\\Session Manager");
2301 l
= strlen(PreString
);
2302 len1
= strlen(fn1
) + l
+ 1;
2305 len2
= strlen(fn2
) + l
+ 1;
2306 if (flags
& MOVEFILE_REPLACE_EXISTING
) len2
++; /* Plus 1 because of the leading '!' */
2308 else len2
= 1; /* minimum is the 0 byte for the empty second string */
2310 /* First we check if the key exists and if so how many bytes it already contains. */
2311 if (RegQueryValueExA(Reboot
, ValueName
, NULL
, &Type
, NULL
, &DataSize
) == ERROR_SUCCESS
)
2313 if (Type
!= REG_MULTI_SZ
) goto Quit
;
2314 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, DataSize
+ len1
+ len2
+ 1 ))) goto Quit
;
2315 if (RegQueryValueExA(Reboot
, ValueName
, NULL
, &Type
, Buffer
, &DataSize
) != ERROR_SUCCESS
)
2317 if (DataSize
) DataSize
--; /* remove terminating null (will be added back later) */
2321 if (!(Buffer
= HeapAlloc( GetProcessHeap(), 0, len1
+ len2
+ 1 ))) goto Quit
;
2324 sprintf( Buffer
+ DataSize
, "%s%s", PreString
, fn1
);
2328 sprintf( Buffer
+ DataSize
, "%s%s%s",
2329 (flags
& MOVEFILE_REPLACE_EXISTING
) ? "!" : "", PreString
, fn2
);
2332 else Buffer
[DataSize
++] = 0;
2334 Buffer
[DataSize
++] = 0; /* add final null */
2335 rc
= !RegSetValueExA( Reboot
, ValueName
, 0, REG_MULTI_SZ
, Buffer
, DataSize
);
2338 if (Reboot
) RegCloseKey(Reboot
);
2339 if (Buffer
) HeapFree( GetProcessHeap(), 0, Buffer
);
2344 /**************************************************************************
2345 * MoveFileExA (KERNEL32.@)
2347 BOOL WINAPI
MoveFileExA( LPCSTR fn1
, LPCSTR fn2
, DWORD flag
)
2349 DOS_FULL_NAME full_name1
, full_name2
;
2351 TRACE("(%s,%s,%04lx)\n", fn1
, fn2
, flag
);
2353 /* FIXME: <Gerhard W. Gruber>sparhawk@gmx.at
2354 In case of W9x and lesser this function should return 120 (ERROR_CALL_NOT_IMPLEMENTED)
2355 to be really compatible. Most programs wont have any problems though. In case
2356 you encounter one, this is what you should return here. I don't know what's up
2357 with NT 3.5. Is this function available there or not?
2358 Does anybody really care about 3.5? :)
2361 /* Filename1 has to be always set to a valid path. Filename2 may be NULL
2362 if the source file has to be deleted.
2365 SetLastError(ERROR_INVALID_PARAMETER
);
2369 /* This function has to be run through in order to process the name properly.
2370 If the BOOTDELAY flag is set, the file doesn't need to exist though. At least
2371 that is the behaviour on NT 4.0. The operation accepts the filenames as
2372 they are given but it can't reply with a reasonable returncode. Success
2373 means in that case success for entering the values into the registry.
2375 if(!DOSFS_GetFullName( fn1
, TRUE
, &full_name1
))
2377 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
2381 if (fn2
) /* !fn2 means delete fn1 */
2383 if (DOSFS_GetFullName( fn2
, TRUE
, &full_name2
))
2385 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
2387 /* target exists, check if we may overwrite */
2388 if (!(flag
& MOVEFILE_REPLACE_EXISTING
))
2390 /* FIXME: Use right error code */
2391 SetLastError( ERROR_ACCESS_DENIED
);
2398 if (!DOSFS_GetFullName( fn2
, FALSE
, &full_name2
))
2400 if(!(flag
& MOVEFILE_DELAY_UNTIL_REBOOT
))
2405 /* Source name and target path are valid */
2407 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
2409 /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2410 Perhaps we should queue these command and execute it
2411 when exiting... What about using on_exit(2)
2413 FIXME("Please move existing file '%s' to file '%s' when Wine has finished\n",
2415 return FILE_AddBootRenameEntry( fn1
, fn2
, flag
);
2418 if (full_name1
.drive
!= full_name2
.drive
)
2420 /* use copy, if allowed */
2421 if (!(flag
& MOVEFILE_COPY_ALLOWED
))
2423 /* FIXME: Use right error code */
2424 SetLastError( ERROR_FILE_EXISTS
);
2427 return CopyFileA( fn1
, fn2
, !(flag
& MOVEFILE_REPLACE_EXISTING
) );
2429 if (rename( full_name1
.long_name
, full_name2
.long_name
) == -1)
2434 if (is_executable( full_name1
.long_name
) != is_executable( full_name2
.long_name
))
2437 if (stat( full_name2
.long_name
, &fstat
) != -1)
2439 if (is_executable( full_name2
.long_name
))
2440 /* set executable bit where read bit is set */
2441 fstat
.st_mode
|= (fstat
.st_mode
& 0444) >> 2;
2443 fstat
.st_mode
&= ~0111;
2444 chmod( full_name2
.long_name
, fstat
.st_mode
);
2449 else /* fn2 == NULL means delete source */
2451 if (flag
& MOVEFILE_DELAY_UNTIL_REBOOT
)
2453 if (flag
& MOVEFILE_COPY_ALLOWED
) {
2454 WARN("Illegal flag\n");
2455 SetLastError( ERROR_GEN_FAILURE
);
2458 /* FIXME: (bon@elektron.ikp.physik.th-darmstadt.de 970706)
2459 Perhaps we should queue these command and execute it
2460 when exiting... What about using on_exit(2)
2462 FIXME("Please delete file '%s' when Wine has finished\n", fn1
);
2463 return FILE_AddBootRenameEntry( fn1
, NULL
, flag
);
2466 if (unlink( full_name1
.long_name
) == -1)
2471 return TRUE
; /* successfully deleted */
2475 /**************************************************************************
2476 * MoveFileExW (KERNEL32.@)
2478 BOOL WINAPI
MoveFileExW( LPCWSTR fn1
, LPCWSTR fn2
, DWORD flag
)
2480 LPSTR afn1
= HEAP_strdupWtoA( GetProcessHeap(), 0, fn1
);
2481 LPSTR afn2
= HEAP_strdupWtoA( GetProcessHeap(), 0, fn2
);
2482 BOOL res
= MoveFileExA( afn1
, afn2
, flag
);
2483 HeapFree( GetProcessHeap(), 0, afn1
);
2484 HeapFree( GetProcessHeap(), 0, afn2
);
2489 /**************************************************************************
2490 * MoveFileA (KERNEL32.@)
2492 * Move file or directory
2494 BOOL WINAPI
MoveFileA( LPCSTR fn1
, LPCSTR fn2
)
2496 DOS_FULL_NAME full_name1
, full_name2
;
2499 TRACE("(%s,%s)\n", fn1
, fn2
);
2501 if (!DOSFS_GetFullName( fn1
, TRUE
, &full_name1
)) return FALSE
;
2502 if (DOSFS_GetFullName( fn2
, TRUE
, &full_name2
)) {
2503 /* The new name must not already exist */
2504 SetLastError(ERROR_ALREADY_EXISTS
);
2507 if (!DOSFS_GetFullName( fn2
, FALSE
, &full_name2
)) return FALSE
;
2509 if (full_name1
.drive
== full_name2
.drive
) /* move */
2510 return MoveFileExA( fn1
, fn2
, MOVEFILE_COPY_ALLOWED
);
2513 if (stat( full_name1
.long_name
, &fstat
))
2515 WARN("Invalid source file %s\n",
2516 full_name1
.long_name
);
2520 if (S_ISDIR(fstat
.st_mode
)) {
2521 /* No Move for directories across file systems */
2522 /* FIXME: Use right error code */
2523 SetLastError( ERROR_GEN_FAILURE
);
2526 return CopyFileA(fn1
, fn2
, TRUE
); /*fail, if exist */
2530 /**************************************************************************
2531 * MoveFileW (KERNEL32.@)
2533 BOOL WINAPI
MoveFileW( LPCWSTR fn1
, LPCWSTR fn2
)
2535 LPSTR afn1
= HEAP_strdupWtoA( GetProcessHeap(), 0, fn1
);
2536 LPSTR afn2
= HEAP_strdupWtoA( GetProcessHeap(), 0, fn2
);
2537 BOOL res
= MoveFileA( afn1
, afn2
);
2538 HeapFree( GetProcessHeap(), 0, afn1
);
2539 HeapFree( GetProcessHeap(), 0, afn2
);
2544 /**************************************************************************
2545 * CopyFileA (KERNEL32.@)
2547 BOOL WINAPI
CopyFileA( LPCSTR source
, LPCSTR dest
, BOOL fail_if_exists
)
2550 BY_HANDLE_FILE_INFORMATION info
;
2556 if ((h1
= _lopen( source
, OF_READ
)) == HFILE_ERROR
) return FALSE
;
2557 if (!GetFileInformationByHandle( h1
, &info
))
2562 mode
= (info
.dwFileAttributes
& FILE_ATTRIBUTE_READONLY
) ? 0444 : 0666;
2563 if ((h2
= CreateFileA( dest
, GENERIC_WRITE
, FILE_SHARE_READ
| FILE_SHARE_WRITE
, NULL
,
2564 fail_if_exists
? CREATE_NEW
: CREATE_ALWAYS
,
2565 info
.dwFileAttributes
, h1
)) == INVALID_HANDLE_VALUE
)
2570 while ((count
= _lread( h1
, buffer
, sizeof(buffer
) )) > 0)
2575 INT res
= _lwrite( h2
, p
, count
);
2576 if (res
<= 0) goto done
;
2589 /**************************************************************************
2590 * CopyFileW (KERNEL32.@)
2592 BOOL WINAPI
CopyFileW( LPCWSTR source
, LPCWSTR dest
, BOOL fail_if_exists
)
2594 LPSTR sourceA
= HEAP_strdupWtoA( GetProcessHeap(), 0, source
);
2595 LPSTR destA
= HEAP_strdupWtoA( GetProcessHeap(), 0, dest
);
2596 BOOL ret
= CopyFileA( sourceA
, destA
, fail_if_exists
);
2597 HeapFree( GetProcessHeap(), 0, sourceA
);
2598 HeapFree( GetProcessHeap(), 0, destA
);
2603 /**************************************************************************
2604 * CopyFileExA (KERNEL32.@)
2606 * This implementation ignores most of the extra parameters passed-in into
2607 * the "ex" version of the method and calls the CopyFile method.
2608 * It will have to be fixed eventually.
2610 BOOL WINAPI
CopyFileExA(LPCSTR sourceFilename
,
2611 LPCSTR destFilename
,
2612 LPPROGRESS_ROUTINE progressRoutine
,
2614 LPBOOL cancelFlagPointer
,
2617 BOOL failIfExists
= FALSE
;
2620 * Interpret the only flag that CopyFile can interpret.
2622 if ( (copyFlags
& COPY_FILE_FAIL_IF_EXISTS
) != 0)
2624 failIfExists
= TRUE
;
2627 return CopyFileA(sourceFilename
, destFilename
, failIfExists
);
2630 /**************************************************************************
2631 * CopyFileExW (KERNEL32.@)
2633 BOOL WINAPI
CopyFileExW(LPCWSTR sourceFilename
,
2634 LPCWSTR destFilename
,
2635 LPPROGRESS_ROUTINE progressRoutine
,
2637 LPBOOL cancelFlagPointer
,
2640 LPSTR sourceA
= HEAP_strdupWtoA( GetProcessHeap(), 0, sourceFilename
);
2641 LPSTR destA
= HEAP_strdupWtoA( GetProcessHeap(), 0, destFilename
);
2643 BOOL ret
= CopyFileExA(sourceA
,
2650 HeapFree( GetProcessHeap(), 0, sourceA
);
2651 HeapFree( GetProcessHeap(), 0, destA
);
2657 /***********************************************************************
2658 * SetFileTime (KERNEL32.@)
2660 BOOL WINAPI
SetFileTime( HANDLE hFile
,
2661 const FILETIME
*lpCreationTime
,
2662 const FILETIME
*lpLastAccessTime
,
2663 const FILETIME
*lpLastWriteTime
)
2666 SERVER_START_REQ( set_file_time
)
2668 req
->handle
= hFile
;
2669 if (lpLastAccessTime
)
2670 RtlTimeToSecondsSince1970( lpLastAccessTime
, (DWORD
*)&req
->access_time
);
2672 req
->access_time
= 0; /* FIXME */
2673 if (lpLastWriteTime
)
2674 RtlTimeToSecondsSince1970( lpLastWriteTime
, (DWORD
*)&req
->write_time
);
2676 req
->write_time
= 0; /* FIXME */
2677 ret
= !wine_server_call_err( req
);
2684 /**************************************************************************
2685 * LockFile (KERNEL32.@)
2687 BOOL WINAPI
LockFile( HANDLE hFile
, DWORD dwFileOffsetLow
, DWORD dwFileOffsetHigh
,
2688 DWORD nNumberOfBytesToLockLow
, DWORD nNumberOfBytesToLockHigh
)
2691 SERVER_START_REQ( lock_file
)
2693 req
->handle
= hFile
;
2694 req
->offset_low
= dwFileOffsetLow
;
2695 req
->offset_high
= dwFileOffsetHigh
;
2696 req
->count_low
= nNumberOfBytesToLockLow
;
2697 req
->count_high
= nNumberOfBytesToLockHigh
;
2698 ret
= !wine_server_call_err( req
);
2704 /**************************************************************************
2705 * LockFileEx [KERNEL32.@]
2707 * Locks a byte range within an open file for shared or exclusive access.
2714 * Per Microsoft docs, the third parameter (reserved) must be set to 0.
2716 BOOL WINAPI
LockFileEx( HANDLE hFile
, DWORD flags
, DWORD reserved
,
2717 DWORD nNumberOfBytesToLockLow
, DWORD nNumberOfBytesToLockHigh
,
2718 LPOVERLAPPED pOverlapped
)
2720 FIXME("hFile=%d,flags=%ld,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2721 hFile
, flags
, reserved
, nNumberOfBytesToLockLow
, nNumberOfBytesToLockHigh
,
2724 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2727 ERR("reserved == %ld: Supposed to be 0??\n", reserved
);
2728 SetLastError(ERROR_INVALID_PARAMETER
);
2735 /**************************************************************************
2736 * UnlockFile (KERNEL32.@)
2738 BOOL WINAPI
UnlockFile( HANDLE hFile
, DWORD dwFileOffsetLow
, DWORD dwFileOffsetHigh
,
2739 DWORD nNumberOfBytesToUnlockLow
, DWORD nNumberOfBytesToUnlockHigh
)
2742 SERVER_START_REQ( unlock_file
)
2744 req
->handle
= hFile
;
2745 req
->offset_low
= dwFileOffsetLow
;
2746 req
->offset_high
= dwFileOffsetHigh
;
2747 req
->count_low
= nNumberOfBytesToUnlockLow
;
2748 req
->count_high
= nNumberOfBytesToUnlockHigh
;
2749 ret
= !wine_server_call_err( req
);
2756 /**************************************************************************
2757 * UnlockFileEx (KERNEL32.@)
2759 BOOL WINAPI
UnlockFileEx(
2762 DWORD nNumberOfBytesToUnlockLow
,
2763 DWORD nNumberOfBytesToUnlockHigh
,
2764 LPOVERLAPPED lpOverlapped
2767 FIXME("hFile=%d,reserved=%ld,lowbytes=%ld,highbytes=%ld,overlapped=%p: stub.\n",
2768 hFile
, dwReserved
, nNumberOfBytesToUnlockLow
, nNumberOfBytesToUnlockHigh
,
2770 if (dwReserved
== 0)
2771 SetLastError(ERROR_CALL_NOT_IMPLEMENTED
);
2774 ERR("reserved == %ld: Supposed to be 0??\n", dwReserved
);
2775 SetLastError(ERROR_INVALID_PARAMETER
);
2784 struct DOS_FILE_LOCK
{
2785 struct DOS_FILE_LOCK
* next
;
2789 FILE_OBJECT
* dos_file
;
2790 /* char * unix_name;*/
2793 typedef struct DOS_FILE_LOCK DOS_FILE_LOCK
;
2795 static DOS_FILE_LOCK
*locks
= NULL
;
2796 static void DOS_RemoveFileLocks(FILE_OBJECT
*file
);
2799 /* Locks need to be mirrored because unix file locking is based
2800 * on the pid. Inside of wine there can be multiple WINE processes
2801 * that share the same unix pid.
2802 * Read's and writes should check these locks also - not sure
2803 * how critical that is at this point (FIXME).
2806 static BOOL
DOS_AddLock(FILE_OBJECT
*file
, struct flock
*f
)
2808 DOS_FILE_LOCK
*curr
;
2811 processId
= GetCurrentProcessId();
2813 /* check if lock overlaps a current lock for the same file */
2815 for (curr
= locks
; curr
; curr
= curr
->next
) {
2816 if (strcmp(curr
->unix_name
, file
->unix_name
) == 0) {
2817 if ((f
->l_start
== curr
->base
) && (f
->l_len
== curr
->len
))
2818 return TRUE
;/* region is identic */
2819 if ((f
->l_start
< (curr
->base
+ curr
->len
)) &&
2820 ((f
->l_start
+ f
->l_len
) > curr
->base
)) {
2821 /* region overlaps */
2828 curr
= HeapAlloc( GetProcessHeap(), 0, sizeof(DOS_FILE_LOCK
) );
2829 curr
->processId
= GetCurrentProcessId();
2830 curr
->base
= f
->l_start
;
2831 curr
->len
= f
->l_len
;
2832 /* curr->unix_name = HEAP_strdupA( GetProcessHeap(), 0, file->unix_name);*/
2834 curr
->dos_file
= file
;
2839 static void DOS_RemoveFileLocks(FILE_OBJECT
*file
)
2842 DOS_FILE_LOCK
**curr
;
2845 processId
= GetCurrentProcessId();
2848 if ((*curr
)->dos_file
== file
) {
2850 *curr
= (*curr
)->next
;
2851 /* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2852 HeapFree( GetProcessHeap(), 0, rem
);
2855 curr
= &(*curr
)->next
;
2859 static BOOL
DOS_RemoveLock(FILE_OBJECT
*file
, struct flock
*f
)
2862 DOS_FILE_LOCK
**curr
;
2865 processId
= GetCurrentProcessId();
2866 for (curr
= &locks
; *curr
; curr
= &(*curr
)->next
) {
2867 if ((*curr
)->processId
== processId
&&
2868 (*curr
)->dos_file
== file
&&
2869 (*curr
)->base
== f
->l_start
&&
2870 (*curr
)->len
== f
->l_len
) {
2871 /* this is the same lock */
2873 *curr
= (*curr
)->next
;
2874 /* HeapFree( GetProcessHeap(), 0, rem->unix_name );*/
2875 HeapFree( GetProcessHeap(), 0, rem
);
2879 /* no matching lock found */
2884 /**************************************************************************
2885 * LockFile (KERNEL32.@)
2887 BOOL WINAPI
LockFile(
2888 HFILE hFile
,DWORD dwFileOffsetLow
,DWORD dwFileOffsetHigh
,
2889 DWORD nNumberOfBytesToLockLow
,DWORD nNumberOfBytesToLockHigh
)
2894 TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2895 hFile
, dwFileOffsetLow
, dwFileOffsetHigh
,
2896 nNumberOfBytesToLockLow
, nNumberOfBytesToLockHigh
);
2898 if (dwFileOffsetHigh
|| nNumberOfBytesToLockHigh
) {
2899 FIXME("Unimplemented bytes > 32bits\n");
2903 f
.l_start
= dwFileOffsetLow
;
2904 f
.l_len
= nNumberOfBytesToLockLow
;
2905 f
.l_whence
= SEEK_SET
;
2909 if (!(file
= FILE_GetFile(hFile
,0,NULL
))) return FALSE
;
2911 /* shadow locks internally */
2912 if (!DOS_AddLock(file
, &f
)) {
2913 SetLastError( ERROR_LOCK_VIOLATION
);
2917 /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2918 #ifdef USE_UNIX_LOCKS
2919 if (fcntl(file
->unix_handle
, F_SETLK
, &f
) == -1) {
2920 if (errno
== EACCES
|| errno
== EAGAIN
) {
2921 SetLastError( ERROR_LOCK_VIOLATION
);
2926 /* remove our internal copy of the lock */
2927 DOS_RemoveLock(file
, &f
);
2935 /**************************************************************************
2936 * UnlockFile (KERNEL32.@)
2938 BOOL WINAPI
UnlockFile(
2939 HFILE hFile
,DWORD dwFileOffsetLow
,DWORD dwFileOffsetHigh
,
2940 DWORD nNumberOfBytesToUnlockLow
,DWORD nNumberOfBytesToUnlockHigh
)
2945 TRACE("handle %d offsetlow=%ld offsethigh=%ld nbyteslow=%ld nbyteshigh=%ld\n",
2946 hFile
, dwFileOffsetLow
, dwFileOffsetHigh
,
2947 nNumberOfBytesToUnlockLow
, nNumberOfBytesToUnlockHigh
);
2949 if (dwFileOffsetHigh
|| nNumberOfBytesToUnlockHigh
) {
2950 WARN("Unimplemented bytes > 32bits\n");
2954 f
.l_start
= dwFileOffsetLow
;
2955 f
.l_len
= nNumberOfBytesToUnlockLow
;
2956 f
.l_whence
= SEEK_SET
;
2960 if (!(file
= FILE_GetFile(hFile
,0,NULL
))) return FALSE
;
2962 DOS_RemoveLock(file
, &f
); /* ok if fails - may be another wine */
2964 /* FIXME: Unix locking commented out for now, doesn't work with Excel */
2965 #ifdef USE_UNIX_LOCKS
2966 if (fcntl(file
->unix_handle
, F_SETLK
, &f
) == -1) {
2975 /**************************************************************************
2976 * GetFileAttributesExA [KERNEL32.@]
2978 BOOL WINAPI
GetFileAttributesExA(
2979 LPCSTR lpFileName
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
2980 LPVOID lpFileInformation
)
2982 DOS_FULL_NAME full_name
;
2983 BY_HANDLE_FILE_INFORMATION info
;
2985 if (lpFileName
== NULL
) return FALSE
;
2986 if (lpFileInformation
== NULL
) return FALSE
;
2988 if (fInfoLevelId
== GetFileExInfoStandard
) {
2989 LPWIN32_FILE_ATTRIBUTE_DATA lpFad
=
2990 (LPWIN32_FILE_ATTRIBUTE_DATA
) lpFileInformation
;
2991 if (!DOSFS_GetFullName( lpFileName
, TRUE
, &full_name
)) return FALSE
;
2992 if (!FILE_Stat( full_name
.long_name
, &info
)) return FALSE
;
2994 lpFad
->dwFileAttributes
= info
.dwFileAttributes
;
2995 lpFad
->ftCreationTime
= info
.ftCreationTime
;
2996 lpFad
->ftLastAccessTime
= info
.ftLastAccessTime
;
2997 lpFad
->ftLastWriteTime
= info
.ftLastWriteTime
;
2998 lpFad
->nFileSizeHigh
= info
.nFileSizeHigh
;
2999 lpFad
->nFileSizeLow
= info
.nFileSizeLow
;
3002 FIXME("invalid info level %d!\n", fInfoLevelId
);
3010 /**************************************************************************
3011 * GetFileAttributesExW [KERNEL32.@]
3013 BOOL WINAPI
GetFileAttributesExW(
3014 LPCWSTR lpFileName
, GET_FILEEX_INFO_LEVELS fInfoLevelId
,
3015 LPVOID lpFileInformation
)
3017 LPSTR nameA
= HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName
);
3019 GetFileAttributesExA( nameA
, fInfoLevelId
, lpFileInformation
);
3020 HeapFree( GetProcessHeap(), 0, nameA
);