/* -*- 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 #include #include #include #include "sot/stg.hxx" #include "stgelem.hxx" #include "stgcache.hxx" #include "stgstrms.hxx" #include "stgdir.hxx" #include "stgio.hxx" static const sal_uInt16 nMaxLegalStr = 31; static const sal_uInt8 cStgSignature[ 8 ] = { 0xD0,0xCF,0x11,0xE0,0xA1,0xB1,0x1A,0xE1 }; ////////////////////////////// struct ClsId SvStream& ReadClsId( SvStream& r, ClsId& rId ) { r.ReadUInt32( rId.Data1 ) .ReadUInt16( rId.Data2 ) .ReadUInt16( rId.Data3 ) .ReadUChar( rId.Data4[0] ) .ReadUChar( rId.Data4[1] ) .ReadUChar( rId.Data4[2] ) .ReadUChar( rId.Data4[3] ) .ReadUChar( rId.Data4[4] ) .ReadUChar( rId.Data4[5] ) .ReadUChar( rId.Data4[6] ) .ReadUChar( rId.Data4[7] ); return r; } SvStream& WriteClsId( SvStream& r, const ClsId& rId ) { return r .WriteUInt32( rId.Data1 ) .WriteUInt16( rId.Data2 ) .WriteUInt16( rId.Data3 ) .WriteUChar( rId.Data4[0] ) .WriteUChar( rId.Data4[1] ) .WriteUChar( rId.Data4[2] ) .WriteUChar( rId.Data4[3] ) .WriteUChar( rId.Data4[4] ) .WriteUChar( rId.Data4[5] ) .WriteUChar( rId.Data4[6] ) .WriteUChar( rId.Data4[7] ); } ///////////////////////////// class StgHeader StgHeader::StgHeader() : m_nVersion( 0 ) , m_nByteOrder( 0 ) , m_nPageSize( 0 ) , m_nDataPageSize( 0 ) , m_bDirty( sal_uInt8(false) ) , m_nFATSize( 0 ) , m_nTOCstrm( 0 ) , m_nReserved( 0 ) , m_nThreshold( 0 ) , m_nDataFAT( 0 ) , m_nDataFATSize( 0 ) , m_nMasterChain( 0 ) , m_nMaster( 0 ) { memset( m_cSignature, 0, sizeof( m_cSignature ) ); memset( &m_aClsId, 0, sizeof( ClsId ) ); memset( m_cReserved, 0, sizeof( m_cReserved ) ); memset( m_nMasterFAT, 0, sizeof( m_nMasterFAT ) ); } void StgHeader::Init() { memcpy( m_cSignature, cStgSignature, 8 ); memset( &m_aClsId, 0, sizeof( ClsId ) ); m_nVersion = 0x0003003B; m_nByteOrder = 0xFFFE; m_nPageSize = 9; // 512 bytes m_nDataPageSize = 6; // 64 bytes m_bDirty = sal_uInt8(false); memset( m_cReserved, 0, sizeof( m_cReserved ) ); m_nFATSize = 0; m_nTOCstrm = 0; m_nReserved = 0; m_nThreshold = 4096; m_nDataFAT = 0; m_nDataFATSize = 0; m_nMasterChain = STG_EOF; SetTOCStart( STG_EOF ); SetDataFATStart( STG_EOF ); for( short i = 0; i < cFATPagesInHeader; i++ ) SetFATPage( i, STG_FREE ); } bool StgHeader::Load( StgIo& rIo ) { bool bResult = false; if ( rIo.GetStrm() ) { SvStream& r = *rIo.GetStrm(); bResult = Load( r ); bResult = ( bResult && rIo.Good() ); } return bResult; } bool StgHeader::Load( SvStream& r ) { r.Seek( 0L ); r.ReadBytes( m_cSignature, 8 ); ReadClsId( r, m_aClsId ); // 08 Class ID r.ReadInt32( m_nVersion ) // 1A version number .ReadUInt16( m_nByteOrder ) // 1C Unicode byte order indicator .ReadInt16( m_nPageSize ) // 1E 1 << nPageSize = block size .ReadInt16( m_nDataPageSize ); // 20 1 << this size == data block size r.SeekRel( 10 ); r.ReadInt32( m_nFATSize ) // 2C total number of FAT pages .ReadInt32( m_nTOCstrm ) // 30 starting page for the TOC stream .ReadInt32( m_nReserved ) // 34 .ReadInt32( m_nThreshold ) // 38 minimum file size for big data .ReadInt32( m_nDataFAT ) // 3C page # of 1st data FAT block .ReadInt32( m_nDataFATSize ) // 40 # of data FATpages .ReadInt32( m_nMasterChain ) // 44 chain to the next master block .ReadInt32( m_nMaster ); // 48 # of additional master blocks for(sal_Int32 & i : m_nMasterFAT) r.ReadInt32( i ); return (r.GetErrorCode() == ERRCODE_NONE) && Check(); } bool StgHeader::Store( StgIo& rIo ) { if( !m_bDirty ) return true; SvStream& r = *rIo.GetStrm(); r.Seek( 0L ); r.WriteBytes( m_cSignature, 8 ); WriteClsId( r, m_aClsId ); // 08 Class ID r.WriteInt32( m_nVersion ) // 1A version number .WriteUInt16( m_nByteOrder ) // 1C Unicode byte order indicator .WriteInt16( m_nPageSize ) // 1E 1 << nPageSize = block size .WriteInt16( m_nDataPageSize ) // 20 1 << this size == data block size .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt16( 0 ) .WriteInt32( m_nFATSize ) // 2C total number of FAT pages .WriteInt32( m_nTOCstrm ) // 30 starting page for the TOC stream .WriteInt32( m_nReserved ) // 34 .WriteInt32( m_nThreshold ) // 38 minimum file size for big data .WriteInt32( m_nDataFAT ) // 3C page # of 1st data FAT block .WriteInt32( m_nDataFATSize ) // 40 # of data FAT pages .WriteInt32( m_nMasterChain ) // 44 chain to the next master block .WriteInt32( m_nMaster ); // 48 # of additional master blocks for(sal_Int32 i : m_nMasterFAT) r.WriteInt32( i ); m_bDirty = sal_uInt8(!rIo.Good()); return !m_bDirty; } static bool lcl_wontoverflow(short shift) { return shift >= 0 && shift < (short)sizeof(short) * 8 - 1; } static bool isKnownSpecial(sal_Int32 nLocation) { return (nLocation == STG_FREE || nLocation == STG_EOF || nLocation == STG_FAT || nLocation == STG_MASTER); } // Perform thorough checks also on unknown variables bool StgHeader::Check() { return memcmp( m_cSignature, cStgSignature, 8 ) == 0 && (short) ( m_nVersion >> 16 ) == 3 && m_nPageSize == 9 && lcl_wontoverflow(m_nPageSize) && lcl_wontoverflow(m_nDataPageSize) && m_nFATSize > 0 && m_nTOCstrm >= 0 && m_nThreshold > 0 && ( isKnownSpecial(m_nDataFAT) || ( m_nDataFAT >= 0 && m_nDataFATSize > 0 ) ) && ( isKnownSpecial(m_nMasterChain) || m_nMasterChain >=0 ) && m_nMaster >= 0; } sal_Int32 StgHeader::GetFATPage( short n ) const { if( n >= 0 && n < cFATPagesInHeader ) return m_nMasterFAT[ n ]; else return STG_EOF; } void StgHeader::SetFATPage( short n, sal_Int32 nb ) { if( n >= 0 && n < cFATPagesInHeader ) { if( m_nMasterFAT[ n ] != nb ) { m_bDirty = sal_uInt8(true); m_nMasterFAT[ n ] = nb; } } } void StgHeader::SetTOCStart( sal_Int32 n ) { if( n != m_nTOCstrm ) { m_bDirty = sal_uInt8(true); m_nTOCstrm = n; } } void StgHeader::SetDataFATStart( sal_Int32 n ) { if( n != m_nDataFAT ) { m_bDirty = sal_uInt8(true); m_nDataFAT = n; } } void StgHeader::SetDataFATSize( sal_Int32 n ) { if( n != m_nDataFATSize ) { m_bDirty = sal_uInt8(true); m_nDataFATSize = n; } } void StgHeader::SetFATSize( sal_Int32 n ) { if( n != m_nFATSize ) { m_bDirty = sal_uInt8(true); m_nFATSize = n; } } void StgHeader::SetFATChain( sal_Int32 n ) { if( n != m_nMasterChain ) { m_bDirty = sal_uInt8(true); m_nMasterChain = n; } } void StgHeader::SetMasters( sal_Int32 n ) { if( n != m_nMaster ) { m_bDirty = sal_uInt8(true); m_nMaster = n; } } ///////////////////////////// class StgEntry void StgEntry::Init() { memset( m_nName, 0, sizeof( m_nName ) ); m_nNameLen = 0; m_cType = 0; m_cFlags = 0; m_nLeft = 0; m_nRight = 0; m_nChild = 0; memset( &m_aClsId, 0, sizeof( m_aClsId ) ); m_nFlags = 0; m_nMtime[0] = 0; m_nMtime[1] = 0; m_nAtime[0] = 0; m_nAtime[1] = 0; m_nPage1 = 0; m_nSize = 0; m_nUnknown = 0; SetLeaf( STG_LEFT, STG_FREE ); SetLeaf( STG_RIGHT, STG_FREE ); SetLeaf( STG_CHILD, STG_FREE ); SetLeaf( STG_DATA, STG_EOF ); } static OUString ToUpperUnicode( const OUString & rStr ) { // I don't know the locale, so en_US is hopefully fine static CharClass aCC( LanguageTag( css::lang::Locale( "en", "US", "" )) ); return aCC.uppercase( rStr ); } bool StgEntry::SetName( const OUString& rName ) { // I don't know the locale, so en_US is hopefully fine m_aName = ToUpperUnicode( rName ); if(m_aName.getLength() > nMaxLegalStr) { m_aName = m_aName.copy(0, nMaxLegalStr); } sal_Int32 i; for( i = 0; i < rName.getLength() && i <= nMaxLegalStr; i++ ) { m_nName[ i ] = rName[ i ]; } while (i <= nMaxLegalStr) { m_nName[ i++ ] = 0; } m_nNameLen = ( rName.getLength() + 1 ) << 1; return true; } sal_Int32 StgEntry::GetLeaf( StgEntryRef eRef ) const { sal_Int32 n = -1; switch( eRef ) { case STG_LEFT: n = m_nLeft; break; case STG_RIGHT: n = m_nRight; break; case STG_CHILD: n = m_nChild; break; case STG_DATA: n = m_nPage1; break; } return n; } void StgEntry::SetLeaf( StgEntryRef eRef, sal_Int32 nPage ) { switch( eRef ) { case STG_LEFT: m_nLeft = nPage; break; case STG_RIGHT: m_nRight = nPage; break; case STG_CHILD: m_nChild = nPage; break; case STG_DATA: m_nPage1 = nPage; break; } } void StgEntry::SetClassId( const ClsId& r ) { memcpy( &m_aClsId, &r, sizeof( ClsId ) ); } void StgEntry::GetName( OUString& rName ) const { sal_uInt16 n = m_nNameLen; if( n ) n = ( n >> 1 ) - 1; rName = OUString(m_nName, n); } // Compare two entries. Do this case-insensitive. sal_Int32 StgEntry::Compare( const StgEntry& r ) const { if (r.m_nNameLen != m_nNameLen) return r.m_nNameLen > m_nNameLen ? 1 : -1; else return r.m_aName.compareTo(m_aName); } // These load/store operations are a bit more complicated, // since they have to copy their contents into a packed structure. bool StgEntry::Load(const void* pFrom, sal_uInt32 nBufSize, sal_uInt64 nUnderlyingStreamSize) { if ( nBufSize < 128 ) return false; SvMemoryStream r( const_cast(pFrom), nBufSize, StreamMode::READ ); for(sal_Unicode & i : m_nName) r.ReadUtf16( i ); // 00 name as WCHAR r.ReadUInt16( m_nNameLen ) // 40 size of name in bytes including 00H .ReadUChar( m_cType ) // 42 entry type .ReadUChar( m_cFlags ) // 43 0 or 1 (tree balance?) .ReadInt32( m_nLeft ) // 44 left node entry .ReadInt32( m_nRight ) // 48 right node entry .ReadInt32( m_nChild ); // 4C 1st child entry if storage ReadClsId( r, m_aClsId ); // 50 class ID (optional) r.ReadInt32( m_nFlags ) // 60 state flags(?) .ReadInt32( m_nMtime[ 0 ] ) // 64 modification time .ReadInt32( m_nMtime[ 1 ] ) // 64 modification time .ReadInt32( m_nAtime[ 0 ] ) // 6C creation and access time .ReadInt32( m_nAtime[ 1 ] ) // 6C creation and access time .ReadInt32( m_nPage1 ) // 74 starting block (either direct or translated) .ReadInt32( m_nSize ) // 78 file size .ReadInt32( m_nUnknown ); // 7C unknown sal_uInt16 n = m_nNameLen; if( n ) n = ( n >> 1 ) - 1; if (n > nMaxLegalStr) return false; if (m_cType != STG_STORAGE) { if (m_nPage1 < 0 && !isKnownSpecial(m_nPage1)) { //bad pageid return false; } if (m_nSize < 0) { // the size makes no sense for the substorage // TODO/LATER: actually the size should be an unsigned value, but // in this case it would mean a stream of more than 2Gb return false; } if (static_cast(m_nSize) > nUnderlyingStreamSize) { // surely an entry cannot be larger than the underlying file return false; } } m_aName = OUString(m_nName , n); // I don't know the locale, so en_US is hopefully fine m_aName = ToUpperUnicode( m_aName ); if(m_aName.getLength() > nMaxLegalStr) { m_aName = m_aName.copy(0, nMaxLegalStr); } return true; } void StgEntry::Store( void* pTo ) { SvMemoryStream r( pTo, 128, StreamMode::WRITE ); for(sal_Unicode i : m_nName) r.WriteUInt16( i ); // 00 name as WCHAR r.WriteUInt16( m_nNameLen ) // 40 size of name in bytes including 00H .WriteUChar( m_cType ) // 42 entry type .WriteUChar( m_cFlags ) // 43 0 or 1 (tree balance?) .WriteInt32( m_nLeft ) // 44 left node entry .WriteInt32( m_nRight ) // 48 right node entry .WriteInt32( m_nChild ); // 4C 1st child entry if storage; WriteClsId( r, m_aClsId ); // 50 class ID (optional) r.WriteInt32( m_nFlags ) // 60 state flags(?) .WriteInt32( m_nMtime[ 0 ] ) // 64 modification time .WriteInt32( m_nMtime[ 1 ] ) // 64 modification time .WriteInt32( m_nAtime[ 0 ] ) // 6C creation and access time .WriteInt32( m_nAtime[ 1 ] ) // 6C creation and access time .WriteInt32( m_nPage1 ) // 74 starting block (either direct or translated) .WriteInt32( m_nSize ) // 78 file size .WriteInt32( m_nUnknown ); // 7C unknown } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */