summaryrefslogtreecommitdiffstats
path: root/sot/source/sdstor/stgio.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sot/source/sdstor/stgio.cxx')
-rw-r--r--sot/source/sdstor/stgio.cxx400
1 files changed, 400 insertions, 0 deletions
diff --git a/sot/source/sdstor/stgio.cxx b/sot/source/sdstor/stgio.cxx
new file mode 100644
index 000000000..604d08282
--- /dev/null
+++ b/sot/source/sdstor/stgio.cxx
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * 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 "stgelem.hxx"
+#include "stgcache.hxx"
+#include "stgstrms.hxx"
+#include "stgdir.hxx"
+#include "stgio.hxx"
+#include <o3tl/safeint.hxx>
+#include <sal/log.hxx>
+
+#include <memory>
+#include <optional>
+
+///////////////////////////// class StgIo
+
+// This class holds the storage header and all internal streams.
+
+StgIo::StgIo()
+{
+ m_pTOC = nullptr;
+ m_pDataFAT = nullptr;
+ m_pDataStrm = nullptr;
+ m_pFAT = nullptr;
+ m_bCopied = false;
+}
+
+StgIo::~StgIo()
+{
+ delete m_pTOC;
+ delete m_pDataFAT;
+ delete m_pDataStrm;
+ delete m_pFAT;
+}
+
+// Load the header. Do not set an error code if the header is invalid.
+
+bool StgIo::Load()
+{
+ if( GetStrm() )
+ {
+ if( m_aHdr.Load( *this ) )
+ {
+ if( m_aHdr.Check() )
+ SetupStreams();
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ return Good();
+}
+
+// Set up an initial, empty storage
+
+bool StgIo::Init()
+{
+ m_aHdr.Init();
+ SetupStreams();
+ return CommitAll();
+}
+
+void StgIo::SetupStreams()
+{
+ delete m_pTOC;
+ delete m_pDataFAT;
+ delete m_pDataStrm;
+ delete m_pFAT;
+ m_pTOC = nullptr;
+ m_pDataFAT = nullptr;
+ m_pDataStrm = nullptr;
+ m_pFAT = nullptr;
+ ResetError();
+
+ short nPhysPageSize = 1 << m_aHdr.GetPageSize();
+ SetPhysPageSize(nPhysPageSize);
+ sal_Int32 nFatStrmSize;
+ if (o3tl::checked_multiply<sal_Int32>(m_aHdr.GetFATSize(), nPhysPageSize, nFatStrmSize))
+ {
+ SAL_WARN("sot", "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow");
+ SetError(SVSTREAM_FILEFORMAT_ERROR);
+ m_pFAT = nullptr;
+ m_pTOC = nullptr;
+ return;
+ }
+
+ m_pFAT = new StgFATStrm(*this, nFatStrmSize);
+ m_pTOC = new StgDirStrm(*this);
+ if( GetError() )
+ return;
+
+ StgDirEntry* pRoot = m_pTOC->GetRoot();
+ if( pRoot )
+ {
+ m_pDataFAT = new StgDataStrm( *this, m_aHdr.GetDataFATStart(), -1 );
+ m_pDataStrm = new StgDataStrm( *this, *pRoot );
+ m_pDataFAT->SetIncrement( 1 << m_aHdr.GetPageSize() );
+ m_pDataStrm->SetIncrement( GetDataPageSize() );
+ m_pDataStrm->SetEntry( *pRoot );
+ }
+ else
+ SetError( SVSTREAM_FILEFORMAT_ERROR );
+}
+
+// get the logical data page size
+
+short StgIo::GetDataPageSize() const
+{
+ return 1 << m_aHdr.GetDataPageSize();
+}
+
+// Commit everything
+
+bool StgIo::CommitAll()
+{
+ // Store the data (all streams and the TOC)
+ if( m_pTOC && m_pTOC->Store() && m_pDataFAT )
+ {
+ if( Commit() )
+ {
+ m_aHdr.SetDataFATStart( m_pDataFAT->GetStart() );
+ m_aHdr.SetDataFATSize( m_pDataFAT->GetPages() );
+ m_aHdr.SetTOCStart( m_pTOC->GetStart() );
+ if( m_aHdr.Store( *this ) )
+ {
+ GetStrm()->Flush();
+ const ErrCode n = GetStrm()->GetError();
+ SetError( n );
+#ifdef DBG_UTIL
+ if( n==ERRCODE_NONE ) ValidateFATs();
+#endif
+ return n == ERRCODE_NONE;
+ }
+ }
+ }
+ SetError( SVSTREAM_WRITE_ERROR );
+ return false;
+}
+
+namespace {
+
+class EasyFat
+{
+ std::unique_ptr<sal_Int32[]> pFat;
+ std::unique_ptr<bool[]> pFree;
+ sal_Int32 nPages;
+ sal_Int32 nPageSize;
+
+public:
+ EasyFat( StgIo & rIo, StgStrm *pFatStream, sal_Int32 nPSize );
+
+ sal_Int32 GetPageSize() const { return nPageSize; }
+
+ FatError Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect );
+ bool HasUnrefChains() const;
+};
+
+}
+
+EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize )
+ : nPages(pFatStream->GetSize() >> 2), nPageSize(nPSize)
+{
+ pFat.reset( new sal_Int32[ nPages ] );
+ pFree.reset( new bool[ nPages ] );
+
+ rtl::Reference< StgPage > pPage;
+ sal_Int32 nFatPageSize = (1 << rIo.m_aHdr.GetPageSize()) - 2;
+
+ for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
+ {
+ if( ! (nPage % nFatPageSize) )
+ {
+ pFatStream->Pos2Page( nPage << 2 );
+ sal_Int32 nPhysPage = pFatStream->GetPage();
+ pPage = rIo.Get( nPhysPage, true );
+ }
+
+ pFat[ nPage ] = StgCache::GetFromPage( pPage, short( nPage % nFatPageSize ) );
+ pFree[ nPage ] = true;
+ }
+}
+
+bool EasyFat::HasUnrefChains() const
+{
+ for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
+ {
+ if( pFree[ nPage ] && pFat[ nPage ] != -1 )
+ return true;
+ }
+ return false;
+}
+
+FatError EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect )
+{
+ if( nCount > 0 )
+ {
+ --nCount;
+ nCount /= GetPageSize();
+ ++nCount;
+ }
+
+ sal_Int32 nCurPage = nPage;
+ while( nCount != 0 )
+ {
+ if( nCurPage < 0 || nCurPage >= nPages )
+ return FatError::OutOfBounds;
+ pFree[ nCurPage ] = false;
+ nCurPage = pFat[ nCurPage ];
+ // stream too long
+ if( nCurPage != nExpect && nCount == 1 )
+ return FatError::WrongLength;
+ // stream too short
+ if( nCurPage == nExpect && nCount != 1 && nCount != -1 )
+ return FatError::WrongLength;
+ // last block for stream without length
+ if( nCurPage == nExpect && nCount == -1 )
+ nCount = 1;
+ if( nCount != -1 )
+ nCount--;
+ }
+ return FatError::Ok;
+}
+
+namespace {
+
+class Validator
+{
+ FatError nError;
+
+ EasyFat aSmallFat;
+ EasyFat aFat;
+
+ StgIo &rIo;
+
+ FatError ValidateMasterFATs();
+ FatError ValidateDirectoryEntries();
+ FatError FindUnrefedChains() const;
+ FatError MarkAll( StgDirEntry *pEntry );
+
+public:
+ explicit Validator( StgIo &rIo );
+ bool IsError() const { return nError != FatError::Ok; }
+};
+
+}
+
+Validator::Validator( StgIo &rIoP )
+ : aSmallFat( rIoP, rIoP.m_pDataFAT, 1 << rIoP.m_aHdr.GetDataPageSize() ),
+ aFat( rIoP, rIoP.m_pFAT, 1 << rIoP.m_aHdr.GetPageSize() ),
+ rIo( rIoP )
+{
+ FatError nErr = nError = FatError::Ok;
+
+ if( ( nErr = ValidateMasterFATs() ) != FatError::Ok )
+ nError = nErr;
+ else if( ( nErr = ValidateDirectoryEntries() ) != FatError::Ok )
+ nError = nErr;
+ else if( ( nErr = FindUnrefedChains()) != FatError::Ok )
+ nError = nErr;
+}
+
+FatError Validator::ValidateMasterFATs()
+{
+ sal_Int32 nCount = rIo.m_aHdr.GetFATSize();
+ FatError nErr;
+ if ( !rIo.m_pFAT )
+ return FatError::InMemoryError;
+
+ for( sal_Int32 i = 0; i < nCount; i++ )
+ {
+ if( ( nErr = aFat.Mark(rIo.m_pFAT->GetPage(i, false), aFat.GetPageSize(), -3 )) != FatError::Ok)
+ return nErr;
+ }
+ if( rIo.m_aHdr.GetMasters() )
+ if( ( nErr = aFat.Mark(rIo.m_aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FatError::Ok )
+ return nErr;
+
+ return FatError::Ok;
+}
+
+FatError Validator::MarkAll( StgDirEntry *pEntry )
+{
+ if ( !pEntry )
+ return FatError::InMemoryError;
+
+ StgIterator aIter( *pEntry );
+ FatError nErr = FatError::Ok;
+ for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() )
+ {
+ if( p->m_aEntry.GetType() == STG_STORAGE )
+ {
+ nErr = MarkAll( p );
+ if( nErr != FatError::Ok )
+ return nErr;
+ }
+ else
+ {
+ sal_Int32 nSize = p->m_aEntry.GetSize();
+ if( nSize < rIo.m_aHdr.GetThreshold() )
+ nErr = aSmallFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
+ else
+ nErr = aFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
+ if( nErr != FatError::Ok )
+ return nErr;
+ }
+ }
+ return FatError::Ok;
+}
+
+FatError Validator::ValidateDirectoryEntries()
+{
+ if ( !rIo.m_pTOC )
+ return FatError::InMemoryError;
+
+ // Normal DirEntries
+ FatError nErr = MarkAll( rIo.m_pTOC->GetRoot() );
+ if( nErr != FatError::Ok )
+ return nErr;
+ // Small Data
+ nErr = aFat.Mark( rIo.m_pTOC->GetRoot()->m_aEntry.GetStartPage(),
+ rIo.m_pTOC->GetRoot()->m_aEntry.GetSize(), -2 );
+ if( nErr != FatError::Ok )
+ return nErr;
+ // Small Data FAT
+ nErr = aFat.Mark(
+ rIo.m_aHdr.GetDataFATStart(),
+ rIo.m_aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
+ if( nErr != FatError::Ok )
+ return nErr;
+ // TOC
+ nErr = aFat.Mark(
+ rIo.m_aHdr.GetTOCStart(), -1, -2 );
+ return nErr;
+}
+
+FatError Validator::FindUnrefedChains() const
+{
+ if( aSmallFat.HasUnrefChains() ||
+ aFat.HasUnrefChains() )
+ return FatError::UnrefChain;
+ else
+ return FatError::Ok;
+}
+
+FatError StgIo::ValidateFATs()
+{
+ if( m_bFile )
+ {
+ std::optional<Validator> pV( *this );
+ bool bRet1 = !pV->IsError(), bRet2 = true ;
+ pV.reset();
+
+ SvFileStream *pFileStrm = static_cast<SvFileStream *>( GetStrm() );
+ if ( !pFileStrm )
+ return FatError::InMemoryError;
+
+ StgIo aIo;
+ if( aIo.Open( pFileStrm->GetFileName(),
+ StreamMode::READ | StreamMode::SHARE_DENYNONE) &&
+ aIo.Load() )
+ {
+ pV.emplace( aIo );
+ bRet2 = !pV->IsError();
+ pV.reset();
+ }
+
+ FatError nErr;
+ if( bRet1 != bRet2 )
+ nErr = bRet1 ? FatError::OnFileError : FatError::InMemoryError;
+ else nErr = bRet1 ? FatError::Ok : FatError::BothError;
+ if( nErr != FatError::Ok && !m_bCopied )
+ {
+ m_bCopied = true;
+ }
+// DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
+ return nErr;
+ }
+// OSL_FAIL("Do not validate (no FileStorage)");
+ return FatError::Ok;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */