Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sot / source / sdstor / stgio.cxx
blob604d082828f7e6ac62c1c7262d03298b78c4a7c8
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 "stgelem.hxx"
22 #include "stgcache.hxx"
23 #include "stgstrms.hxx"
24 #include "stgdir.hxx"
25 #include "stgio.hxx"
26 #include <o3tl/safeint.hxx>
27 #include <sal/log.hxx>
29 #include <memory>
30 #include <optional>
32 ///////////////////////////// class StgIo
34 // This class holds the storage header and all internal streams.
36 StgIo::StgIo()
38 m_pTOC = nullptr;
39 m_pDataFAT = nullptr;
40 m_pDataStrm = nullptr;
41 m_pFAT = nullptr;
42 m_bCopied = false;
45 StgIo::~StgIo()
47 delete m_pTOC;
48 delete m_pDataFAT;
49 delete m_pDataStrm;
50 delete m_pFAT;
53 // Load the header. Do not set an error code if the header is invalid.
55 bool StgIo::Load()
57 if( GetStrm() )
59 if( m_aHdr.Load( *this ) )
61 if( m_aHdr.Check() )
62 SetupStreams();
63 else
64 return false;
66 else
67 return false;
69 return Good();
72 // Set up an initial, empty storage
74 bool StgIo::Init()
76 m_aHdr.Init();
77 SetupStreams();
78 return CommitAll();
81 void StgIo::SetupStreams()
83 delete m_pTOC;
84 delete m_pDataFAT;
85 delete m_pDataStrm;
86 delete m_pFAT;
87 m_pTOC = nullptr;
88 m_pDataFAT = nullptr;
89 m_pDataStrm = nullptr;
90 m_pFAT = nullptr;
91 ResetError();
93 short nPhysPageSize = 1 << m_aHdr.GetPageSize();
94 SetPhysPageSize(nPhysPageSize);
95 sal_Int32 nFatStrmSize;
96 if (o3tl::checked_multiply<sal_Int32>(m_aHdr.GetFATSize(), nPhysPageSize, nFatStrmSize))
98 SAL_WARN("sot", "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow");
99 SetError(SVSTREAM_FILEFORMAT_ERROR);
100 m_pFAT = nullptr;
101 m_pTOC = nullptr;
102 return;
105 m_pFAT = new StgFATStrm(*this, nFatStrmSize);
106 m_pTOC = new StgDirStrm(*this);
107 if( GetError() )
108 return;
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 );
123 // get the logical data page size
125 short StgIo::GetDataPageSize() const
127 return 1 << m_aHdr.GetDataPageSize();
130 // Commit everything
132 bool StgIo::CommitAll()
134 // Store the data (all streams and the TOC)
135 if( m_pTOC && m_pTOC->Store() && m_pDataFAT )
137 if( Commit() )
139 m_aHdr.SetDataFATStart( m_pDataFAT->GetStart() );
140 m_aHdr.SetDataFATSize( m_pDataFAT->GetPages() );
141 m_aHdr.SetTOCStart( m_pTOC->GetStart() );
142 if( m_aHdr.Store( *this ) )
144 GetStrm()->Flush();
145 const ErrCode n = GetStrm()->GetError();
146 SetError( n );
147 #ifdef DBG_UTIL
148 if( n==ERRCODE_NONE ) ValidateFATs();
149 #endif
150 return n == ERRCODE_NONE;
154 SetError( SVSTREAM_WRITE_ERROR );
155 return false;
158 namespace {
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;
178 EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize )
179 : nPages(pFatStream->GetSize() >> 2), nPageSize(nPSize)
181 pFat.reset( new sal_Int32[ nPages ] );
182 pFree.reset( new bool[ nPages ] );
184 rtl::Reference< StgPage > pPage;
185 sal_Int32 nFatPageSize = (1 << rIo.m_aHdr.GetPageSize()) - 2;
187 for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
189 if( ! (nPage % nFatPageSize) )
191 pFatStream->Pos2Page( nPage << 2 );
192 sal_Int32 nPhysPage = pFatStream->GetPage();
193 pPage = rIo.Get( nPhysPage, true );
196 pFat[ nPage ] = StgCache::GetFromPage( pPage, short( nPage % nFatPageSize ) );
197 pFree[ nPage ] = true;
201 bool EasyFat::HasUnrefChains() const
203 for( sal_Int32 nPage = 0; nPage < nPages; nPage++ )
205 if( pFree[ nPage ] && pFat[ nPage ] != -1 )
206 return true;
208 return false;
211 FatError EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect )
213 if( nCount > 0 )
215 --nCount;
216 nCount /= GetPageSize();
217 ++nCount;
220 sal_Int32 nCurPage = nPage;
221 while( nCount != 0 )
223 if( nCurPage < 0 || nCurPage >= nPages )
224 return FatError::OutOfBounds;
225 pFree[ nCurPage ] = false;
226 nCurPage = pFat[ nCurPage ];
227 // stream too long
228 if( nCurPage != nExpect && nCount == 1 )
229 return FatError::WrongLength;
230 // stream too short
231 if( nCurPage == nExpect && nCount != 1 && nCount != -1 )
232 return FatError::WrongLength;
233 // last block for stream without length
234 if( nCurPage == nExpect && nCount == -1 )
235 nCount = 1;
236 if( nCount != -1 )
237 nCount--;
239 return FatError::Ok;
242 namespace {
244 class Validator
246 FatError nError;
248 EasyFat aSmallFat;
249 EasyFat aFat;
251 StgIo &rIo;
253 FatError ValidateMasterFATs();
254 FatError ValidateDirectoryEntries();
255 FatError FindUnrefedChains() const;
256 FatError MarkAll( StgDirEntry *pEntry );
258 public:
259 explicit Validator( StgIo &rIo );
260 bool IsError() const { return nError != FatError::Ok; }
265 Validator::Validator( StgIo &rIoP )
266 : aSmallFat( rIoP, rIoP.m_pDataFAT, 1 << rIoP.m_aHdr.GetDataPageSize() ),
267 aFat( rIoP, rIoP.m_pFAT, 1 << rIoP.m_aHdr.GetPageSize() ),
268 rIo( rIoP )
270 FatError nErr = nError = FatError::Ok;
272 if( ( nErr = ValidateMasterFATs() ) != FatError::Ok )
273 nError = nErr;
274 else if( ( nErr = ValidateDirectoryEntries() ) != FatError::Ok )
275 nError = nErr;
276 else if( ( nErr = FindUnrefedChains()) != FatError::Ok )
277 nError = nErr;
280 FatError Validator::ValidateMasterFATs()
282 sal_Int32 nCount = rIo.m_aHdr.GetFATSize();
283 FatError nErr;
284 if ( !rIo.m_pFAT )
285 return FatError::InMemoryError;
287 for( sal_Int32 i = 0; i < nCount; i++ )
289 if( ( nErr = aFat.Mark(rIo.m_pFAT->GetPage(i, false), aFat.GetPageSize(), -3 )) != FatError::Ok)
290 return nErr;
292 if( rIo.m_aHdr.GetMasters() )
293 if( ( nErr = aFat.Mark(rIo.m_aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FatError::Ok )
294 return nErr;
296 return FatError::Ok;
299 FatError Validator::MarkAll( StgDirEntry *pEntry )
301 if ( !pEntry )
302 return FatError::InMemoryError;
304 StgIterator aIter( *pEntry );
305 FatError nErr = FatError::Ok;
306 for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() )
308 if( p->m_aEntry.GetType() == STG_STORAGE )
310 nErr = MarkAll( p );
311 if( nErr != FatError::Ok )
312 return nErr;
314 else
316 sal_Int32 nSize = p->m_aEntry.GetSize();
317 if( nSize < rIo.m_aHdr.GetThreshold() )
318 nErr = aSmallFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
319 else
320 nErr = aFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 );
321 if( nErr != FatError::Ok )
322 return nErr;
325 return FatError::Ok;
328 FatError Validator::ValidateDirectoryEntries()
330 if ( !rIo.m_pTOC )
331 return FatError::InMemoryError;
333 // Normal DirEntries
334 FatError nErr = MarkAll( rIo.m_pTOC->GetRoot() );
335 if( nErr != FatError::Ok )
336 return nErr;
337 // Small Data
338 nErr = aFat.Mark( rIo.m_pTOC->GetRoot()->m_aEntry.GetStartPage(),
339 rIo.m_pTOC->GetRoot()->m_aEntry.GetSize(), -2 );
340 if( nErr != FatError::Ok )
341 return nErr;
342 // Small Data FAT
343 nErr = aFat.Mark(
344 rIo.m_aHdr.GetDataFATStart(),
345 rIo.m_aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 );
346 if( nErr != FatError::Ok )
347 return nErr;
348 // TOC
349 nErr = aFat.Mark(
350 rIo.m_aHdr.GetTOCStart(), -1, -2 );
351 return nErr;
354 FatError Validator::FindUnrefedChains() const
356 if( aSmallFat.HasUnrefChains() ||
357 aFat.HasUnrefChains() )
358 return FatError::UnrefChain;
359 else
360 return FatError::Ok;
363 FatError StgIo::ValidateFATs()
365 if( m_bFile )
367 std::optional<Validator> pV( *this );
368 bool bRet1 = !pV->IsError(), bRet2 = true ;
369 pV.reset();
371 SvFileStream *pFileStrm = static_cast<SvFileStream *>( GetStrm() );
372 if ( !pFileStrm )
373 return FatError::InMemoryError;
375 StgIo aIo;
376 if( aIo.Open( pFileStrm->GetFileName(),
377 StreamMode::READ | StreamMode::SHARE_DENYNONE) &&
378 aIo.Load() )
380 pV.emplace( aIo );
381 bRet2 = !pV->IsError();
382 pV.reset();
385 FatError nErr;
386 if( bRet1 != bRet2 )
387 nErr = bRet1 ? FatError::OnFileError : FatError::InMemoryError;
388 else nErr = bRet1 ? FatError::Ok : FatError::BothError;
389 if( nErr != FatError::Ok && !m_bCopied )
391 m_bCopied = true;
393 // DBG_ASSERT( nErr == FatError::Ok ,"Storage broken");
394 return nErr;
396 // OSL_FAIL("Do not validate (no FileStorage)");
397 return FatError::Ok;
400 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */