Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */