diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:21:29 +0000 |
commit | 29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc (patch) | |
tree | 63ef546b10a81d461e5cf5ed9e98a68cd7dee1aa /src/lib/kStuff/kRdr | |
parent | Initial commit. (diff) | |
download | kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.tar.xz kbuild-29cd838eab01ed7110f3ccb2e8c6a35c8a31dbcc.zip |
Adding upstream version 1:0.1.9998svn3589+dfsg.upstream/1%0.1.9998svn3589+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/kStuff/kRdr')
-rw-r--r-- | src/lib/kStuff/kRdr/Makefile.kmk | 48 | ||||
-rw-r--r-- | src/lib/kStuff/kRdr/kRdr.cpp | 281 | ||||
-rw-r--r-- | src/lib/kStuff/kRdr/kRdrBuffered.cpp | 750 | ||||
-rw-r--r-- | src/lib/kStuff/kRdr/kRdrFile.cpp | 1308 | ||||
-rw-r--r-- | src/lib/kStuff/kRdr/kRdrInternal.h | 122 |
5 files changed, 2509 insertions, 0 deletions
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 <bird-kStuff-spamix@anduin.net> +# +# 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 <bird-kStuff-spamix@anduin.net>
+ *
+ * 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 <bird-kStuff-spamix@anduin.net> + * + * 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 <k/kHlpAlloc.h> +#include <k/kHlpString.h> + + +/******************************************************************************* +* 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 <bird-kStuff-spamix@anduin.net>
+ *
+ * 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 <k/kHlpAlloc.h>
+#include <k/kHlpString.h>
+#include <k/kErrors.h>
+
+#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 <k/kHlpSys.h>
+# include <sys/fcntl.h>
+# include <sys/mman.h>
+# include <unistd.h>
+
+#elif K_OS == K_OS_OS2
+# define INCL_ERRORS
+# define INCL_BASE
+# include <os2.h>
+
+#elif K_OS == K_OS_WINDOWS
+# define WIN32_NO_STATUS
+# include <Windows.h>
+# include <ntsecapi.h>
+# include <ntstatus.h>
+
+# 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 <bird-kStuff-spamix@anduin.net> + * + * 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 <k/kHlpAssert.h> +#include <k/kMagics.h> +#include <k/kRdrAll.h> +#include <k/kErrors.h> + +#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 + |