Branch data Line data Source code
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 <string.h> // memcpy()
22 : :
23 : : #include "sot/stg.hxx"
24 : : #include "stgelem.hxx"
25 : : #include "stgcache.hxx"
26 : : #include "stgstrms.hxx"
27 : : #include "stgdir.hxx"
28 : : #include "stgio.hxx"
29 : :
30 : :
31 : : //////////////////////////// class StgDirEntry /////////////////////////////
32 : :
33 : : // This class holds the dir entry data and maintains dirty flags for both
34 : : // the entry and the data.
35 : :
36 : : // Transacted mode for streams: On the first write, a temp stream pTmpStrm
37 : : // is created and operated on. A commit moves pTmpStrm to pCurStrm, which
38 : : // is used for subsequent reads. A new write creates a new copy of pTmpStrm
39 : : // based on pCurStrm. Reverting throws away pTmpStrm.
40 : : // Transacted mode for storages: A copy of the dir ents is kept in aSave.
41 : : // Committing means copying aEntry to aSave. Reverting means to copy aSave
42 : : // to aEntry, delete newly created entries and to reactivate removed entries.
43 : :
44 : : // Problem der Implementation: Keine Hierarchischen commits. Daher nur
45 : : // insgesamt transaktionsorientert oder direkt.
46 : :
47 [ + - ][ + - ]: 2990 : StgDirEntry::StgDirEntry( const void* pBuffer, sal_uInt32 nBufferLen, sal_Bool * pbOk ) : StgAvlNode()
48 : : {
49 [ + - ]: 2990 : *pbOk = aEntry.Load( pBuffer, nBufferLen );
50 : :
51 [ + - ]: 2990 : InitMembers();
52 : 2990 : }
53 : :
54 [ + - ][ + - ]: 8331 : StgDirEntry::StgDirEntry( const StgEntry& r ) : StgAvlNode(), aEntry( r )
55 : : {
56 [ + - ]: 8331 : InitMembers();
57 : 8331 : }
58 : :
59 : : // Helper for all ctors
60 : :
61 : 11321 : void StgDirEntry::InitMembers()
62 : : {
63 : 11321 : aSave = aEntry;
64 : : pUp =
65 : 11321 : pDown = NULL;
66 : 11321 : ppRoot = NULL;
67 : 11321 : pStgStrm = NULL;
68 : : pCurStrm =
69 : 11321 : pTmpStrm = NULL;
70 : : nPos =
71 : : nEntry =
72 : 11321 : nRefCnt = 0;
73 : 11321 : nMode = STREAM_READ;
74 : 11321 : bDirect = sal_True;
75 : : bInvalid =
76 : : bCreated =
77 : : bRenamed =
78 : : bRemoved =
79 : : bTemp =
80 : : bDirty =
81 : 11321 : bZombie = sal_False;
82 : 11321 : }
83 : :
84 [ + - ][ + - ]: 11255 : StgDirEntry::~StgDirEntry()
85 : : {
86 [ + - ]: 11255 : Close();
87 [ - + ][ # # ]: 11255 : delete pCurStrm;
88 [ + + ][ + - ]: 11255 : delete pStgStrm;
89 [ + + ][ + - ]: 11255 : delete pDown;
90 [ - + ]: 15224 : }
91 : :
92 : : // Comparison function
93 : :
94 : 76750 : short StgDirEntry::Compare( const StgAvlNode* p ) const
95 : : {
96 : 76750 : short nResult = -1;
97 [ + - ]: 76750 : if ( p )
98 : : {
99 : 76750 : const StgDirEntry* pEntry = (const StgDirEntry*) p;
100 : 76750 : nResult = aEntry.Compare( pEntry->aEntry );
101 : : }
102 : 76750 : return nResult;
103 : : }
104 : :
105 : : // Enumerate the entry numbers.
106 : : // n is incremented to show the total # of entries.
107 : : // These number are later used as page numbers when storing
108 : : // the TOC tree into the TOC stream. Remember that aSave is
109 : : // stored, not aEntry.
110 : :
111 : 1165 : void StgDirEntry::Enum( sal_Int32& n )
112 : : {
113 : 1165 : sal_Int32 nLeft = STG_FREE, nRight = STG_FREE, nDown = STG_FREE;
114 : 1165 : nEntry = n++;
115 [ + + ]: 1165 : if( pLeft )
116 : : {
117 : 40 : ((StgDirEntry*) pLeft)->Enum( n ); nLeft = ((StgDirEntry*) pLeft)->nEntry;
118 : : }
119 [ + + ]: 1165 : if( pRight )
120 : : {
121 : 847 : ((StgDirEntry*) pRight)->Enum( n ); nRight = ((StgDirEntry*) pRight)->nEntry;
122 : : }
123 [ + + ]: 1165 : if( pDown )
124 : : {
125 : 119 : pDown->Enum( n ); nDown = pDown->nEntry;
126 : : }
127 : 1165 : aSave.SetLeaf( STG_LEFT, nLeft );
128 : 1165 : aSave.SetLeaf( STG_RIGHT, nRight );
129 : 1165 : aSave.SetLeaf( STG_CHILD, nDown );
130 : 1165 : }
131 : :
132 : : // Delete all temporary entries before writing the TOC stream.
133 : : // Until now Deltem is never called with bForce True
134 : :
135 : 1174 : void StgDirEntry::DelTemp( sal_Bool bForce )
136 : : {
137 [ + + ]: 1174 : if( pLeft )
138 : 49 : ((StgDirEntry*) pLeft)->DelTemp( sal_False );
139 [ + + ]: 1174 : if( pRight )
140 : 847 : ((StgDirEntry*) pRight)->DelTemp( sal_False );
141 [ + + ]: 1174 : if( pDown )
142 : : {
143 : : // If the storage is dead, of course all elements are dead, too
144 [ - + ][ # # ]: 119 : if( bInvalid && aEntry.GetType() == STG_STORAGE )
[ - + ]
145 : 0 : bForce = sal_True;
146 : 119 : pDown->DelTemp( bForce );
147 : : }
148 [ + - ]: 1183 : if( ( bForce || bInvalid )
[ + + + - ]
[ + + ]
149 : 9 : && ( aEntry.GetType() != STG_ROOT ) /* && ( nRefCnt <= 1 ) */ )
150 : : {
151 : 9 : Close();
152 [ + - ]: 9 : if( pUp )
153 : : {
154 : : // this deletes the element if refcnt == 0!
155 : 9 : sal_Bool bDel = nRefCnt == 0;
156 : 9 : StgAvlNode::Remove( (StgAvlNode**) &pUp->pDown, this, bDel );
157 [ - + ]: 9 : if( !bDel )
158 : : {
159 : 0 : pLeft = pRight = pDown = 0;
160 : 0 : bInvalid = bZombie = sal_True;
161 : : }
162 : : }
163 : : }
164 : 1174 : }
165 : :
166 : : // Save the tree into the given dir stream
167 : :
168 : 1165 : sal_Bool StgDirEntry::Store( StgDirStrm& rStrm )
169 : : {
170 : 1165 : void* pEntry = rStrm.GetEntry( nEntry, sal_True );
171 [ - + ]: 1165 : if( !pEntry )
172 : 0 : return sal_False;
173 : : // Do not store the current (maybe not commited) entry
174 : 1165 : aSave.Store( pEntry );
175 [ + + ]: 1165 : if( pLeft )
176 [ - + ]: 40 : if( !((StgDirEntry*) pLeft)->Store( rStrm ) )
177 : 0 : return sal_False;
178 [ + + ]: 1165 : if( pRight )
179 [ - + ]: 847 : if( !((StgDirEntry*) pRight)->Store( rStrm ) )
180 : 0 : return sal_False;
181 [ + + ]: 1165 : if( pDown )
182 [ - + ]: 119 : if( !pDown->Store( rStrm ) )
183 : 0 : return sal_False;
184 : 1165 : return sal_True;
185 : : }
186 : :
187 : 1174 : sal_Bool StgDirEntry::StoreStream( StgIo& rIo )
188 : : {
189 [ + + ][ + + ]: 1174 : if( aEntry.GetType() == STG_STREAM || aEntry.GetType() == STG_ROOT )
[ + + ]
190 : : {
191 [ + + ]: 1136 : if( bInvalid )
192 : : {
193 : : // Delete the stream if needed
194 [ - + ]: 9 : if( !pStgStrm )
195 : : {
196 : 0 : OpenStream( rIo );
197 [ # # ]: 0 : delete pStgStrm, pStgStrm = NULL;
198 : : }
199 : : else
200 : 9 : pStgStrm->SetSize( 0 );
201 : : }
202 : : // or write the data stream
203 [ - + ]: 1127 : else if( !Tmp2Strm() )
204 : 0 : return sal_False;
205 : : }
206 : 1174 : return sal_True;
207 : : }
208 : :
209 : : // Save all dirty streams
210 : :
211 : 1174 : sal_Bool StgDirEntry::StoreStreams( StgIo& rIo )
212 : : {
213 [ - + ]: 1174 : if( !StoreStream( rIo ) )
214 : 0 : return sal_False;
215 [ + + ]: 1174 : if( pLeft )
216 [ - + ]: 49 : if( !((StgDirEntry*) pLeft)->StoreStreams( rIo ) )
217 : 0 : return sal_False;
218 [ + + ]: 1174 : if( pRight )
219 [ - + ]: 847 : if( !((StgDirEntry*) pRight)->StoreStreams( rIo ) )
220 : 0 : return sal_False;
221 [ + + ]: 1174 : if( pDown )
222 [ - + ]: 119 : if( !pDown->StoreStreams( rIo ) )
223 : 0 : return sal_False;
224 : 1174 : return sal_True;
225 : : }
226 : :
227 : : // Revert all directory entries after failure to write the TOC stream
228 : :
229 : 0 : void StgDirEntry::RevertAll()
230 : : {
231 : 0 : aEntry = aSave;
232 [ # # ]: 0 : if( pLeft )
233 : 0 : ((StgDirEntry*) pLeft)->RevertAll();
234 [ # # ]: 0 : if( pRight )
235 : 0 : ((StgDirEntry*) pRight)->RevertAll();
236 [ # # ]: 0 : if( pDown )
237 : 0 : pDown->RevertAll();
238 : 0 : }
239 : :
240 : : // Look if any element of the tree is dirty
241 : :
242 : 159 : sal_Bool StgDirEntry::IsDirty()
243 : : {
244 [ - + ][ # # ]: 159 : if( bDirty || bInvalid )
245 : 159 : return sal_True;
246 [ # # ][ # # ]: 0 : if( pLeft && ((StgDirEntry*) pLeft)->IsDirty() )
[ # # ]
247 : 0 : return sal_True;
248 [ # # ][ # # ]: 0 : if( pRight && ((StgDirEntry*) pRight)->IsDirty() )
[ # # ]
249 : 0 : return sal_True;
250 [ # # ][ # # ]: 0 : if( pDown && pDown->IsDirty() )
[ # # ]
251 : 0 : return sal_True;
252 : 159 : return sal_False;
253 : : }
254 : :
255 : : // Set up a stream.
256 : :
257 : 3227 : void StgDirEntry::OpenStream( StgIo& rIo, sal_Bool bForceBig )
258 : : {
259 : 3227 : sal_Int32 nThreshold = (sal_uInt16) rIo.aHdr.GetThreshold();
260 [ + + ]: 3227 : delete pStgStrm;
261 [ + - ][ + + ]: 3227 : if( !bForceBig && aEntry.GetSize() < nThreshold )
[ + + ]
262 [ + - ]: 2515 : pStgStrm = new StgSmallStrm( rIo, *this );
263 : : else
264 [ + - ]: 712 : pStgStrm = new StgDataStrm( rIo, *this );
265 [ - + ][ # # ]: 3227 : if( bInvalid && aEntry.GetSize() )
[ - + ]
266 : : {
267 : : // This entry has invalid data, so delete that data
268 : 0 : SetSize( 0L );
269 : : // bRemoved = bInvalid = sal_False;
270 : : }
271 : 3227 : nPos = 0;
272 : 3227 : }
273 : :
274 : : // Close the open stream without committing. If the entry is marked as
275 : : // temporary, delete it.
276 : : // Do not delete pCurStrm here!
277 : : // (TLX:??? Zumindest pStgStrm muss deleted werden.)
278 : :
279 : 14953 : void StgDirEntry::Close()
280 : : {
281 [ - + ]: 14953 : delete pTmpStrm;
282 : 14953 : pTmpStrm = NULL;
283 : : // nRefCnt = 0;
284 : 14953 : bInvalid = bTemp;
285 : 14953 : }
286 : :
287 : : // Get the current stream size
288 : :
289 : 15728 : sal_Int32 StgDirEntry::GetSize()
290 : : {
291 : : sal_Int32 n;
292 [ - + ]: 15728 : if( pTmpStrm )
293 : 0 : n = pTmpStrm->GetSize();
294 [ - + ]: 15728 : else if( pCurStrm )
295 : 0 : n = pCurStrm->GetSize();
296 : 15728 : else n = aEntry.GetSize();
297 : 15728 : return n;
298 : : }
299 : :
300 : : // Set the stream size. This means also creating a temp stream.
301 : :
302 : 473 : sal_Bool StgDirEntry::SetSize( sal_Int32 nNewSize )
303 : : {
304 [ + - ][ + - ]: 946 : if (
[ + - - + ]
[ - + ]
305 : 473 : !( nMode & STREAM_WRITE ) ||
306 : 473 : (!bDirect && !pTmpStrm && !Strm2Tmp())
307 : : )
308 : : {
309 : 0 : return sal_False;
310 : : }
311 : :
312 [ - + ]: 473 : if( nNewSize < nPos )
313 : 0 : nPos = nNewSize;
314 [ + - ]: 473 : if( pTmpStrm )
315 : : {
316 : 473 : pTmpStrm->SetSize( nNewSize );
317 : 473 : pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
318 : 473 : return sal_Bool( pTmpStrm->GetError() == SVSTREAM_OK );
319 : : }
320 : : else
321 : : {
322 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
323 [ # # ]: 0 : if ( !pStgStrm )
324 : 0 : return sal_False;
325 : :
326 : 0 : sal_Bool bRes = sal_False;
327 : 0 : StgIo& rIo = pStgStrm->GetIo();
328 : 0 : sal_Int32 nThreshold = rIo.aHdr.GetThreshold();
329 : : // ensure the correct storage stream!
330 : 0 : StgStrm* pOld = NULL;
331 : 0 : sal_uInt16 nOldSize = 0;
332 [ # # ][ # # ]: 0 : if( nNewSize >= nThreshold && pStgStrm->IsSmallStrm() )
[ # # ]
333 : : {
334 : 0 : pOld = pStgStrm;
335 : 0 : nOldSize = (sal_uInt16) pOld->GetSize();
336 [ # # ]: 0 : pStgStrm = new StgDataStrm( rIo, STG_EOF, 0 );
337 : : }
338 [ # # ][ # # ]: 0 : else if( nNewSize < nThreshold && !pStgStrm->IsSmallStrm() )
[ # # ]
339 : : {
340 : 0 : pOld = pStgStrm;
341 : 0 : nOldSize = (sal_uInt16) nNewSize;
342 [ # # ]: 0 : pStgStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
343 : : }
344 : : // now set the new size
345 [ # # ]: 0 : if( pStgStrm->SetSize( nNewSize ) )
346 : : {
347 : : // did we create a new stream?
348 [ # # ]: 0 : if( pOld )
349 : : {
350 : : // if so, we probably need to copy the old data
351 [ # # ]: 0 : if( nOldSize )
352 : : {
353 : 0 : void* pBuf = new sal_uInt8[ nOldSize ];
354 : 0 : pOld->Pos2Page( 0L );
355 : 0 : pStgStrm->Pos2Page( 0L );
356 [ # # ]: 0 : if( pOld->Read( pBuf, nOldSize )
[ # # # # ]
357 : 0 : && pStgStrm->Write( pBuf, nOldSize ) )
358 : 0 : bRes = sal_True;
359 [ # # ]: 0 : delete[] static_cast<sal_uInt8*>(pBuf);
360 : : }
361 : : else
362 : 0 : bRes = sal_True;
363 [ # # ]: 0 : if( bRes )
364 : : {
365 : 0 : pOld->SetSize( 0 );
366 [ # # ]: 0 : delete pOld;
367 : 0 : pStgStrm->Pos2Page( nPos );
368 : 0 : pStgStrm->SetEntry( *this );
369 : : }
370 : : else
371 : : {
372 : 0 : pStgStrm->SetSize( 0 );
373 [ # # ]: 0 : delete pStgStrm;
374 : 0 : pStgStrm = pOld;
375 : : }
376 : : }
377 : : else
378 : : {
379 : 0 : pStgStrm->Pos2Page( nPos );
380 : 0 : bRes = sal_True;
381 : : }
382 : : }
383 : 473 : return bRes;
384 : : }
385 : : }
386 : :
387 : : // Seek. On negative values, seek to EOF.
388 : :
389 : 4232291 : sal_Int32 StgDirEntry::Seek( sal_Int32 nNew )
390 : : {
391 [ + + ]: 4232291 : if( pTmpStrm )
392 : : {
393 [ + + ]: 2890 : if( nNew < 0 )
394 : 162 : nNew = pTmpStrm->GetSize();
395 : 2890 : nNew = pTmpStrm->Seek( nNew );
396 : : }
397 [ + + ]: 4229401 : else if( pCurStrm )
398 : : {
399 [ - + ]: 27 : if( nNew < 0 )
400 : 0 : nNew = pCurStrm->GetSize();
401 : 27 : nNew = pCurStrm->Seek( nNew );
402 : : }
403 : : else
404 : : {
405 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
406 [ - + ]: 4229374 : if ( !pStgStrm )
407 : 0 : return nPos;
408 : :
409 : 4229374 : sal_Int32 nSize = aEntry.GetSize();
410 : :
411 [ + + ]: 4229374 : if( nNew < 0 )
412 : 356 : nNew = nSize;
413 : :
414 : : // try to enlarge, the readonly streams should not allow this
415 [ + + ]: 4229374 : if( nNew > nSize )
416 : : {
417 [ - + ][ # # ]: 1326 : if ( !( nMode & STREAM_WRITE ) || !SetSize( nNew ) )
[ + - ]
418 : : {
419 : : OSL_ENSURE( nMode & STREAM_WRITE, "Trying to resize readonly stream by seeking, could be a wrong offset!" );
420 : 1326 : return nPos;
421 : : }
422 : : else
423 : 0 : return Seek( nNew );
424 : : }
425 : 4228048 : pStgStrm->Pos2Page( nNew );
426 : 4228048 : nNew = pStgStrm->GetPos();
427 : : }
428 : :
429 : 4232291 : return nPos = nNew;
430 : : }
431 : :
432 : : // Read
433 : :
434 : 2244715 : sal_Int32 StgDirEntry::Read( void* p, sal_Int32 nLen )
435 : : {
436 [ + + ]: 2244715 : if( nLen <= 0 )
437 : 15 : return 0;
438 [ - + ]: 2244700 : if( pTmpStrm )
439 : 0 : nLen = pTmpStrm->Read( p, nLen );
440 [ - + ]: 2244700 : else if( pCurStrm )
441 : 0 : nLen = pCurStrm->Read( p, nLen );
442 : : else
443 : : {
444 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
445 [ - + ]: 2244700 : if ( !pStgStrm )
446 : 0 : return 0;
447 : :
448 : 2244700 : nLen = pStgStrm->Read( p, nLen );
449 : : }
450 : :
451 : 2244700 : nPos += nLen;
452 : 2244715 : return nLen;
453 : : }
454 : :
455 : : // Write
456 : :
457 : 2040 : sal_Int32 StgDirEntry::Write( const void* p, sal_Int32 nLen )
458 : : {
459 [ + - ][ - + ]: 2040 : if( nLen <= 0 || !( nMode & STREAM_WRITE ) )
460 : 0 : return 0;
461 : :
462 : : // Was this stream committed internally and reopened in direct mode?
463 [ - + ][ # # ]: 2040 : if( bDirect && ( pCurStrm || pTmpStrm ) && !Tmp2Strm() )
[ # # ][ # # ]
[ - + ]
464 : 0 : return 0;
465 : : // Is this stream opened in transacted mode? Do we have to make a copy?
466 [ + - ][ + + ]: 2040 : if( !bDirect && !pTmpStrm && !Strm2Tmp() )
[ - + ][ - + ]
467 : 0 : return 0;
468 : :
469 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
470 [ - + ]: 2040 : if ( !pStgStrm )
471 : 0 : return 0;
472 : :
473 [ + - ]: 2040 : if( pTmpStrm )
474 : : {
475 : 2040 : nLen = pTmpStrm->Write( p, nLen );
476 : 2040 : pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
477 : : }
478 : : else
479 : : {
480 : 0 : sal_Int32 nNew = nPos + nLen;
481 [ # # ]: 0 : if( nNew > pStgStrm->GetSize() )
482 : : {
483 [ # # ]: 0 : if( !SetSize( nNew ) )
484 : 0 : return 0L;
485 : 0 : pStgStrm->Pos2Page( nPos );
486 : : }
487 : 0 : nLen = pStgStrm->Write( p, nLen );
488 : : }
489 : 2040 : nPos += nLen;
490 : 2040 : return nLen;
491 : : }
492 : :
493 : 455 : void StgDirEntry::Copy( BaseStorageStream& rDest )
494 : : {
495 : 455 : sal_Int32 n = GetSize();
496 [ + - ][ + - ]: 455 : if( rDest.SetSize( n ) && n )
[ + - ]
497 : : {
498 [ + - ]: 455 : sal_uLong Pos = rDest.Tell();
499 : : sal_uInt8 aTempBytes[ 4096 ];
500 : 455 : void* p = static_cast<void*>( aTempBytes );
501 [ + - ]: 455 : Seek( 0L );
502 [ + - ]: 455 : rDest.Seek( 0L );
503 [ + + ]: 1016 : while( n )
504 : : {
505 : 561 : sal_Int32 nn = n;
506 [ + + ]: 561 : if( nn > 4096 )
507 : 106 : nn = 4096;
508 [ + - ][ - + ]: 561 : if( Read( p, nn ) != nn )
509 : 0 : break;
510 [ + - ][ - + ]: 561 : if( sal::static_int_cast<sal_Int32>(rDest.Write( p, nn )) != nn )
511 : 0 : break;
512 : 561 : n -= nn;
513 : : }
514 [ + - ]: 455 : rDest.Seek( Pos ); // ?! Seems to be undocumented !
515 : : }
516 : 455 : }
517 : :
518 : : // Commit this entry
519 : :
520 : 4471 : sal_Bool StgDirEntry::Commit()
521 : : {
522 : : // OSL_ENSURE( nMode & STREAM_WRITE, "Trying to commit readonly stream!" );
523 : :
524 : 4471 : aSave = aEntry;
525 : 4471 : sal_Bool bRes = sal_True;
526 [ + + ]: 4471 : if( aEntry.GetType() == STG_STREAM )
527 : : {
528 [ + + ]: 4150 : if( pTmpStrm )
529 [ + + ]: 932 : delete pCurStrm, pCurStrm = pTmpStrm, pTmpStrm = NULL;
530 [ + + ]: 4150 : if( bRemoved )
531 : : // Delete the stream if needed
532 [ + - ]: 9 : if( pStgStrm )
533 : 9 : pStgStrm->SetSize( 0 );
534 : : }
535 [ + + ][ - + ]: 321 : else if( aEntry.GetType() == STG_STORAGE && bDirect && bRes )
[ # # ][ - + ]
536 : : {
537 [ # # ]: 0 : StgIterator aIter( *this );
538 [ # # ][ # # ]: 0 : for( StgDirEntry* p = aIter.First(); p && bRes; p = aIter.Next() )
[ # # ][ # # ]
[ # # ]
539 [ # # ]: 0 : bRes = p->Commit();
540 : : }
541 : 4471 : return bRes;
542 : : }
543 : :
544 : : // Revert the entry
545 : :
546 : 0 : sal_Bool StgDirEntry::Revert()
547 : : {
548 : 0 : aEntry = aSave;
549 [ # # # # ]: 0 : switch( aEntry.GetType() )
550 : : {
551 : : case STG_STREAM:
552 [ # # ]: 0 : if( pCurStrm )
553 [ # # ]: 0 : delete pTmpStrm, pTmpStrm = pCurStrm, pCurStrm = NULL;
554 : 0 : break;
555 : : case STG_STORAGE:
556 : : {
557 : 0 : sal_Bool bSomeRenamed = sal_False;
558 [ # # ]: 0 : StgIterator aOIter( *this );
559 [ # # ]: 0 : StgDirEntry* op = aOIter.First();
560 [ # # ]: 0 : while( op )
561 : : {
562 [ # # ]: 0 : op->aEntry = op->aSave;
563 : 0 : op->bDirty = sal_False;
564 : 0 : bSomeRenamed = sal_Bool( bSomeRenamed | op->bRenamed );
565 : : // Remove any new entries
566 [ # # ]: 0 : if( op->bCreated )
567 : : {
568 : 0 : op->bCreated = sal_False;
569 [ # # ]: 0 : op->Close();
570 : 0 : op->bInvalid = sal_True;
571 : : }
572 : : // Reactivate any removed entries
573 [ # # ]: 0 : else if( op->bRemoved )
574 : 0 : op->bRemoved = op->bInvalid = op->bTemp = sal_False;
575 [ # # ]: 0 : op = aOIter.Next();
576 : : }
577 : : // Resort all renamed entries
578 [ # # ]: 0 : if( bSomeRenamed )
579 : : {
580 [ # # ]: 0 : StgIterator aIter( *this );
581 [ # # ]: 0 : StgDirEntry* p = aIter.First();
582 [ # # ]: 0 : while( p )
583 : : {
584 [ # # ]: 0 : if( p->bRenamed )
585 : : {
586 : : StgAvlNode::Move
587 : : ( (StgAvlNode**) &p->pUp->pDown,
588 [ # # ]: 0 : (StgAvlNode**) &p->pUp->pDown, p );
589 : 0 : p->bRenamed = sal_False;
590 : : }
591 [ # # ]: 0 : p = aIter.Next();
592 : : }
593 : : }
594 [ # # ]: 0 : DelTemp( sal_False );
595 : : break;
596 : : }
597 : : case STG_EMPTY:
598 : : case STG_LOCKBYTES:
599 : : case STG_PROPERTY:
600 : : case STG_ROOT:
601 : 0 : break;
602 : : }
603 : 0 : return sal_True;
604 : : }
605 : :
606 : : // Copy the stg stream to the temp stream
607 : :
608 : 932 : sal_Bool StgDirEntry::Strm2Tmp()
609 : : {
610 [ + - ]: 932 : if( !pTmpStrm )
611 : : {
612 : 932 : sal_uLong n = 0;
613 [ + + ]: 932 : if( pCurStrm )
614 : : {
615 : : // It was already commited once
616 [ + - ]: 9 : pTmpStrm = new StgTmpStrm;
617 [ + - ][ + - ]: 9 : if( pTmpStrm->GetError() == SVSTREAM_OK && pTmpStrm->Copy( *pCurStrm ) )
[ + - ]
618 : 9 : return sal_True;
619 : 0 : n = 1; // indicates error
620 : : }
621 : : else
622 : : {
623 : 923 : n = aEntry.GetSize();
624 [ + - ]: 923 : pTmpStrm = new StgTmpStrm( n );
625 [ + - ]: 923 : if( pTmpStrm->GetError() == SVSTREAM_OK )
626 : : {
627 [ - + ]: 923 : if( n )
628 : : {
629 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
630 [ # # ]: 0 : if ( !pStgStrm )
631 : 0 : return sal_False;
632 : :
633 : : sal_uInt8 aTempBytes[ 4096 ];
634 : 0 : void* p = static_cast<void*>( aTempBytes );
635 [ # # ]: 0 : pStgStrm->Pos2Page( 0L );
636 [ # # ]: 0 : while( n )
637 : : {
638 : 0 : sal_uLong nn = n;
639 [ # # ]: 0 : if( nn > 4096 )
640 : 0 : nn = 4096;
641 [ # # ][ # # ]: 0 : if( (sal_uLong) pStgStrm->Read( p, nn ) != nn )
642 : 0 : break;
643 [ # # ][ # # ]: 0 : if( pTmpStrm->Write( p, nn ) != nn )
644 : 0 : break;
645 : 0 : n -= nn;
646 : : }
647 [ # # ]: 0 : pStgStrm->Pos2Page( nPos );
648 [ # # ]: 0 : pTmpStrm->Seek( nPos );
649 : : }
650 : : }
651 : : else
652 : 0 : n = 1;
653 : : }
654 : :
655 [ - + ]: 923 : if( n )
656 : : {
657 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
658 [ # # ]: 0 : if ( pStgStrm )
659 : 0 : pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
660 : :
661 [ # # ]: 0 : delete pTmpStrm;
662 : 0 : pTmpStrm = NULL;
663 : 0 : return sal_False;
664 : : }
665 : : }
666 : 932 : return sal_True;
667 : : }
668 : :
669 : : // Copy the temp stream to the stg stream during the final commit
670 : :
671 : 1127 : sal_Bool StgDirEntry::Tmp2Strm()
672 : : {
673 : : // We did commit once, but have not written since then
674 [ + - ]: 1127 : if( !pTmpStrm )
675 : 1127 : pTmpStrm = pCurStrm, pCurStrm = NULL;
676 [ + + ]: 1127 : if( pTmpStrm )
677 : : {
678 : : OSL_ENSURE( pStgStrm, "The pointer may not be NULL!" );
679 [ - + ]: 923 : if ( !pStgStrm )
680 : 0 : return sal_False;
681 : 923 : sal_uLong n = pTmpStrm->GetSize();
682 : : StgStrm* pNewStrm;
683 : 923 : StgIo& rIo = pStgStrm->GetIo();
684 : 923 : sal_uLong nThreshold = (sal_uLong) rIo.aHdr.GetThreshold();
685 [ + + ]: 923 : if( n < nThreshold )
686 [ + - ]: 838 : pNewStrm = new StgSmallStrm( rIo, STG_EOF, 0 );
687 : : else
688 [ + - ]: 85 : pNewStrm = new StgDataStrm( rIo, STG_EOF, 0 );
689 [ + - ]: 923 : if( pNewStrm->SetSize( n ) )
690 : : {
691 : : sal_uInt8 p[ 4096 ];
692 [ + - ]: 923 : pTmpStrm->Seek( 0L );
693 [ + + ]: 2067 : while( n )
694 : : {
695 : 1144 : sal_uLong nn = n;
696 [ + + ]: 1144 : if( nn > 4096 )
697 : 221 : nn = 4096;
698 [ + - ][ - + ]: 1144 : if( pTmpStrm->Read( p, nn ) != nn )
699 : 0 : break;
700 [ + - ][ - + ]: 1144 : if( (sal_uLong) pNewStrm->Write( p, nn ) != nn )
701 : 0 : break;
702 : 1144 : n -= nn;
703 : : }
704 [ - + ]: 923 : if( n )
705 : : {
706 [ # # ]: 0 : pTmpStrm->Seek( nPos );
707 [ # # ]: 0 : pStgStrm->GetIo().SetError( pTmpStrm->GetError() );
708 [ # # ][ # # ]: 0 : delete pNewStrm;
709 : 0 : return sal_False;
710 : : }
711 : : else
712 : : {
713 [ + - ]: 923 : pStgStrm->SetSize( 0L );
714 [ + - ][ + - ]: 923 : delete pStgStrm;
715 : 923 : pStgStrm = pNewStrm;
716 [ + - ]: 923 : pNewStrm->SetEntry( *this );
717 [ + - ]: 923 : pNewStrm->Pos2Page( nPos );
718 [ + - ][ + - ]: 923 : delete pTmpStrm;
719 [ - + ][ # # ]: 923 : delete pCurStrm;
720 : 923 : pTmpStrm = pCurStrm = NULL;
721 [ + - ]: 923 : aSave = aEntry;
722 : : }
723 : : }
724 : : }
725 : 1127 : return sal_True;
726 : : }
727 : :
728 : : // Check if the given entry is contained in this entry
729 : :
730 : 0 : sal_Bool StgDirEntry::IsContained( StgDirEntry* pStg )
731 : : {
732 [ # # ]: 0 : if( aEntry.GetType() == STG_STORAGE )
733 : : {
734 [ # # ]: 0 : StgIterator aIter( *this );
735 [ # # ]: 0 : StgDirEntry* p = aIter.First();
736 [ # # ]: 0 : while( p )
737 : : {
738 [ # # ][ # # ]: 0 : if( !p->aEntry.Compare( pStg->aEntry ) )
739 : 0 : return sal_False;
740 [ # # ]: 0 : if( p->aEntry.GetType() == STG_STORAGE )
741 [ # # ][ # # ]: 0 : if( !p->IsContained( pStg ) )
742 : 0 : return sal_False;
743 [ # # ]: 0 : p = aIter.Next();
744 : : }
745 : : }
746 : 0 : return sal_True;
747 : : }
748 : :
749 : : // Invalidate all open entries by setting the RefCount to 0. If the bDel
750 : : // flag is set, also set the invalid flag to indicate deletion during the
751 : : // next dir stream flush.
752 : :
753 : 5444 : void StgDirEntry::Invalidate( sal_Bool bDel )
754 : : {
755 : : // nRefCnt = 0;
756 [ + + ]: 5444 : if( bDel )
757 : 9 : bRemoved = bInvalid = sal_True;
758 [ + + ]: 5444 : switch( aEntry.GetType() )
759 : : {
760 : : case STG_STORAGE:
761 : : case STG_ROOT:
762 : : {
763 [ + - ]: 751 : StgIterator aIter( *this );
764 [ + - ][ + - ]: 5724 : for( StgDirEntry* p = aIter.First(); p; p = aIter.Next() )
[ + + ]
765 [ + - ]: 4973 : p->Invalidate( bDel );
766 : : break;
767 : : }
768 : : default:
769 : 4693 : break;
770 : : }
771 : 5444 : }
772 : :
773 : : ///////////////////////////// class StgDirStrm ////////////////////////////
774 : :
775 : : // This specialized stream is the maintenance stream for the directory tree.
776 : :
777 : 367 : StgDirStrm::StgDirStrm( StgIo& r )
778 : : : StgDataStrm( r, r.aHdr.GetTOCStart(), -1 )
779 : : , pRoot( NULL )
780 : 367 : , nEntries( 0 )
781 : : {
782 [ - + ]: 367 : if( r.GetError() )
783 : 367 : return;
784 : 367 : nEntries = nPageSize / STGENTRY_SIZE;
785 [ + + ]: 367 : if( nStart == STG_EOF )
786 : : {
787 [ + - ]: 75 : StgEntry aRoot;
788 [ + - ]: 75 : aRoot.Init();
789 [ + - ][ + - ]: 75 : aRoot.SetName( rtl::OUString("Root Entry") );
[ + - ]
790 : 75 : aRoot.SetType( STG_ROOT );
791 [ + - ][ + - ]: 75 : pRoot = new StgDirEntry( aRoot );
792 [ + - ]: 75 : pRoot->SetDirty();
793 : : }
794 : : else
795 : : {
796 : : // temporarily use this instance as owner, so
797 : : // the TOC pages can be removed.
798 : 292 : pEntry = (StgDirEntry*) this; // just for a bit pattern
799 [ + - ]: 292 : SetupEntry( 0, pRoot );
800 : 292 : pEntry = NULL;
801 : : }
802 : : }
803 : :
804 : 361 : StgDirStrm::~StgDirStrm()
805 : : {
806 [ + + ][ + - ]: 361 : delete pRoot;
807 [ - + ]: 722 : }
808 : :
809 : : // Recursively parse the directory tree during reading the TOC stream
810 : :
811 : 9208 : void StgDirStrm::SetupEntry( sal_Int32 n, StgDirEntry* pUpper )
812 : : {
813 [ + + ]: 9208 : void* p = ( n == STG_FREE ) ? NULL : GetEntry( n );
814 [ + + ]: 9208 : if( p )
815 : : {
816 : 2990 : sal_Bool bOk(sal_False);
817 [ + - ][ + - ]: 2990 : StgDirEntry* pCur = new StgDirEntry( p, STGENTRY_SIZE, &bOk );
818 : :
819 [ + + ]: 2990 : if( !bOk )
820 : : {
821 [ + - ][ + - ]: 9 : delete pCur;
822 [ + - ]: 9 : rIo.SetError( SVSTREAM_GENERALERROR );
823 : : // an error occurred
824 : : return;
825 : : }
826 : :
827 : : // better it is
828 [ + + ]: 2981 : if( !pUpper )
829 : 286 : pCur->aEntry.SetType( STG_ROOT );
830 : :
831 [ + - ]: 2981 : sal_Int32 nLeft = pCur->aEntry.GetLeaf( STG_LEFT );
832 [ + - ]: 2981 : sal_Int32 nRight = pCur->aEntry.GetLeaf( STG_RIGHT );
833 : : // substorage?
834 : 2981 : sal_Int32 nLeaf = STG_FREE;
835 [ + + ][ + + ]: 2981 : if( pCur->aEntry.GetType() == STG_STORAGE || pCur->aEntry.GetType() == STG_ROOT )
[ + + ]
836 : : {
837 [ + - ]: 497 : nLeaf = pCur->aEntry.GetLeaf( STG_CHILD );
838 [ + + ][ - + ]: 497 : if (nLeaf != STG_FREE && nLeaf == n)
839 : : {
840 [ # # ][ # # ]: 0 : delete pCur;
841 [ # # ]: 0 : rIo.SetError( SVSTREAM_GENERALERROR );
842 : : return;
843 : : }
844 : : }
845 : :
846 [ + - ][ + + ]: 2981 : if( nLeaf != 0 && nLeft != 0 && nRight != 0 )
[ + - ]
847 : : {
848 : : //fdo#41642 Do we need to check full chain upwards for loops ?
849 [ + + ]: 2978 : if (pUpper)
850 : : {
851 [ + - ][ + + ]: 2692 : if (pUpper->aEntry.GetLeaf(STG_CHILD) == nLeaf)
852 : : {
853 : : OSL_FAIL("Leaf node of upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
854 [ + - ][ + - ]: 3 : delete pCur;
855 : : return;
856 : : }
857 : :
858 : 2689 : StgDirEntry *pUpperUpper = pUpper->pUp;
859 [ + + ][ + - ]: 2689 : if (pUpperUpper && pUpperUpper->aEntry.GetLeaf(STG_CHILD) == nLeaf)
[ + + ][ + + ]
860 : : {
861 : : OSL_FAIL("Leaf node of upper-upper StgDirEntry is same as current StgDirEntry's leaf node. Circular entry chain, discarding link");
862 [ + - ][ + - ]: 3 : delete pCur;
863 : : return;
864 : : }
865 : : }
866 : :
867 [ + - ]: 2972 : if( StgAvlNode::Insert
868 [ + + ][ + - ]: 2972 : ( (StgAvlNode**) ( pUpper ? &pUpper->pDown : &pRoot ), pCur ) )
869 : : {
870 : 2972 : pCur->pUp = pUpper;
871 : 2972 : pCur->ppRoot = &pRoot;
872 : : }
873 : : else
874 : : {
875 : : // bnc#682484: There are some really broken docs out there
876 : : // that contain duplicate entries in 'Directory' section
877 : : // so don't set the error flag here and just skip those
878 : : // (was: rIo.SetError( SVSTREAM_CANNOT_MAKE );)
879 [ # # ][ # # ]: 0 : delete pCur;
880 : : return;
881 : : }
882 [ + - ]: 2972 : SetupEntry( nLeft, pUpper );
883 [ + - ]: 2972 : SetupEntry( nRight, pUpper );
884 [ + - ]: 9208 : SetupEntry( nLeaf, pCur );
885 : : }
886 : : }
887 : : }
888 : :
889 : : // Extend or shrink the directory stream.
890 : :
891 : 159 : sal_Bool StgDirStrm::SetSize( sal_Int32 nBytes )
892 : : {
893 : : // Always allocate full pages
894 [ - + ]: 159 : if ( nBytes < 0 )
895 : 0 : nBytes = 0;
896 : :
897 : 159 : nBytes = ( ( nBytes + nPageSize - 1 ) / nPageSize ) * nPageSize;
898 : 159 : return StgStrm::SetSize( nBytes );
899 : : }
900 : :
901 : : // Save the TOC stream into a new substream after saving all data streams
902 : :
903 : 159 : sal_Bool StgDirStrm::Store()
904 : : {
905 [ + - ][ + - ]: 159 : if( !pRoot || !pRoot->IsDirty() )
[ - + ][ - + ]
906 : 0 : return sal_True;
907 [ + - ][ - + ]: 159 : if( !pRoot->StoreStreams( rIo ) )
908 : 0 : return sal_False;
909 : : // After writing all streams, the data FAT stream has changed,
910 : : // so we have to commit the root again
911 [ + - ]: 159 : pRoot->Commit();
912 : : // We want a completely new stream, so fake an empty stream
913 : 159 : sal_Int32 nOldStart = nStart; // save for later deletion
914 : 159 : sal_Int32 nOldSize = nSize;
915 : 159 : nStart = nPage = STG_EOF;
916 : 159 : nSize = nPos = 0;
917 : 159 : nOffset = 0;
918 : : // Delete all temporary entries
919 [ + - ]: 159 : pRoot->DelTemp( sal_False );
920 : : // set the entry numbers
921 : 159 : sal_Int32 n = 0;
922 [ + - ]: 159 : pRoot->Enum( n );
923 [ + - ][ - + ]: 159 : if( !SetSize( n * STGENTRY_SIZE ) )
924 : : {
925 : 0 : nStart = nOldStart; nSize = nOldSize;
926 [ # # ]: 0 : pRoot->RevertAll();
927 : 0 : return sal_False;
928 : : }
929 : : // set up the cache elements for the new stream
930 [ + - ][ - + ]: 159 : if( !Copy( STG_FREE, nSize ) )
931 : : {
932 [ # # ]: 0 : pRoot->RevertAll();
933 : 0 : return sal_False;
934 : : }
935 : : // Write the data to the new stream
936 [ + - ][ - + ]: 159 : if( !pRoot->Store( *this ) )
937 : : {
938 [ # # ]: 0 : pRoot->RevertAll();
939 : 0 : return sal_False;
940 : : }
941 : : // fill any remaining entries with empty data
942 : 159 : sal_Int32 ne = nSize / STGENTRY_SIZE;
943 [ + - ]: 159 : StgEntry aEmpty;
944 [ + - ]: 159 : aEmpty.Init();
945 [ + + ]: 486 : while( n < ne )
946 : : {
947 [ + - ]: 327 : void* p = GetEntry( n++, sal_True );
948 [ - + ]: 327 : if( !p )
949 : : {
950 [ # # ]: 0 : pRoot->RevertAll();
951 : 0 : return sal_False;
952 : : }
953 [ + - ]: 327 : aEmpty.Store( p );
954 : : }
955 : : // Now we can release the old stream
956 [ + - ]: 159 : pFat->FreePages( nOldStart, sal_True );
957 [ + - ]: 159 : rIo.aHdr.SetTOCStart( nStart );
958 [ + - ]: 159 : return sal_True;
959 : : }
960 : :
961 : : // Get a dir entry.
962 : :
963 : 4485 : void* StgDirStrm::GetEntry( sal_Int32 n, sal_Bool bDirty )
964 : : {
965 [ + + ]: 4485 : if( n < 0 )
966 : 3 : return NULL;
967 : :
968 : 4482 : n *= STGENTRY_SIZE;
969 [ - + ][ # # ]: 4482 : if( n < 0 && n >= nSize )
970 : 0 : return NULL;
971 : 4485 : return GetPtr( n, sal_True, bDirty );
972 : : }
973 : :
974 : : // Find a dir entry.
975 : :
976 : 7588 : StgDirEntry* StgDirStrm::Find( StgDirEntry& rStg, const String& rName )
977 : : {
978 [ + + ]: 7588 : if( rStg.pDown )
979 : : {
980 [ + - ]: 7286 : StgEntry aEntry;
981 [ + - ]: 7286 : aEntry.Init();
982 [ + - ][ - + ]: 7286 : if( !aEntry.SetName( rName ) )
983 : : {
984 [ # # ]: 0 : rIo.SetError( SVSTREAM_GENERALERROR );
985 : 0 : return NULL;
986 : : }
987 : : // Look in the directory attached to the entry
988 [ + - ]: 7286 : StgDirEntry aTest( aEntry );
989 [ + - ][ + - ]: 7286 : return (StgDirEntry*) rStg.pDown->Find( &aTest );
[ + - ]
990 : : }
991 : : else
992 : 7588 : return NULL;
993 : : }
994 : :
995 : : // Create a new entry.
996 : :
997 : 970 : StgDirEntry* StgDirStrm::Create
998 : : ( StgDirEntry& rStg, const String& rName, StgEntryType eType )
999 : : {
1000 [ + - ]: 970 : StgEntry aEntry;
1001 [ + - ]: 970 : aEntry.Init();
1002 : 970 : aEntry.SetType( eType );
1003 [ - + ][ + - ]: 970 : if( !aEntry.SetName( rName ) )
1004 : : {
1005 [ # # ]: 0 : rIo.SetError( SVSTREAM_GENERALERROR );
1006 : 0 : return NULL;
1007 : : }
1008 [ + - ]: 970 : StgDirEntry* pRes = Find( rStg, rName );
1009 [ - + ]: 970 : if( pRes )
1010 : : {
1011 [ # # ]: 0 : if( !pRes->bInvalid )
1012 : : {
1013 [ # # ]: 0 : rIo.SetError( SVSTREAM_CANNOT_MAKE );
1014 : 0 : return NULL;
1015 : : }
1016 : : pRes->bInvalid =
1017 : : pRes->bRemoved =
1018 : 0 : pRes->bTemp = sal_False;
1019 : : pRes->bCreated =
1020 : 0 : pRes->bDirty = sal_True;
1021 : : }
1022 : : else
1023 : : {
1024 [ + - ][ + - ]: 970 : pRes = new StgDirEntry( aEntry );
1025 [ + - ][ + - ]: 970 : if( StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, pRes ) )
1026 : : {
1027 : 970 : pRes->pUp = &rStg;
1028 : 970 : pRes->ppRoot = &pRoot;
1029 : : pRes->bCreated =
1030 : 970 : pRes->bDirty = sal_True;
1031 : : }
1032 : : else
1033 : : {
1034 [ # # ]: 0 : rIo.SetError( SVSTREAM_CANNOT_MAKE );
1035 [ # # ][ # # ]: 0 : delete pRes; pRes = NULL;
1036 : : }
1037 : : }
1038 [ + - ]: 970 : return pRes;
1039 : : }
1040 : :
1041 : : // Rename the given entry.
1042 : :
1043 : 0 : sal_Bool StgDirStrm::Rename( StgDirEntry& rStg, const String& rOld, const String& rNew )
1044 : : {
1045 : 0 : StgDirEntry* p = Find( rStg, rOld );
1046 [ # # ]: 0 : if( p )
1047 : : {
1048 : :
1049 [ # # ]: 0 : if( !StgAvlNode::Remove( (StgAvlNode**) &rStg.pDown, p, sal_False ) )
1050 : 0 : return sal_False;
1051 : 0 : p->aEntry.SetName( rNew );
1052 [ # # ]: 0 : if( !StgAvlNode::Insert( (StgAvlNode**) &rStg.pDown, p ) )
1053 : 0 : return sal_False;
1054 : 0 : p->bRenamed = p->bDirty = sal_True;
1055 : 0 : return sal_True;
1056 : : }
1057 : : else
1058 : : {
1059 : 0 : rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1060 : 0 : return sal_False;
1061 : : }
1062 : : }
1063 : :
1064 : : // Move the given entry to a different storage.
1065 : :
1066 : 0 : sal_Bool StgDirStrm::Move( StgDirEntry& rStg1, StgDirEntry& rStg2, const String& rName )
1067 : : {
1068 : 0 : StgDirEntry* p = Find( rStg1, rName );
1069 [ # # ]: 0 : if( p )
1070 : : {
1071 [ # # ]: 0 : if( !StgAvlNode::Move
1072 : 0 : ( (StgAvlNode**) &rStg1.pDown, (StgAvlNode**) &rStg2.pDown, p ) )
1073 : 0 : return sal_False;
1074 : 0 : p->bDirty = sal_True;
1075 : 0 : return sal_True;
1076 : : }
1077 : : else
1078 : : {
1079 : 0 : rIo.SetError( SVSTREAM_FILE_NOT_FOUND );
1080 : 0 : return sal_False;
1081 : : }
1082 : : }
1083 : :
1084 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|