File: | home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx |
Warning: | line 215, column 16 Division by zero |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |||
2 | /* | |||
3 | * This file is part of the LibreOffice project. | |||
4 | * | |||
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/. | |||
8 | * | |||
9 | * This file incorporates work covered by the following license notice: | |||
10 | * | |||
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 . | |||
18 | */ | |||
19 | ||||
20 | ||||
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> | |||
28 | ||||
29 | #include <memory> | |||
30 | ||||
31 | ///////////////////////////// class StgIo | |||
32 | ||||
33 | // This class holds the storage header and all internal streams. | |||
34 | ||||
35 | StgIo::StgIo() : StgCache() | |||
36 | { | |||
37 | m_pTOC = nullptr; | |||
38 | m_pDataFAT = nullptr; | |||
39 | m_pDataStrm = nullptr; | |||
40 | m_pFAT = nullptr; | |||
41 | m_bCopied = false; | |||
42 | } | |||
43 | ||||
44 | StgIo::~StgIo() | |||
45 | { | |||
46 | delete m_pTOC; | |||
47 | delete m_pDataFAT; | |||
48 | delete m_pDataStrm; | |||
49 | delete m_pFAT; | |||
50 | } | |||
51 | ||||
52 | // Load the header. Do not set an error code if the header is invalid. | |||
53 | ||||
54 | bool StgIo::Load() | |||
55 | { | |||
56 | if( GetStrm() ) | |||
57 | { | |||
58 | if( m_aHdr.Load( *this ) ) | |||
59 | { | |||
60 | if( m_aHdr.Check() ) | |||
61 | SetupStreams(); | |||
62 | else | |||
63 | return false; | |||
64 | } | |||
65 | else | |||
66 | return false; | |||
67 | } | |||
68 | return Good(); | |||
69 | } | |||
70 | ||||
71 | // Set up an initial, empty storage | |||
72 | ||||
73 | bool StgIo::Init() | |||
74 | { | |||
75 | m_aHdr.Init(); | |||
76 | SetupStreams(); | |||
77 | return CommitAll(); | |||
78 | } | |||
79 | ||||
80 | void StgIo::SetupStreams() | |||
81 | { | |||
82 | delete m_pTOC; | |||
83 | delete m_pDataFAT; | |||
84 | delete m_pDataStrm; | |||
85 | delete m_pFAT; | |||
86 | m_pTOC = nullptr; | |||
87 | m_pDataFAT = nullptr; | |||
88 | m_pDataStrm = nullptr; | |||
89 | m_pFAT = nullptr; | |||
90 | ResetError(); | |||
91 | ||||
92 | short nPhysPageSize = 1 << m_aHdr.GetPageSize(); | |||
93 | SetPhysPageSize(nPhysPageSize); | |||
94 | sal_Int32 nFatStrmSize; | |||
95 | if (o3tl::checked_multiply<sal_Int32>(m_aHdr.GetFATSize(), nPhysPageSize, nFatStrmSize)) | |||
96 | { | |||
97 | SAL_WARN("sot", "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow")do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "sot")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow") == 1) { :: sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx" ":" "97" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow"; ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx" ":" "97" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow") == 1) { :: sal_detail_log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx" ":" "97" ": "), ::sal::detail::unwrapStream( ::sal::detail:: StreamStart() << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow" ), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "Error: " << m_aHdr.GetFATSize() << " * " << nPhysPageSize << " would overflow"; ::sal::detail ::log( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sot"), ("/home/maarten/src/libreoffice/core/sot/source/sdstor/stgio.cxx" ":" "97" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
98 | SetError(SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 )); | |||
99 | m_pFAT = nullptr; | |||
100 | m_pTOC = nullptr; | |||
101 | return; | |||
102 | } | |||
103 | ||||
104 | m_pFAT = new StgFATStrm(*this, nFatStrmSize); | |||
105 | m_pTOC = new StgDirStrm(*this); | |||
106 | if( GetError() ) | |||
107 | return; | |||
108 | ||||
109 | StgDirEntry* pRoot = m_pTOC->GetRoot(); | |||
110 | if( pRoot ) | |||
111 | { | |||
112 | m_pDataFAT = new StgDataStrm( *this, m_aHdr.GetDataFATStart(), -1 ); | |||
113 | m_pDataStrm = new StgDataStrm( *this, *pRoot ); | |||
114 | m_pDataFAT->SetIncrement( 1 << m_aHdr.GetPageSize() ); | |||
115 | m_pDataStrm->SetIncrement( GetDataPageSize() ); | |||
116 | m_pDataStrm->SetEntry( *pRoot ); | |||
117 | } | |||
118 | else | |||
119 | SetError( SVSTREAM_FILEFORMAT_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Format, 21 ) ); | |||
120 | } | |||
121 | ||||
122 | // get the logical data page size | |||
123 | ||||
124 | short StgIo::GetDataPageSize() const | |||
125 | { | |||
126 | return 1 << m_aHdr.GetDataPageSize(); | |||
127 | } | |||
128 | ||||
129 | // Commit everything | |||
130 | ||||
131 | bool StgIo::CommitAll() | |||
132 | { | |||
133 | // Store the data (all streams and the TOC) | |||
134 | if( m_pTOC && m_pTOC->Store() && m_pDataFAT ) | |||
135 | { | |||
136 | if( Commit() ) | |||
137 | { | |||
138 | m_aHdr.SetDataFATStart( m_pDataFAT->GetStart() ); | |||
139 | m_aHdr.SetDataFATSize( m_pDataFAT->GetPages() ); | |||
140 | m_aHdr.SetTOCStart( m_pTOC->GetStart() ); | |||
141 | if( m_aHdr.Store( *this ) ) | |||
142 | { | |||
143 | GetStrm()->Flush(); | |||
144 | const ErrCode n = GetStrm()->GetError(); | |||
145 | SetError( n ); | |||
146 | #ifdef DBG_UTIL | |||
147 | if( n==ERRCODE_NONEErrCode(0) ) ValidateFATs(); | |||
148 | #endif | |||
149 | return n == ERRCODE_NONEErrCode(0); | |||
150 | } | |||
151 | } | |||
152 | } | |||
153 | SetError( SVSTREAM_WRITE_ERRORErrCode( ErrCodeArea::Io, ErrCodeClass::Write, 16 ) ); | |||
154 | return false; | |||
155 | } | |||
156 | ||||
157 | namespace { | |||
158 | ||||
159 | class EasyFat | |||
160 | { | |||
161 | std::unique_ptr<sal_Int32[]> pFat; | |||
162 | std::unique_ptr<bool[]> pFree; | |||
163 | sal_Int32 nPages; | |||
164 | sal_Int32 nPageSize; | |||
165 | ||||
166 | public: | |||
167 | EasyFat( StgIo & rIo, StgStrm *pFatStream, sal_Int32 nPSize ); | |||
168 | ||||
169 | sal_Int32 GetPageSize() const { return nPageSize; } | |||
170 | ||||
171 | FatError Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect ); | |||
172 | bool HasUnrefChains() const; | |||
173 | }; | |||
174 | ||||
175 | } | |||
176 | ||||
177 | EasyFat::EasyFat( StgIo& rIo, StgStrm* pFatStream, sal_Int32 nPSize ) | |||
178 | : nPages(pFatStream->GetSize() >> 2), nPageSize(nPSize) | |||
179 | { | |||
180 | pFat.reset( new sal_Int32[ nPages ] ); | |||
181 | pFree.reset( new bool[ nPages ] ); | |||
182 | ||||
183 | rtl::Reference< StgPage > pPage; | |||
184 | sal_Int32 nFatPageSize = (1 << rIo.m_aHdr.GetPageSize()) - 2; | |||
185 | ||||
186 | for( sal_Int32 nPage = 0; nPage < nPages; nPage++ ) | |||
187 | { | |||
188 | if( ! (nPage % nFatPageSize) ) | |||
189 | { | |||
190 | pFatStream->Pos2Page( nPage << 2 ); | |||
191 | sal_Int32 nPhysPage = pFatStream->GetPage(); | |||
192 | pPage = rIo.Get( nPhysPage, true ); | |||
193 | } | |||
194 | ||||
195 | pFat[ nPage ] = StgCache::GetFromPage( pPage, short( nPage % nFatPageSize ) ); | |||
196 | pFree[ nPage ] = true; | |||
197 | } | |||
198 | } | |||
199 | ||||
200 | bool EasyFat::HasUnrefChains() const | |||
201 | { | |||
202 | for( sal_Int32 nPage = 0; nPage < nPages; nPage++ ) | |||
203 | { | |||
204 | if( pFree[ nPage ] && pFat[ nPage ] != -1 ) | |||
205 | return true; | |||
206 | } | |||
207 | return false; | |||
208 | } | |||
209 | ||||
210 | FatError EasyFat::Mark( sal_Int32 nPage, sal_Int32 nCount, sal_Int32 nExpect ) | |||
211 | { | |||
212 | if( nCount > 0 ) | |||
213 | { | |||
214 | --nCount; | |||
215 | nCount /= GetPageSize(); | |||
| ||||
216 | ++nCount; | |||
217 | } | |||
218 | ||||
219 | sal_Int32 nCurPage = nPage; | |||
220 | while( nCount != 0 ) | |||
221 | { | |||
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--; | |||
237 | } | |||
238 | return FatError::Ok; | |||
239 | } | |||
240 | ||||
241 | namespace { | |||
242 | ||||
243 | class Validator | |||
244 | { | |||
245 | FatError nError; | |||
246 | ||||
247 | EasyFat aSmallFat; | |||
248 | EasyFat aFat; | |||
249 | ||||
250 | StgIo &rIo; | |||
251 | ||||
252 | FatError ValidateMasterFATs(); | |||
253 | FatError ValidateDirectoryEntries(); | |||
254 | FatError FindUnrefedChains() const; | |||
255 | FatError MarkAll( StgDirEntry *pEntry ); | |||
256 | ||||
257 | public: | |||
258 | explicit Validator( StgIo &rIo ); | |||
259 | bool IsError() const { return nError != FatError::Ok; } | |||
260 | }; | |||
261 | ||||
262 | } | |||
263 | ||||
264 | Validator::Validator( StgIo &rIoP ) | |||
265 | : aSmallFat( rIoP, rIoP.m_pDataFAT, 1 << rIoP.m_aHdr.GetDataPageSize() ), | |||
266 | aFat( rIoP, rIoP.m_pFAT, 1 << rIoP.m_aHdr.GetPageSize() ), | |||
267 | rIo( rIoP ) | |||
268 | { | |||
269 | FatError nErr = nError = FatError::Ok; | |||
270 | ||||
271 | if( ( nErr = ValidateMasterFATs() ) != FatError::Ok ) | |||
272 | nError = nErr; | |||
273 | else if( ( nErr = ValidateDirectoryEntries() ) != FatError::Ok ) | |||
274 | nError = nErr; | |||
275 | else if( ( nErr = FindUnrefedChains()) != FatError::Ok ) | |||
276 | nError = nErr; | |||
277 | } | |||
278 | ||||
279 | FatError Validator::ValidateMasterFATs() | |||
280 | { | |||
281 | sal_Int32 nCount = rIo.m_aHdr.GetFATSize(); | |||
282 | FatError nErr; | |||
283 | if ( !rIo.m_pFAT
| |||
284 | return FatError::InMemoryError; | |||
285 | ||||
286 | for( sal_Int32 i = 0; i < nCount; i++ ) | |||
287 | { | |||
288 | if( ( nErr = aFat.Mark(rIo.m_pFAT->GetPage(i, false), aFat.GetPageSize(), -3 )) != FatError::Ok) | |||
289 | return nErr; | |||
290 | } | |||
291 | if( rIo.m_aHdr.GetMasters() ) | |||
292 | if( ( nErr = aFat.Mark(rIo.m_aHdr.GetFATChain( ), aFat.GetPageSize(), -4 )) != FatError::Ok ) | |||
293 | return nErr; | |||
294 | ||||
295 | return FatError::Ok; | |||
296 | } | |||
297 | ||||
298 | FatError Validator::MarkAll( StgDirEntry *pEntry ) | |||
299 | { | |||
300 | if ( !pEntry ) | |||
301 | return FatError::InMemoryError; | |||
302 | ||||
303 | StgIterator aIter( *pEntry ); | |||
304 | FatError nErr = FatError::Ok; | |||
305 | for( StgDirEntry* p = aIter.First(); p ; p = aIter.Next() ) | |||
306 | { | |||
307 | if( p->m_aEntry.GetType() == STG_STORAGE ) | |||
308 | { | |||
309 | nErr = MarkAll( p ); | |||
310 | if( nErr != FatError::Ok ) | |||
311 | return nErr; | |||
312 | } | |||
313 | else | |||
314 | { | |||
315 | sal_Int32 nSize = p->m_aEntry.GetSize(); | |||
316 | if( nSize < rIo.m_aHdr.GetThreshold() ) | |||
317 | nErr = aSmallFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 ); | |||
318 | else | |||
319 | nErr = aFat.Mark( p->m_aEntry.GetStartPage(),nSize, -2 ); | |||
320 | if( nErr != FatError::Ok ) | |||
321 | return nErr; | |||
322 | } | |||
323 | } | |||
324 | return FatError::Ok; | |||
325 | } | |||
326 | ||||
327 | FatError Validator::ValidateDirectoryEntries() | |||
328 | { | |||
329 | if ( !rIo.m_pTOC ) | |||
330 | return FatError::InMemoryError; | |||
331 | ||||
332 | // Normal DirEntries | |||
333 | FatError nErr = MarkAll( rIo.m_pTOC->GetRoot() ); | |||
334 | if( nErr
| |||
335 | return nErr; | |||
336 | // Small Data | |||
337 | nErr = aFat.Mark( rIo.m_pTOC->GetRoot()->m_aEntry.GetStartPage(), | |||
338 | rIo.m_pTOC->GetRoot()->m_aEntry.GetSize(), -2 ); | |||
339 | if( nErr != FatError::Ok ) | |||
340 | return nErr; | |||
341 | // Small Data FAT | |||
342 | nErr = aFat.Mark( | |||
343 | rIo.m_aHdr.GetDataFATStart(), | |||
344 | rIo.m_aHdr.GetDataFATSize() * aFat.GetPageSize(), -2 ); | |||
345 | if( nErr != FatError::Ok ) | |||
346 | return nErr; | |||
347 | // TOC | |||
348 | nErr = aFat.Mark( | |||
349 | rIo.m_aHdr.GetTOCStart(), -1, -2 ); | |||
350 | return nErr; | |||
351 | } | |||
352 | ||||
353 | FatError Validator::FindUnrefedChains() const | |||
354 | { | |||
355 | if( aSmallFat.HasUnrefChains() || | |||
356 | aFat.HasUnrefChains() ) | |||
357 | return FatError::UnrefChain; | |||
358 | else | |||
359 | return FatError::Ok; | |||
360 | } | |||
361 | ||||
362 | FatError StgIo::ValidateFATs() | |||
363 | { | |||
364 | if( m_bFile ) | |||
| ||||
365 | { | |||
366 | std::unique_ptr<Validator> pV(new Validator( *this )); | |||
367 | bool bRet1 = !pV->IsError(), bRet2 = true ; | |||
368 | pV.reset(); | |||
369 | ||||
370 | SvFileStream *pFileStrm = static_cast<SvFileStream *>( GetStrm() ); | |||
371 | if ( !pFileStrm ) | |||
372 | return FatError::InMemoryError; | |||
373 | ||||
374 | StgIo aIo; | |||
375 | if( aIo.Open( pFileStrm->GetFileName(), | |||
376 | StreamMode::READ | StreamMode::SHARE_DENYNONE) && | |||
377 | aIo.Load() ) | |||
378 | { | |||
379 | pV.reset(new Validator( aIo )); | |||
380 | bRet2 = !pV->IsError(); | |||
381 | pV.reset(); | |||
382 | } | |||
383 | ||||
384 | FatError nErr; | |||
385 | if( bRet1 != bRet2 ) | |||
386 | nErr = bRet1 ? FatError::OnFileError : FatError::InMemoryError; | |||
387 | else nErr = bRet1 ? FatError::Ok : FatError::BothError; | |||
388 | if( nErr != FatError::Ok && !m_bCopied ) | |||
389 | { | |||
390 | m_bCopied = true; | |||
391 | } | |||
392 | // DBG_ASSERT( nErr == FatError::Ok ,"Storage broken"); | |||
393 | return nErr; | |||
394 | } | |||
395 | // OSL_FAIL("Do not validate (no FileStorage)"); | |||
396 | return FatError::Ok; | |||
397 | } | |||
398 | ||||
399 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |