| /* | |
| * File handling functions | |
| * | |
| * Copyright 1993 John Burton | |
| * Copyright 1996 Alexandre Julliard | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| * | |
| * TODO: | |
| * Fix the CopyFileEx methods to implement the "extended" functionality. | |
| * Right now, they simply call the CopyFile method. | |
| */ | |
| #include "config.h" | |
| #include "wine/port.h" | |
| #include <stdarg.h> | |
| #define NONAMELESSUNION | |
| #define NONAMELESSSTRUCT | |
| #include "winerror.h" | |
| #include "ntstatus.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "winternl.h" | |
| #include "wincon.h" | |
| #include "wine/winbase16.h" | |
| #include "kernel_private.h" | |
| #include "wine/unicode.h" | |
| #include "wine/debug.h" | |
| #include "async.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(file); | |
| HANDLE dos_handles[DOS_TABLE_SIZE]; | |
| /************************************************************************** | |
| * Operations on file handles * | |
| **************************************************************************/ | |
| /*********************************************************************** | |
| * FILE_InitProcessDosHandles | |
| * | |
| * Allocates the default DOS handles for a process. Called either by | |
| * Win32HandleToDosFileHandle below or by the DOSVM stuff. | |
| */ | |
| static void FILE_InitProcessDosHandles( void ) | |
| { | |
| static BOOL init_done /* = FALSE */; | |
| HANDLE cp = GetCurrentProcess(); | |
| if (init_done) return; | |
| init_done = TRUE; | |
| DuplicateHandle(cp, GetStdHandle(STD_INPUT_HANDLE), cp, &dos_handles[0], | |
| 0, TRUE, DUPLICATE_SAME_ACCESS); | |
| DuplicateHandle(cp, GetStdHandle(STD_OUTPUT_HANDLE), cp, &dos_handles[1], | |
| 0, TRUE, DUPLICATE_SAME_ACCESS); | |
| DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[2], | |
| 0, TRUE, DUPLICATE_SAME_ACCESS); | |
| DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[3], | |
| 0, TRUE, DUPLICATE_SAME_ACCESS); | |
| DuplicateHandle(cp, GetStdHandle(STD_ERROR_HANDLE), cp, &dos_handles[4], | |
| 0, TRUE, DUPLICATE_SAME_ACCESS); | |
| } | |
| /*********************************************************************** | |
| * GetOverlappedResult (KERNEL32.@) | |
| * | |
| * Check the result of an Asynchronous data transfer from a file. | |
| * | |
| * Parameters | |
| * HANDLE hFile [in] handle of file to check on | |
| * LPOVERLAPPED lpOverlapped [in/out] pointer to overlapped | |
| * LPDWORD lpTransferred [in/out] number of bytes transferred | |
| * BOOL bWait [in] wait for the transfer to complete ? | |
| * | |
| * RETURNS | |
| * TRUE on success | |
| * FALSE on failure | |
| * | |
| * If successful (and relevant) lpTransferred will hold the number of | |
| * bytes transferred during the async operation. | |
| * | |
| * BUGS | |
| * | |
| * Currently only works for WaitCommEvent, ReadFile, WriteFile | |
| * with communications ports. | |
| * | |
| */ | |
| BOOL WINAPI GetOverlappedResult(HANDLE hFile, LPOVERLAPPED lpOverlapped, | |
| LPDWORD lpTransferred, BOOL bWait) | |
| { | |
| DWORD r; | |
| TRACE("(%p %p %p %x)\n", hFile, lpOverlapped, lpTransferred, bWait); | |
| if (lpOverlapped==NULL) | |
| { | |
| ERR("lpOverlapped was null\n"); | |
| return FALSE; | |
| } | |
| if (!lpOverlapped->hEvent) | |
| { | |
| ERR("lpOverlapped->hEvent was null\n"); | |
| return FALSE; | |
| } | |
| if ( bWait ) | |
| { | |
| do { | |
| TRACE("waiting on %p\n",lpOverlapped); | |
| r = WaitForSingleObjectEx(lpOverlapped->hEvent, INFINITE, TRUE); | |
| TRACE("wait on %p returned %ld\n",lpOverlapped,r); | |
| } while (r==STATUS_USER_APC); | |
| } | |
| else if ( lpOverlapped->Internal == STATUS_PENDING ) | |
| { | |
| /* Wait in order to give APCs a chance to run. */ | |
| /* This is cheating, so we must set the event again in case of success - | |
| it may be a non-manual reset event. */ | |
| do { | |
| TRACE("waiting on %p\n",lpOverlapped); | |
| r = WaitForSingleObjectEx(lpOverlapped->hEvent, 0, TRUE); | |
| TRACE("wait on %p returned %ld\n",lpOverlapped,r); | |
| } while (r==STATUS_USER_APC); | |
| if ( r == WAIT_OBJECT_0 ) | |
| NtSetEvent ( lpOverlapped->hEvent, NULL ); | |
| } | |
| if(lpTransferred) | |
| *lpTransferred = lpOverlapped->InternalHigh; | |
| switch ( lpOverlapped->Internal ) | |
| { | |
| case STATUS_SUCCESS: | |
| return TRUE; | |
| case STATUS_PENDING: | |
| SetLastError ( ERROR_IO_INCOMPLETE ); | |
| if ( bWait ) ERR ("PENDING status after waiting!\n"); | |
| return FALSE; | |
| default: | |
| SetLastError ( RtlNtStatusToDosError ( lpOverlapped->Internal ) ); | |
| return FALSE; | |
| } | |
| } | |
| /*********************************************************************** | |
| * CancelIo (KERNEL32.@) | |
| */ | |
| BOOL WINAPI CancelIo(HANDLE handle) | |
| { | |
| async_private *ovp,*t; | |
| TRACE("handle = %p\n",handle); | |
| for (ovp = NtCurrentTeb()->pending_list; ovp; ovp = t) | |
| { | |
| t = ovp->next; | |
| if ( ovp->handle == handle ) | |
| cancel_async ( ovp ); | |
| } | |
| SleepEx(1,TRUE); | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * _hread (KERNEL32.@) | |
| */ | |
| LONG WINAPI _hread( HFILE hFile, LPVOID buffer, LONG count) | |
| { | |
| return _lread( hFile, buffer, count ); | |
| } | |
| /*********************************************************************** | |
| * _hwrite (KERNEL32.@) | |
| * | |
| * experimentation yields that _lwrite: | |
| * o truncates the file at the current position with | |
| * a 0 len write | |
| * o returns 0 on a 0 length write | |
| * o works with console handles | |
| * | |
| */ | |
| LONG WINAPI _hwrite( HFILE handle, LPCSTR buffer, LONG count ) | |
| { | |
| DWORD result; | |
| TRACE("%d %p %ld\n", handle, buffer, count ); | |
| if (!count) | |
| { | |
| /* Expand or truncate at current position */ | |
| if (!SetEndOfFile( (HANDLE)handle )) return HFILE_ERROR; | |
| return 0; | |
| } | |
| if (!WriteFile( (HANDLE)handle, buffer, count, &result, NULL )) | |
| return HFILE_ERROR; | |
| return result; | |
| } | |
| /*********************************************************************** | |
| * _lclose (KERNEL32.@) | |
| */ | |
| HFILE WINAPI _lclose( HFILE hFile ) | |
| { | |
| TRACE("handle %d\n", hFile ); | |
| return CloseHandle( (HANDLE)hFile ) ? 0 : HFILE_ERROR; | |
| } | |
| /*********************************************************************** | |
| * _lcreat (KERNEL32.@) | |
| */ | |
| HFILE WINAPI _lcreat( LPCSTR path, INT attr ) | |
| { | |
| /* Mask off all flags not explicitly allowed by the doc */ | |
| attr &= FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM; | |
| TRACE("%s %02x\n", path, attr ); | |
| return (HFILE)CreateFileA( path, GENERIC_READ | GENERIC_WRITE, | |
| FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, | |
| CREATE_ALWAYS, attr, 0 ); | |
| } | |
| /*********************************************************************** | |
| * _lopen (KERNEL32.@) | |
| */ | |
| HFILE WINAPI _lopen( LPCSTR path, INT mode ) | |
| { | |
| DWORD access, sharing; | |
| TRACE("('%s',%04x)\n", path, mode ); | |
| FILE_ConvertOFMode( mode, &access, &sharing ); | |
| return (HFILE)CreateFileA( path, access, sharing, NULL, OPEN_EXISTING, 0, 0 ); | |
| } | |
| /*********************************************************************** | |
| * _lread (KERNEL32.@) | |
| */ | |
| UINT WINAPI _lread( HFILE handle, LPVOID buffer, UINT count ) | |
| { | |
| DWORD result; | |
| if (!ReadFile( (HANDLE)handle, buffer, count, &result, NULL )) | |
| return HFILE_ERROR; | |
| return result; | |
| } | |
| /*********************************************************************** | |
| * _llseek (KERNEL32.@) | |
| */ | |
| LONG WINAPI _llseek( HFILE hFile, LONG lOffset, INT nOrigin ) | |
| { | |
| return SetFilePointer( (HANDLE)hFile, lOffset, NULL, nOrigin ); | |
| } | |
| /*********************************************************************** | |
| * _lwrite (KERNEL32.@) | |
| */ | |
| UINT WINAPI _lwrite( HFILE hFile, LPCSTR buffer, UINT count ) | |
| { | |
| return (UINT)_hwrite( hFile, buffer, (LONG)count ); | |
| } | |
| /*********************************************************************** | |
| * FlushFileBuffers (KERNEL32.@) | |
| */ | |
| BOOL WINAPI FlushFileBuffers( HANDLE hFile ) | |
| { | |
| NTSTATUS nts; | |
| IO_STATUS_BLOCK ioblk; | |
| if (is_console_handle( hFile )) | |
| { | |
| /* this will fail (as expected) for an output handle */ | |
| /* FIXME: wait until FlushFileBuffers is moved to dll/kernel */ | |
| /* return FlushConsoleInputBuffer( hFile ); */ | |
| return TRUE; | |
| } | |
| nts = NtFlushBuffersFile( hFile, &ioblk ); | |
| if (nts != STATUS_SUCCESS) | |
| { | |
| SetLastError( RtlNtStatusToDosError( nts ) ); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /*********************************************************************** | |
| * GetFileSize (KERNEL32.@) | |
| */ | |
| DWORD WINAPI GetFileSize( HANDLE hFile, LPDWORD filesizehigh ) | |
| { | |
| BY_HANDLE_FILE_INFORMATION info; | |
| if (!GetFileInformationByHandle( hFile, &info )) return -1; | |
| if (filesizehigh) *filesizehigh = info.nFileSizeHigh; | |
| return info.nFileSizeLow; | |
| } | |
| /*********************************************************************** | |
| * GetFileSizeEx (KERNEL32.@) | |
| */ | |
| BOOL WINAPI GetFileSizeEx( HANDLE hFile, PLARGE_INTEGER lpFileSize ) | |
| { | |
| BY_HANDLE_FILE_INFORMATION info; | |
| if (!lpFileSize) | |
| { | |
| SetLastError( ERROR_INVALID_PARAMETER ); | |
| return FALSE; | |
| } | |
| if (!GetFileInformationByHandle( hFile, &info )) | |
| { | |
| return FALSE; | |
| } | |
| lpFileSize->u.LowPart = info.nFileSizeLow; | |
| lpFileSize->u.HighPart = info.nFileSizeHigh; | |
| return TRUE; | |
| } | |
| /************************************************************************** | |
| * LockFile (KERNEL32.@) | |
| */ | |
| BOOL WINAPI LockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high, | |
| DWORD count_low, DWORD count_high ) | |
| { | |
| NTSTATUS status; | |
| LARGE_INTEGER count, offset; | |
| TRACE( "%p %lx%08lx %lx%08lx\n", | |
| hFile, offset_high, offset_low, count_high, count_low ); | |
| count.u.LowPart = count_low; | |
| count.u.HighPart = count_high; | |
| offset.u.LowPart = offset_low; | |
| offset.u.HighPart = offset_high; | |
| status = NtLockFile( hFile, 0, NULL, NULL, | |
| NULL, &offset, &count, NULL, TRUE, TRUE ); | |
| if (status != STATUS_SUCCESS) SetLastError( RtlNtStatusToDosError(status) ); | |
| return !status; | |
| } | |
| /************************************************************************** | |
| * LockFileEx [KERNEL32.@] | |
| * | |
| * Locks a byte range within an open file for shared or exclusive access. | |
| * | |
| * RETURNS | |
| * success: TRUE | |
| * failure: FALSE | |
| * | |
| * NOTES | |
| * Per Microsoft docs, the third parameter (reserved) must be set to 0. | |
| */ | |
| BOOL WINAPI LockFileEx( HANDLE hFile, DWORD flags, DWORD reserved, | |
| DWORD count_low, DWORD count_high, LPOVERLAPPED overlapped ) | |
| { | |
| NTSTATUS status; | |
| LARGE_INTEGER count, offset; | |
| if (reserved) | |
| { | |
| SetLastError( ERROR_INVALID_PARAMETER ); | |
| return FALSE; | |
| } | |
| TRACE( "%p %lx%08lx %lx%08lx flags %lx\n", | |
| hFile, overlapped->OffsetHigh, overlapped->Offset, | |
| count_high, count_low, flags ); | |
| count.u.LowPart = count_low; | |
| count.u.HighPart = count_high; | |
| offset.u.LowPart = overlapped->Offset; | |
| offset.u.HighPart = overlapped->OffsetHigh; | |
| status = NtLockFile( hFile, overlapped->hEvent, NULL, NULL, | |
| NULL, &offset, &count, NULL, | |
| flags & LOCKFILE_FAIL_IMMEDIATELY, | |
| flags & LOCKFILE_EXCLUSIVE_LOCK ); | |
| if (status) SetLastError( RtlNtStatusToDosError(status) ); | |
| return !status; | |
| } | |
| /************************************************************************** | |
| * UnlockFile (KERNEL32.@) | |
| */ | |
| BOOL WINAPI UnlockFile( HANDLE hFile, DWORD offset_low, DWORD offset_high, | |
| DWORD count_low, DWORD count_high ) | |
| { | |
| NTSTATUS status; | |
| LARGE_INTEGER count, offset; | |
| count.u.LowPart = count_low; | |
| count.u.HighPart = count_high; | |
| offset.u.LowPart = offset_low; | |
| offset.u.HighPart = offset_high; | |
| status = NtUnlockFile( hFile, NULL, &offset, &count, NULL); | |
| if (status) SetLastError( RtlNtStatusToDosError(status) ); | |
| return !status; | |
| } | |
| /************************************************************************** | |
| * UnlockFileEx (KERNEL32.@) | |
| */ | |
| BOOL WINAPI UnlockFileEx( HANDLE hFile, DWORD reserved, DWORD count_low, DWORD count_high, | |
| LPOVERLAPPED overlapped ) | |
| { | |
| if (reserved) | |
| { | |
| SetLastError( ERROR_INVALID_PARAMETER ); | |
| return FALSE; | |
| } | |
| if (overlapped->hEvent) FIXME("Unimplemented overlapped operation\n"); | |
| return UnlockFile( hFile, overlapped->Offset, overlapped->OffsetHigh, count_low, count_high ); | |
| } | |
| /*********************************************************************** | |
| * Win32HandleToDosFileHandle (KERNEL32.21) | |
| * | |
| * Allocate a DOS handle for a Win32 handle. The Win32 handle is no | |
| * longer valid after this function (even on failure). | |
| * | |
| * Note: this is not exactly right, since on Win95 the Win32 handles | |
| * are on top of DOS handles and we do it the other way | |
| * around. Should be good enough though. | |
| */ | |
| HFILE WINAPI Win32HandleToDosFileHandle( HANDLE handle ) | |
| { | |
| int i; | |
| if (!handle || (handle == INVALID_HANDLE_VALUE)) | |
| return HFILE_ERROR; | |
| FILE_InitProcessDosHandles(); | |
| for (i = 0; i < DOS_TABLE_SIZE; i++) | |
| if (!dos_handles[i]) | |
| { | |
| dos_handles[i] = handle; | |
| TRACE("Got %d for h32 %p\n", i, handle ); | |
| return (HFILE)i; | |
| } | |
| CloseHandle( handle ); | |
| SetLastError( ERROR_TOO_MANY_OPEN_FILES ); | |
| return HFILE_ERROR; | |
| } | |
| /*********************************************************************** | |
| * DosFileHandleToWin32Handle (KERNEL32.20) | |
| * | |
| * Return the Win32 handle for a DOS handle. | |
| * | |
| * Note: this is not exactly right, since on Win95 the Win32 handles | |
| * are on top of DOS handles and we do it the other way | |
| * around. Should be good enough though. | |
| */ | |
| HANDLE WINAPI DosFileHandleToWin32Handle( HFILE handle ) | |
| { | |
| HFILE16 hfile = (HFILE16)handle; | |
| if (hfile < 5) FILE_InitProcessDosHandles(); | |
| if ((hfile >= DOS_TABLE_SIZE) || !dos_handles[hfile]) | |
| { | |
| SetLastError( ERROR_INVALID_HANDLE ); | |
| return INVALID_HANDLE_VALUE; | |
| } | |
| return dos_handles[hfile]; | |
| } | |
| /*********************************************************************** | |
| * DisposeLZ32Handle (KERNEL32.22) | |
| * | |
| * Note: this is not entirely correct, we should only close the | |
| * 32-bit handle and not the 16-bit one, but we cannot do | |
| * this because of the way our DOS handles are implemented. | |
| * It shouldn't break anything though. | |
| */ | |
| void WINAPI DisposeLZ32Handle( HANDLE handle ) | |
| { | |
| int i; | |
| if (!handle || (handle == INVALID_HANDLE_VALUE)) return; | |
| for (i = 5; i < DOS_TABLE_SIZE; i++) | |
| if (dos_handles[i] == handle) | |
| { | |
| dos_handles[i] = 0; | |
| CloseHandle( handle ); | |
| break; | |
| } | |
| } | |
| /************************************************************************** | |
| * Operations on file names * | |
| **************************************************************************/ | |
| /************************************************************************** | |
| * ReplaceFileW (KERNEL32.@) | |
| * ReplaceFile (KERNEL32.@) | |
| */ | |
| BOOL WINAPI ReplaceFileW(LPCWSTR lpReplacedFileName,LPCWSTR lpReplacementFileName, | |
| LPCWSTR lpBackupFileName, DWORD dwReplaceFlags, | |
| LPVOID lpExclude, LPVOID lpReserved) | |
| { | |
| FIXME("(%s,%s,%s,%08lx,%p,%p) stub\n",debugstr_w(lpReplacedFileName),debugstr_w(lpReplacementFileName), | |
| debugstr_w(lpBackupFileName),dwReplaceFlags,lpExclude,lpReserved); | |
| SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT); | |
| return FALSE; | |
| } | |
| /************************************************************************** | |
| * ReplaceFileA (KERNEL32.@) | |
| */ | |
| BOOL WINAPI ReplaceFileA(LPCSTR lpReplacedFileName,LPCSTR lpReplacementFileName, | |
| LPCSTR lpBackupFileName, DWORD dwReplaceFlags, | |
| LPVOID lpExclude, LPVOID lpReserved) | |
| { | |
| FIXME("(%s,%s,%s,%08lx,%p,%p) stub\n",lpReplacedFileName,lpReplacementFileName, | |
| lpBackupFileName,dwReplaceFlags,lpExclude,lpReserved); | |
| SetLastError(ERROR_UNABLE_TO_MOVE_REPLACEMENT); | |
| return FALSE; | |
| } |