Coverage Report

Created: 2026-05-16 09:25

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