lok: vcl: fix multiple floatwin removal case more robustly.
[LibreOffice.git] / sot / source / sdstor / stgio.cxx
blobe41c3d356ce42da77fba4b6e87b3ea51d8d79f46
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
25 #include "stgdir.hxx"
26 #include "stgio.hxx"
27 #include <o3tl/safeint.hxx>
28 #include <rtl/instance.hxx>
29 #include <sal/log.hxx>
31 #include <memory>
33 ///////////////////////////// class StgIo
35 // This class holds the storage header and all internal streams.
37 StgIo::StgIo() : StgCache()
39 m_pTOC = nullptr;
40 m_pDataFAT = nullptr;
41 m_pDataStrm = nullptr;
42 m_pFAT = nullptr;
43 m_bCopied = false;
46 StgIo::~StgIo()
48 delete m_pTOC;
49 delete m_pDataFAT;
50 delete m_pDataStrm;
51 delete m_pFAT;
54 // Load the header. Do not set an error code if the header is invalid.
56 bool StgIo::Load()
58 if( GetStrm() )
60 if( m_aHdr.Load( *this ) )
62 if( m_aHdr.Check() )
63 SetupStreams();
64 else
65 return false;
67 else
68 return false;
70 return Good();
73 // Set up an initial, empty storage
75 bool StgIo::Init()
77 m_aHdr.Init();
78 SetupStreams();
79 return CommitAll();
82 void StgIo::SetupStreams()
84 delete m_pTOC;
85 delete m_pDataFAT;
86 delete m_pDataStrm;
87 delete m_pFAT;
88 m_pTOC = nullptr;
89 m_pDataFAT = nullptr;
90 m_pDataStrm = nullptr;
91 m_pFAT = nullptr;
92 ResetError();
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);
101 m_pFAT = nullptr;
102 m_pTOC = nullptr;
103 return;
106 m_pFAT = new StgFATStrm(*this, nFatStrmSize);
107 m_pTOC = new StgDirStrm(*this);
108 if( !GetError() )
110 StgDirEntry* pRoot = m_pTOC->GetRoot();
111 if( pRoot )
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 );
119 else
120 SetError( SVSTREAM_FILEFORMAT_ERROR );
124 // get the logical data page size
126 short StgIo::GetDataPageSize() const
128 return 1 << m_aHdr.GetDataPageSize();
131 // Commit everything
133 bool StgIo::CommitAll()
135 // Store the data (all streams and the TOC)
136 if( m_pTOC && m_pTOC->Store() && m_pDataFAT )
138 if( Commit() )
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 ) )
145 GetStrm()->Flush();
146 const ErrCode n = GetStrm()->GetError();
147 SetError( n );
148 #ifdef DBG_UTIL
149 if( n==ERRCODE_NONE ) ValidateFATs();
150 #endif
151 return n == ERRCODE_NONE;
155 SetError( SVSTREAM_WRITE_ERROR );
156 return false;
160 class EasyFat
162 std::unique_ptr<sal_Int32[]> pFat;
163 std::unique_ptr<bool[]> pFree;
164 sal_Int32 nPages;
165 sal_Int32 nPageSize;
167 public:
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;
179 nPageSize = nPSize;
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 )
205 return true;
207 return false;
210 FatError EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect )
212 if( nCount > 0 )
214 --nCount;
215 nCount /= GetPageSize();
216 ++nCount;
219 sal_Int32 nCurPage = nPage;
220 while( nCount != 0 )
222 if( nCurPage < 0 || nCurPage >= nPages )
223 return FatError::OutOfBounds;
224 pFree[ nCurPage ] = false;
225 nCurPage = pFat[ nCurPage ];
226 // stream too long
227 if( nCurPage != nExpect && nCount == 1 )
228 return FatError::WrongLength;
229 // stream too short
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 )
234 nCount = 1;
235 if( nCount != -1 )
236 nCount--;
238 return FatError::Ok;
241 class Validator
243 FatError nError;
245 EasyFat aSmallFat;
246 EasyFat aFat;
248 StgIo &rIo;
250 FatError ValidateMasterFATs();
251 FatError ValidateDirectoryEntries();
252 FatError FindUnrefedChains() const;
253 FatError MarkAll( StgDirEntry *pEntry );
255 public:
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() ),
263 rIo( rIoP )
265 FatError nErr = nError = FatError::Ok;
267 if( ( nErr = ValidateMasterFATs() ) != FatError::Ok )
268 nError = nErr;
269 else if( ( nErr = ValidateDirectoryEntries() ) != FatError::Ok )
270 nError = nErr;
271 else if( ( nErr = FindUnrefedChains()) != FatError::Ok )
272 nError = nErr;
275 FatError Validator::ValidateMasterFATs()
277 sal_Int32 nCount = rIo.m_aHdr.GetFATSize();
278 FatError nErr;
279 if ( !rIo.m_pFAT )
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)
285 return nErr;
287 if( rIo.m_aHdr.GetMasters() )
288 if( ( nErr = aFat.Mark(rIo.m_aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FatError::Ok )
289 return nErr;
291 return FatError::Ok;
294 FatError Validator::MarkAll( StgDirEntry *pEntry )
296 if ( !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 )
305 nErr = MarkAll( p );
306 if( nErr != FatError::Ok )
307 return nErr;
309 else
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 );
314 else
315 nErr = aFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
316 if( nErr != FatError::Ok )
317 return nErr;
320 return FatError::Ok;
323 FatError Validator::ValidateDirectoryEntries()
325 if ( !rIo.m_pTOC )
326 return FatError::InMemoryError;
328 // Normal DirEntries
329 FatError nErr = MarkAll( rIo.m_pTOC->GetRoot() );
330 if( nErr != FatError::Ok )
331 return nErr;
332 // Small Data
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 )
336 return nErr;
337 // Small Data FAT
338 nErr = aFat.Mark(
339 rIo.m_aHdr.GetDataFATStart(),
340 rIo.m_aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
341 if( nErr != FatError::Ok )
342 return nErr;
343 // TOC
344 nErr = aFat.Mark(
345 rIo.m_aHdr.GetTOCStart(), -1, -2 );
346 return nErr;
349 FatError Validator::FindUnrefedChains() const
351 if( aSmallFat.HasUnrefChains() ||
352 aFat.HasUnrefChains() )
353 return FatError::UnrefChain;
354 else
355 return FatError::Ok;
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()
372 if( m_bFile )
374 std::unique_ptr<Validator> pV(new Validator( *this ));
375 bool bRet1 = !pV->IsError(), bRet2 = true ;
376 pV.reset();
378 SvFileStream *pFileStrm = static_cast<SvFileStream *>( GetStrm() );
379 if ( !pFileStrm )
380 return FatError::InMemoryError;
382 StgIo aIo;
383 if( aIo.Open( pFileStrm->GetFileName(),
384 StreamMode::READ | StreamMode::SHARE_DENYNONE) &&
385 aIo.Load() )
387 pV.reset(new Validator( aIo ));
388 bRet2 = !pV->IsError();
389 pV.reset();
392 FatError nErr;
393 if( bRet1 != bRet2 )
394 nErr = bRet1 ? FatError::OnFileError : FatError::InMemoryError;
395 else nErr = bRet1 ? FatError::Ok : FatError::BothError;
396 if( nErr != FatError::Ok && !m_bCopied )
398 StgLinkArg aArg;
399 aArg.aFile = pFileStrm->GetFileName();
400 ErrorLink::get().Call( aArg );
401 m_bCopied = true;
403 // DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
404 return nErr;
406 // OSL_FAIL("Do not validate (no FileStorage)");
407 return FatError::Ok;
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */