blob: 7fbff84b8b2a472f6235edd586b61ff8cd46feda [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sot.hxx"
#include "sot/stg.hxx"
#include "stgelem.hxx"
#include "stgcache.hxx"
#include "stgstrms.hxx"
#include "stgdir.hxx"
#include "stgio.hxx"
#include <rtl/instance.hxx>
///////////////////////////// class StgIo //////////////////////////////
// This class holds the storage header and all internal streams.
StgIo::StgIo() : StgCache()
{
pTOC = NULL;
pDataFAT = NULL;
pDataStrm = NULL;
pFAT = NULL;
bCopied = sal_False;
}
StgIo::~StgIo()
{
delete pTOC;
delete pDataFAT;
delete pDataStrm;
delete pFAT;
}
// Load the header. Do not set an error code if the header is invalid.
sal_Bool StgIo::Load()
{
if( pStrm )
{
if( aHdr.Load( *this ) )
{
if( aHdr.Check() )
SetupStreams();
else
return sal_False;
}
else
return sal_False;
}
return Good();
}
// Set up an initial, empty storage
sal_Bool StgIo::Init()
{
aHdr.Init();
SetupStreams();
return CommitAll();
}
void StgIo::SetupStreams()
{
delete pTOC;
delete pDataFAT;
delete pDataStrm;
delete pFAT;
pTOC = NULL;
pDataFAT = NULL;
pDataStrm = NULL;
pFAT = NULL;
ResetError();
SetPhysPageSize( 1 << aHdr.GetPageSize() );
pFAT = new StgFATStrm( *this );
pTOC = new StgDirStrm( *this );
if( !GetError() )
{
StgDirEntry* pRoot = pTOC->GetRoot();
if( pRoot )
{
pDataFAT = new StgDataStrm( *this, aHdr.GetDataFATStart(), -1 );
pDataStrm = new StgDataStrm( *this, *pRoot );
pDataFAT->SetIncrement( 1 << aHdr.GetPageSize() );
pDataStrm->SetIncrement( GetDataPageSize() );
pDataStrm->SetEntry( *pRoot );
}
else
SetError( SVSTREAM_FILEFORMAT_ERROR );
}
}
// get the logical data page size
short StgIo::GetDataPageSize()
{
return 1 << aHdr.GetDataPageSize();
}
// Commit everything
sal_Bool StgIo::CommitAll()
{
// Store the data (all streams and the TOC)
if( pTOC && pTOC->Store() && pDataFAT )
{
if( Commit( NULL ) )
{
aHdr.SetDataFATStart( pDataFAT->GetStart() );
aHdr.SetDataFATSize( pDataFAT->GetPages() );
aHdr.SetTOCStart( pTOC->GetStart() );
if( aHdr.Store( *this ) )
{
pStrm->Flush();
sal_uLong n = pStrm->GetError();
SetError( n );
#ifdef DBG_UTIL
if( n==0 ) ValidateFATs();
#endif
return sal_Bool( n == 0 );
}
}
}
SetError( SVSTREAM_WRITE_ERROR );
return sal_False;
}
class EasyFat
{
sal_Int32 *pFat;
sal_Bool *pFree;
sal_Int32 nPages;
sal_Int32 nPageSize;
public:
EasyFat( StgIo & rIo, StgStrm *pFatStream, sal_Int32 nPSize );
~EasyFat() { delete[] pFat; delete[] pFree; }
sal_Int32 GetPageSize() { return nPageSize; }
sal_Int32 Count() { return nPages; }
sal_Int32 operator[]( sal_Int32 nOffset )
{
OSL_ENSURE( nOffset >= 0 && nOffset < nPages, "Unexpected offset!" );
return nOffset >= 0 && nOffset < nPages ? pFat[ nOffset ] : -2;
}
sal_uLong Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect );
sal_Bool HasUnrefChains();
};
EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize )
{
nPages = pFatStream->GetSize() >> 2;
nPageSize = nPSize;
pFat = new sal_Int32[ nPages ];
pFree = new sal_Bool[ nPages ];
StgPage *pPage = NULL;
sal_Int32 nFatPageSize = (1 << rIo.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, sal_True );
}
pFat[ nPage ] = pPage->GetPage( short( nPage % nFatPageSize ) );
pFree[ nPage ] = sal_True;
}
}
sal_Bool EasyFat::HasUnrefChains()
{
for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
{
if( pFree[ nPage ] && pFat[ nPage ] != -1 )
return sal_True;
}
return sal_False;
}
sal_uLong EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect )
{
if( nCount > 0 )
--nCount /= GetPageSize(), nCount++;
sal_Int32 nCurPage = nPage;
while( nCount != 0 )
{
if( nCurPage < 0 || nCurPage >= nPages )
return FAT_OUTOFBOUNDS;
pFree[ nCurPage ] = sal_False;
nCurPage = pFat[ nCurPage ];
//Stream zu lang
if( nCurPage != nExpect && nCount == 1 )
return FAT_WRONGLENGTH;
//Stream zu kurz
if( nCurPage == nExpect && nCount != 1 && nCount != -1 )
return FAT_WRONGLENGTH;
// letzter Block bei Stream ohne Laenge
if( nCurPage == nExpect && nCount == -1 )
nCount = 1;
if( nCount != -1 )
nCount--;
}
return FAT_OK;
}
class Validator
{
sal_uLong nError;
EasyFat aSmallFat;
EasyFat aFat;
StgIo &rIo;
sal_uLong ValidateMasterFATs();
sal_uLong ValidateDirectoryEntries();
sal_uLong FindUnrefedChains();
sal_uLong MarkAll( StgDirEntry *pEntry );
public:
Validator( StgIo &rIo );
sal_Bool IsError() { return nError != 0; }
};
Validator::Validator( StgIo &rIoP )
: aSmallFat( rIoP, rIoP.pDataFAT, 1 << rIoP.aHdr.GetDataPageSize() ),
aFat( rIoP, rIoP.pFAT, 1 << rIoP.aHdr.GetPageSize() ),
rIo( rIoP )
{
sal_uLong nErr = nError = FAT_OK;
if( ( nErr = ValidateMasterFATs() ) != FAT_OK )
nError = nErr;
else if( ( nErr = ValidateDirectoryEntries() ) != FAT_OK )
nError = nErr;
else if( ( nErr = FindUnrefedChains()) != FAT_OK )
nError = nErr;
}
sal_uLong Validator::ValidateMasterFATs()
{
sal_Int32 nCount = rIo.aHdr.GetFATSize();
sal_uLong nErr;
if ( !rIo.pFAT )
return FAT_INMEMORYERROR;
for( sal_Int32 i = 0; i < nCount; i++ )
{
if( ( nErr = aFat.Mark(rIo.pFAT->GetPage( short(i), sal_False ), aFat.GetPageSize(), -3 )) != FAT_OK )
return nErr;
}
if( rIo.aHdr.GetMasters() )
if( ( nErr = aFat.Mark(rIo.aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FAT_OK )
return nErr;
return FAT_OK;
}
sal_uLong Validator::MarkAll( StgDirEntry *pEntry )
{
if ( !pEntry )
return FAT_INMEMORYERROR;
StgIterator aIter( *pEntry );
sal_uLong nErr = FAT_OK;
for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() )
{
if( p->aEntry.GetType() == STG_STORAGE )
{
nErr = MarkAll( p );
if( nErr != FAT_OK )
return nErr;
}
else
{
sal_Int32 nSize = p->aEntry.GetSize();
if( nSize < rIo.aHdr.GetThreshold() )
nErr = aSmallFat.Mark( p->aEntry.GetStartPage(),nSize, -2 );
else
nErr = aFat.Mark( p->aEntry.GetStartPage(),nSize, -2 );
if( nErr != FAT_OK )
return nErr;
}
}
return FAT_OK;
}
sal_uLong Validator::ValidateDirectoryEntries()
{
if ( !rIo.pTOC )
return FAT_INMEMORYERROR;
// Normale DirEntries
sal_uLong nErr = MarkAll( rIo.pTOC->GetRoot() );
if( nErr != FAT_OK )
return nErr;
// Small Data
nErr = aFat.Mark( rIo.pTOC->GetRoot()->aEntry.GetStartPage(),
rIo.pTOC->GetRoot()->aEntry.GetSize(), -2 );
if( nErr != FAT_OK )
return nErr;
// Small Data FAT
nErr = aFat.Mark(
rIo.aHdr.GetDataFATStart(),
rIo.aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
if( nErr != FAT_OK )
return nErr;
// TOC
nErr = aFat.Mark(
rIo.aHdr.GetTOCStart(), -1, -2 );
return nErr;
}
sal_uLong Validator::FindUnrefedChains()
{
if( aSmallFat.HasUnrefChains() ||
aFat.HasUnrefChains() )
return FAT_UNREFCHAIN;
else
return FAT_OK;
}
namespace { struct ErrorLink : public rtl::Static<Link, ErrorLink > {}; }
void StgIo::SetErrorLink( const Link& rLink )
{
ErrorLink::get() = rLink;
}
const Link& StgIo::GetErrorLink()
{
return ErrorLink::get();
}
sal_uLong StgIo::ValidateFATs()
{
if( bFile )
{
Validator *pV = new Validator( *this );
sal_Bool bRet1 = !pV->IsError(), bRet2 = sal_True ;
delete pV;
SvFileStream *pFileStrm = ( SvFileStream *) GetStrm();
if ( !pFileStrm )
return FAT_INMEMORYERROR;
StgIo aIo;
if( aIo.Open( pFileStrm->GetFileName(),
STREAM_READ | STREAM_SHARE_DENYNONE) &&
aIo.Load() )
{
pV = new Validator( aIo );
bRet2 = !pV->IsError();
delete pV;
}
sal_uLong nErr;
if( bRet1 != bRet2 )
nErr = bRet1 ? FAT_ONFILEERROR : FAT_INMEMORYERROR;
else nErr = bRet1 ? FAT_OK : FAT_BOTHERROR;
if( nErr != FAT_OK && !bCopied )
{
StgLinkArg aArg;
aArg.aFile = pFileStrm->GetFileName();
aArg.nErr = nErr;
ErrorLink::get().Call( &aArg );
bCopied = sal_True;
}
// DBG_ASSERT( nErr == FAT_OK ,"Storage kaputt");
return nErr;
}
// DBG_ERROR("Validiere nicht (kein FileStorage)");
return FAT_OK;
}