/src/libreoffice/sot/source/sdstor/stgdir.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 <sot/stg.hxx> |
22 | | #include "stgelem.hxx" |
23 | | #include "stgstrms.hxx" |
24 | | #include "stgdir.hxx" |
25 | | #include "stgio.hxx" |
26 | | |
27 | | #include <osl/diagnose.h> |
28 | | #include <sal/log.hxx> |
29 | | |
30 | | #include <memory> |
31 | | |
32 | | //////////////////////////// class StgDirEntry |
33 | | |
34 | | // This class holds the dir entry data and maintains dirty flags for both |
35 | | // the entry and the data. |
36 | | |
37 | | // Transacted mode for streams: On the first write, a temp stream pTmpStrm |
38 | | // is created and operated on. A commit moves pTmpStrm to pCurStrm, which |
39 | | // is used for subsequent reads. A new write creates a new copy of pTmpStrm |
40 | | // based on pCurStrm. Reverting throws away pTmpStrm. |
41 | | // Transacted mode for storages: A copy of the dir ents is kept in aSave. |
42 | | // Committing means copying aEntry to aSave. Reverting means to copy aSave |
43 | | // to aEntry, delete newly created entries and to reactivate removed entries. |
44 | | |
45 | | // Problem of implementation: No hierarchical commits. Therefore only |
46 | | // overall transaction-oriented or direct. |
47 | | |
48 | | StgDirEntry::StgDirEntry( const void* pBuffer, sal_uInt32 nBufferLen, sal_uInt64 nUnderlyingStreamSize, bool * pbOk ) |
49 | 2.20M | { |
50 | 2.20M | *pbOk = m_aEntry.Load( pBuffer, nBufferLen, nUnderlyingStreamSize ); |
51 | | |
52 | 2.20M | InitMembers(); |
53 | 2.20M | } |
54 | | |
55 | 5.24M | StgDirEntry::StgDirEntry( const StgEntry& r ) : m_aEntry( r ) |
56 | 5.24M | { |
57 | 5.24M | InitMembers(); |
58 | 5.24M | } |
59 | | |
60 | | // Helper for all ctors |
61 | | |
62 | | void StgDirEntry::InitMembers() |
63 | 7.44M | { |
64 | 7.44M | m_aSave = m_aEntry; |
65 | 7.44M | m_pUp = |
66 | 7.44M | m_pDown = nullptr; |
67 | 7.44M | m_pStgStrm = nullptr; |
68 | 7.44M | m_pCurStrm = |
69 | 7.44M | m_pTmpStrm = nullptr; |
70 | 7.44M | m_nPos = |
71 | 7.44M | m_nEntry = |
72 | 7.44M | m_nRefCnt = 0; |
73 | 7.44M | m_nMode = StreamMode::READ; |
74 | 7.44M | m_bDirect = true; |
75 | 7.44M | m_bInvalid = |
76 | 7.44M | m_bRemoved = |
77 | 7.44M | m_bTemp = |
78 | 7.44M | m_bDirty = |
79 | 7.44M | m_bZombie = false; |
80 | 7.44M | } |
81 | | |
82 | | StgDirEntry::~StgDirEntry() |
83 | 7.44M | { |
84 | 7.44M | Close(); |
85 | 7.44M | delete m_pCurStrm; |
86 | 7.44M | delete m_pStgStrm; |
87 | 7.44M | delete m_pDown; |
88 | 7.44M | } |
89 | | |
90 | | // Comparison function |
91 | | |
92 | | sal_Int32 StgDirEntry::Compare( const StgAvlNode* p ) const |
93 | 24.7M | { |
94 | 24.7M | sal_Int32 nResult = -1; |
95 | 24.7M | if ( p ) |
96 | 24.7M | { |
97 | 24.7M | const StgDirEntry* pEntry = static_cast<const StgDirEntry*>(p); |
98 | 24.7M | nResult = m_aEntry.Compare( pEntry->m_aEntry ); |
99 | 24.7M | } |
100 | 24.7M | return nResult; |
101 | 24.7M | } |
102 | | |
103 | | // Enumerate the entry numbers. |
104 | | // n is incremented to show the total # of entries. |
105 | | // These number are later used as page numbers when storing |
106 | | // the TOC tree into the TOC stream. Remember that aSave is |
107 | | // stored, not aEntry. |
108 | | |
109 | | void StgDirEntry::Enum( sal_Int32& n ) |
110 | 920k | { |
111 | 920k | sal_Int32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE; |
112 | 920k | m_nEntry = n++; |
113 | 920k | if( m_pLeft ) |
114 | 1.67k | { |
115 | 1.67k | static_cast<StgDirEntry*>(m_pLeft)->Enum( n ); |
116 | 1.67k | nLeft = static_cast<StgDirEntry*>(m_pLeft)->m_nEntry; |
117 | 1.67k | } |
118 | 920k | if( m_pRight ) |
119 | 639k | { |
120 | 639k | static_cast<StgDirEntry*>(m_pRight)->Enum( n ); |
121 | 639k | nRight = static_cast<StgDirEntry*>(m_pRight)->m_nEntry; |
122 | 639k | } |
123 | 920k | if( m_pDown ) |
124 | 164k | { |
125 | 164k | m_pDown->Enum( n ); nDown = m_pDown->m_nEntry; |
126 | 164k | } |
127 | 920k | m_aSave.SetLeaf( STG_LEFT, nLeft ); |
128 | 920k | m_aSave.SetLeaf( STG_RIGHT, nRight ); |
129 | 920k | m_aSave.SetLeaf( STG_CHILD, nDown ); |
130 | 920k | } |
131 | | |
132 | | // Delete all temporary entries before writing the TOC stream. |
133 | | // Until now Deltemp is never called with bForce True |
134 | | |
135 | | void StgDirEntry::DelTemp( bool bForce ) |
136 | 925k | { |
137 | 925k | if( m_pLeft ) |
138 | 3.28k | static_cast<StgDirEntry*>(m_pLeft)->DelTemp( false ); |
139 | 925k | if( m_pRight ) |
140 | 643k | static_cast<StgDirEntry*>(m_pRight)->DelTemp( false ); |
141 | 925k | if( m_pDown ) |
142 | 164k | { |
143 | | // If the storage is dead, of course all elements are dead, too |
144 | 164k | if( m_bInvalid && m_aEntry.GetType() == STG_STORAGE ) |
145 | 173 | bForce = true; |
146 | 164k | m_pDown->DelTemp( bForce ); |
147 | 164k | } |
148 | 925k | if( !( bForce || m_bInvalid ) || ( m_aEntry.GetType() == STG_ROOT ) ) |
149 | 920k | return; |
150 | | |
151 | 5.09k | Close(); |
152 | 5.09k | if( m_pUp ) |
153 | 5.09k | { |
154 | | // this deletes the element if refcnt == 0! |
155 | 5.09k | bool bDel = m_nRefCnt == 0; |
156 | 5.09k | StgAvlNode::Remove( reinterpret_cast<StgAvlNode**>(&m_pUp->m_pDown), this, bDel ); |
157 | 5.09k | if( !bDel ) |
158 | 0 | { |
159 | 0 | m_pLeft = m_pRight = m_pDown = nullptr; |
160 | 0 | m_bInvalid = m_bZombie = true; |
161 | 0 | } |
162 | 5.09k | } |
163 | 5.09k | } |
164 | | |
165 | | // Save the tree into the given dir stream |
166 | | |
167 | | bool StgDirEntry::Store( StgDirStrm& rStrm ) |
168 | 920k | { |
169 | 920k | void* pEntry = rStrm.GetEntry( m_nEntry, true ); |
170 | 920k | if( !pEntry ) |
171 | 0 | return false; |
172 | | // Do not store the current (maybe not committed) entry |
173 | 920k | m_aSave.Store( pEntry ); |
174 | 920k | if( m_pLeft ) |
175 | 1.67k | if( !static_cast<StgDirEntry*>(m_pLeft)->Store( rStrm ) ) |
176 | 0 | return false; |
177 | 920k | if( m_pRight ) |
178 | 639k | if( !static_cast<StgDirEntry*>(m_pRight)->Store( rStrm ) ) |
179 | 0 | return false; |
180 | 920k | if( m_pDown && !m_pDown->Store( rStrm ) ) |
181 | 0 | return false; |
182 | 920k | return true; |
183 | 920k | } |
184 | | |
185 | | bool StgDirEntry::StoreStream( StgIo& rIo ) |
186 | 925k | { |
187 | 925k | if( m_aEntry.GetType() == STG_STREAM || m_aEntry.GetType() == STG_ROOT ) |
188 | 763k | { |
189 | 763k | if( m_bInvalid ) |
190 | 2.12k | { |
191 | | // Delete the stream if needed |
192 | 2.12k | if( !m_pStgStrm ) |
193 | 0 | { |
194 | 0 | OpenStream( rIo ); |
195 | 0 | delete m_pStgStrm; |
196 | 0 | m_pStgStrm = nullptr; |
197 | 0 | } |
198 | 2.12k | else |
199 | 2.12k | m_pStgStrm->SetSize( 0 ); |
200 | 2.12k | } |
201 | | // or write the data stream |
202 | 761k | else if( !Tmp2Strm() ) |
203 | 0 | return false; |
204 | 763k | } |
205 | 925k | return true; |
206 | 925k | } |
207 | | |
208 | | // Save all dirty streams |
209 | | |
210 | | bool StgDirEntry::StoreStreams( StgIo& rIo ) |
211 | 925k | { |
212 | 925k | if( !StoreStream( rIo ) ) |
213 | 0 | return false; |
214 | 925k | if( m_pLeft ) |
215 | 3.28k | if( !static_cast<StgDirEntry*>(m_pLeft)->StoreStreams( rIo ) ) |
216 | 0 | return false; |
217 | 925k | if( m_pRight ) |
218 | 643k | if( !static_cast<StgDirEntry*>(m_pRight)->StoreStreams( rIo ) ) |
219 | 0 | return false; |
220 | 925k | if( m_pDown ) |
221 | 164k | if( !m_pDown->StoreStreams( rIo ) ) |
222 | 0 | return false; |
223 | 925k | return true; |
224 | 925k | } |
225 | | |
226 | | // Revert all directory entries after failure to write the TOC stream |
227 | | |
228 | | void StgDirEntry::RevertAll() |
229 | 0 | { |
230 | 0 | m_aEntry = m_aSave; |
231 | 0 | if( m_pLeft ) |
232 | 0 | static_cast<StgDirEntry*>(m_pLeft)->RevertAll(); |
233 | 0 | if( m_pRight ) |
234 | 0 | static_cast<StgDirEntry*>(m_pRight)->RevertAll(); |
235 | 0 | if( m_pDown ) |
236 | 0 | m_pDown->RevertAll(); |
237 | 0 | } |
238 | | |
239 | | // Look if any element of the tree is dirty |
240 | | |
241 | | bool StgDirEntry::IsDirty() |
242 | 114k | { |
243 | 114k | if( m_bDirty || m_bInvalid ) |
244 | 114k | return true; |
245 | 0 | if( m_pLeft && static_cast<StgDirEntry*>(m_pLeft)->IsDirty() ) |
246 | 0 | return true; |
247 | 0 | if( m_pRight && static_cast<StgDirEntry*>(m_pRight)->IsDirty() ) |
248 | 0 | return true; |
249 | 0 | if( m_pDown && m_pDown->IsDirty() ) |
250 | 0 | return true; |
251 | 0 | return false; |
252 | 0 | } |
253 | | |
254 | | // Set up a stream. |
255 | | |
256 | | void StgDirEntry::OpenStream( StgIo& rIo ) |
257 | 1.76M | { |
258 | 1.76M | sal_Int32 nThreshold = static_cast<sal_uInt16>(rIo.m_aHdr.GetThreshold()); |
259 | 1.76M | delete m_pStgStrm; |
260 | 1.76M | if( m_aEntry.GetSize() < nThreshold ) |
261 | 1.67M | m_pStgStrm = new StgSmallStrm( rIo, *this ); |
262 | 86.4k | else |
263 | 86.4k | m_pStgStrm = new StgDataStrm( rIo, *this ); |
264 | 1.76M | if( m_bInvalid && m_aEntry.GetSize() ) |
265 | 0 | { |
266 | | // This entry has invalid data, so delete that data |
267 | 0 | SetSize( 0 ); |
268 | | // bRemoved = bInvalid = false; |
269 | 0 | } |
270 | 1.76M | m_nPos = 0; |
271 | 1.76M | } |
272 | | |
273 | | // Close the open stream without committing. If the entry is marked as |
274 | | // temporary, delete it. |
275 | | // Do not delete pCurStrm here! |
276 | | // (TLX:??? At least pStgStrm must be deleted.) |
277 | | |
278 | | void StgDirEntry::Close() |
279 | 9.82M | { |
280 | 9.82M | delete m_pTmpStrm; |
281 | 9.82M | m_pTmpStrm = nullptr; |
282 | | // nRefCnt = 0; |
283 | 9.82M | m_bInvalid = m_bTemp; |
284 | 9.82M | } |
285 | | |
286 | | // Get the current stream size |
287 | | |
288 | | sal_Int32 StgDirEntry::GetSize() const |
289 | 246M | { |
290 | 246M | sal_Int32 n; |
291 | 246M | if( m_pTmpStrm ) |
292 | 0 | n = m_pTmpStrm->GetSize(); |
293 | 246M | else if( m_pCurStrm ) |
294 | 0 | n = m_pCurStrm->GetSize(); |
295 | 246M | else n = m_aEntry.GetSize(); |
296 | 246M | return n; |
297 | 246M | } |
298 | | |
299 | | // Set the stream size. This means also creating a temp stream. |
300 | | |
301 | | bool StgDirEntry::SetSize( sal_Int32 nNewSize ) |
302 | 661k | { |
303 | 661k | if ( |
304 | 661k | !( m_nMode & StreamMode::WRITE ) || |
305 | 661k | (!m_bDirect && !m_pTmpStrm && !Strm2Tmp()) |
306 | 661k | ) |
307 | 0 | { |
308 | 0 | return false; |
309 | 0 | } |
310 | | |
311 | 661k | if( nNewSize < m_nPos ) |
312 | 0 | m_nPos = nNewSize; |
313 | 661k | if( m_pTmpStrm ) |
314 | 661k | { |
315 | 661k | m_pTmpStrm->SetSize( nNewSize ); |
316 | 661k | m_pStgStrm->GetIo().SetError( m_pTmpStrm->GetError() ); |
317 | 661k | return m_pTmpStrm->GetError() == ERRCODE_NONE; |
318 | 661k | } |
319 | 0 | else |
320 | 0 | { |
321 | 0 | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
322 | 0 | if ( !m_pStgStrm ) |
323 | 0 | return false; |
324 | | |
325 | 0 | bool bRes = false; |
326 | 0 | StgIo& rIo = m_pStgStrm->GetIo(); |
327 | 0 | sal_Int32 nThreshold = rIo.m_aHdr.GetThreshold(); |
328 | | // ensure the correct storage stream! |
329 | 0 | StgStrm* pOld = nullptr; |
330 | 0 | sal_uInt16 nOldSize = 0; |
331 | 0 | if( nNewSize >= nThreshold && m_pStgStrm->IsSmallStrm() ) |
332 | 0 | { |
333 | 0 | pOld = m_pStgStrm; |
334 | 0 | nOldSize = static_cast<sal_uInt16>(pOld->GetSize()); |
335 | 0 | m_pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 ); |
336 | 0 | } |
337 | 0 | else if( nNewSize < nThreshold && !m_pStgStrm->IsSmallStrm() ) |
338 | 0 | { |
339 | 0 | pOld = m_pStgStrm; |
340 | 0 | nOldSize = static_cast<sal_uInt16>(nNewSize); |
341 | 0 | m_pStgStrm = new StgSmallStrm( rIo, STG_EOF ); |
342 | 0 | } |
343 | | // now set the new size |
344 | 0 | if( m_pStgStrm->SetSize( nNewSize ) ) |
345 | 0 | { |
346 | | // did we create a new stream? |
347 | 0 | if( pOld ) |
348 | 0 | { |
349 | | // if so, we probably need to copy the old data |
350 | 0 | if( nOldSize ) |
351 | 0 | { |
352 | 0 | std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nOldSize ]); |
353 | 0 | pOld->Pos2Page( 0 ); |
354 | 0 | m_pStgStrm->Pos2Page( 0 ); |
355 | 0 | if( pOld->Read( pBuf.get(), nOldSize ) |
356 | 0 | && m_pStgStrm->Write( pBuf.get(), nOldSize ) ) |
357 | 0 | bRes = true; |
358 | 0 | } |
359 | 0 | else |
360 | 0 | bRes = true; |
361 | 0 | if( bRes ) |
362 | 0 | { |
363 | 0 | pOld->SetSize( 0 ); |
364 | 0 | delete pOld; |
365 | 0 | m_pStgStrm->Pos2Page( m_nPos ); |
366 | 0 | m_pStgStrm->SetEntry( *this ); |
367 | 0 | } |
368 | 0 | else |
369 | 0 | { |
370 | 0 | m_pStgStrm->SetSize( 0 ); |
371 | 0 | delete m_pStgStrm; |
372 | 0 | m_pStgStrm = pOld; |
373 | 0 | } |
374 | 0 | } |
375 | 0 | else |
376 | 0 | { |
377 | 0 | m_pStgStrm->Pos2Page( m_nPos ); |
378 | 0 | bRes = true; |
379 | 0 | } |
380 | 0 | } |
381 | 0 | return bRes; |
382 | 0 | } |
383 | 661k | } |
384 | | |
385 | | // Seek. On negative values, seek to EOF. |
386 | | |
387 | | sal_Int32 StgDirEntry::Seek( sal_Int32 nNew ) |
388 | 130M | { |
389 | 130M | if( m_pTmpStrm ) |
390 | 1.25M | { |
391 | 1.25M | if( nNew < 0 ) |
392 | 0 | nNew = m_pTmpStrm->GetSize(); |
393 | 1.25M | nNew = m_pTmpStrm->Seek( nNew ); |
394 | 1.25M | } |
395 | 129M | else if( m_pCurStrm ) |
396 | 175 | { |
397 | 175 | if( nNew < 0 ) |
398 | 0 | nNew = m_pCurStrm->GetSize(); |
399 | 175 | nNew = m_pCurStrm->Seek( nNew ); |
400 | 175 | } |
401 | 129M | else |
402 | 129M | { |
403 | 129M | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
404 | 129M | if ( !m_pStgStrm ) |
405 | 0 | return m_nPos; |
406 | | |
407 | 129M | sal_Int32 nSize = m_aEntry.GetSize(); |
408 | | |
409 | 129M | if( nNew < 0 ) |
410 | 629 | nNew = nSize; |
411 | | |
412 | | // try to enlarge, readonly streams do not allow this |
413 | 129M | if( nNew > nSize ) |
414 | 5.99M | { |
415 | 5.99M | if ( !( m_nMode & StreamMode::WRITE ) || !SetSize( nNew ) ) |
416 | 5.99M | { |
417 | 5.99M | return m_nPos; |
418 | 5.99M | } |
419 | 0 | else |
420 | 0 | return Seek( nNew ); |
421 | 5.99M | } |
422 | 123M | m_pStgStrm->Pos2Page( nNew ); |
423 | 123M | nNew = m_pStgStrm->GetPos(); |
424 | 123M | } |
425 | | |
426 | 124M | m_nPos = nNew; |
427 | 124M | return m_nPos; |
428 | 130M | } |
429 | | |
430 | | // Read |
431 | | |
432 | | sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen ) |
433 | 79.1M | { |
434 | 79.1M | if( nLen <= 0 ) |
435 | 5.98k | return 0; |
436 | 79.1M | if( m_pTmpStrm ) |
437 | 0 | nLen = m_pTmpStrm->ReadBytes( p, nLen ); |
438 | 79.1M | else if( m_pCurStrm ) |
439 | 175 | nLen = m_pCurStrm->ReadBytes( p, nLen ); |
440 | 79.1M | else |
441 | 79.1M | { |
442 | 79.1M | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
443 | 79.1M | if ( !m_pStgStrm ) |
444 | 0 | return 0; |
445 | | |
446 | 79.1M | nLen = m_pStgStrm->Read( p, nLen ); |
447 | 79.1M | } |
448 | | |
449 | 79.1M | m_nPos += nLen; |
450 | 79.1M | return nLen; |
451 | 79.1M | } |
452 | | |
453 | | // Write |
454 | | |
455 | | sal_Int32 StgDirEntry::Write( const void* p, sal_Int32 nLen ) |
456 | 369k | { |
457 | 369k | if( nLen <= 0 || !( m_nMode & StreamMode::WRITE ) ) |
458 | 0 | return 0; |
459 | | |
460 | | // Was this stream committed internally and reopened in direct mode? |
461 | 369k | if( m_bDirect && ( m_pCurStrm || m_pTmpStrm ) && !Tmp2Strm() ) |
462 | 0 | return 0; |
463 | | // Is this stream opened in transacted mode? Do we have to make a copy? |
464 | 369k | if( !m_bDirect && !m_pTmpStrm && !Strm2Tmp() ) |
465 | 0 | return 0; |
466 | | |
467 | 369k | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
468 | 369k | if ( !m_pStgStrm ) |
469 | 0 | return 0; |
470 | | |
471 | 369k | if( m_pTmpStrm ) |
472 | 369k | { |
473 | 369k | nLen = m_pTmpStrm->WriteBytes( p, nLen ); |
474 | 369k | m_pStgStrm->GetIo().SetError( m_pTmpStrm->GetError() ); |
475 | 369k | } |
476 | 0 | else |
477 | 0 | { |
478 | 0 | sal_Int32 nNew = m_nPos + nLen; |
479 | 0 | if( nNew > m_pStgStrm->GetSize() ) |
480 | 0 | { |
481 | 0 | if( !SetSize( nNew ) ) |
482 | 0 | return 0; |
483 | 0 | m_pStgStrm->Pos2Page( m_nPos ); |
484 | 0 | } |
485 | 0 | nLen = m_pStgStrm->Write( p, nLen ); |
486 | 0 | } |
487 | 369k | m_nPos += nLen; |
488 | 369k | return nLen; |
489 | 369k | } |
490 | | |
491 | | void StgDirEntry::Copy( BaseStorageStream& rDest ) |
492 | 661k | { |
493 | 661k | sal_Int32 n = GetSize(); |
494 | 661k | if( !(rDest.SetSize( n ) && n) ) |
495 | 217k | return; |
496 | | |
497 | 444k | sal_uInt64 Pos = rDest.Tell(); |
498 | 444k | sal_uInt8 aTempBytes[ 4096 ]; |
499 | 444k | void* p = static_cast<void*>( aTempBytes ); |
500 | 444k | Seek( 0 ); |
501 | 444k | rDest.Seek( 0 ); |
502 | 813k | while( n ) |
503 | 555k | { |
504 | 555k | sal_Int32 nn = n; |
505 | 555k | if( nn > 4096 ) |
506 | 120k | nn = 4096; |
507 | 555k | if( Read( p, nn ) != nn ) |
508 | 186k | break; |
509 | 368k | if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn ) |
510 | 0 | break; |
511 | 368k | n -= nn; |
512 | 368k | } |
513 | 444k | rDest.Seek( Pos ); // ?! Seems to be undocumented ! |
514 | 444k | } |
515 | | |
516 | | // Commit this entry |
517 | | |
518 | | bool StgDirEntry::Commit() |
519 | 4.17M | { |
520 | | // OSL_ENSURE( nMode & StreamMode::WRITE, "Trying to commit readonly stream!" ); |
521 | | |
522 | 4.17M | m_aSave = m_aEntry; |
523 | 4.17M | bool bRes = true; |
524 | 4.17M | if( m_aEntry.GetType() == STG_STREAM ) |
525 | 3.59M | { |
526 | 3.59M | if( m_pTmpStrm ) |
527 | 662k | { |
528 | 662k | delete m_pCurStrm; |
529 | 662k | m_pCurStrm = m_pTmpStrm; |
530 | 662k | m_pTmpStrm = nullptr; |
531 | 662k | } |
532 | 3.59M | if( m_bRemoved ) |
533 | | // Delete the stream if needed |
534 | 0 | if( m_pStgStrm ) |
535 | 0 | m_pStgStrm->SetSize( 0 ); |
536 | 3.59M | } |
537 | 579k | else if( m_aEntry.GetType() == STG_STORAGE && m_bDirect && bRes ) |
538 | 48.3k | { |
539 | 48.3k | StgIterator aIter( *this ); |
540 | 243k | for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() ) |
541 | 195k | bRes = p->Commit(); |
542 | 48.3k | } |
543 | 4.17M | return bRes; |
544 | 4.17M | } |
545 | | |
546 | | // Copy the stg stream to the temp stream |
547 | | |
548 | | bool StgDirEntry::Strm2Tmp() |
549 | 662k | { |
550 | 662k | if( !m_pTmpStrm ) |
551 | 662k | { |
552 | 662k | sal_uInt64 n = 0; |
553 | 662k | if( m_pCurStrm ) |
554 | 321 | { |
555 | | // It was already committed once |
556 | 321 | m_pTmpStrm = new StgTmpStrm; |
557 | 321 | if( m_pTmpStrm->GetError() == ERRCODE_NONE && m_pTmpStrm->Copy( *m_pCurStrm ) ) |
558 | 321 | return true; |
559 | 0 | n = 1; // indicates error |
560 | 0 | } |
561 | 661k | else |
562 | 661k | { |
563 | 661k | n = m_aEntry.GetSize(); |
564 | 661k | m_pTmpStrm = new StgTmpStrm( n ); |
565 | 661k | if( m_pTmpStrm->GetError() == ERRCODE_NONE ) |
566 | 661k | { |
567 | 661k | if( n ) |
568 | 0 | { |
569 | 0 | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
570 | 0 | if ( !m_pStgStrm ) |
571 | 0 | return false; |
572 | | |
573 | 0 | sal_uInt8 aTempBytes[ 4096 ]; |
574 | 0 | void* p = static_cast<void*>( aTempBytes ); |
575 | 0 | m_pStgStrm->Pos2Page( 0 ); |
576 | 0 | while( n ) |
577 | 0 | { |
578 | 0 | sal_uInt64 nn = n; |
579 | 0 | if( nn > 4096 ) |
580 | 0 | nn = 4096; |
581 | 0 | if( static_cast<sal_uInt64>(m_pStgStrm->Read( p, nn )) != nn ) |
582 | 0 | break; |
583 | 0 | if (m_pTmpStrm->WriteBytes( p, nn ) != nn) |
584 | 0 | break; |
585 | 0 | n -= nn; |
586 | 0 | } |
587 | 0 | m_pStgStrm->Pos2Page( m_nPos ); |
588 | 0 | m_pTmpStrm->Seek( m_nPos ); |
589 | 0 | } |
590 | 661k | } |
591 | 0 | else |
592 | 0 | n = 1; |
593 | 661k | } |
594 | | |
595 | 661k | if( n ) |
596 | 0 | { |
597 | 0 | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
598 | 0 | if ( m_pStgStrm ) |
599 | 0 | m_pStgStrm->GetIo().SetError( m_pTmpStrm->GetError() ); |
600 | |
|
601 | 0 | delete m_pTmpStrm; |
602 | 0 | m_pTmpStrm = nullptr; |
603 | 0 | return false; |
604 | 0 | } |
605 | 661k | } |
606 | 661k | return true; |
607 | 662k | } |
608 | | |
609 | | // Copy the temp stream to the stg stream during the final commit |
610 | | |
611 | | bool StgDirEntry::Tmp2Strm() |
612 | 761k | { |
613 | | // We did commit once, but have not written since then |
614 | 761k | if( !m_pTmpStrm ) |
615 | 761k | { |
616 | 761k | m_pTmpStrm = m_pCurStrm; |
617 | 761k | m_pCurStrm = nullptr; |
618 | 761k | } |
619 | 761k | if( m_pTmpStrm ) |
620 | 647k | { |
621 | 647k | OSL_ENSURE( m_pStgStrm, "The pointer may not be NULL!" ); |
622 | 647k | if ( !m_pStgStrm ) |
623 | 0 | return false; |
624 | 647k | sal_uInt64 n = m_pTmpStrm->GetSize(); |
625 | 647k | std::unique_ptr<StgStrm> pNewStrm; |
626 | 647k | StgIo& rIo = m_pStgStrm->GetIo(); |
627 | 647k | sal_uInt64 nThreshold = rIo.m_aHdr.GetThreshold(); |
628 | 647k | if( n < nThreshold ) |
629 | 619k | pNewStrm.reset(new StgSmallStrm( rIo, STG_EOF )); |
630 | 27.7k | else |
631 | 27.7k | pNewStrm.reset(new StgDataStrm( rIo, STG_EOF )); |
632 | 647k | if( pNewStrm->SetSize( n ) ) |
633 | 647k | { |
634 | 647k | sal_uInt8 p[ 4096 ]; |
635 | 647k | m_pTmpStrm->Seek( 0 ); |
636 | 1.06M | while( n ) |
637 | 422k | { |
638 | 422k | sal_uInt64 nn = n; |
639 | 422k | if( nn > 4096 ) |
640 | 163k | nn = 4096; |
641 | 422k | if (m_pTmpStrm->ReadBytes( p, nn ) != nn) |
642 | 0 | break; |
643 | 422k | if( static_cast<sal_uInt64>(pNewStrm->Write( p, nn )) != nn ) |
644 | 0 | break; |
645 | 422k | n -= nn; |
646 | 422k | } |
647 | 647k | if( n ) |
648 | 0 | { |
649 | 0 | m_pTmpStrm->Seek( m_nPos ); |
650 | 0 | m_pStgStrm->GetIo().SetError( m_pTmpStrm->GetError() ); |
651 | 0 | return false; |
652 | 0 | } |
653 | 647k | else |
654 | 647k | { |
655 | 647k | m_pStgStrm->SetSize( 0 ); |
656 | 647k | delete m_pStgStrm; |
657 | 647k | m_pStgStrm = pNewStrm.release(); |
658 | 647k | m_pStgStrm->SetEntry(*this); |
659 | 647k | m_pStgStrm->Pos2Page(m_nPos); |
660 | 647k | delete m_pTmpStrm; |
661 | 647k | delete m_pCurStrm; |
662 | 647k | m_pTmpStrm = m_pCurStrm = nullptr; |
663 | 647k | m_aSave = m_aEntry; |
664 | 647k | } |
665 | 647k | } |
666 | 647k | } |
667 | 761k | return true; |
668 | 761k | } |
669 | | |
670 | | // Invalidate all open entries by setting the RefCount to 0. If the bDel |
671 | | // flag is set, also set the invalid flag to indicate deletion during the |
672 | | // next dir stream flush. |
673 | | |
674 | | void StgDirEntry::Invalidate( bool bDel ) |
675 | 7.26M | { |
676 | | // nRefCnt = 0; |
677 | 7.26M | if( bDel ) |
678 | 0 | m_bRemoved = m_bInvalid = true; |
679 | 7.26M | switch( m_aEntry.GetType() ) |
680 | 7.26M | { |
681 | 1.69M | case STG_STORAGE: |
682 | 1.85M | case STG_ROOT: |
683 | 1.85M | { |
684 | 1.85M | StgIterator aIter( *this ); |
685 | 8.50M | for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() ) |
686 | 6.65M | p->Invalidate( bDel ); |
687 | 1.85M | break; |
688 | 1.69M | } |
689 | 5.40M | default: |
690 | 5.40M | break; |
691 | 7.26M | } |
692 | 7.26M | } |
693 | | |
694 | | ///////////////////////////// class StgDirStrm |
695 | | |
696 | | // This specialized stream is the maintenance stream for the directory tree. |
697 | | |
698 | | StgDirStrm::StgDirStrm( StgIo& r ) |
699 | 155k | : StgDataStrm( r, r.m_aHdr.GetTOCStart(), -1 ) |
700 | 155k | , m_pRoot( nullptr ) |
701 | 155k | { |
702 | 155k | if( r.GetError() ) |
703 | 884 | return; |
704 | 154k | if( m_nStart == STG_EOF ) |
705 | 58.0k | { |
706 | 58.0k | StgEntry aRoot; |
707 | 58.0k | aRoot.Init(); |
708 | 58.0k | static constexpr OUStringLiteral sRootEntry = u"Root Entry"; |
709 | 58.0k | aRoot.SetName( sRootEntry ); |
710 | 58.0k | aRoot.SetType( STG_ROOT ); |
711 | 58.0k | m_pRoot = new StgDirEntry( std::move(aRoot) ); |
712 | 58.0k | m_pRoot->SetDirty(); |
713 | 58.0k | } |
714 | 96.8k | else |
715 | 96.8k | { |
716 | | // temporarily use this instance as owner, so |
717 | | // the TOC pages can be removed. |
718 | 96.8k | m_pEntry = reinterpret_cast<StgDirEntry*>(this); // just for a bit pattern |
719 | 96.8k | SetupEntry( 0, m_pRoot ); |
720 | 96.8k | m_pEntry = nullptr; |
721 | 96.8k | } |
722 | 154k | } |
723 | | |
724 | | StgDirStrm::~StgDirStrm() |
725 | 155k | { |
726 | 155k | delete m_pRoot; |
727 | 155k | } |
728 | | |
729 | | // Recursively parse the directory tree during reading the TOC stream |
730 | | |
731 | | void StgDirStrm::SetupEntry( sal_Int32 n, StgDirEntry* pUpper ) |
732 | 5.79M | { |
733 | 5.79M | void* p = ( n == STG_FREE ) ? nullptr : GetEntry( n, false ); |
734 | 5.79M | if( !p ) |
735 | 3.58M | return; |
736 | | |
737 | 2.20M | SvStream *pUnderlyingStream = m_rIo.GetStrm(); |
738 | 2.20M | sal_uInt64 nUnderlyingStreamSize = pUnderlyingStream->TellEnd(); |
739 | | |
740 | 2.20M | bool bOk(false); |
741 | 2.20M | std::unique_ptr<StgDirEntry> pCur(new StgDirEntry( p, STGENTRY_SIZE, nUnderlyingStreamSize, &bOk )); |
742 | | |
743 | 2.20M | if( !bOk ) |
744 | 8.64k | { |
745 | 8.64k | m_rIo.SetError( SVSTREAM_GENERALERROR ); |
746 | | // an error occurred |
747 | 8.64k | return; |
748 | 8.64k | } |
749 | | |
750 | | // better it is |
751 | 2.19M | if( !pUpper ) |
752 | 102k | pCur->m_aEntry.SetType( STG_ROOT ); |
753 | | |
754 | 2.19M | sal_Int32 nLeft = pCur->m_aEntry.GetLeaf( STG_LEFT ); |
755 | 2.19M | sal_Int32 nRight = pCur->m_aEntry.GetLeaf( STG_RIGHT ); |
756 | | // substorage? |
757 | 2.19M | sal_Int32 nLeaf = STG_FREE; |
758 | 2.19M | if( pCur->m_aEntry.GetType() == STG_STORAGE || pCur->m_aEntry.GetType() == STG_ROOT ) |
759 | 654k | { |
760 | 654k | nLeaf = pCur->m_aEntry.GetLeaf( STG_CHILD ); |
761 | 654k | if (nLeaf != STG_FREE && nLeaf == n) |
762 | 1.30k | { |
763 | 1.30k | m_rIo.SetError( SVSTREAM_GENERALERROR ); |
764 | 1.30k | return; |
765 | 1.30k | } |
766 | 654k | } |
767 | | |
768 | 2.19M | if( !(nLeaf != 0 && nLeft != 0 && nRight != 0) ) |
769 | 53.3k | return; |
770 | | |
771 | | //fdo#41642 |
772 | 2.13M | StgDirEntry *pUp = pUpper; |
773 | 9.03M | while (pUp) |
774 | 7.07M | { |
775 | 7.07M | if (pUp->m_aEntry.GetLeaf(STG_CHILD) == nLeaf) |
776 | 177k | { |
777 | 177k | SAL_WARN("sot", "Leaf node of upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link"); |
778 | 177k | return; |
779 | 177k | } |
780 | 6.89M | pUp = pUp->m_pUp; |
781 | 6.89M | } |
782 | | |
783 | 1.96M | if( StgAvlNode::Insert |
784 | 1.96M | ( reinterpret_cast<StgAvlNode**>( pUpper ? &pUpper->m_pDown : &m_pRoot ), pCur.get() ) ) |
785 | 1.89M | { |
786 | 1.89M | pCur->m_pUp = pUpper; |
787 | 1.89M | } |
788 | 63.8k | else |
789 | 63.8k | { |
790 | | // bnc#682484: There are some really broken docs out there |
791 | | // that contain duplicate entries in 'Directory' section |
792 | | // so don't set the error flag here and just skip those |
793 | | // (was: rIo.SetError( SVSTREAM_CANNOT_MAKE );) |
794 | 63.8k | return; |
795 | 63.8k | } |
796 | 1.89M | SetupEntry( nLeft, pUpper ); |
797 | 1.89M | SetupEntry( nRight, pUpper ); |
798 | 1.89M | SetupEntry( nLeaf, pCur.release() ); |
799 | 1.89M | } |
800 | | |
801 | | // Extend or shrink the directory stream. |
802 | | |
803 | | bool StgDirStrm::SetSize( sal_Int32 nBytes ) |
804 | 114k | { |
805 | | // Always allocate full pages |
806 | 114k | if ( nBytes < 0 ) |
807 | 0 | nBytes = 0; |
808 | | |
809 | 114k | nBytes = ( ( nBytes + m_nPageSize - 1 ) / m_nPageSize ) * m_nPageSize; |
810 | 114k | return StgStrm::SetSize( nBytes ); |
811 | 114k | } |
812 | | |
813 | | // Save the TOC stream into a new substream after saving all data streams |
814 | | |
815 | | bool StgDirStrm::Store() |
816 | 114k | { |
817 | 114k | if( !m_pRoot || !m_pRoot->IsDirty() ) |
818 | 0 | return true; |
819 | 114k | if( !m_pRoot->StoreStreams( m_rIo ) ) |
820 | 0 | return false; |
821 | | // After writing all streams, the data FAT stream has changed, |
822 | | // so we have to commit the root again |
823 | 114k | m_pRoot->Commit(); |
824 | | // We want a completely new stream, so fake an empty stream |
825 | 114k | sal_Int32 nOldStart = m_nStart; // save for later deletion |
826 | 114k | sal_Int32 nOldSize = m_nSize; |
827 | 114k | m_nStart = m_nPage = STG_EOF; |
828 | 114k | m_nSize = 0; |
829 | 114k | SetPos(0, true); |
830 | 114k | m_nOffset = 0; |
831 | | // Delete all temporary entries |
832 | 114k | m_pRoot->DelTemp( false ); |
833 | | // set the entry numbers |
834 | 114k | sal_Int32 n = 0; |
835 | 114k | m_pRoot->Enum( n ); |
836 | 114k | if( !SetSize( n * STGENTRY_SIZE ) ) |
837 | 0 | { |
838 | 0 | m_nStart = nOldStart; m_nSize = nOldSize; |
839 | 0 | m_pRoot->RevertAll(); |
840 | 0 | return false; |
841 | 0 | } |
842 | | // set up the cache elements for the new stream |
843 | 114k | if( !Copy( STG_FREE, m_nSize ) ) |
844 | 0 | { |
845 | 0 | m_pRoot->RevertAll(); |
846 | 0 | return false; |
847 | 0 | } |
848 | | // Write the data to the new stream |
849 | 114k | if( !m_pRoot->Store( *this ) ) |
850 | 0 | { |
851 | 0 | m_pRoot->RevertAll(); |
852 | 0 | return false; |
853 | 0 | } |
854 | | // fill any remaining entries with empty data |
855 | 114k | sal_Int32 ne = m_nSize / STGENTRY_SIZE; |
856 | 114k | StgEntry aEmpty; |
857 | 114k | aEmpty.Init(); |
858 | 389k | while( n < ne ) |
859 | 275k | { |
860 | 275k | void* p = GetEntry( n++, true ); |
861 | 275k | if( !p ) |
862 | 0 | { |
863 | 0 | m_pRoot->RevertAll(); |
864 | 0 | return false; |
865 | 0 | } |
866 | 275k | aEmpty.Store( p ); |
867 | 275k | } |
868 | | // Now we can release the old stream |
869 | 114k | m_pFat->FreePages( nOldStart, true ); |
870 | 114k | m_rIo.m_aHdr.SetTOCStart( m_nStart ); |
871 | 114k | return true; |
872 | 114k | } |
873 | | |
874 | | // Get a dir entry. |
875 | | |
876 | | void* StgDirStrm::GetEntry( sal_Int32 n, bool bDirty ) |
877 | 3.87M | { |
878 | 3.87M | return n < 0 || n >= m_nSize / STGENTRY_SIZE |
879 | 3.87M | ? nullptr : GetPtr( n * STGENTRY_SIZE, bDirty ); |
880 | 3.87M | } |
881 | | |
882 | | // Find a dir entry. |
883 | | |
884 | | StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const OUString& rName ) |
885 | 4.67M | { |
886 | 4.67M | if( rStg.m_pDown ) |
887 | 4.33M | { |
888 | 4.33M | StgEntry aEntry; |
889 | 4.33M | aEntry.Init(); |
890 | 4.33M | aEntry.SetName( rName ); |
891 | | // Look in the directory attached to the entry |
892 | 4.33M | StgDirEntry aTest( std::move(aEntry) ); |
893 | 4.33M | return static_cast<StgDirEntry*>( rStg.m_pDown->Find( &aTest ) ); |
894 | 4.33M | } |
895 | 340k | else |
896 | 340k | return nullptr; |
897 | 4.67M | } |
898 | | |
899 | | // Create a new entry. |
900 | | |
901 | | StgDirEntry* StgDirStrm::Create( StgDirEntry& rStg, const OUString& rName, StgEntryType eType ) |
902 | 847k | { |
903 | 847k | StgEntry aEntry; |
904 | 847k | aEntry.Init(); |
905 | 847k | aEntry.SetType( eType ); |
906 | 847k | aEntry.SetName( rName ); |
907 | 847k | StgDirEntry* pRes = Find( rStg, rName ); |
908 | 847k | if( pRes ) |
909 | 0 | { |
910 | 0 | if( !pRes->m_bInvalid ) |
911 | 0 | { |
912 | 0 | m_rIo.SetError( SVSTREAM_CANNOT_MAKE ); |
913 | 0 | return nullptr; |
914 | 0 | } |
915 | 0 | pRes->m_bInvalid = |
916 | 0 | pRes->m_bRemoved = |
917 | 0 | pRes->m_bTemp = false; |
918 | 0 | pRes->m_bDirty = true; |
919 | 0 | return pRes; |
920 | 0 | } |
921 | 847k | else |
922 | 847k | { |
923 | 847k | std::unique_ptr<StgDirEntry> pNewRes(new StgDirEntry( std::move(aEntry) )); |
924 | 847k | if( StgAvlNode::Insert( reinterpret_cast<StgAvlNode**>(&rStg.m_pDown), pNewRes.get() ) ) |
925 | 847k | { |
926 | 847k | pNewRes->m_pUp = &rStg; |
927 | 847k | pNewRes->m_bDirty = true; |
928 | 847k | } |
929 | 0 | else |
930 | 0 | { |
931 | 0 | m_rIo.SetError( SVSTREAM_CANNOT_MAKE ); |
932 | 0 | pNewRes.reset(); |
933 | 0 | } |
934 | 847k | return pNewRes.release(); |
935 | 847k | } |
936 | 847k | } |
937 | | |
938 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |