1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <sot/stg.hxx>
22 #include "stgelem.hxx"
23 #include "stgcache.hxx"
24 #include "stgstrms.hxx"
27 #include <o3tl/safeint.hxx>
28 #include <rtl/instance.hxx>
29 #include <sal/log.hxx>
33 ///////////////////////////// class StgIo
35 // This class holds the storage header and all internal streams.
37 StgIo::StgIo() : StgCache()
41 m_pDataStrm
= nullptr;
54 // Load the header. Do not set an error code if the header is invalid.
60 if( m_aHdr
.Load( *this ) )
73 // Set up an initial, empty storage
82 void StgIo::SetupStreams()
90 m_pDataStrm
= nullptr;
94 short nPhysPageSize
= 1 << m_aHdr
.GetPageSize();
95 SetPhysPageSize(nPhysPageSize
);
96 sal_Int32 nFatStrmSize
;
97 if (o3tl::checked_multiply
<sal_Int32
>(m_aHdr
.GetFATSize(), nPhysPageSize
, nFatStrmSize
))
99 SAL_WARN("sot", "Error: " << m_aHdr
.GetFATSize() << " * " << nPhysPageSize
<< " would overflow");
100 SetError(SVSTREAM_FILEFORMAT_ERROR
);
106 m_pFAT
= new StgFATStrm(*this, nFatStrmSize
);
107 m_pTOC
= new StgDirStrm(*this);
110 StgDirEntry
* pRoot
= m_pTOC
->GetRoot();
113 m_pDataFAT
= new StgDataStrm( *this, m_aHdr
.GetDataFATStart(), -1 );
114 m_pDataStrm
= new StgDataStrm( *this, *pRoot
);
115 m_pDataFAT
->SetIncrement( 1 << m_aHdr
.GetPageSize() );
116 m_pDataStrm
->SetIncrement( GetDataPageSize() );
117 m_pDataStrm
->SetEntry( *pRoot
);
120 SetError( SVSTREAM_FILEFORMAT_ERROR
);
124 // get the logical data page size
126 short StgIo::GetDataPageSize() const
128 return 1 << m_aHdr
.GetDataPageSize();
133 bool StgIo::CommitAll()
135 // Store the data (all streams and the TOC)
136 if( m_pTOC
&& m_pTOC
->Store() && m_pDataFAT
)
140 m_aHdr
.SetDataFATStart( m_pDataFAT
->GetStart() );
141 m_aHdr
.SetDataFATSize( m_pDataFAT
->GetPages() );
142 m_aHdr
.SetTOCStart( m_pTOC
->GetStart() );
143 if( m_aHdr
.Store( *this ) )
146 const ErrCode n
= GetStrm()->GetError();
149 if( n
==ERRCODE_NONE
) ValidateFATs();
151 return n
== ERRCODE_NONE
;
155 SetError( SVSTREAM_WRITE_ERROR
);
162 std::unique_ptr
<sal_Int32
[]> pFat
;
163 std::unique_ptr
<bool[]> pFree
;
168 EasyFat( StgIo
& rIo
, StgStrm
*pFatStream
, sal_Int32 nPSize
);
170 sal_Int32
GetPageSize() const { return nPageSize
; }
172 FatError
Mark( sal_Int32 nPage
, sal_Int32 nCount
, sal_Int32 nExpect
);
173 bool HasUnrefChains() const;
176 EasyFat::EasyFat( StgIo
& rIo
, StgStrm
* pFatStream
, sal_Int32 nPSize
)
178 nPages
= pFatStream
->GetSize() >> 2;
180 pFat
.reset( new sal_Int32
[ nPages
] );
181 pFree
.reset( new bool[ nPages
] );
183 rtl::Reference
< StgPage
> pPage
;
184 sal_Int32 nFatPageSize
= (1 << rIo
.m_aHdr
.GetPageSize()) - 2;
186 for( sal_Int32 nPage
= 0; nPage
< nPages
; nPage
++ )
188 if( ! (nPage
% nFatPageSize
) )
190 pFatStream
->Pos2Page( nPage
<< 2 );
191 sal_Int32 nPhysPage
= pFatStream
->GetPage();
192 pPage
= rIo
.Get( nPhysPage
, true );
195 pFat
[ nPage
] = StgCache::GetFromPage( pPage
, short( nPage
% nFatPageSize
) );
196 pFree
[ nPage
] = true;
200 bool EasyFat::HasUnrefChains() const
202 for( sal_Int32 nPage
= 0; nPage
< nPages
; nPage
++ )
204 if( pFree
[ nPage
] && pFat
[ nPage
] != -1 )
210 FatError
EasyFat::Mark( sal_Int32 nPage
, sal_Int32 nCount
, sal_Int32 nExpect
)
215 nCount
/= GetPageSize();
219 sal_Int32 nCurPage
= nPage
;
222 if( nCurPage
< 0 || nCurPage
>= nPages
)
223 return FatError::OutOfBounds
;
224 pFree
[ nCurPage
] = false;
225 nCurPage
= pFat
[ nCurPage
];
227 if( nCurPage
!= nExpect
&& nCount
== 1 )
228 return FatError::WrongLength
;
230 if( nCurPage
== nExpect
&& nCount
!= 1 && nCount
!= -1 )
231 return FatError::WrongLength
;
232 // last block for stream without length
233 if( nCurPage
== nExpect
&& nCount
== -1 )
250 FatError
ValidateMasterFATs();
251 FatError
ValidateDirectoryEntries();
252 FatError
FindUnrefedChains() const;
253 FatError
MarkAll( StgDirEntry
*pEntry
);
256 explicit Validator( StgIo
&rIo
);
257 bool IsError() const { return nError
!= FatError::Ok
; }
260 Validator::Validator( StgIo
&rIoP
)
261 : aSmallFat( rIoP
, rIoP
.m_pDataFAT
, 1 << rIoP
.m_aHdr
.GetDataPageSize() ),
262 aFat( rIoP
, rIoP
.m_pFAT
, 1 << rIoP
.m_aHdr
.GetPageSize() ),
265 FatError nErr
= nError
= FatError::Ok
;
267 if( ( nErr
= ValidateMasterFATs() ) != FatError::Ok
)
269 else if( ( nErr
= ValidateDirectoryEntries() ) != FatError::Ok
)
271 else if( ( nErr
= FindUnrefedChains()) != FatError::Ok
)
275 FatError
Validator::ValidateMasterFATs()
277 sal_Int32 nCount
= rIo
.m_aHdr
.GetFATSize();
280 return FatError::InMemoryError
;
282 for( sal_Int32 i
= 0; i
< nCount
; i
++ )
284 if( ( nErr
= aFat
.Mark(rIo
.m_pFAT
->GetPage(i
, false), aFat
.GetPageSize(), -3 )) != FatError::Ok
)
287 if( rIo
.m_aHdr
.GetMasters() )
288 if( ( nErr
= aFat
.Mark(rIo
.m_aHdr
.GetFATChain( ), aFat
.GetPageSize(), -4 )) != FatError::Ok
)
294 FatError
Validator::MarkAll( StgDirEntry
*pEntry
)
297 return FatError::InMemoryError
;
299 StgIterator
aIter( *pEntry
);
300 FatError nErr
= FatError::Ok
;
301 for( StgDirEntry
* p
= aIter
.First(); p
; p
= aIter
.Next() )
303 if( p
->m_aEntry
.GetType() == STG_STORAGE
)
306 if( nErr
!= FatError::Ok
)
311 sal_Int32 nSize
= p
->m_aEntry
.GetSize();
312 if( nSize
< rIo
.m_aHdr
.GetThreshold() )
313 nErr
= aSmallFat
.Mark( p
->m_aEntry
.GetStartPage(),nSize
, -2 );
315 nErr
= aFat
.Mark( p
->m_aEntry
.GetStartPage(),nSize
, -2 );
316 if( nErr
!= FatError::Ok
)
323 FatError
Validator::ValidateDirectoryEntries()
326 return FatError::InMemoryError
;
329 FatError nErr
= MarkAll( rIo
.m_pTOC
->GetRoot() );
330 if( nErr
!= FatError::Ok
)
333 nErr
= aFat
.Mark( rIo
.m_pTOC
->GetRoot()->m_aEntry
.GetStartPage(),
334 rIo
.m_pTOC
->GetRoot()->m_aEntry
.GetSize(), -2 );
335 if( nErr
!= FatError::Ok
)
339 rIo
.m_aHdr
.GetDataFATStart(),
340 rIo
.m_aHdr
.GetDataFATSize() * aFat
.GetPageSize(), -2 );
341 if( nErr
!= FatError::Ok
)
345 rIo
.m_aHdr
.GetTOCStart(), -1, -2 );
349 FatError
Validator::FindUnrefedChains() const
351 if( aSmallFat
.HasUnrefChains() ||
352 aFat
.HasUnrefChains() )
353 return FatError::UnrefChain
;
358 namespace { struct ErrorLink
: public rtl::Static
<Link
<StgLinkArg
&,void>, ErrorLink
> {}; }
360 void StgIo::SetErrorLink( const Link
<StgLinkArg
&,void>& rLink
)
362 ErrorLink::get() = rLink
;
365 const Link
<StgLinkArg
&,void>& StgIo::GetErrorLink()
367 return ErrorLink::get();
370 FatError
StgIo::ValidateFATs()
374 std::unique_ptr
<Validator
> pV(new Validator( *this ));
375 bool bRet1
= !pV
->IsError(), bRet2
= true ;
378 SvFileStream
*pFileStrm
= static_cast<SvFileStream
*>( GetStrm() );
380 return FatError::InMemoryError
;
383 if( aIo
.Open( pFileStrm
->GetFileName(),
384 StreamMode::READ
| StreamMode::SHARE_DENYNONE
) &&
387 pV
.reset(new Validator( aIo
));
388 bRet2
= !pV
->IsError();
394 nErr
= bRet1
? FatError::OnFileError
: FatError::InMemoryError
;
395 else nErr
= bRet1
? FatError::Ok
: FatError::BothError
;
396 if( nErr
!= FatError::Ok
&& !m_bCopied
)
399 aArg
.aFile
= pFileStrm
->GetFileName();
400 ErrorLink::get().Call( aArg
);
403 // DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
406 // OSL_FAIL("Do not validate (no FileStorage)");
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */