summaryrefslogtreecommitdiff
path: root/desktop/source/deployment/dp_persmap.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'desktop/source/deployment/dp_persmap.cxx')
-rw-r--r--desktop/source/deployment/dp_persmap.cxx546
1 files changed, 359 insertions, 187 deletions
diff --git a/desktop/source/deployment/dp_persmap.cxx b/desktop/source/deployment/dp_persmap.cxx
index 047ad5331e6c..d1d859290350 100644
--- a/desktop/source/deployment/dp_persmap.cxx
+++ b/desktop/source/deployment/dp_persmap.cxx
@@ -1,250 +1,422 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*************************************************************************
+/*
+ * This file is part of the LibreOffice project.
*
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
- * Copyright 2000, 2010 Oracle and/or its affiliates.
+ * This file incorporates work covered by the following license notice:
*
- * OpenOffice.org - a multi-platform office productivity suite
- *
- * This file is part of OpenOffice.org.
- *
- * OpenOffice.org is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 3
- * only, as published by the Free Software Foundation.
- *
- * OpenOffice.org is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License version 3 for more details
- * (a copy is included in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU Lesser General Public License
- * version 3 along with OpenOffice.org. If not, see
- * <http://www.openoffice.org/license.html>
- * for a copy of the LGPLv3 License.
- *
- ************************************************************************/
-
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
#include "dp_misc.h"
-#include "dp_ucb.h"
#include "dp_persmap.h"
#include "rtl/strbuf.hxx"
-#include "rtl/ustrbuf.hxx"
-#include "osl/file.hxx"
-#include "osl/thread.h"
+#ifndef DISABLE_BDB2PMAP
+# include <vector>
+#endif
-using namespace ::com::sun::star;
-using namespace ::com::sun::star::uno;
using namespace ::rtl;
-using ::osl::File;
+
+// the persistent map is used to manage a handful of key-value string pairs
+// this implementation replaces a rather heavy-weight berkeleydb integration
+
+// the file backing up a persistent map consists of line pairs with
+// - a key string (encoded with chars 0x00..0x0F being escaped)
+// - a value string (encoded with chars 0x00..0x0F being escaped)
namespace dp_misc
{
-//______________________________________________________________________________
-void PersistentMap::throw_rtexc( int err, char const * pmsg ) const
+static const char PmapMagic[4] = {'P','m','p','1'};
+
+PersistentMap::PersistentMap( OUString const & url_, bool readOnly )
+: m_MapFile( expandUnoRcUrl(url_) )
+, m_bReadOnly( readOnly )
+, m_bIsOpen( false )
+, m_bToBeCreated( !readOnly )
+, m_bIsDirty( false )
{
- OUStringBuffer buf;
- buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("[") );
- buf.append( m_sysPath );
- buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("] Berkeley Db error (") );
- buf.append( static_cast<sal_Int32>(err) );
- buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("): ") );
- if (pmsg == 0)
- pmsg = DbEnv::strerror(err);
- const OString msg(pmsg);
- buf.append( OUString( msg.getStr(), msg.getLength(),
- osl_getThreadTextEncoding() ) );
- const OUString msg_(buf.makeStringAndClear());
- OSL_FAIL( rtl::OUStringToOString(
- msg_, RTL_TEXTENCODING_UTF8 ).getStr() );
- throw RuntimeException( msg_, uno::Reference<XInterface>() );
+#ifndef DISABLE_BDB2PMAP
+ m_MapFileName = expandUnoRcUrl( url_ );
+#endif
+ open();
}
-//______________________________________________________________________________
+PersistentMap::PersistentMap()
+: m_MapFile( OUString() )
+, m_bReadOnly( false )
+, m_bIsOpen( false )
+, m_bToBeCreated( false )
+, m_bIsDirty( false )
+{}
+
PersistentMap::~PersistentMap()
{
- try {
- m_db.close(0);
- }
- catch (DbException & exc) {
- (void) exc; // avoid warnings
- OSL_FAIL( DbEnv::strerror( exc.get_errno() ) );
- }
+ if( m_bIsDirty )
+ flush();
+ if( m_bIsOpen )
+ m_MapFile.close();
}
-//______________________________________________________________________________
-PersistentMap::PersistentMap( OUString const & url )
- : m_db( 0 )
+
+// replace 0x00..0x0F with "%0".."%F"
+// replace "%" with "%%"
+static OString encodeString( const OString& rStr)
{
- try
+ const sal_Char* pChar = rStr.getStr();
+ const sal_Int32 nLen = rStr.getLength();
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
{
- rtl::OUString fileURL = expandUnoRcUrl(url);
- if ( File::getSystemPathFromFileURL( fileURL, m_sysPath ) != File::E_None )
- OSL_ASSERT( false );
-
- OString cstr_sysPath(
- OUStringToOString( m_sysPath, RTL_TEXTENCODING_UTF8 ) );
- int err = m_db.open(
- // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
- 0, cstr_sysPath.getStr(), 0, DB_HASH,
- DB_CREATE/* | DB_THREAD*/, 0664 /* fs mode */ );
- if (err != 0)
- throw_rtexc(err);
+ const sal_Char c = *(pChar++);
+ if( (0x00 <= c) && (c <= 0x0F))
+ break;
+ if( c == '%')
+ break;
}
- catch (const DbException & exc)
+ if( i < 0)
+ return rStr;
+
+ // escape chars 0x00..0x0F with "%0".."%F"
+ OStringBuffer aEncStr( nLen + 32);
+ aEncStr.append( pChar - (nLen-i), nLen - i);
+ while( --i >= 0)
{
- throw_rtexc( exc.get_errno(), exc.what() );
+ sal_Char c = *(pChar++);
+ if( (0x00 <= c) && (c <= 0x0F))
+ {
+ aEncStr.append( '%');
+ c += (c <= 0x09) ? '0' : 'A'-10;
+ } else if( c == '%')
+ aEncStr.append( '%');
+ aEncStr.append( c);
}
+
+ return aEncStr.makeStringAndClear();
}
-//______________________________________________________________________________
-PersistentMap::PersistentMap()
- : m_db( 0 )
+// replace "%0".."%F" with 0x00..0x0F
+// replace "%%" with "%"
+static OString decodeString( const sal_Char* pEncChars, int nLen)
{
- try {
- // xxx todo: DB_THREAD, DB_DBT_MALLOC currently not used
- int err = m_db.open( 0, 0, 0, DB_HASH, DB_CREATE/* | DB_THREAD*/, 0 );
- if (err != 0)
- throw_rtexc(err);
- }
- catch (DbException & exc) {
- throw_rtexc( exc.get_errno(), exc.what() );
+ const char* pChar = pEncChars;
+ sal_Int32 i = nLen;
+ // short circuit for the simple non-encoded case
+ while( --i >= 0)
+ if( *(pChar++) == '%')
+ break;
+ if( i < 0)
+ return OString( pEncChars, nLen);
+
+ // replace escaped chars with their decoded counterparts
+ OStringBuffer aDecStr( nLen);
+ pChar = pEncChars;
+ for( i = nLen; --i >= 0;)
+ {
+ sal_Char c = *(pChar++);
+ // handle escaped character
+ if( c == '%')
+ {
+ --i;
+ OSL_ASSERT( i >= 0);
+ c = *(pChar++);
+ if( ('0' <= c) && (c <= '9'))
+ c -= '0';
+ else
+ {
+ OSL_ASSERT( ('A' <= c) && (c <= 'F'));
+ c -= ('A'-10);
+ }
+ }
+ aDecStr.append( c);
}
+
+ return aDecStr.makeStringAndClear();
}
-//______________________________________________________________________________
-bool PersistentMap::has( OString const & key ) const
+bool PersistentMap::open()
{
- return get( 0, key );
-}
+ // open the existing file
+ sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read;
+ if( !m_bReadOnly)
+ nOpenFlags |= osl_File_OpenFlag_Write;
-// for 3 functions here MSVC gives C4702 "unreachable code" if optimization
-// is enabled and return is there and C4715 "not all control paths return
-// a value" if optimization disabled and no return...
-#ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable: 4702 )
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+
+ // or create later if needed
+ m_bToBeCreated &= (rcOpen == osl::File::E_NOENT) && !m_bIsOpen;
+
+#ifndef DISABLE_BDB2PMAP
+ if( m_bToBeCreated )
+ importFromBDB();
#endif
+ if( !m_bIsOpen)
+ return m_bToBeCreated;
+
+ return readAll();
+}
+
//______________________________________________________________________________
-bool PersistentMap::get( OString * value, OString const & key ) const
+bool PersistentMap::readAll()
{
- try {
- Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
- Dbt dbData;
- int err = m_db.get( 0, &dbKey, &dbData, 0 );
- if (err == DB_NOTFOUND)
+ // prepare for re-reading the map-file
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ (void)nRes;
+ m_entries.clear();
+
+ // read header and check magic
+ char aHeaderBytes[ sizeof(PmapMagic)];
+ sal_uInt64 nBytesRead = 0;
+ m_MapFile.read( aHeaderBytes, sizeof(aHeaderBytes), nBytesRead);
+ OSL_ASSERT( nBytesRead == sizeof(aHeaderBytes));
+ if( nBytesRead != sizeof(aHeaderBytes))
+ return false;
+ // check header magic
+ for( int i = 0; i < (int)sizeof(PmapMagic); ++i)
+ if( aHeaderBytes[i] != PmapMagic[i])
return false;
- if (err == 0) {
- if (value != 0) {
- *value = OString(
- static_cast< sal_Char const * >(dbData.get_data()),
- dbData.get_size() );
- }
- return true;
- }
- throw_rtexc(err);
- }
- catch (DbException & exc) {
- throw_rtexc( exc.get_errno(), exc.what() );
+
+ // read key value pairs and add them to the map
+ ByteSequence aKeyLine;
+ ByteSequence aValLine;
+ for(;;)
+ {
+ // read key-value line pair
+ // an empty key name indicates the end of the line pairs
+ if( m_MapFile.readLine( aKeyLine) != osl::File::E_None)
+ return false;
+ if( !aKeyLine.getLength())
+ break;
+ if( m_MapFile.readLine( aValLine) != osl::File::E_None)
+ return false;
+ // decode key and value strings
+ const OString aKeyName = decodeString( (sal_Char*)aKeyLine.getConstArray(), aKeyLine.getLength());
+ const OString aValName = decodeString( (sal_Char*)aValLine.getConstArray(), aValLine.getLength());
+ // insert key-value pair into map
+ add( aKeyName, aValName );
+ // check end-of-file status
+ sal_Bool bIsEOF = true;
+ if( m_MapFile.isEndOfFile( &bIsEOF) != osl::File::E_None )
+ return false;
+ if( bIsEOF )
+ break;
}
- return false; // avoiding warning
+
+ m_bIsDirty = false;
+ return true;
}
-//______________________________________________________________________________
-void PersistentMap::put( OString const & key, OString const & value )
+void PersistentMap::flush()
{
- try {
- Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
- Dbt dbData( const_cast< sal_Char * >(
- value.getStr()), value.getLength() );
- int err = m_db.put( 0, &dbKey, &dbData, 0 );
- if (err == 0) {
-#if OSL_DEBUG_LEVEL > 0
- OString v;
- OSL_ASSERT( get( &v, key ) );
- OSL_ASSERT( v.equals( value ) );
-#endif
- err = m_db.sync(0);
- }
- if (err != 0)
- throw_rtexc(err);
+ if( !m_bIsDirty)
+ return;
+ OSL_ASSERT( !m_bReadOnly);
+ if( m_bToBeCreated && !m_entries.empty())
+ {
+ const sal_uInt32 nOpenFlags = osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create;
+ const osl::File::RC rcOpen = m_MapFile.open( nOpenFlags);
+ m_bIsOpen = (rcOpen == osl::File::E_None);
+ m_bToBeCreated = !m_bIsOpen;
}
- catch (DbException & exc) {
- throw_rtexc( exc.get_errno(), exc.what() );
+ if( !m_bIsOpen)
+ return;
+
+ // write header magic
+ const osl::FileBase::RC nRes = m_MapFile.setPos( osl_Pos_Absolut, 0);
+ (void)nRes;
+ sal_uInt64 nBytesWritten = 0;
+ m_MapFile.write( PmapMagic, sizeof(PmapMagic), nBytesWritten);
+
+ // write key value pairs
+ t_string2string_map::const_iterator it = m_entries.begin();
+ for(; it != m_entries.end(); ++it) {
+ // write line for key
+ const OString aKeyString = encodeString( (*it).first);
+ const sal_Int32 nKeyLen = aKeyString.getLength();
+ m_MapFile.write( aKeyString.getStr(), nKeyLen, nBytesWritten);
+ OSL_ASSERT( nKeyLen == (sal_Int32)nBytesWritten);
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // write line for value
+ const OString& rValString = encodeString( (*it).second);
+ const sal_Int32 nValLen = rValString.getLength();
+ m_MapFile.write( rValString.getStr(), nValLen, nBytesWritten);
+ OSL_ASSERT( nValLen == (sal_Int32)nBytesWritten);
+ m_MapFile.write( "\n", 1, nBytesWritten);
}
+
+ // write a file delimiter (an empty key-string)
+ m_MapFile.write( "\n", 1, nBytesWritten);
+ // truncate file here
+ sal_uInt64 nNewFileSize;
+ if( m_MapFile.getPos( nNewFileSize) == osl::File::E_None)
+ m_MapFile.setSize( nNewFileSize);
+ // flush to disk
+ m_MapFile.sync();
+ // the in-memory map now matches to the file on disk
+ m_bIsDirty = false;
+}
+
+bool PersistentMap::has( OString const & key ) const
+{
+ return get( NULL, key );
+}
+
+bool PersistentMap::get( OString * value, OString const & key ) const
+{
+ t_string2string_map::const_iterator it = m_entries.find( key);
+ if( it == m_entries.end())
+ return false;
+ if( value)
+ *value = it->second;
+ return true;
+}
+
+void PersistentMap::add( OString const & key, OString const & value )
+{
+ if( m_bReadOnly)
+ return;
+ typedef std::pair<t_string2string_map::iterator,bool> InsertRC;
+ InsertRC r = m_entries.insert( t_string2string_map::value_type(key,value));
+ m_bIsDirty = r.second;
}
//______________________________________________________________________________
+void PersistentMap::put( OString const & key, OString const & value )
+{
+ add( key, value);
+ // HACK: flush now as the extension manager does not seem
+ // to properly destruct this object in some situations
+ if(m_bIsDirty)
+ flush();
+}
+
bool PersistentMap::erase( OString const & key, bool flush_immediately )
{
- try {
- Dbt dbKey( const_cast< sal_Char * >(key.getStr()), key.getLength() );
- int err = m_db.del( &dbKey, 0 );
- if (err == 0) {
- if (flush_immediately) {
- err = m_db.sync(0);
- if (err != 0)
- throw_rtexc(err);
- }
- return true;
- }
- if (err == DB_NOTFOUND)
- return false;
- throw_rtexc(err);
- }
- catch (DbException & exc) {
- throw_rtexc( exc.get_errno(), exc.what() );
- }
- return false; // avoiding warning
+ if( m_bReadOnly)
+ return false;
+ size_t nCount = m_entries.erase( key);
+ if( !nCount)
+ return false;
+ m_bIsDirty = true;
+ if( flush_immediately)
+ flush();
+ return true;
}
-//______________________________________________________________________________
t_string2string_map PersistentMap::getEntries() const
{
- try {
- Dbc * pcurs = 0;
- int err = m_db.cursor( 0, &pcurs, 0 );
- if (err != 0)
- throw_rtexc(err);
-
- t_string2string_map ret;
- for (;;) {
- Dbt dbKey, dbData;
- err = pcurs->get( &dbKey, &dbData, DB_NEXT );
- if (err == DB_NOTFOUND)
+ // TODO: return by const reference instead?
+ return m_entries;
+}
+
+#ifndef DISABLE_BDB2PMAP
+bool PersistentMap::importFromBDB()
+{
+ if( m_bReadOnly)
+ return false;
+
+ // get the name of its BDB counterpart
+ rtl::OUString aDBName = m_MapFileName;
+ if( !aDBName.endsWithAsciiL( ".pmap", 5))
+ return false;
+ aDBName = aDBName.replaceAt( aDBName.getLength()-5, 5, OUSTR(".db"));
+
+ // open the corresponding BDB file for reading
+ osl::File aDBFile( aDBName);
+ osl::File::RC rc = aDBFile.open( osl_File_OpenFlag_Read);
+ if( rc != osl::File::E_None)
+ return false;
+ sal_uInt64 nFileSize = 0;
+ if( aDBFile.getSize( nFileSize) != osl::File::E_None)
+ return false;
+
+ // read the BDB file
+ std::vector<sal_uInt8> aRawBDB( nFileSize);
+ for( sal_uInt64 nOfs = 0; nOfs < nFileSize;) {
+ sal_uInt64 nBytesRead = 0;
+ rc = aDBFile.read( (void*)&aRawBDB[nOfs], nFileSize - nOfs, nBytesRead);
+ if( (rc != osl::File::E_None) || !nBytesRead)
+ return false;
+ nOfs += nBytesRead;
+ }
+
+ // check BDB file header for non_encrypted Hash format v4..9
+ if( nFileSize < 0x0100)
+ return false;
+ if( aRawBDB[24] != 0) // only not-encrypted migration
+ return false;
+ if( aRawBDB[25] != 8) // we expect a P_HASHMETA page
+ return false;
+ const bool bLE = (aRawBDB[12]==0x61 && aRawBDB[13]==0x15 && aRawBDB[14]==0x06);
+ const bool bBE = (aRawBDB[15]==0x61 && aRawBDB[14]==0x15 && aRawBDB[13]==0x06);
+ if( bBE == bLE)
+ return false;
+ if( (aRawBDB[16] < 4) || (9 < aRawBDB[16])) // version
+ return false;
+ const sal_uInt64 nPgSize = bLE
+ ? (aRawBDB[20] + (aRawBDB[21]<<8) + (aRawBDB[22]<<16) + (aRawBDB[23]<<24))
+ : (aRawBDB[23] + (aRawBDB[22]<<8) + (aRawBDB[21]<<16) + (aRawBDB[20]<<24));
+ const int nPgCount = nFileSize / nPgSize;
+ if( nPgCount * nPgSize != nFileSize)
+ return false;
+
+ // find PackageManager's new_style entries
+ // using a simple heuristic for BDB_Hash pages
+ int nEntryCount = 0;
+ for( int nPgNo = 1; nPgNo < nPgCount; ++nPgNo) {
+ // parse the next _db_page
+ const sal_uInt8* const pPage = &aRawBDB[ nPgNo * nPgSize];
+ const sal_uInt8* const pEnd = pPage + nPgSize;
+ const int nHfOffset = bLE ? (pPage[22] + (pPage[23]<<8)) : (pPage[23] + (pPage[22]<<8));
+ if( nHfOffset <= 0)
+ continue;
+ const sal_uInt8* pCur = pPage + nHfOffset;
+ // iterate through the entries
+ for(; pCur < pEnd; ++pCur) {
+ if( pCur[0] != 0x01)
+ continue;
+ // get the value-candidate
+ const sal_uInt8* pVal = pCur + 1;
+ while( ++pCur < pEnd)
+ if( (*pCur < ' ') || ((*pCur > 0x7F) && (*pCur != 0xFF)))
+ break;
+ if( pCur >= pEnd)
break;
- if (err != 0)
- throw_rtexc(err);
+ if( (pCur[0] != 0x01) || (pCur[1] != 0xFF))
+ continue;
+ const OString aVal( (sal_Char*)pVal, pCur - pVal);
+ // get the key-candidate
+ const sal_uInt8* pKey = pCur + 1;
+ while( ++pCur < pEnd)
+ if( (*pCur < ' ') || ((*pCur > 0x7F) && (*pCur != 0xFF)))
+ break;
+ if( (pCur < pEnd) && (*pCur > 0x01))
+ continue;
+ const OString aKey( (sal_Char*)pKey, pCur - pKey);
+ --pCur; // prepare for next round by rewinding to end of key-string
-#if OSL_DEBUG_LEVEL > 0
- ::std::pair<t_string2string_map::iterator, bool> insertion =
-#endif
- ret.insert(
- t_string2string_map::value_type(
- OString( static_cast<sal_Char const*>(dbKey.get_data()), dbKey.get_size() ),
- OString( static_cast<sal_Char const*>(dbData.get_data()), dbData.get_size() )
- ) );
- OSL_ASSERT( insertion.second );
+ // add the key/value pair
+ add( aKey, aVal);
+ ++nEntryCount;
}
- err = pcurs->close();
- if (err != 0)
- throw_rtexc(err);
- return ret;
- }
- catch (DbException & exc) {
- throw_rtexc( exc.get_errno(), exc.what() );
}
- return t_string2string_map(); // avoiding warning
-}
-#ifdef _MSC_VER
-#pragma warning( pop )
-#endif
+ return (nEntryCount > 0);
+}
+#endif // DISABLE_BDB2PMAP
}