From 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:21:29 +0200 Subject: Adding upstream version 1:0.1.9998svn3589+dfsg. Signed-off-by: Daniel Baumann --- src/lib/kStuff/kRdr/Makefile.kmk | 48 ++ src/lib/kStuff/kRdr/kRdr.cpp | 281 ++++++++ src/lib/kStuff/kRdr/kRdrBuffered.cpp | 750 +++++++++++++++++++ src/lib/kStuff/kRdr/kRdrFile.cpp | 1308 ++++++++++++++++++++++++++++++++++ src/lib/kStuff/kRdr/kRdrInternal.h | 122 ++++ 5 files changed, 2509 insertions(+) create mode 100644 src/lib/kStuff/kRdr/Makefile.kmk create mode 100644 src/lib/kStuff/kRdr/kRdr.cpp create mode 100644 src/lib/kStuff/kRdr/kRdrBuffered.cpp create mode 100644 src/lib/kStuff/kRdr/kRdrFile.cpp create mode 100644 src/lib/kStuff/kRdr/kRdrInternal.h (limited to 'src/lib/kStuff/kRdr') diff --git a/src/lib/kStuff/kRdr/Makefile.kmk b/src/lib/kStuff/kRdr/Makefile.kmk new file mode 100644 index 0000000..357acf6 --- /dev/null +++ b/src/lib/kStuff/kRdr/Makefile.kmk @@ -0,0 +1,48 @@ +# $Id: Makefile.kmk 29 2009-07-01 20:30:29Z bird $ +## @file +# kRdr - The File Provider, sub-makefile. +# + +# +# Copyright (c) 2006-2007 Knut St. Osmundsen +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. +# + +DEPTH ?= .. +SUB_DEPTH = .. +include $(PATH_KBUILD)/subheader.kmk + +# +# kRdrStatic - The file provider module. +# +LIBRARIES += kRdrStatic +kRdrStatic_TEMPLATE = kStuffLIB +kRdrStatic_DEFS = KDBG_BUILDING +kRdrStatic_SOURCES = \ + kRdr.cpp \ + kRdrFile.cpp \ + kRdrBuffered.cpp + +# Generate the rules +include $(PATH_KBUILD)/subfooter.kmk + diff --git a/src/lib/kStuff/kRdr/kRdr.cpp b/src/lib/kStuff/kRdr/kRdr.cpp new file mode 100644 index 0000000..5952cb1 --- /dev/null +++ b/src/lib/kStuff/kRdr/kRdr.cpp @@ -0,0 +1,281 @@ +/* $Id: kRdr.cpp 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kRdr - The File Provider. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kRdrInternal.h" + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The list of file providers. */ +static PCKRDROPS g_pRdrHead = &g_kRdrFileOps; + + +/** + * Adds a new file provider. + * + * @param pAdd The new file provider. + */ +KRDR_DECL(void) kRdrAddProvider(PKRDROPS pAdd) +{ + pAdd->pNext = g_pRdrHead; + g_pRdrHead = pAdd; +} + + +/** + * Tries to opens a file. + * + * @returns 0 on success, OS status code on failure. + * @param ppRdr Where to store the file provider instance. + * @param pszFilename The filename. + */ +KRDR_DECL(int) kRdrOpen(PPKRDR ppRdr, const char *pszFilename) +{ + int rc = -1; + PCKRDROPS pCur; + for (pCur = g_pRdrHead; pCur; pCur = pCur->pNext) + { + rc = pCur->pfnCreate(ppRdr, pszFilename); + if (!rc) + return 0; + } + return rc; +} + + +/** + * Closes the file. + * + * @returns 0 on success, OS specific error code on failure. + * On failure, the file provider instance will be in an indeterminate state - don't touch it! + * @param pRdr The file provider instance. + */ +KRDR_DECL(int) kRdrClose(PKRDR pRdr) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnDestroy(pRdr); +} + + +/** Read bits from the file. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param pvBuf Where to put the bits. + * @param cb The number of bytes to read. + * @param off Where to start reading. + */ +KRDR_DECL(int) kRdrRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnRead(pRdr, pvBuf, cb, off); +} + + +/** Map all the file bits into memory (read only). + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param ppvBits Where to store the address of the mapping. + * The size can be obtained using pfnSize. + */ +KRDR_DECL(int) kRdrAllMap(PKRDR pRdr, const void **ppvBits) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnAllMap(pRdr, ppvBits); +} + + +/** Unmap a file bits mapping obtained by KRDROPS::pfnAllMap. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param pvBits The mapping address. + */ +KRDR_DECL(int) kRdrAllUnmap(PKRDR pRdr, const void *pvBits) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnAllUnmap(pRdr, pvBits); +} + + +/** Get the file size. + * + * @returns The file size. Returns -1 on failure. + * @param pRdr The file provider instance. + */ +KRDR_DECL(KFOFF) kRdrSize(PKRDR pRdr) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnSize(pRdr); +} + + +/** Get the file pointer offset. + * + * @returns The file pointer offset. Returns -1 on failure. + * @param pRdr The file provider instance. + */ +KRDR_DECL(KFOFF) kRdrTell(PKRDR pRdr) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnTell(pRdr); +} + + +/** Get the file name. + * + * @returns The file name. Returns NULL on failure. + * @param pRdr The file provider instance. + */ +KRDR_DECL(const char *) kRdrName(PKRDR pRdr) +{ + KRDR_VALIDATE_EX(pRdr, NULL); + return pRdr->pOps->pfnName(pRdr); +} + + +/** Get the native file handle if possible. + * + * @returns The native file handle. Returns -1 if not available. + * @param pRdr The file provider instance. + */ +KRDR_DECL(KIPTR) kRdrNativeFH(PKRDR pRdr) +{ + KRDR_VALIDATE_EX(pRdr, -1); + return pRdr->pOps->pfnNativeFH(pRdr); +} + + +/** + * Gets the page size used when mapping sections of the file. + * + * @returns The page size. + * @param pRdr The file provider instance. + */ +KRDR_DECL(KSIZE) kRdrPageSize(PKRDR pRdr) +{ + KRDR_VALIDATE_EX(pRdr, 0x10000); + return pRdr->pOps->pfnPageSize(pRdr); +} + + +/** + * Maps the segments of a image into memory. + * + * The file reader will be using the RVA member of each segment to figure out where + * it goes relative to the image base address. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param ppvBase On input when fFixed is set, this contains the base address of the mapping. + * On output this contains the base of the image mapping. + * @param cSegments The number of segments in the array pointed to by paSegments. + * @param paSegments The segments thats going to be mapped. + * @param fFixed If set, the address at *ppvBase should be the base address of the mapping. + */ +KRDR_DECL(int) kRdrMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnMap(pRdr, ppvBase, cSegments, paSegments, fFixed); +} + + +/** + * Reloads dirty pages in mapped image. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param pvBase The base address of the image mapping. + * @param cSegments The number of segments in the array pointed to by paSegments. + * @param paSegments The segments thats going to be mapped. + */ +KRDR_DECL(int) kRdrRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnRefresh(pRdr, pvBase, cSegments, paSegments); +} + + +/** + * Protects or unprotects an image mapping. + * + * This is typically used for getting write access to read or execute only + * pages while applying fixups. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param pvBase The base address of the image mapping. + * @param cSegments The number of segments in the array pointed to by paSegments. + * @param paSegments The segments thats going to be mapped. + * @param fUnprotectOrProtect When set the all mapped segments are made writable. + * When clean the segment protection is restored. + */ +KRDR_DECL(int) kRdrProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnProtect(pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect); +} + + +/** + * Unmaps a image mapping. + * + * @returns 0 on success, OS specific error code on failure. + * @param pRdr The file provider instance. + * @param pvBase The base address of the image mapping. + * @param cSegments The number of segments in the array pointed to by paSegments. + * @param paSegments The segments thats going to be mapped. + */ +KRDR_DECL(int) kRdrUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + KRDR_VALIDATE(pRdr); + return pRdr->pOps->pfnUnmap(pRdr, pvBase, cSegments, paSegments); +} + + +/** + * We're done reading from the file but would like to keep file mappings. + * + * If the OS support closing the file handle while the file is mapped, + * the reader should do so. + * + * @param pRdr The file provider instance. + */ +KRDR_DECL(void) kRdrDone(PKRDR pRdr) +{ + KRDR_VALIDATE_VOID(pRdr); + pRdr->pOps->pfnDone(pRdr); +} + diff --git a/src/lib/kStuff/kRdr/kRdrBuffered.cpp b/src/lib/kStuff/kRdr/kRdrBuffered.cpp new file mode 100644 index 0000000..fc589cd --- /dev/null +++ b/src/lib/kStuff/kRdr/kRdrBuffered.cpp @@ -0,0 +1,750 @@ +/* $Id: kRdrBuffered.cpp 79 2016-07-27 14:25:09Z bird $ */ +/** @file + * kRdrBuffered - Buffered File Provider. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kRdrInternal.h" +#include +#include + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * The buffered file provier instance. + * This is just a wrapper around another file provider. + */ +typedef struct KRDRBUF +{ + /** The file reader vtable. */ + KRDR Core; + /** The actual file provider that we're wrapping. */ + PKRDR pRdr; + /** The current file offset. */ + KFOFF offFile; + /** The file size. */ + KFOFF cbFile; + /** The offset of the buffer. */ + KFOFF offBuf; + /** The offset of the end of the buffer. */ + KFOFF offBufEnd; + /** The number of valid buffer bytes. */ + KSIZE cbBufValid; + /** The size of the buffer. */ + KSIZE cbBuf; + /** The buffer. */ + KU8 *pbBuf; + /** Whether the pRdr instance should be closed together with us or not. */ + KBOOL fCloseIt; + /** Set if the buffer has been messed up by kRdrBufLineQ. */ + KBOOL fTainedByLineQ; +} KRDRBUF, *PKRDRBUF; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static void krdrBufDone(PKRDR pRdr); +static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect); +static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed); +static KSIZE krdrBufPageSize(PKRDR pRdr); +static const char *krdrBufName(PKRDR pRdr); +static KIPTR krdrBufNativeFH(PKRDR pRdr); +static KFOFF krdrBufTell(PKRDR pRdr); +static KFOFF krdrBufSize(PKRDR pRdr); +static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits); +static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits); +static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off); +static int krdrBufDestroy(PKRDR pRdr); +static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Native file provider operations. + * + * @remark This is not in the file provider list as its intended for wrapping + * other kRdr instances. + */ +static const KRDROPS g_krdrBufOps = +{ + "Buffered kRdr", + NULL, + krdrBufCreate, + krdrBufDestroy, + krdrBufRead, + krdrBufAllMap, + krdrBufAllUnmap, + krdrBufSize, + krdrBufTell, + krdrBufName, + krdrBufNativeFH, + krdrBufPageSize, + krdrBufMap, + krdrBufRefresh, + krdrBufProtect, + krdrBufUnmap, + krdrBufDone, + 42 +}; + + +/** @copydoc KRDROPS::pfnDone */ +static void krdrBufDone(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnDone(pThis->pRdr); +} + + +/** @copydoc KRDROPS::pfnUnmap */ +static int krdrBufUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnUnmap(pThis->pRdr, pvBase, cSegments, paSegments); +} + + +/** @copydoc KRDROPS::pfnProtect */ +static int krdrBufProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnProtect(pThis->pRdr, pvBase, cSegments, paSegments, fUnprotectOrProtect); +} + + +/** @copydoc KRDROPS::pfnRefresh */ +static int krdrBufRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnRefresh(pThis->pRdr, pvBase, cSegments, paSegments); +} + + +/** @copydoc KRDROPS::pfnMap */ +static int krdrBufMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnMap(pThis->pRdr, ppvBase, cSegments, paSegments, fFixed); +} + + +/** @copydoc KRDROPS::pfnPageSize */ +static KSIZE krdrBufPageSize(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnPageSize(pThis->pRdr); +} + + +/** @copydoc KRDROPS::pfnName */ +static const char *krdrBufName(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnName(pThis->pRdr); +} + + +/** @copydoc KRDROPS::pfnNativeFH */ +static KIPTR krdrBufNativeFH(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnNativeFH(pThis->pRdr); +} + + +/** @copydoc KRDROPS::pfnTell */ +static KFOFF krdrBufTell(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->offFile; +} + + +/** @copydoc KRDROPS::pfnSize */ +static KFOFF krdrBufSize(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->cbFile; +} + + +/** @copydoc KRDROPS::pfnAllUnmap */ +static int krdrBufAllUnmap(PKRDR pRdr, const void *pvBits) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnAllUnmap(pThis->pRdr, pvBits); +} + + +/** @copydoc KRDROPS::pfnAllMap */ +static int krdrBufAllMap(PKRDR pRdr, const void **ppvBits) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + return pThis->pRdr->pOps->pfnAllMap(pThis->pRdr, ppvBits); +} + + +/** + * Fills the buffer with file bits starting at the specified offset. + * + * @returns 0 on success, pfnRead error code on failure. + * @param pThis The instance. + * @param off Where to start reading. + */ +static int krdrBufFillBuffer(PKRDRBUF pThis, KFOFF off) +{ + kRdrAssert(off < pThis->cbFile); + + /* Reposition the buffer if it's past the end of the file so that + we maximize its usability. We leave one unused byte at the end + of the buffer so kRdrBufLineQ can terminate its string properly. + Of course, this might end up re-reading a lot of stuff for no + future gain, but whatever... */ + kRdrAssert(pThis->cbBuf <= pThis->cbFile + 1); + KFOFF cbLeft = pThis->cbFile - off; + KSIZE cbRead = pThis->cbBuf; + if ((KSSIZE)cbRead - 1 >= cbLeft) + { + cbRead--; + off = pThis->cbFile - cbRead; + } + int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pThis->pbBuf, cbRead, off); + if (!rc) + { + pThis->offBuf = off; + pThis->offBufEnd = off + cbRead; + pThis->cbBufValid = cbRead; + } + else + { + pThis->offBuf = pThis->offBufEnd = 0; + pThis->cbBufValid = 0; + } + pThis->fTainedByLineQ = K_FALSE; + return rc; +} + + +/** @copydoc KRDROPS::pfnRead */ +static int krdrBufRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + + /* + * We need to validate and update the file offset before + * we start making partial reads from the buffer and stuff. + */ + KFOFF offEnd = off + cb; + if ( off >= pThis->cbFile + || offEnd > pThis->cbFile + || offEnd < off) + return KERR_OUT_OF_RANGE; /* includes EOF. */ + pThis->offFile = offEnd; + if (!cb) + return 0; + + /* + * Scratch the buffer if kRdrBufLineQ has tained it. + */ + if (pThis->fTainedByLineQ) + { + pThis->offBuf = pThis->offBufEnd = 0; + pThis->cbBufValid = 0; + } + + /* + * Is any part of the request in the buffer? + * + * We will currently ignore buffer hits in the middle of the + * request because it's annoying to implement and it's + * questionable whether it'll benefit much performance wise. + */ + if (pThis->cbBufValid > 0) + { + if (off >= pThis->offBuf) + { + if (off < pThis->offBufEnd) + { + /* head (or all) of the request is in the buffer. */ + KSIZE cbMaxChunk = (KSIZE)(pThis->offBufEnd - off); + KSIZE cbChunk = K_MIN(cb, cbMaxChunk); + kHlpMemCopy(pvBuf, &pThis->pbBuf[off - pThis->offBuf], cbChunk); + if (cbChunk == cb) + return 0; + + cb -= cbChunk; + pvBuf = (KU8 *)pvBuf + cbChunk; + off += cbChunk; + } + } + else if ( offEnd > pThis->offBuf + && offEnd <= pThis->offBufEnd) + { + /* the end of the request is in the buffer. */ + KSIZE cbChunk = (KSIZE)(pThis->offBufEnd - (offEnd)); + kHlpMemCopy((KU8 *)pvBuf + (pThis->offBuf - off), pThis->pbBuf, cbChunk); + kRdrAssert(cbChunk < cb); + cb -= cbChunk; + offEnd -= cbChunk; + } + } + + /* + * If the buffer is larger than the read request, read a full buffer + * starting at the requested offset. Otherwise perform an unbuffered + * read. + */ + if (pThis->cbBuf > cb) + { + int rc = krdrBufFillBuffer(pThis, off); + if (rc) + return rc; + if (pThis->offBuf == off) + kHlpMemCopy(pvBuf, pThis->pbBuf, cb); + else + { + kRdrAssert(off > pThis->offBuf); + kRdrAssert(off + cb <= pThis->offBufEnd); + kHlpMemCopy(pvBuf, pThis->pbBuf + (off - pThis->offBuf), cb); + } + } + else + { + int rc = pThis->pRdr->pOps->pfnRead(pThis->pRdr, pvBuf, cb, off); + if (rc) + return rc; + } + return 0; +} + + +/** @copydoc KRDROPS::pfnDestroy */ +static int krdrBufDestroy(PKRDR pRdr) +{ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + + /* Close the kRdr instance that we're wrapping. */ + if (pThis->fCloseIt) + { + int rc = pThis->pRdr->pOps->pfnDestroy(pThis->pRdr); + if (rc) + return rc; + pThis->fCloseIt = K_FALSE; + pThis->pRdr = NULL; + } + + kHlpFree(pThis->pbBuf); + pThis->pbBuf = NULL; + kHlpFree(pRdr); + return 0; +} + + +/** @copydoc KRDROPS::pfnCreate */ +static int krdrBufCreate(PPKRDR ppRdr, const char *pszFilename) +{ + K_NOREF(ppRdr); + K_NOREF(pszFilename); + return KERR_NOT_IMPLEMENTED; +} + + +/** + * Worker for kRdrBufOpen and kRdrBufWrap. + * + * It's essentially kRdrBufWrap without error checking. + * + * @returns 0 on success, one of the kErrors status code on failure. + * @param ppRdr Where to store the new file provider instance. + * @param pRdrWrapped The file provider instance to buffer. + * @param fCloseIt Whether it the pRdrWrapped instance should be closed + * when the new instance is closed. + */ +static int krdrBufWrapIt(PPKRDR ppRdr, PKRDR pRdrWrapped, KBOOL fCloseIt) +{ + PKRDRBUF pThis = (PKRDRBUF)kHlpAlloc(sizeof(*pThis)); + if (pThis) + { + pThis->Core.u32Magic = KRDR_MAGIC; + pThis->Core.pOps = &g_krdrBufOps; + pThis->pRdr = pRdrWrapped; + pThis->offFile = pRdrWrapped->pOps->pfnTell(pRdrWrapped); + pThis->cbFile = pRdrWrapped->pOps->pfnSize(pRdrWrapped); + pThis->offBuf = pThis->offBufEnd = 0; + pThis->cbBufValid = 0; + pThis->fCloseIt = fCloseIt; + pThis->fTainedByLineQ = K_FALSE; + if (pThis->cbFile < 128*1024) + pThis->cbBuf = (KSIZE)pThis->cbFile + 1; /* need space for the kRdrBufLineQ terminator. */ + else + pThis->cbBuf = 64*1024; + pThis->pbBuf = (KU8 *)kHlpAlloc(pThis->cbBuf); + if (pThis->pbBuf) + { + *ppRdr = &pThis->Core; + return 0; + } + + pThis->Core.u32Magic = 0; + kHlpFree(pThis); + } + return KERR_NO_MEMORY; +} + + +/** + * Opens a file provider with a buffered wrapper. + * + * @returns 0 on success, KERR_* on failure. + * @param ppRdr Where to store the buffered file reader instance on success. + * @param pszFilename The name of the file that should be opened. + */ +KRDR_DECL(int) kRdrBufOpen(PPKRDR ppRdr, const char *pszFilename) +{ + kRdrAssertPtrReturn(ppRdr, KERR_INVALID_POINTER); + *ppRdr = NULL; + + PKRDR pRdrWrapped; + int rc = kRdrOpen(&pRdrWrapped, pszFilename); + if (!rc) + { + rc = krdrBufWrapIt(ppRdr, pRdrWrapped, K_TRUE); + if (rc) + kRdrClose(pRdrWrapped); + } + return rc; +} + + +/** + * Creates a buffered file provider instance for an existing one. + * + * @returns 0 on success, KERR_* on failure. + * @param ppRdr Where to store the new file provider pointer. + * @param pRdr The file provider instance to wrap. + * @param fCLoseIt Whether it the wrapped reader should be automatically + * closed when the wrapper closes. + */ +KRDR_DECL(int) kRdrBufWrap(PPKRDR ppRdr, PKRDR pRdr, KBOOL fCloseIt) +{ + KRDR_VALIDATE(pRdr); + return krdrBufWrapIt(ppRdr, pRdr, fCloseIt); +} + + +/** + * Checks whether the file provider instance is of the buffered type or not. + * + * @returns K_TRUE if it is, otherwise K_FALSE. + * @param pRdr The file provider instance to check. + */ +KRDR_DECL(KBOOL) kRdrBufIsBuffered(PKRDR pRdr) +{ + KRDR_VALIDATE_EX(pRdr, K_FALSE); + return pRdr->pOps == &g_krdrBufOps; +} + + +/** + * Reads a line from a buffered file provider. + * + * The trailing '\n' or '\r\n' is stripped. + * + * @returns 0 on success. KERR_* on failure. + * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer. + * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader. + * @param pRdr The buffered file reader. + * @param pszLine Where to store the line. + * @param cbLine The size of the the line buffer. + */ +KRDR_DECL(int) kRdrBufLine(PKRDR pRdr, char *pszLine, KSIZE cbLine) +{ + return kRdrBufLineEx(pRdr, pszLine, &cbLine); +} + + +/** + * Reads a line from a buffered file provider. + * + * The trailing '\n' or '\r\n' is stripped. + * + * @returns 0 on success. KERR_* on failure. + * @retval KRDR_ERR_LINE_TOO_LONG if the line is too long to fit in the passed in buffer. + * @retval KRDR_ERR_NOT_BUFFERED_RDR if pRdr isn't a buffered reader. + * @param pRdr The buffered file reader. + * @param pszLine Where to store the line. + * @param pcbLine The size of the the line buffer on input, the length of the + * returned line on output. + */ +KRDR_DECL(int) kRdrBufLineEx(PKRDR pRdr, char *pszLine, KSIZE *pcbLine) +{ + /* + * Validate input. + */ + kRdrAssertPtrReturn(pcbLine, KERR_INVALID_POINTER); + KSIZE cbLeft = *pcbLine; + *pcbLine = 0; + kRdrAssertReturn(cbLeft > 0, KERR_INVALID_PARAMETER); + KRDR_VALIDATE(pRdr); + kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, KRDR_ERR_NOT_BUFFERED_RDR); + kRdrAssertPtrReturn(pszLine, KERR_INVALID_POINTER); + + /* check for EOF */ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + if (pThis->offFile >= pThis->cbFile) + { + kRdrAssert(pThis->offFile == pThis->cbFile); + *pszLine = '\0'; + *pcbLine = 0; + return KERR_EOF; + } + + /* + * Scratch the buffer if kRdrBufLineQ has tained it. + */ + if (pThis->fTainedByLineQ) + { + pThis->offBuf = pThis->offBufEnd = 0; + pThis->cbBufValid = 0; + } + + /* + * Buffered read loop. + * + * The overflow logic is a bit fishy wrt to overflowing at an "\r\n" + * that arrives at a buffer boundrary. The current policy is to try + * our best to not to fail with overflow in the EOL sequence or EOF. + * If it's the end of the buffer, it will not be refilled just to + * check for this because that's too much work. + */ + cbLeft--; /* reserve space for the terminator. */ + char *pszOut = pszLine; + for (;;) + { + /* + * Do we need to (re-)fill the buffer or does it contain something + * that we can work on already? + */ + if ( !pThis->cbBufValid + || pThis->offFile >= pThis->offBufEnd + || pThis->offFile < pThis->offBuf) + { + int rc = krdrBufFillBuffer(pThis, pThis->offFile); + if (rc) + { + *pszOut = '\0'; + return rc; + } + } + + /* + * Parse the buffer looking for the EOL indicator. + */ + kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd); + kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf)); + const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf]; + const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid]; + const char *psz = pszStart; + while (psz < pszEnd) + { + const char ch = *psz; + if (ch == '\n') + { + /* found the EOL, update file position and line length. */ + pThis->offFile += psz - pszStart + 1; + *pcbLine += psz - pszStart; + + /* terminate the string, checking for "\r\n" first. */ + if ( *pcbLine + && pszOut[-1] == '\r') + { + *pcbLine -= 1; + pszOut--; + } + *pszOut = '\0'; + return 0; + } + if (!cbLeft) + { + /* the line is *probably* too long. */ + pThis->offFile += psz - pszStart; + *pcbLine += psz - pszStart; + *pszOut = '\0'; + + /* The only possible case where the line actually isn't too long + is if we're at a "\r\n" sequence. We will re-fill the buffer + if necessary to check for the '\n' as it's not that much work. */ + if ( ch == '\r' + && pThis->offFile + 2 <= pThis->cbFile) + { + if (psz + 1 >= pszEnd) + { + int rc = krdrBufFillBuffer(pThis, pThis->offFile); + if (rc) + { + *pszOut = '\0'; + return rc; + } + } + psz = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf]; + kRdrAssert(*psz == '\r'); + if (psz[1] == '\n') + { + *pcbLine -= 1; + pszOut[-1] = '\0'; + pThis->offFile += 2; + return 0; + } + } + return KRDR_ERR_LINE_TOO_LONG; + } + + /* copy and advance */ + *pszOut++ = ch; + cbLeft--; + psz++; + } + + /* advance past the buffer and check for EOF. */ + *pcbLine += pszEnd - pszStart; + pThis->offFile = pThis->offBufEnd; + if (pThis->offFile >= pThis->cbFile) + { + kRdrAssert(pThis->offFile == pThis->cbFile); + *pszOut = '\0'; + return 0; + } + } +} + + +/** + * Worker for kRdrBufLineQ that searches the current buffer for EOL or EOF. + * + * When a EOF marker is found + * + * + * @returns NULL if EOL/EOF isn't found the buffer. + * @param pThis The buffered reader instance. + */ +static const char * krdrBufLineQWorker(PKRDRBUF pThis) +{ + kRdrAssert(pThis->offFile >= pThis->offBuf && pThis->offFile < pThis->offBufEnd); + + /* + * Search the buffer. + */ + kRdrAssert(sizeof(char) == sizeof(*pThis->pbBuf)); + const char * const pszStart = (const char *)&pThis->pbBuf[pThis->offFile - pThis->offBuf]; + const char * const pszEnd = (const char *)&pThis->pbBuf[pThis->cbBufValid]; + char *psz = (char *)pszStart; + while (psz < pszEnd) + { + char ch = *psz; + if (ch == '\n') + { + pThis->offFile += psz - pszStart; + pThis->fTainedByLineQ = K_TRUE; + *psz = '\0'; + if ( psz > pszStart + && psz[-1] == '\r') + *--psz = '\0'; + return pszStart; + } + psz++; + } + + /* + * Check for EOF. There must be room for a terminator char here. + */ + if ( pThis->offBufEnd >= pThis->cbFile + && (pThis->offBufEnd - pThis->offBuf) < (KSSIZE)pThis->cbBuf) + { + pThis->offFile = pThis->cbFile; + pThis->pbBuf[pThis->cbBufValid] = '\0'; + return pszStart; + } + + return NULL; +} + + +/** + * Get the pointer to the next next line in the buffer. + * The returned line is zero terminated. + * + * @returns A pointer to the line on success. This becomes invalid + * upon the next call to this kRdr instance. + * @returns NULL on EOF, read error of if the line was too long. + * @param pRdr The buffered file reader. + */ +KRDR_DECL(const char *) kRdrBufLineQ(PKRDR pRdr) +{ + /* + * Validate input. + */ + KRDR_VALIDATE_EX(pRdr, NULL); + kRdrAssertReturn(pRdr->pOps != &g_krdrBufOps, NULL); + + /* check for EOF */ + PKRDRBUF pThis = (PKRDRBUF)pRdr; + if (pThis->offFile >= pThis->cbFile) + { + kRdrAssert(pThis->offFile == pThis->cbFile); + return NULL; + } + + /* + * Search the current buffer if possible + */ + if ( pThis->cbBufValid + && pThis->offFile >= pThis->offBuf + && pThis->offFile < pThis->offBufEnd) + { + const char *psz = krdrBufLineQWorker(pThis); + if (psz) + return psz; + } + + /* + * Fill the buffer in an optimal way and look for the EOL/EOF (again). + */ + int rc = krdrBufFillBuffer(pThis, pThis->offFile); + if (rc) + return NULL; + return krdrBufLineQWorker(pThis); +} + diff --git a/src/lib/kStuff/kRdr/kRdrFile.cpp b/src/lib/kStuff/kRdr/kRdrFile.cpp new file mode 100644 index 0000000..27dd803 --- /dev/null +++ b/src/lib/kStuff/kRdr/kRdrFile.cpp @@ -0,0 +1,1308 @@ +/* $Id: kRdrFile.cpp 81 2016-08-18 22:10:38Z bird $ */ +/** @file + * kRdrFile - The Native File Provider + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "kRdrInternal.h" +#include +#include +#include + +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS +# include +# include +# include +# include + +#elif K_OS == K_OS_OS2 +# define INCL_ERRORS +# define INCL_BASE +# include + +#elif K_OS == K_OS_WINDOWS +# define WIN32_NO_STATUS +# include +# include +# include + +# ifdef __cplusplus + extern "C" { +# endif + + /** @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 PSIZE_T 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 PSIZE_T AllocationSize, + IN ULONG AllocationType, + IN ULONG Protect + ); + + NTOSAPI + NTSTATUS + NTAPI + NtFreeVirtualMemory( + IN HANDLE ProcessHandle, + IN OUT PVOID *BaseAddress, + IN OUT PSIZE_T FreeSize, + IN ULONG FreeType + ); + +# ifdef __cplusplus + } +# endif + +#else +# error "port me" +#endif + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * Prepared stuff. + */ +typedef struct KRDRFILEPREP +{ + /** The address of the prepared region. */ + void *pv; + /** The size of the prepared region. */ + KSIZE cb; +#if K_OS == K_OS_WINDOWS + /** Handle to the section created to map the file. */ + HANDLE hSection; +#endif +} KRDRFILEPREP, *PKRDRFILEPREP; + +/** + * The file provier instance for native files. + */ +typedef struct KRDRFILE +{ + /** The file reader vtable. */ + KRDR Core; + /** The file handle. */ +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + int File; +#elif K_OS == K_OS_OS2 + HFILE File; +#elif K_OS == K_OS_WINDOWS + HANDLE File; +#else +# error "Port me!" +#endif + /** The current file offset. */ + KFOFF off; + /** The file size. */ + KFOFF cb; + /** Array where we stuff the mapping area data. */ + KRDRFILEPREP aPreps[4]; + /** The number of current preps. */ + KU32 cPreps; + /** Number of mapping references. */ + KI32 cMappings; + /** The memory mapping. */ + void *pvMapping; + /** The filename. */ + char szFilename[1]; +} KRDRFILE, *PKRDRFILE; + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static void krdrFileDone(PKRDR pRdr); +static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect); +static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect); +static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments); +static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed); +static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed); +static KSIZE krdrFilePageSize(PKRDR pRdr); +static const char *krdrFileName(PKRDR pRdr); +static KIPTR krdrFileNativeFH(PKRDR pRdr); +static KFOFF krdrFileTell(PKRDR pRdr); +static KFOFF krdrFileSize(PKRDR pRdr); +static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits); +static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits); +static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off); +static int krdrFileDestroy(PKRDR pRdr); +static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename); + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** Native file provider operations. */ +const KRDROPS g_kRdrFileOps = +{ + "native file", + NULL, + krdrFileCreate, + krdrFileDestroy, + krdrFileRead, + krdrFileAllMap, + krdrFileAllUnmap, + krdrFileSize, + krdrFileTell, + krdrFileName, + krdrFileNativeFH, + krdrFilePageSize, + krdrFileMap, + krdrFileRefresh, + krdrFileProtect, + krdrFileUnmap, + krdrFileDone, + 42 +}; + + +#if K_OS == K_OS_WINDOWS +/** + * Converts a kLdr segment protection to NT protection for a mapping. + * + * @returns Nt page protection. + * @param enmProt kLdr protection. + */ +static ULONG krdrFileGetNtMapProt(KPROT enmProt) +{ + switch (enmProt) + { + case KPROT_NOACCESS: return PAGE_NOACCESS; + case KPROT_READONLY: return PAGE_READONLY; + case KPROT_READWRITE: return PAGE_READWRITE; + case KPROT_WRITECOPY: return PAGE_WRITECOPY; + case KPROT_EXECUTE: return PAGE_EXECUTE; + case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ; + case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE; + case KPROT_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 krdrFileGetNtAllocProt(KPROT enmProt) +{ + switch (enmProt) + { + case KPROT_NOACCESS: return PAGE_NOACCESS; + case KPROT_READONLY: return PAGE_READONLY; + case KPROT_WRITECOPY: + case KPROT_READWRITE: return PAGE_READWRITE; + case KPROT_EXECUTE: return PAGE_EXECUTE; + case KPROT_EXECUTE_READ: return PAGE_EXECUTE_READ; + case KPROT_EXECUTE_WRITECOPY: + case KPROT_EXECUTE_READWRITE: return PAGE_EXECUTE_READWRITE; + default: return ~(ULONG)0; + } +} +#endif + + +/** @copydoc KRDROPS::pfnDone */ +static void krdrFileDone(PKRDR 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 PKRDRFILEPREP krdrFileFindPrepExact(PKRDRFILE pFile, void *pv) +{ + KI32 i = pFile->cPreps; + while (i-- > 0) + if (pFile->aPreps[i].pv == pv) + return &pFile->aPreps[i]; + return NULL; +} + + +/** @copydoc KRDROPS::pfnUnmap */ +static int krdrFileUnmap(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase); + int rc; + if (!pPrep) + return KERR_INVALID_PARAMETER; + +#if K_OS == K_OS_WINDOWS + if (pPrep->hSection != NULL) + { + /** @todo implement me. */ + return -1; + } +#endif + + rc = krdrFileGenericUnmap(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 krdrFileUnmap. */ +static int krdrFileGenericUnmap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments) +{ + krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); + return kHlpPageFree(pPrep->pv, pPrep->cb); +} + + +/** @copydoc KRDROPS::pfnProtect */ +static int krdrFileProtect(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase); + if (!pPrep) + return KERR_INVALID_PARAMETER; + +#if K_OS == K_OS_WINDOWS + if (pPrep->hSection != NULL) + { + /** @todo implement me. */ + return -1; + } +#endif + + return krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, fUnprotectOrProtect); +} + + +/** Generic implementation of krdrFileProtect. */ +static int krdrFileGenericProtect(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fUnprotectOrProtect) +{ + KU32 i; + + /* + * Iterate the segments and apply memory protection changes. + */ + for (i = 0; i < cSegments; i++) + { + int rc; + void *pv; + KPROT enmProt; + + if (paSegments[i].RVA == NIL_KLDRADDR) + continue; + + /* calc new protection. */ + enmProt = (KPROT)paSegments[i].enmProt; /** @todo drop cast */ + if (fUnprotectOrProtect) + { + switch (enmProt) + { + case KPROT_NOACCESS: + case KPROT_READONLY: + case KPROT_READWRITE: + case KPROT_WRITECOPY: + enmProt = KPROT_READWRITE; + break; + case KPROT_EXECUTE: + case KPROT_EXECUTE_READ: + case KPROT_EXECUTE_READWRITE: + case KPROT_EXECUTE_WRITECOPY: + enmProt = KPROT_EXECUTE_READWRITE; + break; + default: + kRdrAssert(!"bad enmProt"); + return -1; + } + } + else + { + /* copy on write -> normal write. */ + if (enmProt == KPROT_EXECUTE_WRITECOPY) + enmProt = KPROT_EXECUTE_READWRITE; + else if (enmProt == KPROT_WRITECOPY) + enmProt = KPROT_READWRITE; + } + + pv = (KU8 *)pPrep->pv + paSegments[i].RVA; + + rc = kHlpPageProtect(pv, paSegments[i].cbMapped, enmProt); + if (rc) + break; + } + + return 0; +} + + +/** @copydoc KRDROPS::pfnRefresh */ +static int krdrFileRefresh(PKRDR pRdr, void *pvBase, KU32 cSegments, PCKLDRSEG paSegments) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + PKRDRFILEPREP pPrep = krdrFileFindPrepExact(pRdrFile, pvBase); + if (!pPrep) + return KERR_INVALID_PARAMETER; + +#if K_OS == K_OS_WINDOWS + if (pPrep->hSection != NULL) + { + /** @todo implement me. */ + return -1; + } +#endif + + return krdrFileGenericRefresh(pRdr, pPrep, cSegments, paSegments); +} + + +/** Generic implementation of krdrFileRefresh. */ +static int krdrFileGenericRefresh(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments) +{ + int rc; + int rc2; + KU32 i; + + /* + * Make everything writable again. + */ + rc = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); + if (rc) + { + krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); + return rc; + } + + /* + * Clear everything. + */ + /** @todo only zero the areas not covered by raw file bits. */ + kHlpMemSet(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 = (KU8 *)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 = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); + if (rc2 && rc) + rc = rc2; + + return rc; +} + + +/** @copydoc KRDROPS::pfnMap */ +static int krdrFileMap(PKRDR pRdr, void **ppvBase, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + PKRDRFILEPREP pPrep = &pRdrFile->aPreps[pRdrFile->cPreps]; + KLDRSIZE cbTotal; + const KSIZE cbPage = pRdr->pOps->pfnPageSize(pRdr); + int rc; + KU32 i; + + if (pRdrFile->cPreps >= K_ELEMENTS(pRdrFile->aPreps)) + return KRDR_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 = (KSIZE)cbTotal; + if (pPrep->cb != cbTotal) + return KLDR_ERR_ADDRESS_OVERFLOW; + pPrep->cb = (pPrep->cb + (cbPage - 1)) & ~(cbPage- 1); + +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + /** @todo */ + +#elif K_OS == K_OS_WINDOWS + /* + * 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 = (KSIZE)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 = (KU8 *)pPrep->pv + paSegments[i].RVA; + if (paSegments[i].cbFile > 0) + { + SectionOffset.QuadPart = paSegments[i].offFile; + ViewSize = paSegments[i].cbFile; + fPageProt = krdrFileGetNtMapProt(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 = krdrFileGetNtAllocProt(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 = (KU8 *)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 = krdrFileGenericMap(pRdr, pPrep, cSegments, paSegments, fFixed); + if (!rc) + { + *ppvBase = pPrep->pv; + pRdrFile->cPreps++; + } + + return rc; +} + + +/** Generic implementation of krdrFileMap. */ +static int krdrFileGenericMap(PKRDR pRdr, PKRDRFILEPREP pPrep, KU32 cSegments, PCKLDRSEG paSegments, KBOOL fFixed) +{ + int rc; + KU32 i; + + /* + * Generic mapping code using kHlpPageAlloc(), kHlpPageFree() and kHlpPageProtect(). + */ + rc = kHlpPageAlloc(&pPrep->pv, pPrep->cb, KPROT_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 = (KU8 *)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 = krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 0 /* protect */); + if (!rc) + return 0; + krdrFileGenericProtect(pRdr, pPrep, cSegments, paSegments, 1 /* unprotect */); + } + + /* bailout */ + kHlpPageFree(pPrep->pv, pPrep->cb); + return rc; +} + + +/** @copydoc KRDROPS::pfnPageSize */ +static KSIZE krdrFilePageSize(PKRDR pRdr) +{ +#if K_OS == K_OS_DARWIN + return 0x1000; /** @todo find some header somewhere... */ + +#elif K_OS == K_OS_LINUX + return 0x1000; /** @todo find some header somewhere... */ + +#elif K_OS == K_OS_OS2 + /* The page size on OS/2 wont change anytime soon. :-) */ + return 0x1000; + +#elif K_OS == K_OS_WINDOWS + SYSTEM_INFO SysInfo; + GetSystemInfo(&SysInfo); + return SysInfo.dwPageSize; + /*return SysInfo.dwAllocationGranularity;*/ +#else +# error "port me" +#endif +} + + +/** @copydoc KRDROPS::pfnName */ +static const char *krdrFileName(PKRDR pRdr) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + return &pRdrFile->szFilename[0]; +} + + +static KIPTR krdrFileNativeFH(PKRDR pRdr) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_OS2 \ + || K_OS == K_OS_SOLARIS \ + || K_OS == K_OS_WINDOWS + return (KIPTR)pRdrFile->File; +#else +# error "port me" +#endif +} + + +/** @copydoc KRDROPS::pfnTell */ +static KFOFF krdrFileTell(PKRDR pRdr) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + + /* + * If the offset is undefined, try figure out what it is. + */ + if (pRdrFile->off == -1) + { +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_CUR, 0); + if (pRdrFile->off < 0) + pRdrFile->off = -1; + +#elif K_OS == K_OS_OS2 + ULONG ulNew; + APIRET rc = DosSetFilePtr(pRdrFile->File, 0, FILE_CURRENT, &ulNew); + if (rc) + return -1; + pRdrFile->off = ulNew; + +#elif K_OS == K_OS_WINDOWS + 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 = ((KFOFF)offHigh << 32) | offLow; + +#else +# error "port me." +#endif + } + return pRdrFile->off; +} + + +/** @copydoc KRDROPS::pfnSize */ +static KFOFF krdrFileSize(PKRDR pRdr) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + return pRdrFile->cb; +} + + +/** @copydoc KRDROPS::pfnAllUnmap */ +static int krdrFileAllUnmap(PKRDR pRdr, const void *pvBits) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + + /* check for underflow */ + if (pRdrFile->cMappings <= 0) + return KERR_INVALID_PARAMETER; + + /* decrement usage counter, free mapping if no longer in use. */ + if (!--pRdrFile->cMappings) + { + kHlpFree(pRdrFile->pvMapping); + pRdrFile->pvMapping = NULL; + } + + return 0; +} + + +/** @copydoc KRDROPS::pfnAllMap */ +static int krdrFileAllMap(PKRDR pRdr, const void **ppvBits) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + + /* + * Do we need to map it? + */ + if (!pRdrFile->pvMapping) + { + int rc; + KFOFF cbFile = pRdrFile->Core.pOps->pfnSize(pRdr); + KSIZE cb = (KSIZE)cbFile; + if (cb != cbFile) + return KERR_NO_MEMORY; + + pRdrFile->pvMapping = kHlpAlloc(cb); + if (!pRdrFile->pvMapping) + return KERR_NO_MEMORY; + rc = pRdrFile->Core.pOps->pfnRead(pRdr, pRdrFile->pvMapping, cb, 0); + if (rc) + { + kHlpFree(pRdrFile->pvMapping); + pRdrFile->pvMapping = NULL; + return rc; + } + pRdrFile->cMappings = 0; + } + + *ppvBits = pRdrFile->pvMapping; + pRdrFile->cMappings++; + return 0; +} + + +/** @copydoc KRDROPS::pfnRead */ +static int krdrFileRead(PKRDR pRdr, void *pvBuf, KSIZE cb, KFOFF off) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + + /* + * Do a seek if needed. + */ + if (pRdrFile->off != off) + { +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + pRdrFile->off = kHlpSys_lseek(pRdrFile->File, SEEK_SET, off); + if (pRdrFile->off < 0) + { + int rc = (int)-pRdrFile->off; + pRdrFile->off = -1; + return -rc; + } + +#elif K_OS == K_OS_OS2 + ULONG ulNew; + APIRET rc; + + rc = DosSetFilePtr(pRdrFile->File, off, FILE_BEGIN, &ulNew); + if (rc) + { + pRdrFile->off = -1; + return rc; + } + +#elif K_OS == K_OS_WINDOWS + LONG offHigh; + LONG offLow; + + offHigh = (LONG)(off >> 32); + offLow = SetFilePointer(pRdrFile->File, (LONG)off, &offHigh, FILE_BEGIN); + if ( offLow != (LONG)off + || offHigh != (LONG)(off >> 32)) + { + int rc = GetLastError(); + if (!rc) + rc = KERR_GENERAL_FAILURE; + pRdrFile->off = -1; + return rc; + } + +#else +# error "port me." +#endif + } + + /* + * Do the read. + */ +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + { + KSSIZE cbRead; + + cbRead = kHlpSys_read(pRdrFile->File, pvBuf, cb); + if (cbRead != cb) + { + pRdrFile->off = -1; + if (cbRead < 0) + return -cbRead; + return KERR_GENERAL_FAILURE; + } + } + +#elif K_OS == K_OS_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 KERR_GENERAL_FAILURE; + } + } + +#elif K_OS == K_OS_WINDOWS + { + DWORD cbRead = 0; + if (!ReadFile(pRdrFile->File, pvBuf, cb, &cbRead, NULL)) + { + int rc = GetLastError(); + if (!rc) + rc = KERR_GENERAL_FAILURE; + pRdrFile->off = -1; + return rc; + } + if (cbRead != cb) + { + pRdrFile->off = -1; + return KERR_GENERAL_FAILURE; + } + } + +#else +# error "port me." +#endif + + pRdrFile->off = off + cb; + return 0; +} + + +/** @copydoc KRDROPS::pfnDestroy */ +static int krdrFileDestroy(PKRDR pRdr) +{ + PKRDRFILE pRdrFile = (PKRDRFILE)pRdr; + int rc; + +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + rc = kHlpSys_close(pRdrFile->File); + +#elif K_OS == K_OS_OS2 + rc = DosClose(pRdrFile->File); + +#elif K_OS == K_OS_WINDOWS + rc = 0; + if (!CloseHandle(pRdrFile->File)) + rc = GetLastError(); + +#else +# error "port me" +#endif + + if (pRdrFile->pvMapping) + { + kHlpFree(pRdrFile->pvMapping); + pRdrFile->pvMapping = NULL; + } + + kHlpFree(pRdr); + return rc; +} + + +/** @copydoc KRDROPS::pfnCreate */ +static int krdrFileCreate(PPKRDR ppRdr, const char *pszFilename) +{ + KSIZE cchFilename; + PKRDRFILE pRdrFile; + + /* + * Open the file, determin its size and correct filename. + */ +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + int File; + KFOFF cb; + KFOFF rc; + char szFilename[1024]; + + cchFilename = kHlpStrLen(pszFilename); + if (cchFilename >= sizeof(szFilename)) + return KERR_OUT_OF_RANGE; + kHlpMemCopy(szFilename, pszFilename, cchFilename + 1); + /** @todo normalize the filename. */ + +# ifdef O_BINARY + File = kHlpSys_open(pszFilename, O_RDONLY | O_BINARY, 0); +# else + File = kHlpSys_open(pszFilename, O_RDONLY, 0); +# endif + if (File < 0) + return -File; + + cb = kHlpSys_lseek(File, SEEK_END, 0); + rc = kHlpSys_lseek(File, SEEK_SET, 0); + if ( cb < 0 + || rc < 0) + { + kHlpSys_close(File); + return cb < 0 ? -cb : -rc; + } + +#elif K_OS == K_OS_OS2 + ULONG ulAction = 0; + FILESTATUS3 Info; + APIRET rc; + HFILE File = 0; + KFOFF cb; + char szFilename[CCHMAXPATH]; + + if ((uintptr_t)pszFilename >= 0x20000000) + { + char *psz; + cchFilename = kHlpStrLen(szFilename); + psz = (char *)kHlpAllocA(cchFilename + 1); + kHlpMemCopy(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 K_OS == K_OS_WINDOWS + SECURITY_ATTRIBUTES SecAttr; + DWORD High; + DWORD Low; + int rc; + HANDLE File; + KFOFF 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; + } + cb = ((KFOFF)High << 32) | Low; + +#else +# error "port me" +#endif + + + /* + * Allocate the reader instance. + */ + cchFilename = kHlpStrLen(szFilename); + pRdrFile = (PKRDRFILE)kHlpAlloc(sizeof(*pRdrFile) + cchFilename); + if (!pRdrFile) + { +#if K_OS == K_OS_DARWIN \ + || K_OS == K_OS_FREEBSD \ + || K_OS == K_OS_LINUX \ + || K_OS == K_OS_NETBSD \ + || K_OS == K_OS_OPENBSD \ + || K_OS == K_OS_SOLARIS + kHlpSys_close(File); +#elif K_OS == K_OS_OS2 + DosClose(File); +#elif K_OS == K_OS_WINDOWS + CloseHandle(File); +#else +# error "port me" +#endif + return KERR_NO_MEMORY; + } + + /* + * Initialize it and return successfully. + */ + pRdrFile->Core.u32Magic = KRDR_MAGIC; + pRdrFile->Core.pOps = &g_kRdrFileOps; + pRdrFile->File = File; + pRdrFile->cb = cb; + pRdrFile->off = 0; + pRdrFile->cPreps = 0; + pRdrFile->cMappings = 0; + pRdrFile->pvMapping = NULL; + kHlpMemCopy(&pRdrFile->szFilename[0], szFilename, cchFilename + 1); + + *ppRdr = &pRdrFile->Core; + return 0; +} + diff --git a/src/lib/kStuff/kRdr/kRdrInternal.h b/src/lib/kStuff/kRdr/kRdrInternal.h new file mode 100644 index 0000000..d8f67db --- /dev/null +++ b/src/lib/kStuff/kRdr/kRdrInternal.h @@ -0,0 +1,122 @@ +/* $Id: kRdrInternal.h 29 2009-07-01 20:30:29Z bird $ */ +/** @file + * kRdr - Internal Header. + */ + +/* + * Copyright (c) 2006-2007 Knut St. Osmundsen + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef ___kRdrInternal_h___ +#define ___kRdrInternal_h___ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @defgroup grp_kRdrInternal - Internals + * @internal + * @addtogroup grp_kRdr + * @{ + */ + +/** @def KRDR_STRICT + * If defined the kRdr assertions and other runtime checks will be enabled. */ +#ifdef K_ALL_STRICT +# undef KRDR_STRICT +# define KRDR_STRICT +#endif + +/** @name Our Assert macros + * @{ */ +#ifdef KRDR_STRICT +# define kRdrAssert(expr) kHlpAssert(expr) +# define kRdrAssertReturn(expr, rcRet) kHlpAssertReturn(expr, rcRet) +# define kRdrAssertMsg(expr, msg) kHlpAssertMsg(expr, msg) +# define kRdrAssertMsgReturn(expr, msg, rcRet) kHlpAssertMsgReturn(expr, msg, rcRet) +#else /* !KRDR_STRICT */ +# define kRdrAssert(expr) do { } while (0) +# define kRdrAssertReturn(expr, rcRet) do { if (!(expr)) return (rcRet); } while (0) +# define kRdrAssertMsg(expr, msg) do { } while (0) +# define kRdrAssertMsgReturn(expr, msg, rcRet) do { if (!(expr)) return (rcRet); } while (0) +#endif /* !KRDR_STRICT */ + +#define kRdrAssertPtr(ptr) kRdrAssertMsg(K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kRdrAssertPtrReturn(ptr, rcRet) kRdrAssertMsgReturn(K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kRdrAssertPtrNull(ptr) kRdrAssertMsg(!(ptr) || K_VALID_PTR(ptr), ("%s = %p\n", #ptr, (ptr))) +#define kRdrAssertPtrNullReturn(ptr, rcRet) kRdrAssertMsgReturn(!(ptr) || K_VALID_PTR(ptr), ("%s = %p -> %d\n", #ptr, (ptr), (rcRet)), (rcRet)) +#define kRdrAssertRC(rc) kRdrAssertMsg((rc) == 0, ("%s = %d\n", #rc, (rc))) +#define kRdrAssertRCReturn(rc, rcRet) kRdrAssertMsgReturn((rc) == 0, ("%s = %d -> %d\n", #rc, (rc), (rcRet)), (rcRet)) +#define kRdrAssertFailed() kRdrAssert(0) +#define kRdrAssertFailedReturn(rcRet) kRdrAssertReturn(0, (rcRet)) +#define kRdrAssertMsgFailed(msg) kRdrAssertMsg(0, msg) +#define kRdrAssertMsgFailedReturn(msg, rcRet) kRdrAssertMsgReturn(0, msg, (rcRet)) +/** @} */ + +/** Return / crash validation of a reader argument. */ +#define KRDR_VALIDATE_EX(pRdr, rc) \ + do { \ + if ( (pRdr)->u32Magic != KRDR_MAGIC \ + || (pRdr)->pOps == NULL \ + )\ + { \ + return (rc); \ + } \ + } while (0) + +/** Return / crash validation of a reader argument. */ +#define KRDR_VALIDATE(pRdr) \ + KRDR_VALIDATE_EX(pRdr, KERR_INVALID_PARAMETER) + +/** Return / crash validation of a reader argument. */ +#define KRDR_VALIDATE_VOID(pRdr) \ + do { \ + if ( !K_VALID_PTR(pRdr) \ + || (pRdr)->u32Magic != KRDR_MAGIC \ + || (pRdr)->pOps == NULL \ + )\ + { \ + return; \ + } \ + } while (0) + + +/** @name Built-in Providers + * @{ */ +extern const KRDROPS g_kRdrFileOps; +/** @} */ + +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif + -- cgit v1.2.3