/* $Id: kLdrRdrFile.c 2860 2006-11-08 04:10:14Z bird $ */ /** @file * * kLdr - The Dynamic Loader, file abstraction. * * Copyright (c) 2006 knut st. osmundsen * * * This file is part of kLdr. * * kLdr is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * kLdr 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with kLdr; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /******************************************************************************* * Header Files * *******************************************************************************/ #ifdef __OS2__ # define INCL_ERRORS # define INCL_BASE # include #elif defined(__WIN32__) || defined(__WIN64__) || defined(__WIN__) # include # ifndef __WIN__ # define __WIN__ # endif #else # error "port me" #endif #include #include "kLdrHlp.h" /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** * Prepared stuff. */ typedef struct KLDRRDRFILEPREP { /** The address of the prepared region. */ void *pv; /** The size of the prepared region. */ size_t cb; #if defined(__WIN__) || defined(__NT__) /** Handle to the section created to map the file. */ HANDLE hSection; #endif } KLDRRDRFILEPREP, *PKLDRRDRFILEPREP; /** * The file provier instance for native files. */ typedef struct KLDRRDRFILE { /** The file reader vtable. */ KLDRRDR Core; /** The file handle. */ #ifdef __OS2__ HFILE File; #elif defined(__WIN__) || defined(__NT__) HANDLE File; #else # error "Port me!" #endif /** The current file offset. */ off_t off; /** The file size. */ off_t cb; /** Array where we stuff the mapping area data. */ KLDRRDRFILEPREP aPreps[4]; /** The number of current preps. */ uint32_t cPreps; /** Number of mapping references. */ int32_t cMappings; /** The memory mapping. */ void *pvMapping; /** The filename. */ char szFilename[1]; } KLDRRDRFILE, *PKLDRRDRFILE; /******************************************************************************* * Internal Functions * *******************************************************************************/ static void kldrRdrFileDone(PKLDRRDR pRdr); static int kldrRdrFileUnprepare(PKLDRRDR pRdr, void *pv, size_t cb); static int kldrRdrFileUnmap(PKLDRRDR pRdr, void *pv, size_t cb); static int kldrRdrFileProtect(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt); static int kldrRdrFileRefreshMap(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt, off_t offFile, size_t cbFile); static int kldrRdrFileMap(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt, off_t offFile, size_t cbFile); static int kldrRdrFilePrepare(PKLDRRDR pRdr, void **ppv, size_t cb, unsigned fFixed); static size_t kldrRdrFilePageSize(PKLDRRDR pRdr); static const char *kldrRdrFileName(PKLDRRDR pRdr); static off_t kldrRdrFileTell(PKLDRRDR pRdr); static off_t kldrRdrFileSize(PKLDRRDR pRdr); static int kldrRdrFileAllUnmap(PKLDRRDR pRdr, const void *pvBits); static int kldrRdrFileAllMap(PKLDRRDR pRdr, const void **ppvBits); static int kldrRdrFileRead(PKLDRRDR pRdr, void *pvBuf, size_t cb, off_t off); static int kldrRdrFileDestroy(PKLDRRDR pRdr); static int kldrRdrFileCreate(PPKLDRRDR ppRdr, const char *pszFilename); /******************************************************************************* * Global Variables * *******************************************************************************/ /** Native file provider operations. */ const KLDRRDROPS g_kLdrRdrFileOps = { "native file", NULL, kldrRdrFileCreate, kldrRdrFileDestroy, kldrRdrFileRead, kldrRdrFileAllMap, kldrRdrFileAllUnmap, kldrRdrFileSize, kldrRdrFileTell, kldrRdrFileName, kldrRdrFilePageSize, kldrRdrFilePrepare, kldrRdrFileMap, kldrRdrFileRefreshMap, kldrRdrFileProtect, kldrRdrFileUnmap, kldrRdrFileUnprepare, kldrRdrFileDone, 42 }; /** @copydoc KLDRRDR::pfnDone */ static void kldrRdrFileDone(PKLDRRDR pRdr) { } /** * Finds a prepared mapping region. * * @returns Pointer to the aPrep entry. * @param pFile The instance data. * @param pv The base of the region. * @param cb The size of the region. */ static PKLDRRDRFILEPREP kldrRdrFileFindPrepExact(PKLDRRDRFILE pFile, void *pv, size_t cb) { int32_t i = pFile->cPreps; while (i-- > 0) if ( pFile->aPreps[i].pv == pv || pFile->aPreps[i].cb == cb) return &pFile->aPreps[i]; return NULL; } /** * Finds a prepared mapping region containing the specified region. * * @returns Pointer to the aPrep entry. * @param pFile The instance data. * @param pv The base of the sub region. * @param cb The size of the sub region. */ static PKLDRRDRFILEPREP kldrRdrFileFindPrepWithin(PKLDRRDRFILE pFile, void *pv, size_t cb) { int32_t i = pFile->cPreps; while (i-- > 0) if ((uintptr_t)pv - (uintptr_t)pFile->aPreps[i].pv < pFile->aPreps[i].cb) { if ((uintptr_t)pv - (uintptr_t)pFile->aPreps[i].pv + cb <= pFile->aPreps[i].cb) return &pFile->aPreps[i]; return NULL; } return NULL; } /** @copydoc KLDRRDR::pfnUnprepare */ static int kldrRdrFileUnprepare(PKLDRRDR pRdr, void *pv, size_t cb) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pv, cb); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; return -1; } /** @copydoc KLDRRDR::pfnUnmap */ static int kldrRdrFileUnmap(PKLDRRDR pRdr, void *pv, size_t cb) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pv, cb); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; return -1; } /** @copydoc KLDRRDR::pfnProtect */ static int kldrRdrFileProtect(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pv, cb); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; return -1; } /** @copydoc KLDRRDR::pfnRefreshMap */ static int kldrRdrFileRefreshMap(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt, off_t offFile, size_t cbFile) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pv, cb); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; return -1; } /** @copydoc KLDRRDR::pfnMap */ static int kldrRdrFileMap(PKLDRRDR pRdr, void *pv, size_t cb, KLDRPROT enmProt, off_t offFile, size_t cbFile) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pv, cb); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; return -1; } /** @copydoc KLDRRDR:pfnPrepare */ static int kldrRdrFilePrepare(PKLDRRDR pRdr, void **ppv, size_t cb, unsigned fFixed) { #ifdef __OS2__ #elif defined(__WIN__) #else # error "port me." #endif return -1; } /** @copydoc KLDRRDR::pfnPageSize */ static size_t kldrRdrFilePageSize(PKLDRRDR pRdr) { #ifdef __OS2__ /* The page size on OS/2 wont change anytime soon. :-) */ return 0x1000; #elif defined(__WIN__) SYSTEM_INFO SysInfo; GetSystemInfo(&SysInfo); return SysInfo.dwPageSize; /*return SysInfo.dwAllocationGranularity;*/ #else # error "port me" #endif } /** @copydoc KLDRRDR::pfnName */ static const char *kldrRdrFileName(PKLDRRDR pRdr) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; return &pRdrFile->szFilename[0]; } /** @copydoc KLDRRDR::pfnTell */ static off_t kldrRdrFileTell(PKLDRRDR pRdr) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; /* * If the offset is undefined, try figure out what it is. */ if (pRdrFile->off == -1) { #ifdef __OS2__ ULONG ulNew; APIRET rc = DosSetFilePtr(pRdrFile->File, 0, FILE_CURRENT, &ulNew); if (rc) return -1; pRdrFile->off = ulNew; #elif defined(__WIN__) LONG offHigh = 0; LONG offLow; int rc; SetLastError(0); offLow = SetFilePointer(pRdrFile->File, 0, &offHigh, FILE_CURRENT); rc = GetLastError(); if (rc) return -1; pRdrFile->off = ((off_t)offHigh << 32) | offLow; #else # error "port me." #endif } return pRdrFile->off; } /** @copydoc KLDRRDR::pfnSize */ static off_t kldrRdrFileSize(PKLDRRDR pRdr) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; return pRdrFile->cb; } /** @copydoc KLDRRDR::pfnAllUnmap */ static int kldrRdrFileAllUnmap(PKLDRRDR pRdr, const void *pvBits) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; /* check for underflow */ if (pRdrFile->cMappings <= 0) #if defined(__OS2__) || defined(__WIN__) return ERROR_INVALID_PARAMETER; #else # error "port me" #endif /* decrement usage counter, free mapping if no longer in use. */ if (!--pRdrFile->cMappings) { kldrHlpFree(pRdrFile->pvMapping); pRdrFile->pvMapping = NULL; } return 0; } /** @copydoc KLDRRDR::pfnAllMap */ static int kldrRdrFileAllMap(PKLDRRDR pRdr, const void **ppvBits) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; /* * Do we need to map it? */ if (!pRdrFile->pvMapping) { int rc; off_t cb = pRdrFile->Core.pOps->pfnSize(pRdr); pRdrFile->pvMapping = kldrHlpAlloc(cb); if (!pRdrFile->pvMapping) #if defined(__OS2__) || defined(__WIN__) return ERROR_NOT_ENOUGH_MEMORY; #else # error "port me" #endif rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0); if (rc) { kldrHlpFree(pRdrFile->pvMapping); pRdrFile->pvMapping = NULL; return rc; } pRdrFile->cMappings = 0; } pRdrFile->cMappings++; return 0; } /** @copydoc KLDRRDR::pfnRead */ static int kldrRdrFileRead(PKLDRRDR pRdr, void *pvBuf, size_t cb, off_t off) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; /* * Do a seek if needed. */ if (pRdrFile->off != off) { #ifdef __OS2__ ULONG ulNew; APIRET rc; rc = DosSetFilePtr(pRdrFile->File, off, FILE_BEGIN, &ulNew); if (rc) { pRdrFile->off = -1; return rc; } #elif defined(__WIN__) LONG offHigh; LONG offLow; offHigh = sizeof(off_t) == 4 ? 0 : (off >> 32); offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN); if ( offLow != (LONG)off || offHigh != (LONG)(sizeof(off_t) == 4 ? 0 : (off >> 32))) { int rc = GetLastError(); if (!rc) rc = ERROR_GEN_FAILURE; pRdrFile->off = -1; return rc; } #else # error "port me." #endif } /* * Do the read. */ #ifdef __OS2__ { ULONG cbRead = 0; APIRET rc = DosRead(pRdrFile->File, pvBuf, cb, &cbRead); if (rc) { pRdrFile->off = -1; return rc; } if (cbRead != cb) { pRdrFile->off = -1; return ERROR_GEN_FAILURE; } } #elif defined(__WIN__) { DWORD cbRead = 0; if (!ReadFile(pRdrFile->File, pvBuf, cb, &cbRead, NULL)) { int rc = GetLastError(); if (!rc) rc = ERROR_GEN_FAILURE; pRdrFile->off = -1; return rc; } if (cbRead != cb) { pRdrFile->off = -1; return ERROR_GEN_FAILURE; } } #else # error "port me." #endif pRdrFile->off = off + cb; return 0; } /** @copydoc KLDRRDR::pfnDestroy */ static int kldrRdrFileDestroy(PKLDRRDR pRdr) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; int rc; #ifdef __OS2__ rc = DosClose(pRdrFile->File); #elif defined(__WIN__) rc = 0; if (!CloseHandle(pRdrFile->File)) rc = GetLastError(); #else # error "port me" #endif if (pRdrFile->pvMapping) { kldrHlpFree(pRdrFile->pvMapping); pRdrFile->pvMapping = NULL; } kldrHlpFree(pRdr); return rc; } /** @copydoc KLDRRDROPS::pfnCreate */ static int kldrRdrFileCreate(PPKLDRRDR ppRdr, const char *pszFilename) { size_t cchFilename; PKLDRRDRFILE pRdrFile; /* * Open the file and determin its size. */ #ifdef __OS2__ ULONG ulAction = 0; FILESTATUS3 Info; APIRET rc; HFILE File = 0; off_t cb; char szFilename[CCHMAXPATH]; if ((uintptr_t)pszFilename >= 0x20000000) { char *psz = (char *)kLdrHlpAllocA(cchFilename + 1); kLdrHlpMemCopy(psz, pszFilename, cchFilename + 1); pszFilename = psz; } rc = DosOpen((PCSZ)pszFilename, &File, &ulAction, 0, FILE_NORMAL, OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | OPEN_ACCESS_READONLY | OPEN_FLAGS_RANDOMSEQUENTIAL, NULL); if (rc) return rc; rc = DosQueryPathInfo(pszFilename, FIL_QUERYFULLNAME, szFilename, sizeof(szFilename)); if (rc) { DosClose(File); return rc; } rc = DosQueryFileInfo(File, FIL_STANDARD, &Info, sizeof(Info)); if (rc) { DosClose(File); return rc; } cb = Info.cbFile; #elif defined(__WIN__) SECURITY_ATTRIBUTES SecAttr; DWORD High; DWORD Low; int rc; HANDLE File; off_t cb; char szFilename[MAX_PATH]; SecAttr.bInheritHandle = FALSE; SecAttr.lpSecurityDescriptor = NULL; SecAttr.nLength = 0; File = CreateFile(pszFilename, GENERIC_READ, FILE_SHARE_READ, &SecAttr, OPEN_ALWAYS, 0, NULL); if (File == INVALID_HANDLE_VALUE) return GetLastError(); if (!GetFullPathName(pszFilename, sizeof(szFilename), szFilename, NULL)) { rc = GetLastError(); CloseHandle(File); return rc; } SetLastError(0); Low = GetFileSize(File, &High); rc = GetLastError(); if (rc) { CloseHandle(File); return rc; } if (sizeof(off_t) == 4) cb = High ? 0x7fffffff : Low; else cb = ((off_t)High << 32) | Low; #else # error "port me" #endif /* * Allocate the reader instance. */ cchFilename = kLdrHlpStrLen(szFilename); pRdrFile = (PKLDRRDRFILE)kldrHlpAlloc(sizeof(*pRdrFile) + cchFilename); if (!pRdrFile) #if defined(__OS2__) { DosClose(File); return ERROR_NOT_ENOUGH_MEMORY; } #elif defined(__WIN__) { CloseHandle(File); return ERROR_NOT_ENOUGH_MEMORY; } #else # error "port me" #endif /* * Initialize it and return successfully. */ pRdrFile->Core.u32Magic = KLDRRDR_MAGIC; pRdrFile->Core.pOps = &g_kLdrRdrFileOps; pRdrFile->File = File; pRdrFile->cb = cb; pRdrFile->off = 0; pRdrFile->cMappings = 0; pRdrFile->cPreps = 0; kLdrHlpMemCopy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1); *ppRdr = &pRdrFile->Core; return 0; }