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