/* $Id: GuestCtrlIO.cpp 39843 2012-01-23 18:38:18Z vboxsync $ */ /** @file * * IO helper for IGuest COM class implementations. */ /* * Copyright (C) 2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /****************************************************************************** * Header Files * ******************************************************************************/ #include "GuestCtrlImplPrivate.h" #ifdef DEBUG # include "Logging.h" # include #endif /* DEBUG */ /****************************************************************************** * Structures and Typedefs * ******************************************************************************/ /** @todo *NOT* thread safe yet! */ /** @todo Add exception handling for STL stuff! */ GuestProcessStreamBlock::GuestProcessStreamBlock() { } /* GuestProcessStreamBlock::GuestProcessStreamBlock(const GuestProcessStreamBlock &otherBlock) { for (GuestCtrlStreamPairsIter it = otherBlock.m_mapPairs.begin(); it != otherBlock.end(); it++) { m_mapPairs[it->first] = new if (it->second.pszValue) { RTMemFree(it->second.pszValue); it->second.pszValue = NULL; } } }*/ GuestProcessStreamBlock::~GuestProcessStreamBlock() { Clear(); } /** * Destroys the currently stored stream pairs. * * @return IPRT status code. */ void GuestProcessStreamBlock::Clear() { m_mapPairs.clear(); } #ifdef DEBUG void GuestProcessStreamBlock::Dump() { LogFlowFunc(("Dumping contents of stream block=0x%p (%ld items):\n", this, m_mapPairs.size())); for (GuestCtrlStreamPairMapIterConst it = m_mapPairs.begin(); it != m_mapPairs.end(); it++) { LogFlowFunc(("\t%s=%s\n", it->first.c_str(), it->second.mValue.c_str())); } } #endif /** * Returns a 64-bit signed integer of a specified key. * * @return IPRT status code. VERR_NOT_FOUND if key was not found. * @param pszKey Name of key to get the value for. * @param piVal Pointer to value to return. */ int GuestProcessStreamBlock::GetInt64Ex(const char *pszKey, int64_t *piVal) { AssertPtrReturn(pszKey, VERR_INVALID_POINTER); AssertPtrReturn(piVal, VERR_INVALID_POINTER); const char *pszValue = GetString(pszKey); if (pszValue) { *piVal = RTStrToInt64(pszValue); return VINF_SUCCESS; } return VERR_NOT_FOUND; } /** * Returns a 64-bit integer of a specified key. * * @return int64_t Value to return, 0 if not found / on failure. * @param pszKey Name of key to get the value for. */ int64_t GuestProcessStreamBlock::GetInt64(const char *pszKey) { int64_t iVal; if (RT_SUCCESS(GetInt64Ex(pszKey, &iVal))) return iVal; return 0; } /** * Returns the current number of stream pairs. * * @return uint32_t Current number of stream pairs. */ size_t GuestProcessStreamBlock::GetCount() { return m_mapPairs.size(); } /** * Returns a string value of a specified key. * * @return uint32_t Pointer to string to return, NULL if not found / on failure. * @param pszKey Name of key to get the value for. */ const char* GuestProcessStreamBlock::GetString(const char *pszKey) { AssertPtrReturn(pszKey, NULL); try { GuestCtrlStreamPairMapIterConst itPairs = m_mapPairs.find(Utf8Str(pszKey)); if (itPairs != m_mapPairs.end()) return itPairs->second.mValue.c_str(); } catch (const std::exception &ex) { NOREF(ex); } return NULL; } /** * Returns a 32-bit unsigned integer of a specified key. * * @return IPRT status code. VERR_NOT_FOUND if key was not found. * @param pszKey Name of key to get the value for. * @param puVal Pointer to value to return. */ int GuestProcessStreamBlock::GetUInt32Ex(const char *pszKey, uint32_t *puVal) { AssertPtrReturn(pszKey, VERR_INVALID_POINTER); AssertPtrReturn(puVal, VERR_INVALID_POINTER); const char *pszValue = GetString(pszKey); if (pszValue) { *puVal = RTStrToUInt32(pszValue); return VINF_SUCCESS; } return VERR_NOT_FOUND; } /** * Returns a 32-bit unsigned integer of a specified key. * * @return uint32_t Value to return, 0 if not found / on failure. * @param pszKey Name of key to get the value for. */ uint32_t GuestProcessStreamBlock::GetUInt32(const char *pszKey) { uint32_t uVal; if (RT_SUCCESS(GetUInt32Ex(pszKey, &uVal))) return uVal; return 0; } /** * Sets a value to a key or deletes a key by setting a NULL value. * * @return IPRT status code. * @param pszKey Key name to process. * @param pszValue Value to set. Set NULL for deleting the key. */ int GuestProcessStreamBlock::SetValue(const char *pszKey, const char *pszValue) { AssertPtrReturn(pszKey, VERR_INVALID_POINTER); int rc = VINF_SUCCESS; try { Utf8Str Utf8Key(pszKey); /* Take a shortcut and prevent crashes on some funny versions * of STL if map is empty initially. */ if (!m_mapPairs.empty()) { GuestCtrlStreamPairMapIter it = m_mapPairs.find(Utf8Key); if (it != m_mapPairs.end()) m_mapPairs.erase(it); } if (pszValue) { GuestProcessStreamValue val(pszValue); m_mapPairs[Utf8Key] = val; } } catch (const std::exception &ex) { NOREF(ex); } return rc; } /////////////////////////////////////////////////////////////////////////////// GuestProcessStream::GuestProcessStream() : m_cbAllocated(0), m_cbSize(0), m_cbOffset(0), m_pbBuffer(NULL) { } GuestProcessStream::~GuestProcessStream() { Destroy(); } /** * Adds data to the internal parser buffer. Useful if there * are multiple rounds of adding data needed. * * @return IPRT status code. * @param pbData Pointer to data to add. * @param cbData Size (in bytes) of data to add. */ int GuestProcessStream::AddData(const BYTE *pbData, size_t cbData) { AssertPtrReturn(pbData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); int rc = VINF_SUCCESS; /* Rewind the buffer if it's empty. */ size_t cbInBuf = m_cbSize - m_cbOffset; bool const fAddToSet = cbInBuf == 0; if (fAddToSet) m_cbSize = m_cbOffset = 0; /* Try and see if we can simply append the data. */ if (cbData + m_cbSize <= m_cbAllocated) { memcpy(&m_pbBuffer[m_cbSize], pbData, cbData); m_cbSize += cbData; } else { /* Move any buffered data to the front. */ cbInBuf = m_cbSize - m_cbOffset; if (cbInBuf == 0) m_cbSize = m_cbOffset = 0; else if (m_cbOffset) /* Do we have something to move? */ { memmove(m_pbBuffer, &m_pbBuffer[m_cbOffset], cbInBuf); m_cbSize = cbInBuf; m_cbOffset = 0; } /* Do we need to grow the buffer? */ if (cbData + m_cbSize > m_cbAllocated) { size_t cbAlloc = m_cbSize + cbData; cbAlloc = RT_ALIGN_Z(cbAlloc, _64K); void *pvNew = RTMemRealloc(m_pbBuffer, cbAlloc); if (pvNew) { m_pbBuffer = (uint8_t *)pvNew; m_cbAllocated = cbAlloc; } else rc = VERR_NO_MEMORY; } /* Finally, copy the data. */ if (RT_SUCCESS(rc)) { if (cbData + m_cbSize <= m_cbAllocated) { memcpy(&m_pbBuffer[m_cbSize], pbData, cbData); m_cbSize += cbData; } else rc = VERR_BUFFER_OVERFLOW; } } return rc; } /** * Destroys the the internal data buffer. */ void GuestProcessStream::Destroy() { if (m_pbBuffer) { RTMemFree(m_pbBuffer); m_pbBuffer = NULL; } m_cbAllocated = 0; m_cbSize = 0; m_cbOffset = 0; } #ifdef DEBUG void GuestProcessStream::Dump(const char *pszFile) { LogFlowFunc(("Dumping contents of stream=0x%p (cbAlloc=%u, cbSize=%u, cbOff=%u) to %s\n", m_pbBuffer, m_cbAllocated, m_cbSize, m_cbOffset, pszFile)); RTFILE hFile; int rc = RTFileOpen(&hFile, pszFile, RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE); if (RT_SUCCESS(rc)) { rc = RTFileWrite(hFile, m_pbBuffer, m_cbSize, NULL /* pcbWritten */); RTFileClose(hFile); } } #endif /** * Returns the current offset of the parser within * the internal data buffer. * * @return uint32_t Parser offset. */ uint32_t GuestProcessStream::GetOffset() { return m_cbOffset; } uint32_t GuestProcessStream::GetSize() { return m_cbSize; } /** * Tries to parse the next upcoming pair block within the internal * buffer. * * Returns VERR_NO_DATA is no data is in internal buffer or buffer has been * completely parsed already. * * Returns VERR_MORE_DATA if current block was parsed (with zero or more pairs * stored in stream block) but still contains incomplete (unterminated) * data. * * Returns VINF_SUCCESS if current block was parsed until the next upcoming * block (with zero or more pairs stored in stream block). * * @return IPRT status code. * @param streamBlock Reference to guest stream block to fill. * */ int GuestProcessStream::ParseBlock(GuestProcessStreamBlock &streamBlock) { if ( !m_pbBuffer || !m_cbSize) { return VERR_NO_DATA; } AssertReturn(m_cbOffset <= m_cbSize, VERR_INVALID_PARAMETER); if (m_cbOffset == m_cbSize) return VERR_NO_DATA; int rc = VINF_SUCCESS; char *pszOff = (char*)&m_pbBuffer[m_cbOffset]; char *pszStart = pszOff; uint32_t uDistance; while (*pszStart) { size_t pairLen = strlen(pszStart); uDistance = (pszStart - pszOff); if (m_cbOffset + uDistance + pairLen + 1 >= m_cbSize) { rc = VERR_MORE_DATA; break; } else { char *pszSep = strchr(pszStart, '='); char *pszVal = NULL; if (pszSep) pszVal = pszSep + 1; if (!pszSep || !pszVal) { rc = VERR_MORE_DATA; break; } /* Terminate the separator so that we can * use pszStart as our key from now on. */ *pszSep = '\0'; rc = streamBlock.SetValue(pszStart, pszVal); if (RT_FAILURE(rc)) return rc; } /* Next pair. */ pszStart += pairLen + 1; } /* If we did not do any movement but we have stuff left * in our buffer just skip the current termination so that * we can try next time. */ uDistance = (pszStart - pszOff); if ( !uDistance && *pszStart == '\0' && m_cbOffset < m_cbSize) { uDistance++; } m_cbOffset += uDistance; return rc; }