/* $Id: kLdrRdrFile.c 2974 2007-02-14 10:12:44Z 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__) # define WIN32_NO_STATUS # include # ifndef __WIN__ # define __WIN__ # endif # include # include /** @todo find a non-conflicting header with NTSTATUS, NTAPI, ++ */ typedef LONG NTSTATUS; #define NT_SUCCESS(x) ((x)>=0) typedef struct _OBJECT_ATTRIBUTES { ULONG Length; HANDLE RootDirectory; PUNICODE_STRING ObjectName; ULONG Attributes; PVOID SecurityDescriptor; PVOID SecurityQualityOfService; } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES; typedef enum _SECTION_INHERIT { ViewShare = 1, ViewUnmap = 2 } SECTION_INHERIT; # define NTOSAPI __declspec(dllimport) # define NtCurrentProcess() GetCurrentProcess() # ifndef MEM_DOS_LIM # define MEM_DOS_LIM 0x40000000UL # endif NTOSAPI NTSTATUS NTAPI NtCreateSection( OUT PHANDLE SectionHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER SectionSize OPTIONAL, IN ULONG Protect, IN ULONG Attributes, IN HANDLE FileHandle OPTIONAL ); NTOSAPI NTSTATUS NTAPI NtMapViewOfSection( IN HANDLE SectionHandle, IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN ULONG ZeroBits, IN ULONG CommitSize, IN OUT PLARGE_INTEGER SectionOffset OPTIONAL, IN OUT PSIZE_T ViewSize, IN SECTION_INHERIT InheritDisposition, IN ULONG AllocationType, IN ULONG Protect ); NTOSAPI NTSTATUS NTAPI NtUnmapViewOfSection( IN HANDLE ProcessHandle, IN PVOID BaseAddress ); NTOSAPI NTSTATUS NTAPI NtClose( IN HANDLE Handle ); NTOSAPI NTSTATUS NTAPI ZwProtectVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN OUT PULONG ProtectSize, IN ULONG NewProtect, OUT PULONG OldProtect ); # define NtProtectVirtualMemory ZwProtectVirtualMemory NTOSAPI NTSTATUS NTAPI NtAllocateVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN ULONG ZeroBits, IN OUT PULONG AllocationSize, IN ULONG AllocationType, IN ULONG Protect ); NTOSAPI NTSTATUS NTAPI NtFreeVirtualMemory( IN HANDLE ProcessHandle, IN OUT PVOID *BaseAddress, IN OUT PULONG FreeSize, IN ULONG FreeType ); #else # error "port me" #endif #include #include "kLdrHlp.h" /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ /** @def KLDRRDRFILE_STRICT * Define KLDRRDRFILE_STRICT to enabled strict checks in KLDRRDRFILE. */ #define KLDRRDRFILE_STRICT 1 /** @def KLDRRDRFILE_ASSERT * Assert that an expression is true when KLDRRDRFILE_STRICT is defined. */ #ifdef KLDRRDRFILE_STRICT # define KLDRRDRFILE_ASSERT(expr) kldrHlpAssert(expr) #else # define KLDRRDRFILE_ASSERT(expr) do {} while (0) #endif /******************************************************************************* * 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. */ KLDRFOFF off; /** The file size. */ KLDRFOFF 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 kldrRdrFileUnmap(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments); static int kldrRdrFileGenericUnmap(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments); static int kldrRdrFileProtect(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fUnprotectOrProtect); static int kldrRdrFileGenericProtect(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fUnprotectOrProtect); static int kldrRdrFileRefresh(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments); static int kldrRdrFileGenericRefresh(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments); static int kldrRdrFileMap(PKLDRRDR pRdr, void **ppvBase, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fFixed); static int kldrRdrFileGenericMap(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fFixed); static size_t kldrRdrFilePageSize(PKLDRRDR pRdr); static const char *kldrRdrFileName(PKLDRRDR pRdr); static KLDRFOFF kldrRdrFileTell(PKLDRRDR pRdr); static KLDRFOFF 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, KLDRFOFF 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, kldrRdrFileMap, kldrRdrFileRefresh, kldrRdrFileProtect, kldrRdrFileUnmap, kldrRdrFileDone, 42 }; #if defined(__WIN__) || defined(__NT__) /** * Converts a kLdr segment protection to NT protection for a mapping. * * @returns Nt page protection. * @param enmProt kLdr protection. */ static ULONG kldrRdrFileGetNtMapProt(KLDRPROT enmProt) { switch (enmProt) { case KLDRPROT_NOACCESS: return PAGE_NOACCESS; case KLDRPROT_READONLY: return PAGE_READONLY; case KLDRPROT_READWRITE: return PAGE_READWRITE; case KLDRPROT_WRITECOPY: return PAGE_WRITECOPY; case KLDRPROT_EXECUTE: return PAGE_EXECUTE; case KLDRPROT_EXECUTE_READ: return PAGE_EXECUTE_READ; case KLDRPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE; case KLDRPROT_EXECUTE_WRITECOPY: return PAGE_EXECUTE_WRITECOPY; default: return ~(ULONG)0; } } /** * Converts a kLdr segment protection to NT protection for a allocation. * * @returns Nt page protection. * @param enmProt kLdr protection. */ static ULONG kldrRdrFileGetNtAllocProt(KLDRPROT enmProt) { switch (enmProt) { case KLDRPROT_NOACCESS: return PAGE_NOACCESS; case KLDRPROT_READONLY: return PAGE_READONLY; case KLDRPROT_WRITECOPY: case KLDRPROT_READWRITE: return PAGE_READWRITE; case KLDRPROT_EXECUTE: return PAGE_EXECUTE; case KLDRPROT_EXECUTE_READ: return PAGE_EXECUTE_READ; case KLDRPROT_EXECUTE_WRITECOPY: case KLDRPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE; default: return ~(ULONG)0; } } #endif /** @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. */ static PKLDRRDRFILEPREP kldrRdrFileFindPrepExact(PKLDRRDRFILE pFile, void *pv) { int32_t i = pFile->cPreps; while (i-- > 0) if (pFile->aPreps[i].pv == pv) return &pFile->aPreps[i]; return NULL; } /** @copydoc KLDRRDR::pfnUnmap */ static int kldrRdrFileUnmap(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pvBase); int rc; if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; #if defined(__WIN__) || defined(__NT__) if (pPrep->hSection != NULL) { /** @todo implement me. */ return -1; } #endif rc = kldrRdrFileGenericUnmap(pRdr, pPrep, cSegments, paSegments); /* remove the mapping data on success. */ if (!rc) { pRdrFile->cPreps--; if (pPrep != &pRdrFile->aPreps[pRdrFile->cPreps]) *pPrep = pRdrFile->aPreps[pRdrFile->cPreps]; } return rc; } /** Generic implementation of kldrRdrFileUnmap. */ static int kldrRdrFileGenericUnmap(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments) { kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); return kldrHlpPageFree(pPrep->pv, pPrep->cb); } /** @copydoc KLDRRDR::pfnProtect */ static int kldrRdrFileProtect(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fUnprotectOrProtect) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pvBase); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; #if defined(__WIN__) || defined(__NT__) if (pPrep->hSection != NULL) { /** @todo implement me. */ return -1; } #endif return kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect); } /** Generic implementation of kldrRdrFileProtect. */ static int kldrRdrFileGenericProtect(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fUnprotectOrProtect) { uint32_t i; /* * Iterate the segments and apply memory protection changes. */ for (i = 0; i < cSegments; i++) { int rc; void *pv; KLDRPROT enmProt; if (paSegments[i].RVA == NIL_KLDRADDR) continue; /* calc new protection. */ enmProt = paSegments[i].enmProt; if (fUnprotectOrProtect) { switch (enmProt) { case KLDRPROT_NOACCESS: case KLDRPROT_READONLY: case KLDRPROT_READWRITE: case KLDRPROT_WRITECOPY: enmProt = KLDRPROT_READWRITE; break; case KLDRPROT_EXECUTE: case KLDRPROT_EXECUTE_READ: case KLDRPROT_EXECUTE_READWRITE: case KLDRPROT_EXECUTE_WRITECOPY: enmProt = KLDRPROT_EXECUTE_READWRITE; break; default: KLDRRDRFILE_ASSERT(!"bad enmProt"); return -1; } } else { /* copy on write -> normal write. */ if (enmProt == KLDRPROT_EXECUTE_WRITECOPY) enmProt = KLDRPROT_EXECUTE_READWRITE; else if (enmProt == KLDRPROT_WRITECOPY) enmProt = KLDRPROT_READWRITE; } pv = (uint8_t *)pPrep->pv + paSegments[i].RVA; rc = kldrHlpPageProtect(pv, paSegments[i].cbMapped, enmProt); if (rc) break; } return 0; } /** @copydoc KLDRRDR::pfnRefresh */ static int kldrRdrFileRefresh(PKLDRRDR pRdr, void *pvBase, uint32_t cSegments, PCKLDRSEG paSegments) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = kldrRdrFileFindPrepExact(pRdrFile, pvBase); if (!pPrep) return KLDR_ERR_INVALID_PARAMETER; #if defined(__WIN__) || defined(__NT__) if (pPrep->hSection != NULL) { /** @todo implement me. */ return -1; } #endif return kldrRdrFileGenericRefresh(pRdr, pPrep, cSegments, paSegments); } /** Generic implementation of kldrRdrFileRefresh. */ static int kldrRdrFileGenericRefresh(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments) { int rc; int rc2; uint32_t i; /* * Make everything writable again. */ rc = kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); if (rc) { kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); return rc; } /* * Clear everything. */ /** @todo only zero the areas not covered by raw file bits. */ kLdrHlpMemSet(pPrep->pv, 0, pPrep->cb); /* * Reload all the segments. * We could possibly skip some segments, but we currently have * no generic way of figuring out which at the moment. */ for (i = 0; i < cSegments; i++) { void *pv; if ( paSegments[i].RVA == NIL_KLDRADDR || paSegments[i].cbFile <= 0) continue; pv = (uint8_t *)pPrep->pv + paSegments[i].RVA; rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile); if (rc) break; } /* * Protect the bits again. */ rc2 = kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); if (rc2 && rc) rc = rc2; return rc; } /** @copydoc KLDRRDR::pfnMap */ static int kldrRdrFileMap(PKLDRRDR pRdr, void **ppvBase, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fFixed) { PKLDRRDRFILE pRdrFile = (PKLDRRDRFILE)pRdr; PKLDRRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps]; KLDRSIZE cbTotal; const size_t cbPage = pRdr->pOps->pfnPageSize(pRdr); int rc; uint32_t i; if (pRdrFile->cPreps >= KLDR_ELEMENTS(pRdrFile->aPreps)) return KLDR_ERR_TOO_MANY_MAPPINGS; /* * Calc the total mapping space needed. */ cbTotal = 0; for (i = 0; i < cSegments; i++) { KLDRSIZE uRVASegmentEnd; if (paSegments[i].RVA == NIL_KLDRADDR) continue; uRVASegmentEnd = paSegments[i].RVA + paSegments[i].cbMapped; if (cbTotal < uRVASegmentEnd) cbTotal = uRVASegmentEnd; } pPrep->cb = (size_t)cbTotal; if (pPrep->cb != cbTotal) return KLDR_ERR_ADDRESS_OVERFLOW; pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1); #if defined(__WIN__) || defined(__NT__) /* * The NT memory mapped file API sucks in a lot of ways. Unless you're actually * trying to map a PE image and the kernel can parse the file for it self, the * API just isn't up to scratch. * * Problems: * 1. Reserving memory for the views is risky because you can't reserve and * map into the reserved space. So, other threads might grab the memory * before we get to it. * 2. The page aligning of file offsets makes it impossible to map most * executable images since these are commonly sector aligned. * 3. When mapping a read+execute file, its not possible to create section * larger than the file since the section size is bound to the data file * size. This wouldn't have been such a problem if it was possible to * map views beyond the section restriction, i.e. have a file size and * view size. * 4. Only x86 can map views at page granularity it seems, and that only * using an undocument flag. The default granularity is 64KB. * 5. There is more crappyness here... * * So, first we'll have to check if we can the file using the crappy NT APIs. * Chances are we can't. */ for (i = 0; i < cSegments; i++) { if (paSegments[i].RVA == NIL_KLDRADDR) continue; /* The file backing of the segments must be page aligned. */ if ( paSegments[i].cbFile > 0 && paSegments[i].offFile & (cbPage - 1)) break; /* Only page alignment gaps between the file size and the mapping size. */ if ( paSegments[i].cbFile > 0 && (paSegments[i].cbFile & ~(cbPage - 1)) != (paSegments[i].cbMapped & ~(cbPage - 1)) ) break; /* The mapping addresses of the segments must be page aligned. * Non-x86 will probably require 64KB alignment here. */ if (paSegments[i].RVA & (cbPage - 1)) break; /* If we do have to allocate the segment it's RVA must be 64KB aligned. */ if ( paSegments[i].cbFile > 0 && (paSegments[i].RVA & 0xffff)) break; } /** @todo if this is a PE image, we might just try a SEC_IMAGE mapping. It'll work if the host and image machines matches. */ if (i == cSegments) { /* WOW! it may work out! Incredible! */ SIZE_T ViewSize; LARGE_INTEGER SectionOffset; LARGE_INTEGER MaxiumSize; NTSTATUS Status; PVOID pv; MaxiumSize.QuadPart = pRdr->pOps->pfnSize(pRdr); if (MaxiumSize.QuadPart > (LONGLONG)cbTotal) MaxiumSize.QuadPart = cbTotal; Status = NtCreateSection(&pPrep->hSection, SECTION_MAP_EXECUTE | SECTION_MAP_READ, /* desired access */ NULL, /* object attributes */ &MaxiumSize, PAGE_EXECUTE_WRITECOPY, /* page attributes */ SEC_COMMIT, /* section attributes */ pRdrFile->File); if (!NT_SUCCESS(Status)) return (int)Status; /* * Determin the base address. */ if (fFixed) pPrep->pv = *ppvBase; else { pv = NULL; ViewSize = (size_t)cbTotal; Status = NtAllocateVirtualMemory(NtCurrentProcess(), &pv, 0, /* ZeroBits */ &ViewSize, MEM_RESERVE, PAGE_READONLY); if (NT_SUCCESS(Status)) { pPrep->pv = *ppvBase = pv; ViewSize = 0; Status = NtFreeVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, MEM_RELEASE); } if (!NT_SUCCESS(Status)) { NtClose(pPrep->hSection); return Status; } } /* * Map the segments. */ for (i = 0; i < cSegments; i++) { ULONG fPageProt; if (paSegments[i].RVA == NIL_KLDRADDR) continue; pv = (uint8_t *)pPrep->pv + paSegments[i].RVA; if (paSegments[i].cbFile > 0) { SectionOffset.QuadPart = paSegments[i].offFile; ViewSize = paSegments[i].cbFile; fPageProt = kldrRdrFileGetNtMapProt(paSegments[i].enmProt); /* STATUS_MAPPED_ALIGNMENT STATUS_CONFLICTING_ADDRESSES STATUS_INVALID_VIEW_SIZE */ Status = NtMapViewOfSection(pPrep->hSection, NtCurrentProcess(), &pv, 0, /* ZeroBits */ 0, /* CommitSize */ &SectionOffset, /* SectionOffset */ &ViewSize, ViewUnmap, MEM_DOS_LIM, /* AllocationType */ fPageProt); /* do we have to zero anything? */ if ( NT_SUCCESS(Status) && 0/*later*/) { /*ULONG OldPageProt = 0; NtProtectVirtualMemory(NtCurrentProcess(), &pv, &ViewSize, , */ } } else { ViewSize = paSegments[i].cbMapped; fPageProt = kldrRdrFileGetNtAllocProt(paSegments[i].enmProt); Status = NtAllocateVirtualMemory(NtCurrentProcess(), &pv, 0, /* ZeroBits */ &ViewSize, MEM_COMMIT, fPageProt); } if (!NT_SUCCESS(Status)) break; } /* * On success, commit the mapping and return. */ if (NT_SUCCESS(Status)) { pRdrFile->cPreps++; return 0; } /* bail out and fall back on the generic code. */ while (i-- > 0) { PVOID pv; if (paSegments[i].RVA == NIL_KLDRADDR) continue; pv = (uint8_t *)pPrep->pv + paSegments[i].RVA; if (paSegments[i].cbFile > 0) NtUnmapViewOfSection(NtCurrentProcess(), pv); else NtFreeVirtualMemory(NtCurrentProcess(), &pv, NULL, MEM_RELEASE); } NtClose(pPrep->hSection); } /* else: fall back to the generic code */ pPrep->hSection = NULL; #endif /* * Use the generic map emulation. */ pPrep->pv = fFixed ? *ppvBase : NULL; rc = kldrRdrFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed); if (!rc) { *ppvBase = pPrep->pv; pRdrFile->cPreps++; } return rc; } /** Generic implementation of kldrRdrFileMap. */ static int kldrRdrFileGenericMap(PKLDRRDR pRdr, PKLDRRDRFILEPREP pPrep, uint32_t cSegments, PCKLDRSEG paSegments, unsigned fFixed) { int rc; uint32_t i; /* * Generic mapping code using kldrHlpPageAlloc(), kldrHlpPageFree() and kldrHlpPageProtect(). */ rc = kldrHlpPageAlloc(&pPrep->pv, pPrep->cb, KLDRPROT_EXECUTE_READWRITE, fFixed); if (rc) return rc; /* * Load the data. */ for (i = 0; i < cSegments; i++) { void *pv; if ( paSegments[i].RVA == NIL_KLDRADDR || paSegments[i].cbFile <= 0) continue; pv = (uint8_t *)pPrep->pv + paSegments[i].RVA; rc = pRdr->pOps->pfnRead(pRdr, pv, paSegments[i].cbFile, paSegments[i].offFile); if (rc) break; } /* * Set segment protection. */ if (!rc) { rc = kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); if (!rc) return 0; kldrRdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); } /* bailout */ kldrHlpPageFree(pPrep->pv, pPrep->cb); return rc; } /** @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 KLDRFOFF 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 = ((KLDRFOFF)offHigh << 32) | offLow; #else # error "port me." #endif } return pRdrFile->off; } /** @copydoc KLDRRDR::pfnSize */ static KLDRFOFF 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; KLDRFOFF 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; } *ppvBits = pRdrFile->pvMapping; pRdrFile->cMappings++; return 0; } /** @copydoc KLDRRDR::pfnRead */ static int kldrRdrFileRead(PKLDRRDR pRdr, void *pvBuf, size_t cb, KLDRFOFF 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(KLDRFOFF) == 4 ? 0 : (off >> 32); offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN); if ( offLow != (LONG)off || offHigh != (LONG)(sizeof(KLDRFOFF) == 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; KLDRFOFF cb; char szFilename[CCHMAXPATH]; if ((uintptr_t)pszFilename >= 0x20000000) { char *psz; cchFilename = kLdrHlpStrLen(szFilename); 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((PCSZ)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; KLDRFOFF cb; char szFilename[MAX_PATH]; SecAttr.bInheritHandle = FALSE; SecAttr.lpSecurityDescriptor = NULL; SecAttr.nLength = 0; File = CreateFile(pszFilename, GENERIC_READ | GENERIC_EXECUTE, FILE_SHARE_READ, &SecAttr, OPEN_EXISTING, 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(KLDRFOFF) == 4) cb = High ? 0x7fffffff : Low; else cb = ((KLDRFOFF)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; }