/* $Id: Fileio.cpp,v 1.71 2003-03-03 16:39:53 sandervl Exp $ */ /* * Win32 File IO API functions for OS/2 * * Copyright 1998-2000 Sander van Leeuwen * Copyright 1998 Patrick Haller * * Some parts based on Wine code (CopyFileExA/W, FindFirstFileExW, * GetShortPathNameA/W, GetLongPathNameA/W) * * Copyright 1993 John Burton * Copyright 1993 Erik Bos * Copyright 1996 Alexandre Julliard * * * Project Odin Software License can be found in LICENSE.TXT * */ /***************************************************************************** * Includes * *****************************************************************************/ #include #include #include #include #include #include #include "unicode.h" #include #include "handlemanager.h" #include "oslibdos.h" #include "oslibwps.h" #define DBG_LOCALLOG DBG_fileio #include "dbglocal.h" ODINDEBUGCHANNEL(KERNEL32-FILEIO) #include #include "fileio.h" #include /*********************************************************************** * DOSFS_ValidDOSName * * Return 1 if OS/2 file 'name' is also a valid MS-DOS name * (i.e. contains only valid DOS chars, lower-case only, fits in 8.3 format). * File name can be terminated by '\0', '\\' or '/'. */ static int DOSFS_ValidDOSName( const char *name, int ignore_case ) { static const char invalid_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" INVALID_DOS_CHARS; const char *p = name; const char *invalid = ignore_case ? (invalid_chars + 26) : invalid_chars; int len = 0; if (*p == '.') { /* Check for "." and ".." */ p++; if (*p == '.') p++; /* All other names beginning with '.' are invalid */ return (IS_END_OF_NAME(*p)); } while (!IS_END_OF_NAME(*p)) { if (strchr( invalid, *p )) return 0; /* Invalid char */ if (*p == '.') break; /* Start of the extension */ if (++len > 8) return 0; /* Name too long */ p++; } if (*p != '.') return 1; /* End of name */ p++; if (IS_END_OF_NAME(*p)) return 0; /* Empty extension not allowed */ len = 0; while (!IS_END_OF_NAME(*p)) { if (strchr( invalid, *p )) return 0; /* Invalid char */ if (*p == '.') return 0; /* Second extension not allowed */ if (++len > 3) return 0; /* Extension too long */ p++; } return 1; } /*********************************************************************** * DOSFS_Hash * * Transform an OS/2 file name into a hashed DOS name. If the name is a valid * DOS name, it is converted to upper-case; otherwise it is replaced by a * hashed version that fits in 8.3 format. * File name can be terminated by '\0', '\\' or '/'. * 'buffer' must be at least 13 characters long. */ static void DOSFS_Hash( LPCSTR name, LPSTR buffer, BOOL dir_format, BOOL ignore_case ) { static const char invalid_chars[] = INVALID_DOS_CHARS "~."; static const char hash_chars[33] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"; const char *p, *ext; char *dst; unsigned short hash; int i; if (dir_format) strcpy( buffer, " " ); if (DOSFS_ValidDOSName( name, ignore_case )) { /* Check for '.' and '..' */ if (*name == '.') { buffer[0] = '.'; if (!dir_format) buffer[1] = buffer[2] = '\0'; if (name[1] == '.') buffer[1] = '.'; return; } /* Simply copy the name, converting to uppercase */ for (dst = buffer; !IS_END_OF_NAME(*name) && (*name != '.'); name++) *dst++ = FILE_toupper(*name); if (*name == '.') { if (dir_format) dst = buffer + 8; else *dst++ = '.'; for (name++; !IS_END_OF_NAME(*name); name++) *dst++ = FILE_toupper(*name); } if (!dir_format) *dst = '\0'; return; } /* Compute the hash code of the file name */ /* If you know something about hash functions, feel free to */ /* insert a better algorithm here... */ if (ignore_case) { for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++) hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p) ^ (FILE_tolower(p[1]) << 8); hash = (hash<<3) ^ (hash>>5) ^ FILE_tolower(*p); /* Last character*/ } else { for (p = name, hash = 0xbeef; !IS_END_OF_NAME(p[1]); p++) hash = (hash << 3) ^ (hash >> 5) ^ *p ^ (p[1] << 8); hash = (hash << 3) ^ (hash >> 5) ^ *p; /* Last character */ } /* Find last dot for start of the extension */ for (p = name+1, ext = NULL; !IS_END_OF_NAME(*p); p++) if (*p == '.') ext = p; if (ext && IS_END_OF_NAME(ext[1])) ext = NULL; /* Empty extension ignored */ /* Copy first 4 chars, replacing invalid chars with '_' */ for (i = 4, p = name, dst = buffer; i > 0; i--, p++) { if (IS_END_OF_NAME(*p) || (p == ext)) break; *dst++ = strchr( invalid_chars, *p ) ? '_' : FILE_toupper(*p); } /* Pad to 5 chars with '~' */ while (i-- >= 0) *dst++ = '~'; /* Insert hash code converted to 3 ASCII chars */ *dst++ = hash_chars[(hash >> 10) & 0x1f]; *dst++ = hash_chars[(hash >> 5) & 0x1f]; *dst++ = hash_chars[hash & 0x1f]; /* Copy the first 3 chars of the extension (if any) */ if (ext) { if (!dir_format) *dst++ = '.'; for (i = 3, ext++; (i > 0) && !IS_END_OF_NAME(*ext); i--, ext++) *dst++ = strchr( invalid_chars, *ext ) ? '_' : FILE_toupper(*ext); } if (!dir_format) *dst = '\0'; } //****************************************************************************** //****************************************************************************** HANDLE WIN32API CreateFileA(LPCSTR lpszName, DWORD fdwAccess, DWORD fdwShareMode, LPSECURITY_ATTRIBUTES lpsa, DWORD fdwCreate, DWORD fdwAttrsAndFlags, HANDLE hTemplateFile) { dprintf(("CreateFileA %s", lpszName)); return(HMCreateFile(lpszName, fdwAccess, fdwShareMode, lpsa, fdwCreate, fdwAttrsAndFlags, hTemplateFile)); } //****************************************************************************** //****************************************************************************** HANDLE WIN32API CreateFileW(LPCWSTR lpszName, DWORD fdwAccess, DWORD fdwShareMode, LPSECURITY_ATTRIBUTES lpsa, DWORD fdwCreate, DWORD fdwAttrsAndFlags, HANDLE hTemplateFile) { HANDLE rc; char *astring; astring = UnicodeToAsciiString((LPWSTR)lpszName); rc = CreateFileA(astring, fdwAccess, fdwShareMode, lpsa, fdwCreate, fdwAttrsAndFlags, hTemplateFile); FreeAsciiString(astring); return(rc); } //****************************************************************************** //****************************************************************************** HANDLE WINAPI FindFirstFileA(LPCSTR lpFileName, WIN32_FIND_DATAA *lpFindFileData) { return FindFirstFileExA(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch, NULL, 0); } /***************************************************************************** * Name : HANDLE WIN32API FindFirstFileExA * Purpose : The FindFirstFileExA function searches a directory for a file * whose name and attributes match those specified in the * function call. * Parameters: LPCSTR lpFileName pointer to the name of the file * to search for * FINDEX_INFO_LEVELS fInfoLevelId information level of the returned data * LPVOID lpFindFileData pointer to the returned information * FINDEX_SEARCH_OPS fSearchOp type of filtering to perform * LPVOID lpSearchFilter pointer to search criteria * DWORD dwAdditionalFlags additional search control flags * Variables : * Result : If the function succeeds, the return value is a search handle * that can be used in a subsequent call to the FindNextFile or * FindClose functions. * If the function fails, the return value is INVALID_HANDLE_VALUE * Remark : * Status : * * Author : SvL *****************************************************************************/ HANDLE WIN32API FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { HANDLE hFind; if(lpFileName == NULL || lpFindFileData == NULL || lpSearchFilter != NULL) { dprintf(("!ERROR!: invalid parameter(s)")); SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } if(fSearchOp == FindExSearchLimitToDevices) { dprintf(("!ERROR!: FindExSearchLimitToDevices not implemented")); SetLastError(ERROR_NOT_SUPPORTED); return INVALID_HANDLE_VALUE; } else if(fSearchOp == FindExSearchLimitToDirectories) { //NOTE: According to the SDK docs we are allowed to silently ignore this option dprintf(("!WARNING!: FindExSearchLimitToDirectories IGNORED")); fSearchOp = FindExSearchNameMatch; } if(dwAdditionalFlags != 0) { dprintf(("!ERROR!: options not implemented 0x%08x 0x%08lx\n", fSearchOp, dwAdditionalFlags )); SetLastError(ERROR_NOT_SUPPORTED); return INVALID_HANDLE_VALUE; } dprintf(("FindFirstFileExA %s %x %x %x %x %x", lpFileName, fInfoLevelId, lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags)); switch(fInfoLevelId) { case FindExInfoStandard: { char *filename; int namelen; //Strip the backslash if 'x:\'. This function is supposed //to work in that case, but fail with 'x:\directory\' namelen = strlen(lpFileName); if(namelen == 3 && lpFileName[1] == ':' && lpFileName[2] == '\\') { filename = (char *)alloca(namelen+1); strcpy(filename, lpFileName); filename[namelen-1] = 0; } else filename = (char *)lpFileName; return (HANDLE)OSLibDosFindFirst(filename, (WIN32_FIND_DATAA *)lpFindFileData); } default: //should never happen dprintf(("!ERROR! unsupported fInfoLevelId")); SetLastError(ERROR_INVALID_PARAMETER); break; } return INVALID_HANDLE_VALUE; } //****************************************************************************** //****************************************************************************** HANDLE WINAPI FindFirstFileW(LPCWSTR lpFileName, WIN32_FIND_DATAW *lpFindFileData) { return FindFirstFileExW(lpFileName, FindExInfoStandard, lpFindFileData, FindExSearchNameMatch, NULL, 0); } /***************************************************************************** * Name : HANDLE WIN32API FindFirstFileExW * Purpose : The FindFirstFileExW function searches a directory for a file * whose name and attributes match those specified in the * function call. * Parameters: LPCWSTR lpFileName pointer to the name of the file * to search for * FINDEX_INFO_LEVELS fInfoLevelId information level of the returned data * LPVOID lpFindFileData pointer to the returned information * FINDEX_SEARCH_OPS fSearchOp type of filtering to perform * LPVOID lpSearchFilter pointer to search criteria * DWORD dwAdditionalFlags additional search control flags * Variables : * Result : If the function succeeds, the return value is a search handle * that can be used in a subsequent call to the FindNextFile or * FindClose functions. * If the function fails, the return value is INVALID_HANDLE_VALUE * Remark : * Status : * * Author : Wine *****************************************************************************/ HANDLE WIN32API FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { HANDLE handle; WIN32_FIND_DATAA dataA; LPVOID _lpFindFileData; LPSTR pathA; switch(fInfoLevelId) { case FindExInfoStandard: { _lpFindFileData = &dataA; } break; default: dprintf(("!ERROR! unsupported fInfoLevelId")); SetLastError(ERROR_INVALID_PARAMETER); break; } pathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName ); handle = FindFirstFileExA(pathA, fInfoLevelId, _lpFindFileData, fSearchOp, lpSearchFilter, dwAdditionalFlags); HeapFree( GetProcessHeap(), 0, pathA ); if (handle == INVALID_HANDLE_VALUE) return handle; switch(fInfoLevelId) { case FindExInfoStandard: { WIN32_FIND_DATAW *dataW = (WIN32_FIND_DATAW*) lpFindFileData; dataW->dwFileAttributes = dataA.dwFileAttributes; dataW->ftCreationTime = dataA.ftCreationTime; dataW->ftLastAccessTime = dataA.ftLastAccessTime; dataW->ftLastWriteTime = dataA.ftLastWriteTime; dataW->nFileSizeHigh = dataA.nFileSizeHigh; dataW->nFileSizeLow = dataA.nFileSizeLow; MultiByteToWideChar( CP_ACP, 0, dataA.cFileName, -1, dataW->cFileName, sizeof(dataW->cFileName)/sizeof(WCHAR) ); MultiByteToWideChar( CP_ACP, 0, dataA.cAlternateFileName, -1, dataW->cAlternateFileName, sizeof(dataW->cAlternateFileName)/sizeof(WCHAR) ); } break; } return handle; } //****************************************************************************** // internal function for faster access (SHELL32) //****************************************************************************** HANDLE WIN32API FindFirstFileMultiA(LPCSTR lpFileName, WIN32_FIND_DATAA * lpFindFileData, DWORD * count) { dprintf(("FindFirstFileMultiA %s %x %x", lpFileName, lpFindFileData, count)); return (HANDLE)OSLibDosFindFirstMulti(lpFileName,lpFindFileData,count); } //****************************************************************************** //****************************************************************************** BOOL WIN32API FindNextFileA(HANDLE hFindFile, WIN32_FIND_DATAA * lpFindFileData) { return OSLibDosFindNext(hFindFile,lpFindFileData); } //****************************************************************************** // internal function for faster access (SHELL32) //****************************************************************************** BOOL WIN32API FindNextFileMultiA(HANDLE hFindFile, WIN32_FIND_DATAA * lpFindFileData, DWORD *count) { dprintf(("FindNextFileMultiA %x %x %x", hFindFile, lpFindFileData, count)); return OSLibDosFindNextMulti(hFindFile,lpFindFileData,count); } //****************************************************************************** //****************************************************************************** BOOL WIN32API FindNextFileW(HANDLE hFindFile, WIN32_FIND_DATAW * lpFindFileData) { WIN32_FIND_DATAA wfda; BOOL rc; rc = OSLibDosFindNext(hFindFile,&wfda); if(rc == 0) { memset(lpFindFileData, 0, sizeof(WIN32_FIND_DATAW)); } else { // convert back the result structure memcpy(lpFindFileData, &wfda, sizeof(WIN32_FIND_DATAA)); lstrcpynAtoW (lpFindFileData->cFileName, wfda.cFileName, sizeof(wfda.cFileName)); lstrcpynAtoW (lpFindFileData->cAlternateFileName, wfda.cAlternateFileName, sizeof(wfda.cAlternateFileName)); } return rc; } //****************************************************************************** //****************************************************************************** BOOL WIN32API FindClose(HANDLE hFindFile) { return OSLibDosFindClose(hFindFile); } //****************************************************************************** //****************************************************************************** INT WIN32API CompareFileTime(FILETIME * lpft1, FILETIME * lpft2) { if (lpft1 == NULL || lpft2 == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return -1; } if(lpft1->dwHighDateTime > lpft2->dwHighDateTime) return 1; if(lpft1->dwHighDateTime < lpft2->dwHighDateTime) return -1; if(lpft1->dwLowDateTime > lpft2->dwLowDateTime) return 1; if(lpft1->dwLowDateTime < lpft2->dwLowDateTime) return -1; return 0; //equal } //****************************************************************************** //****************************************************************************** BOOL WIN32API CopyFileA(LPCSTR arg1, LPCSTR arg2, BOOL arg3) { dprintf(("CopyFileA %s %s %d", arg1, arg2, arg3)); return OSLibDosCopyFile(arg1, arg2, arg3); } //****************************************************************************** //****************************************************************************** BOOL WIN32API CopyFileW(LPCWSTR arg1, LPCWSTR arg2, BOOL arg3) { BOOL rc; char *astring1, *astring2; astring1 = UnicodeToAsciiString((LPWSTR)arg1); astring2 = UnicodeToAsciiString((LPWSTR)arg2); rc = CopyFileA(astring1, astring2, arg3); FreeAsciiString(astring2); FreeAsciiString(astring1); return(rc); } /***************************************************************************** * Name : BOOL WIN32API CopyFileExA * Purpose : The CopyFileExA function copies an existing file to a new file. * This function preserves extended attributes, OLE structured * storage, NTFS alternate data streams, and file attributes. * Security attributes for the existing file are not copied to * the new file. * Parameters: LPCSTR lpExistingFileName pointer to name of an existing file * LPCSTR lpNewFileName pointer to filename to copy to * LPPROGRESS_ROUTINE lpProgressRoutine pointer to the callback function * LPVOID lpData to be passed to the callback function * LPBOOL pbCancel flag that can be used to cancel the operation * DWORD dwCopyFlags flags that specify how the file is copied * Variables : * Result : f the function succeeds, the return value is nonzero. * If the function fails, the return value is zero. * To get extended error information call GetLastError. * Remark : * Status : UNTESTED STUB * * Author : Markus Montkowski [Thu, 1998/05/19 11:46] *****************************************************************************/ BOOL WIN32API CopyFileExA( LPCSTR lpExistingFileName, LPCSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags) { dprintf(("KERNEL32: CopyFileExA(%08x,%08x,%08x,%08x,%08x,%08x) not properly implemented\n", lpExistingFileName, lpNewFileName, lpProgressRoutine, lpData, pbCancel, dwCopyFlags )); BOOL failIfExists = FALSE; /* * Interpret the only flag that CopyFile can interpret. */ if((dwCopyFlags & COPY_FILE_FAIL_IF_EXISTS) != 0) { failIfExists = TRUE; } return CopyFileA(lpExistingFileName, lpNewFileName, failIfExists); } /***************************************************************************** * Name : BOOL WIN32API CopyFileExW * Purpose : The CopyFileExW function copies an existing file to a new file. * This function preserves extended attributes, OLE structured * storage, NTFS alternate data streams, and file attributes. * Security attributes for the existing file are not copied to * the new file. * Parameters: LPCWSTR lpExistingFileName pointer to name of an existing file * LPCWSTR lpNewFileName pointer to filename to copy to * LPPROGRESS_ROUTINE lpProgressRoutine pointer to the callback function * LPVOID lpData to be passed to the callback function * LPBOOL pbCancel flag that can be used to cancel the operation * DWORD dwCopyFlags flags that specify how the file is copied * Variables : * Result : f the function succeeds, the return value is nonzero. * If the function fails, the return value is zero. * To get extended error information call GetLastError. * Remark : * Status : UNTESTED STUB * * Author : Markus Montkowski [Thu, 1998/05/19 11:46] *****************************************************************************/ BOOL WIN32API CopyFileExW( LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, LPPROGRESS_ROUTINE lpProgressRoutine, LPVOID lpData, LPBOOL pbCancel, DWORD dwCopyFlags) { LPSTR sourceA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpExistingFileName ); LPSTR destA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpNewFileName ); BOOL ret = CopyFileExA(sourceA, destA, lpProgressRoutine, lpData, pbCancel, dwCopyFlags); HeapFree( GetProcessHeap(), 0, sourceA ); HeapFree( GetProcessHeap(), 0, destA ); return ret; } //****************************************************************************** //****************************************************************************** BOOL WIN32API DeleteFileA(LPCSTR lpszFile) { BOOL rc; #if 0 if((strstr(lpszFile, "odin32_") || strstr(lpszFile, "pe_")) && strstr(lpszFile, ".log")) { return TRUE; } #endif if(lpszFile == NULL) { SetLastError(ERROR_INVALID_PARAMETER); //?? return FALSE; } //If the app is deleting a shellink file (.lnk), then we must delete the WPS object if(OSLibIsShellLink((LPSTR)lpszFile)) { OSLibWinDeleteObject((LPSTR)lpszFile); } rc = OSLibDosDelete((LPSTR)lpszFile); if(!rc) { dprintf(("DeleteFileA %s returned FALSE; last error %x", lpszFile, GetLastError())); if(GetLastError() == ERROR_BAD_UNIT) { return TRUE; } } else dprintf(("DeleteFileA %s", lpszFile)); return rc; } //****************************************************************************** //****************************************************************************** BOOL WIN32API DeleteFileW(LPCWSTR arg1) { BOOL rc; char *astring; astring = UnicodeToAsciiString((LPWSTR)arg1); rc = DeleteFileA(astring); FreeAsciiString(astring); return(rc); } //****************************************************************************** //****************************************************************************** UINT WIN32API GetTempFileNameA(LPCSTR lpPathName, LPCSTR lpPrefixString, UINT uUnique, LPSTR lpTempFileName) { dprintf(("GetTempFileNameA %s %s", lpPathName, lpPrefixString)); UINT rc = O32_GetTempFileName(lpPathName, lpPrefixString, uUnique, lpTempFileName); dprintf(("GetTempFileNameA: returns %d (%s)\n", rc, rc > 0 && lpTempFileName ? lpTempFileName : "")); return rc; } //****************************************************************************** //****************************************************************************** UINT WIN32API GetTempFileNameW(LPCWSTR lpPathName, LPCWSTR lpPrefixString, UINT uUnique, LPWSTR lpTempFileName) { char *asciipath, *asciiprefix; char *asciitemp = (char *)malloc(MAX_PATH+1); UINT rc; asciipath = UnicodeToAsciiString((LPWSTR)lpPathName); asciiprefix = UnicodeToAsciiString((LPWSTR)lpPrefixString); rc = GetTempFileNameA(asciipath, asciiprefix, uUnique, asciitemp); if(rc) AsciiToUnicode(asciitemp, lpTempFileName); FreeAsciiString(asciiprefix); FreeAsciiString(asciipath); free(asciitemp); return(rc); } //****************************************************************************** //****************************************************************************** BOOL WIN32API ReadFile(HANDLE hFile, PVOID pBuffer, DWORD dwLength, PDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped) { if(lpNumberOfBytesRead) *lpNumberOfBytesRead = 0; if(dwLength == 0) { dprintf(("!WARNING!: Nothing to do")); //TODO: should we fail here instead?? (wine doesn't) return TRUE; } return (HMReadFile(hFile, pBuffer, dwLength, lpNumberOfBytesRead, lpOverlapped, NULL)); } //****************************************************************************** //****************************************************************************** BOOL WIN32API ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { if(nNumberOfBytesToRead == 0) { dprintf(("!WARNING!: Nothing to do")); //TODO: should we fail here instead?? (wine doesn't) return TRUE; } if(!lpOverlapped || !lpCompletionRoutine) { dprintf(("!WARNING!: !lpOverlapped || !lpCompletionRoutine")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } //SDK docs say ReadFileEx will fail if this condition is true if(GetFileType(hFile) == FILE_TYPE_PIPE && (lpOverlapped->Offset || lpOverlapped->OffsetHigh)) { dprintf(("!WARNING!: lpOverlapped->Offset & lpOverlapped->OffsetHigh must be ZERO for named pipes")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return (HMReadFile(hFile, lpBuffer, nNumberOfBytesToRead, NULL, lpOverlapped, lpCompletionRoutine)); } //****************************************************************************** //****************************************************************************** BOOL WIN32API WriteFile(HANDLE hFile, LPCVOID buffer, DWORD nrbytes, LPDWORD nrbyteswritten, LPOVERLAPPED lpOverlapped) { if(nrbyteswritten) *nrbyteswritten = 0; if(nrbytes == 0) { dprintf(("!WARNING!: Nothing to do")); //TODO: should we fail here instead?? (wine doesn't) return TRUE; } return (HMWriteFile(hFile, buffer, nrbytes, nrbyteswritten, lpOverlapped, NULL)); } /***************************************************************************** * Name : BOOL WriteFileEx * Purpose : The WriteFileEx function writes data to a file. It is designed * solely for asynchronous operation, unlike WriteFile, which is * designed for both synchronous and asynchronous operation. * WriteFileEx reports its completion status asynchronously, * calling a specified completion routine when writing is completed * and the calling thread is in an alertable wait state. * Parameters: HANDLE hFile handle of file to write * LPVOID lpBuffer address of buffer * DWORD nNumberOfBytesToRead number of bytes to write * LPOVERLAPPED lpOverlapped address of offset * LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine address of completion routine * Variables : * Result : TRUE / FALSE * Remark : * Status : UNTESTED STUB * * Author : Patrick Haller [Mon, 1998/06/15 08:00] *****************************************************************************/ BOOL WIN32API WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine) { if(nNumberOfBytesToWrite == 0) { dprintf(("!WARNING!: Nothing to do")); //TODO: should we fail here instead?? (wine doesn't) return TRUE; } if(!lpOverlapped || !lpCompletionRoutine) { dprintf(("!WARNING!: !lpOverlapped || !lpCompletionRoutine")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } //SDK docs say WriteFileEx will fail if this condition is true if(GetFileType(hFile) == FILE_TYPE_PIPE && (lpOverlapped->Offset || lpOverlapped->OffsetHigh)) { dprintf(("!WARNING!: lpOverlapped->Offset & lpOverlapped->OffsetHigh must be ZERO for named pipes")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return (HMWriteFile(hFile, (LPVOID)lpBuffer, nNumberOfBytesToWrite, NULL, lpOverlapped, lpCompletionRoutine)); } //****************************************************************************** //****************************************************************************** DWORD WIN32API GetFileAttributesA(LPCSTR lpszFileName) { DWORD rc, error; //Disable error popus. ULONG oldmode = SetErrorMode(SEM_FAILCRITICALERRORS); rc = OSLibGetFileAttributes((LPSTR)lpszFileName); SetErrorMode(oldmode); //SvL: Open32 returns FILE_ATTRIBUTE_DIRECTORY|FILE_ATTRIBUTE_NORMAL for // directories whereas NT 4 (SP6) only returns FILE_ATTRIBUTE_DIRECTORY if(rc != -1 && (rc & FILE_ATTRIBUTE_DIRECTORY)) { rc = FILE_ATTRIBUTE_DIRECTORY; } #if 0 // need more tests, maybe there is also a better way to hide simulated b: if(rc == -1 && lpszFileName != NULL && !strnicmp(lpszFileName, "B:", 2)) { error = GetLastError(); if(error = ERROR_DISK_CHANGE) SetLastError(ERROR_NOT_READY); else SetLastError(error); } #endif dprintf(("KERNEL32: GetFileAttributes of %s returned %d\n", lpszFileName, rc)); return(rc); } //****************************************************************************** //****************************************************************************** DWORD WIN32API GetFileAttributesW(LPCWSTR arg1) { DWORD rc; char *astring; astring = UnicodeToAsciiString((LPWSTR)arg1); dprintf(("GetFileAttributesW %s", astring)); rc = GetFileAttributesA(astring); FreeAsciiString(astring); return(rc); } //****************************************************************************** //****************************************************************************** BOOL WIN32API SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) { dprintf(("KERNEL32: SetFileAttributes of %s", lpFileName)); return O32_SetFileAttributes(lpFileName, dwFileAttributes); } //****************************************************************************** //****************************************************************************** BOOL WIN32API SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) { char *asciifile; BOOL rc; asciifile = UnicodeToAsciiString((LPWSTR)lpFileName); rc = SetFileAttributesA(asciifile, dwFileAttributes); FreeAsciiString(asciifile); return(rc); } //****************************************************************************** //****************************************************************************** DWORD WIN32API GetFullPathNameA(LPCSTR lpFileName, DWORD nBufferLength, LPSTR lpBuffer, LPSTR *lpFilePart) { char *ptr, *lpszFileName; DWORD rc; dprintf(("KERNEL32: GetFullPathName(%s,%d,0x%X)", lpFileName, nBufferLength, lpBuffer)); lpszFileName = strdup(lpFileName); while((ptr = strchr(lpszFileName, '/')) != NULL) *ptr = '\\'; rc = O32_GetFullPathName(lpszFileName, nBufferLength, lpBuffer, lpFilePart); #ifdef DEBUG if (rc > nBufferLength) { dprintf(("KERNEL32: GetFullPathName returns %d (needs a bgger buffer)", rc)); } else if (rc > 0) { dprintf(("KERNEL32: GetFullPathName returns %d (%s,%s)", rc, lpBuffer, lpFilePart ? *lpFilePart : NULL)); } #endif free(lpszFileName); return rc; } //****************************************************************************** //****************************************************************************** DWORD WIN32API GetFullPathNameW(LPCWSTR lpFileName, DWORD nBufferLength, LPWSTR lpBuffer, LPWSTR *lpFilePart) { char *astring = NULL, *asciibuffer = NULL, *asciipart = NULL; DWORD rc; if(nBufferLength) { asciibuffer = (char *)malloc(nBufferLength+1); } astring = UnicodeToAsciiString((LPWSTR)lpFileName); rc = GetFullPathNameA(astring, nBufferLength, asciibuffer, &asciipart); if(rc>0 && rc return required length // (INCLUDING 0 terminator) //- if lpszLongPath == NULL -> ERROR_INVALID_PARAMETER (return 0) //- if lpszLongPath empty -> proceed as if nothing is wrong //- does NOT clear the last error if successful! //- if successful -> return length of string (excluding 0 terminator) //****************************************************************************** DWORD WIN32API GetShortPathNameA(LPCTSTR lpszLongPath, LPTSTR lpszShortPath, DWORD cchBuffer) { char short_name[MAX_PATHNAME_LEN]; /* Long pathname in Unix format */ int length, marker = 0; LPSTR tmpshortpath,tmplongpath; DWORD attr, sp = 0, lp = 0; int tmplen, drive; dprintf(("KERNEL32: GetShortPathNameA %s", lpszLongPath)); if(!lpszLongPath) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (!lpszLongPath[0]) { SetLastError(ERROR_BAD_PATHNAME); return 0; } if ( ( tmpshortpath = (char*) HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) { SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); return 0; } if ( ( tmplongpath = (char*) HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) { SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); return 0; } lstrcpyA(tmplongpath,lpszLongPath); /* check for drive letter */ if ( lpszLongPath[1] == ':' ) { tmpshortpath[0] = lpszLongPath[0]; tmpshortpath[1] = ':'; sp = 2; lp = 2; } //todo: check drive validity! while ( lpszLongPath[lp] ) { marker = 0; /* check for path delimiters and reproduce them */ if ( lpszLongPath[lp] == '\\' || lpszLongPath[lp] == '/' ) { if (!sp || tmpshortpath[sp-1]!= '\\') { /* strip double "\\" */ tmpshortpath[sp] = '\\'; sp++; } tmpshortpath[sp]=0;/*terminate string*/ lp++; continue; } tmplen = strcspn ( lpszLongPath + lp, "\\/" ); lstrcpynA ( tmpshortpath+sp, lpszLongPath + lp, tmplen+1 ); /* Check, if the current element is a valid dos name */ if ( DOSFS_ValidDOSName ( lpszLongPath + lp, TRUE ) ) { sp += tmplen; lp += tmplen; continue; } if (tmplongpath[lp + tmplen] == '\\') { tmplongpath[lp + tmplen] = 0; marker = 1; } attr = GetFileAttributesA(tmplongpath); if (attr == -1) { SetLastError ( ERROR_FILE_NOT_FOUND ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return 0; } DOSFS_Hash(tmpshortpath+sp, short_name, FALSE, TRUE ); strcpy( tmpshortpath+sp, short_name); sp += strlen ( tmpshortpath+sp ); if (marker) tmplongpath[lp + tmplen] = '\\'; lp += tmplen; } tmpshortpath[sp] = 0; lstrcpynA ( lpszShortPath, tmpshortpath, cchBuffer ); dprintf(("returning %s\n", lpszShortPath)); tmplen = strlen ( lpszShortPath ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return tmplen; } //****************************************************************************** //****************************************************************************** DWORD WIN32API GetShortPathNameW(LPCWSTR lpszLongPath, LPWSTR lpszShortPath, DWORD cchBuffer) { LPSTR longpathA, shortpathA; DWORD ret = 0; longpathA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpszLongPath ); shortpathA = (LPSTR) HeapAlloc ( GetProcessHeap(), 0, cchBuffer ); ret = GetShortPathNameA ( longpathA, shortpathA, cchBuffer ); if (cchBuffer > 0 && !MultiByteToWideChar( CP_ACP, 0, shortpathA, -1, lpszShortPath, cchBuffer )) lpszShortPath[cchBuffer-1] = 0; HeapFree( GetProcessHeap(), 0, longpathA ); HeapFree( GetProcessHeap(), 0, shortpathA ); return ret; } //****************************************************************************** //Behaviour in NT 4, SP6: (presumably the same as GetShortPathNameA; TODO check) //- converts short filename to long filenames (TODO: not yet done here!) //- if lpszShortPath 0 or cchBuffer too small -> return required length // (INCLUDING 0 terminator) //- if lpszLongPath == NULL -> ERROR_INVALID_PARAMETER (return 0) //- if lpszLongPath empty -> proceed as if nothing is wrong //- does NOT clear the last error if successful! //- if successful -> return length of string (excluding 0 terminator) //****************************************************************************** DWORD WINAPI GetLongPathNameA( LPCSTR lpszShortPath, LPSTR lpszLongPath, DWORD cchBuffer ) { int tmplen; char short_name[MAX_PATHNAME_LEN]; /* Long pathname in Unix format */ WIN32_FIND_DATAA FindFileData; HANDLE hFind; DWORD sp = 0, lp = 0,attr; LPSTR tmpshortpath,tmplongpath; LPCSTR lpszShortPath1; LPSTR lpszLongPath1; dprintf(("GetLongPathNameA %s %x %d", lpszShortPath, lpszLongPath, cchBuffer)); if(!lpszShortPath) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if ( ( tmpshortpath = (char*) HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) { SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); return 0; } if ( ( tmplongpath = (char*) HeapAlloc ( GetProcessHeap(), 0, MAX_PATHNAME_LEN ) ) == NULL ) { SetLastError ( ERROR_NOT_ENOUGH_MEMORY ); return 0; } lpszShortPath1 = ODINHelperStripUNC(lpszShortPath); lpszLongPath1 = ODINHelperStripUNC(lpszLongPath); lstrcpyA(tmpshortpath,lpszShortPath1); /* check for drive letter */ if ( lpszShortPath1[1] == ':' ) { tmplongpath[0] = lpszShortPath1[0]; tmplongpath[1] = ':'; sp = 2; lp = 2; } //todo: check drive validity! while ( lpszShortPath1[lp] ) { /* check for path delimiters and reproduce them */ if ( lpszShortPath1[lp] == '\\' || lpszShortPath1[lp] == '/' ) { if (!sp || tmplongpath[sp-1]!= '\\') { /* strip double "\\" */ tmplongpath[sp] = '\\'; sp++; } tmplongpath[sp]=0;/*terminate string*/ lp++; continue; } tmplen = strcspn ( lpszShortPath1 + lp, "\\/" ); lstrcpynA ( tmplongpath+sp, lpszShortPath1 + lp, tmplen+1 ); attr = GetFileAttributesA(tmplongpath); if (attr != -1) { sp += tmplen; lp += tmplen; continue; } else // it may be hashed name or name with weird characters! if ((tmplongpath+sp)[4] == '~') { //hashed entry Wine does linear dir search.. incredible.. we will be //better ;) if (strchr(tmplongpath+sp,'_')) { (tmplongpath+sp)[0] = '*'; (tmplongpath+sp)[1] = 0; } else { (tmplongpath+sp)[4] = '*'; (tmplongpath+sp)[5] = 0; } hFind = FindFirstFileExA(tmplongpath, FindExInfoStandard, &FindFileData, FindExSearchNameMatch, NULL, 0 ); if (hFind == INVALID_HANDLE_VALUE) { //no possible variants! SetLastError ( ERROR_FILE_NOT_FOUND ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return 0; } else do { DOSFS_Hash(FindFileData.cFileName, short_name, FALSE, TRUE ); //this happens on files like [hello world] if (!lstrncmpA(short_name, lpszShortPath1+lp, (lpszShortPath1+lp+tmplen)[-1] == '.' ? tmplen-1 : tmplen )) { strcpy( tmplongpath+sp, FindFileData.cFileName); sp += strlen ( tmplongpath+sp ); lp += tmplen; break; } } while (FindNextFileA(hFind, &FindFileData)); // no FindClose() here or else GetLastError() will not give its error if (GetLastError() == ERROR_NO_MORE_FILES) { FindClose(hFind); SetLastError ( ERROR_FILE_NOT_FOUND ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return 0; } FindClose(hFind); } else { // if this file can't be found in common or hashed files // it does not exist SetLastError ( ERROR_FILE_NOT_FOUND ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return 0; } } tmplongpath[sp] = 0; lstrcpynA ( lpszLongPath1, tmplongpath, cchBuffer ); dprintf(("returning %s\n", lpszLongPath)); tmplen = strlen ( lpszLongPath ); HeapFree ( GetProcessHeap(), 0, tmpshortpath ); HeapFree ( GetProcessHeap(), 0, tmplongpath ); return tmplen; } //****************************************************************************** //****************************************************************************** DWORD WINAPI GetLongPathNameW( LPCWSTR lpszShortPath, LPWSTR lpszLongPath, DWORD cchBuffer ) { int length; dprintf(("GetLongPathNameW %x %ls %d", lpszShortPath, lpszLongPath, cchBuffer)); dprintf(("WARNING: WIN98 ONLY!!")); if(!lpszShortPath) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } length = lstrlenW(lpszShortPath) + 1; if(length > cchBuffer) { if(lpszLongPath) { *lpszLongPath = 0; } return(length); //return length required (including 0 terminator) } lstrcpyW(lpszLongPath, lpszShortPath); return(length-1); } //****************************************************************************** //****************************************************************************** void WIN32API SetFileApisToANSI() { dprintf(("!WARNING! SetFileApisToANSI() stub\n")); } /***************************************************************************** * Name : DWORD GetCompressedFileSizeA * Purpose : The GetCompressedFileSizeA function obtains the compressed * size, in bytes, of a specified file. * Parameters: LPCTSTR lpFileName, // pointer to name of file * LPDWORD lpFileSizeHigh // pointer to DWORD to receive * high-order doubleword of file size * Variables : * Result : size of compressed file * Remark : * Status : UNTESTED * * Author : Patrick Haller [Mon, 1998/06/15 08:00] *****************************************************************************/ DWORD WIN32API GetCompressedFileSizeA(LPCTSTR lpFileName, LPDWORD lpFileSizeHigh) { dprintf(("KERNEL32: GetCompressedFileSizeA (%s, %08xh) not implemented.\n", lpFileName, lpFileSizeHigh)); /* @@@PH: simply return the standard filesize */ return 0; } /***************************************************************************** * Name : DWORD GetCompressedFileSizeW * Purpose : The GetCompressedFileSizeE function obtains the compressed * size, in bytes, of a specified file. * Parameters: LPCWSTR lpFileName, // pointer to name of file * LPDWORD lpFileSizeHigh // pointer to DWORD to receive * high-order doubleword of file size * Variables : * Result : size of compressed file * Remark : * Status : UNTESTED * * Author : Patrick Haller [Mon, 1998/06/15 08:00] *****************************************************************************/ DWORD WIN32API GetCompressedFileSizeW(LPCWSTR lpFileName, LPDWORD lpFileSizeHigh) { LPCTSTR lpAsciiFileName; /* converted filename */ DWORD rc; /* function result */ dprintf(("KERNEL32: GetCompressedFileSizeW (%s, %08xh)\n", lpFileName, lpFileSizeHigh)); lpAsciiFileName = UnicodeToAsciiString( (LPWSTR) lpFileName); rc = GetCompressedFileSizeA(lpAsciiFileName, lpFileSizeHigh); FreeAsciiString( (char *) lpAsciiFileName); return (rc); /* return result */ } /***************************************************************************** * Name : BOOL GetFileAttributesExA * Purpose : * Parameters: * Variables : * Result : * Remark : KERNEL32.874 * Status : UNTESTED * * Author : Patrick Haller [Mon, 1998/06/15 08:00] *****************************************************************************/ BOOL WIN32API GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) { BOOL rc; dprintf(("KERNEL32: GetFileAttributesExA(%s,%08xh,%08xh) mostly implemented.\n", lpFileName, fInfoLevelId, lpFileInformation)); if (lpFileName == NULL) return FALSE; if (lpFileInformation == NULL) return FALSE; if (fInfoLevelId == GetFileExInfoStandard) { LPWIN32_FILE_ATTRIBUTE_DATA lpFad = (LPWIN32_FILE_ATTRIBUTE_DATA) lpFileInformation; rc = OSLibDosGetFileAttributesEx((LPSTR)lpFileName, fInfoLevelId, lpFileInformation); return (rc); } else { dprintf(("KERNEL32: GetFileAttributesExA - invalid info level %d!\n", fInfoLevelId)); return FALSE; } } /***************************************************************************** * Name : BOOL GetFileAttributesExW * Purpose : * Parameters: * Variables : * Result : * Remark : KERNEL32.875 * Status : UNTESTED * * Author : Patrick Haller [Mon, 1998/06/15 08:00] *****************************************************************************/ BOOL WIN32API GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation) { LPSTR nameA = HEAP_strdupWtoA( GetProcessHeap(), 0, lpFileName ); BOOL res = GetFileAttributesExA( nameA, fInfoLevelId, lpFileInformation); HeapFree( GetProcessHeap(), 0, nameA ); return res; } //****************************************************************************** //****************************************************************************** HANDLE WIN32API FindFirstChangeNotificationA(LPCSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter) { dprintf(("KERNEL32: FindFirstChangeNotificationA %s, Not implemented (faked)", lpPathName)); return -1; } //****************************************************************************** //****************************************************************************** BOOL WIN32API FindNextChangeNotification(HANDLE hChange) { dprintf(("KERNEL32: FindNextChangeNotification (%08xh), Not implemented\n", hChange)); return FALSE; } //****************************************************************************** //****************************************************************************** BOOL WIN32API FindCloseChangeNotification(HANDLE hChange) { dprintf(("KERNEL32: OS2FindNextChangeNotification, Not implemented\n")); return(TRUE); } /***************************************************************************** * Name : HANDLE WIN32API FindFirstChangeNotificationW * Purpose : The FindFirstChangeNotification function creates a change * notification handle and sets up initial change notification * filter conditions. A wait on a notification handle succeeds when * a change matching the filter conditions occurs in the specified * directory or subtree. * Parameters: LPCWSTR lpPathName pointer to name of directory to watch * BOOL bWatchSubtree flag for monitoring directory or * directory tree * DWORD dwNotifyFilter filter conditions to watch for * Variables : * Result : If the function succeeds, the return value is a handle to a find * change notification object. * If the function fails, the return value is INVALID_HANDLE_VALUE * Remark : * Status : UNTESTED STUB * * Author : Markus Montkowski [Tha, 1998/05/21 20:57] *****************************************************************************/ HANDLE WIN32API FindFirstChangeNotificationW(LPCWSTR lpPathName, BOOL bWatchSubtree, DWORD dwNotifyFilter) { LPSTR lpAsciiPath; HANDLE hChange; lpAsciiPath = UnicodeToAsciiString( (LPWSTR) lpPathName); hChange = FindFirstChangeNotificationA(lpAsciiPath, bWatchSubtree, dwNotifyFilter ); if (lpAsciiPath) FreeAsciiString(lpAsciiPath); return hChange; } /***************************************************************************** * Name : BOOL GetOverlappedResult * Purpose : forward call to Open32 * Parameters: * Variables : * Result : * Remark : handle translation is done in GetOverlappedResult * Status : * * Author : Patrick Haller [Fri, 1999/06/18 03:44] *****************************************************************************/ BOOL WIN32API GetOverlappedResult(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 ? */ { //NOTE: According to the SDK docs lpOverlapped->hEvent can be 0. This function // is supposed to wait on the file handle in that case. We don't support // this, so we just fail. if(lpOverlapped == NULL || lpOverlapped->hEvent == 0) { dprintf(("!ERROR!: lpOverlapped == NULL || lpOverlapped->hEvent == 0")); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return HMGetOverlappedResult(hFile, lpOverlapped, lpTransferred, bWait); } //****************************************************************************** //******************************************************************************